Subscription cancellation reasons usually reach you before the refund request does, and Google Play hands over far more of them than Apple
Google Play records subscription cancellation reasons from its cancel survey and Apple sends only an expiration code. Here's what each store tells you, which signals point at a coming refund or chargeback, and what each one costs you.

Key takeaways
- Google Play asks subscribers why they're leaving when they cancel, and the answer reaches the developer through canceledStateContext.userInitiatedCancellation.cancelSurveyResult in purchases.subscriptionsv2.
- Google Play's cancel survey has five answers a developer can read: not enough usage, technical issues, cost related, found a better app, and other. Free text in reasonUserInput is set only when the user picks other.
- A SUBSCRIPTION_CANCELED notification from Google Play carries only the purchase token. The cancellation reason has to be fetched with purchases.subscriptionsv2.get, and Google clears every cancellation field if the user restores the subscription.
- Apple's App Store Server API has no cancel survey. Its expirationIntent field gives one of five codes: customer canceled, billing error, no consent to a price increase or offer conversion, product unavailable, or other.
- Apple's DID_CHANGE_RENEWAL_STATUS notification with subtype AUTO_RENEW_DISABLED can mean the App Store turned off auto-renewal after the customer requested a refund, not only that the customer switched it off.
- A cancellation reason never gives a developer a vote on a refund. Only Apple's CONSUMPTION_REQUEST, answered within 12 hours, and Google Play's chargeback review through orders.reviewrefund, answered within 24 hours, ask for evidence.
- A Google Play subscriber who cancels citing technical issues is the one most likely to want money back, and for orders placed after August 3, 2026, a chargeback they win costs the developer the purchase price less Play's service fee plus the bank's fees.
Most subscribers who end up refunded cancel first. That cancellation carries a reason, and the two stores hand you very different amounts of it. Google Play runs a short survey and passes the answer to your server, free text included. Apple sends a numeric code that says how the subscription ended, not why the customer wanted out. Read both properly and you'll see refund requests coming while there's still something you can do about them.
What Google Play tells you about subscription cancellation reasons
When a user cancels in the Play subscriptions center, Google asks them why. The answer lands in the subscription resource you get from purchases.subscriptionsv2.get, under canceledStateContext. That object only exists while the subscription is in the canceled or expired state, and it says who ended the subscription: the user, Google's system, you, or a replacement such as a plan change.
When the user did it, the userInitiatedCancellation object holds two fields. cancelTime says when they canceled. cancelSurveyResult holds the survey answer.
| Survey reason | Google's description | What it often signals |
|---|---|---|
| CANCEL_SURVEY_REASON_NOT_ENOUGH_USAGE | Not enough usage of the subscription | Low usage, so little cost sunk into the account |
| CANCEL_SURVEY_REASON_TECHNICAL_ISSUES | Technical issues while using the app | The strongest refund and chargeback signal |
| CANCEL_SURVEY_REASON_COST_RELATED | Cost related issues | Price sensitivity, sometimes a renewal they didn't expect |
| CANCEL_SURVEY_REASON_FOUND_BETTER_APP | The user found a better app | A competitor won, rarely a dispute |
| CANCEL_SURVEY_REASON_OTHERS | Other reasons | Read reasonUserInput, the user's own words |
| CANCEL_SURVEY_REASON_UNSPECIFIED | Unspecified cancel survey reason | No answer to work with |
The right-hand column is our reading, not Google's. The first two columns come straight from the API reference.
The notification doesn't carry the reason
A canceled subscription triggers a SUBSCRIPTION_CANCELED real-time developer notification, type 3. Google is explicit that these notifications only say the purchase state changed. The payload gives you a purchase token and nothing else useful, so your handler has to call purchases.subscriptionsv2.get to read the survey answer.
The Play Console view
You don't need code to see the totals. Play Console's subscription reports include a Cancellations card that splits churn into voluntary, involuntary and other, and a Cancellation survey responses card for users who chose to answer. Free-text answers from users who picked other can be downloaded as a CSV from the Other responses card. That download needs the View financial data permission, the same one you'd grant a tool that reads your orders.
What Apple tells you, and what it leaves out
Apple runs no cancellation survey that reports back to developers. The closest field is expirationIntent in the signed renewal info, and it describes how a subscription ended, not what the customer thought.
| expirationIntent | Apple's meaning | Matching EXPIRED subtype |
|---|---|---|
| 1 | The customer canceled their subscription | VOLUNTARY |
| 2 | Billing error, such as invalid payment information | BILLING_RETRY |
| 3 | No consent to a price increase or offer conversion that required it | PRICE_INCREASE |
| 4 | The product wasn't available for purchase at renewal | PRODUCT_NOT_FOR_SALE |
| 5 | Some other reason | No subtype |
Code 1 is the only one that describes a choice the customer made, and it says nothing about the motive. A customer who left because the app kept crashing and one who simply finished a project look identical.
The signal hiding in AUTO_RENEW_DISABLED
There's one Apple notification worth a closer look. DID_CHANGE_RENEWAL_STATUS with subtype AUTO_RENEW_DISABLED usually means the customer turned off auto-renewal. Apple's documentation adds a second case: the App Store also turns auto-renewal off after the customer requests a refund.
So on iOS, a renewal-off event can be the first trace of a refund request. If the purchase is a consumable or an auto-renewable subscription, a CONSUMPTION_REQUEST may follow, and from that point you have 12 hours to send consumption data. A renewal-off event that arrives with no visible action in your app is worth treating as a heads-up to get that account's usage data ready.
Apple and Google side by side
| Google Play | Apple App Store | |
|---|---|---|
| Asks the user why | Yes, a cancel survey | No developer-visible survey |
| Where the reason lives | canceledStateContext in purchases.subscriptionsv2 | expirationIntent in renewal info |
| Free text | reasonUserInput, only for other | None |
| Who canceled | User, system, developer or replacement | Inferred from the code and subtype |
| Notification | SUBSCRIPTION_CANCELED, then call get | DID_CHANGE_RENEWAL_STATUS, EXPIRED |
| Lost if the user resubscribes | Yes, cleared on restore | Renewal info reflects the current state |
| Dashboard | Cancellation survey responses card | None for reasons |

Where a cancellation turns into a refund
A cancellation stops the next charge. It doesn't return the last one. Whether money goes back depends on which refund flow the customer reaches next, and most of those flows run without you.
- On Google Play, a buyer can get a refund within 48 hours of purchase on their own, and Google decides it.
- Refunds through Google's or Apple's support are decided by the store.
- Apple's CONSUMPTION_REQUEST asks for your consumption data, with a 12-hour window.
- Google Play's chargeback review asks for evidence through orders.reviewrefund, with a 24-hour window. The bank's decision on a chargeback is final.
A cancellation reason doesn't change any of those rules. What it changes is how early you know which customer is unhappy, and whether your evidence is ready when one of the two flows that ask for it arrives.
What each reason costs you
The survey answer is also a rough guide to what an account has already cost to serve, which is the money a refund leaves you holding.
Technical issues
This is the expensive one. The customer used the product enough to hit problems, so you've paid for the compute behind their sessions, the model API calls their requests made, the storage for whatever they created, and any payout owed to a creator whose content they used. If they then get a refund, the price goes back and those costs stay with you. If they go to their bank on Google Play, an order placed after August 3, 2026 costs you the purchase price less Play's service fee, plus the bank's fees.
Cost related
A price complaint costs you less than it looks, unless it hides an unexpected renewal. That's the classic chargeback story, so check whether the cancel came right after a renewal charge and whether your renewal reminders went out.
Not enough usage
Low usage means low serving costs, so a refund here mostly returns money you didn't spend much to earn. It's also the reason where honest consumption data, if Apple asks for it, shows exactly what the customer did and didn't use.
Found a better app
These customers rarely dispute anything. They left for a competitor. The reason is useful for product decisions and almost never for refund defense.
A routine for reading cancellation reasons
- On every Google Play SUBSCRIPTION_CANCELED, call purchases.subscriptionsv2.get and save canceledStateContext with the purchase token and cancelTime.
- Save reasonUserInput verbatim when the reason is other. It's the only free text either store gives you.
- On every Apple DID_CHANGE_RENEWAL_STATUS with AUTO_RENEW_DISABLED, log the event and pull that account's usage, in case a CONSUMPTION_REQUEST follows.
- On every Apple EXPIRED notification, record the subtype and expirationIntent so voluntary churn and billing failures don't blur together.
- For Google Play cancels citing technical issues, check your error logs for that account and reach out before the customer reaches their bank.
- Keep per-account usage logs, so a CONSUMPTION_REQUEST gets an answer inside 12 hours and a chargeback review inside 24.
Frequently asked questions
- How do I see why a Google Play subscriber canceled?
- Call purchases.subscriptionsv2.get and read canceledStateContext.userInitiatedCancellation.cancelSurveyResult. The reason field holds the survey answer, and reasonUserInput holds free text when the user picked other. Play Console also shows totals in the Cancellation survey responses card.
- Does Apple tell developers why a customer canceled a subscription?
- No. Apple has no developer-visible cancel survey. The expirationIntent field says how the subscription ended, with codes for customer canceled, billing error, no consent to a price increase, product unavailable, or other, but not the customer's motive.
- Does the SUBSCRIPTION_CANCELED notification include the cancellation reason?
- No. Google Play's real-time developer notifications only say the purchase state changed. Fetch the subscription with purchases.subscriptionsv2.get to read the reason, and store it right away, because Google clears cancellation fields if the user restores the subscription.
- Can an Apple AUTO_RENEW_DISABLED notification mean a refund request?
- Yes. Apple's documentation says the AUTO_RENEW_DISABLED subtype covers both a customer turning off auto-renewal and the App Store turning it off after the customer requested a refund.
- Can I revoke access when a subscriber cancels?
- Not on Google Play while they're still entitled. Google says a canceled subscriber keeps access until the end of the billing cycle, and removing access early violates Google Play's subscriptions policy. Use expiryTime to decide when access ends.
Sources and further reading
- Google Play Developer API: purchases.subscriptionsv2
- Google Play Billing: Subscription lifecycle
- Google Play Billing: Real-time developer notifications reference
- Play Console Help: Review in-app subscription performance
- Apple Developer: expirationIntent
- Apple Developer: notificationType
- Apple Developer: Send Consumption Information
- Play Console Help: Updates to refund protection and chargeback cost responsibility
RefundHalt
The refund autopilot for the App Store and Google Play
Keep reading
Excessive refund requests can cost you more than the refunds, because both app stores treat them as a quality signal
Apple's Guideline 5.6.4 names excessive refund requests as a sign an app isn't meeting quality standards, and Google's Distribution Agreement lets it remove new developers' apps on refund rates. Here's what each store says, what removal costs, and how to stay clear.
Verify App Store server notifications before you act on a refund, because anyone can post to your webhook URL
A refund notification is just an HTTPS request until you check its signature. Here's how to verify App Store server notifications and Google Play RTDN messages, what each store signs, and what a forged or wrongly rejected refund costs you.