Why are store notifications not arriving?
Start with the app's notification_status in setup health. It tells you whether nothing has arrived (waiting), something arrived but none of it counted (received), the newest one failed (failing), or all is well (ready). The usual causes are a wrong URL in App Store Connect or Pub/Sub, a bundle ID or package name that does not match the app, a Pub/Sub push token RevenueDot rejects, or notifications about purchases RevenueDot has never seen.
Read the status#
curl -s -H "Authorization: Bearer $SECRET_KEY" \
https://revenuedot.example.com/v2/projects/$PROJECT_ID/setup_healthEach app in apps carries these fields. The dashboard shows the same on the app page.
| Field | Meaning |
|---|---|
notification_url |
The URL to give the store: /v1/notifications/apple/{app_id} or /v1/notifications/google/{app_id} |
notification_status |
ready, failing, received or waiting (below) |
last_notification_at |
The last time a notification was processed for a purchase RevenueDot knows, or was the store's test message |
last_notification_received_at |
The last time anything arrived |
last_notification_error |
The newest failure: at, type and message |
notification_status |
What it means | What to do |
|---|---|---|
waiting |
Nothing has ever arrived for this app | Check the URL in the store console, and that the server is reachable from the internet over HTTPS |
received |
Notifications arrive, but none was for a purchase RevenueDot knows | Normal before the first purchase. Send the store's test notification, or turn on track_new_purchases (below) |
failing |
The newest notification failed, and no later one succeeded | Read last_notification_error.message |
ready |
A notification was processed for a known purchase, or was the store's test message | Nothing |
The logic is in notification-health.ts.
Send a test notification#
- App Store: use Request a Test Notification from the App Store Server API (Apple docs). A valid test turns the app
ready. - Google Play: click Send Test Message where you set up real-time developer notifications in Play Console (Google docs). The test message turns the app
ready.
Causes and fixes#
- The URL is wrong or unreachable. Copy
notification_urlexactly. It must be the public HTTPS address of your server. Behind a proxy, RevenueDot builds it fromX-Forwarded-HostandX-Forwarded-Proto. - App Store Connect only has the production URL. Sandbox purchases need the same URL in the Sandbox Server URL field, and the version must be Version 2 notifications. See Connect the App Store.
- The bundle ID does not match. RevenueDot answers 400 and records "The notification is for bundle id X, not Y." Fix
bundle_idon the app, or point this bundle's notifications at the right app ID. - The Apple app ID does not match. If the app has an
app_apple_idcredential, production notifications for another Apple app ID are refused with 400. - The package name does not match. RevenueDot answers 200 so Pub/Sub stops redelivering, and records "package X does not match the app's Y". The status turns
failing. Fixpackage_name, or use a separate topic per app. - Pub/Sub push authentication fails. If the app has the
pubsub_audiencecredential, every push must carry a Google-signed token for that audience, and, whenpubsub_service_accountis set, from that service account. A missing or wrong token gets HTTP 401 with code 7224. Turn on authentication on the push subscription with the same audience and service account (Google docs), or removepubsub_audience. - The purchases are unknown to RevenueDot. A notification for a purchase no app has posted to
/v1/receiptsis stored but not applied, and the status staysreceived. This is normal in a dual run until users open the app. To create those purchases from notifications, settrack_new_purchases:UseShellcurl -s -X POST https://revenuedot.example.com/v2/projects/$PROJECT_ID/apps/$APP_ID \ -H "Authorization: Bearer $SECRET_KEY" -H "Content-Type: application/json" \ -d '{"app_store":{"track_new_purchases":true}}'play_storeinstead ofapp_storefor a Google Play app. For the App Store, the purchase lands on the customer named by the transaction'sappAccountTokenwhen it matches one. Otherwise it lands on a new anonymous customer until the app posts it. - Google cannot be reached with the service account. Google notifications only carry a purchase token, so RevenueDot reads the purchase from the Play Developer API. A missing or rejected service account makes each notification fail with 500 and Pub/Sub retries it. See Connect Google Play.
What RevenueDot answers, and why#
The stores redeliver anything that is not a 2xx. RevenueDot answers so that a store retries only what could succeed later.
| Store | Answer | When |
|---|---|---|
| App Store | 200 {"ok":true} |
The payload verified, including notifications for purchases RevenueDot does not track |
| App Store | 400 | The payload cannot be verified or belongs to another app: not JSON, no signedPayload, a bad signature, another bundle ID or Apple app ID |
| App Store | 404 | No App Store app has this ID |
| App Store | 500 | RevenueDot itself failed; Apple retries |
| Google Play | 200 with status |
processed, unknown_purchase, ignored (unreadable data or another package), invalid_token or duplicate |
| Google Play | 400, code 7000 | The body is not a Pub/Sub push message |
| Google Play | 401, code 7224 | The Pub/Sub push token is missing or invalid |
| Google Play | 404 | No Google Play app has this ID |
| Google Play | 500 or 503 | A temporary failure at RevenueDot or Google; Pub/Sub retries |
Every notification is stored before it is processed, so you can see it even when it failed. During a dual run, RevenueDot also copies the exact body to the app's notification_forward_url. last_forward in GET /v2/projects/{project_id}/apps/{app_id}/store_settings shows the last forward's HTTP status, where 0 means no answer within 10 seconds. See Dual run.