What does the App Store notification DID_RENEW with subtype BILLING_RECOVERY mean?
The App Store subtype BILLING_RECOVERY on DID_RENEW means a subscription that failed to renew has now renewed after the App Store retried billing.
Quick facts#
| Notification | notificationType DID_RENEW, subtype BILLING_RECOVERY |
| Where | App Store Server Notifications v2 (responseBodyV2DecodedPayload) |
| Sent by | The App Store server, to your notification URL |
| What Apple says | The expired subscription that previously failed to renew has successfully renewed. |
What triggers it#
- The renewal charge failed earlier and the subscription entered the billing retry period. Apple keeps retrying for up to 60 days, or until the customer fixes billing or cancels.
- The customer updated their payment method, or the retry succeeded on its own.
- If the subscription had a billing grace period and recovered inside it, the customer never lost access.
What your server should do#
- Verify the
signedPayloadwith Apple's App Store Server Library (SignedDataVerifier.verifyAndDecodeNotification) before you act on anything in it. - Restore access, and extend it to the new
expiresDate. - Remove any in-app message that asked the customer to fix their payment method.
- Do not count this as a new subscriber: it is the same subscription, recovered.
- Answer HTTP 200 (any code from 200 to 206) once you have stored the notification. Apple retries other answers five times, at 1, 12, 24, 48 and 72 hours after the previous attempt, so make your handler safe to run twice (the payload carries a
notificationUUID).
Example#
JavaScript
// Run on the decoded payload, after you verified Apple's signature.
const notification = {"notificationType":"DID_RENEW","subtype":"BILLING_RECOVERY","data":{"environment":"Sandbox","bundleId":"com.example.app"}};
function decide(n) {
const recovered = n.notificationType === "DID_RENEW" && n.subtype === "BILLING_RECOVERY";
return recovered ? "restore access, remove the billing message, no new subscriber" : "ignore";
}
console.log(decide(notification));Run-checked: npm run check:snippets runs this snippet with Node and compares its output with restore access (checked 2026-10-03).
How RevenueDot handles it#
RevenueDot verifies Apple's signature, checks the bundle ID, stores the notification and answers 200. A purchase RevenueDot has not seen is applied only when the app's Track new purchases from server-to-server notifications setting is on. The stored purchase loses its billing issue and takes the new period, so RevenueDot sends RENEWAL and no UNCANCELLATION: the diff code skips UNCANCELLATION when a renewal recovers a billing issue.
Related#
- What does the App Store notification DID_RENEW mean?
- What does the App Store notification DID_FAIL_TO_RENEW mean?
- What does the App Store notification GRACE_PERIOD_EXPIRED mean?
- Which webhook events does RevenueDot send?
- Set up App Store notifications in RevenueDot
Source#
- Apple: notificationType
- Apple: subtype
- Apple: Responding to App Store Server Notifications
- Apple: app-store-server-library-node (SignedDataVerifier)
- RevenueDot server: stores/apple/notifications.ts (how the notification is applied)
- RevenueDot server: stores/apple/map.ts (how the transaction becomes a stored purchase)
- RevenueDot core: events.ts (how a change becomes a webhook event)
Checked: 2026-10-03