Answers · Mobile

What happens when someone cancels a subscription in the App Store?

Apple sends a server notification to the endpoint you registered, and your app updates the local Subscription row so entitlement ends at the period they paid for. If that endpoint is not wired, the cancellation happens entirely on Apple's side and your app keeps granting access forever, with nothing reporting it.

The endpoints exist already

bin/rails routes | grep webhooks

config/routes.rb declares the App Store and Play notification endpoints alongside the Stripe one. Register the App Store URL in App Store Connect and the Play one in the Play Console, or they are routes nothing ever calls.

What arrives

Renewals, cancellations, billing retries, refunds and grace periods, each as a notification type. They are verified through app/adapters/app_store.rb and app/adapters/play_store.rb for the same reason the Stripe webhook verifies its signature: the endpoint is public and unauthenticated by necessity.

The handler writes to the same Subscription row a Stripe cancellation would, so entitlement logic is unchanged.

Cancellation is not immediate

Both stores let the subscriber keep access until the end of the period they already paid for, then stop renewing. So the correct behavior is to keep entitlement until the expiry date in the notification, not to revoke on receipt.

Revoking immediately is the wrong reading and it produces angry support email from people who paid for the month.

Refunds are different

A store-issued refund does mean immediate revocation. Both platforms send a distinct notification for it, and treating it like a cancellation leaves someone with free access to a month they were paid back for.

Check for the silent version of this bug

bin/kamal app exec --interactive --reuse 'bin/rails console'
Subscription.where(status: "active").where("updated_at < ?", 90.days.ago).count

Active subscriptions that no notification has touched in three months are either genuinely long-lived or evidence that no notification is arriving at all. On a monthly plan, the second one is the answer.

Test it before launch

Both platforms have sandbox environments where subscription periods are compressed to minutes, so a full renew and cancel cycle takes an afternoon rather than two months. That is the only way to see these notifications before real customers generate them.

Related questions

Do I have to use Apple's in-app purchase for subscriptions?

Inside the app, yes, for anything digital your users consume in the app. Apple takes 15 to 30 percent and rejects apps that link out to an external checkout for it. On the web, in a browser, you keep using Stripe. The same account can be entitled by either path.

How do I tell if a request came from the native mobile app?

Use the hotwire_native_app? helper in app/controllers/application_controller.rb. The shells append "Hotwire Native" to the user agent, and the helper reads it. That check is how you show the native purchase trigger in the app and the Stripe button on the web, which the store rules require you to get right.

How does a native in-app purchase activate access on my server?

The app completes the purchase natively, then posts the receipt to /iap/purchase. Your server verifies it with Apple or Google through the store adapters and calls the same Subscription.activate! a Stripe checkout would. One entitlement row, two possible ways of paying for it.