订阅取消原因通常比退款请求更早到达,而 Google Play 提供的信息远多于 Apple
Google Play 会通过取消调查记录订阅取消原因,Apple 只发送一个到期代码。本文说明两家商店各自告诉你什么,哪些信号预示即将到来的退款或拒付,以及每种情况会让你付出多少成本。

要点
- Google Play 会在订阅者取消时询问离开原因,答案通过 purchases.subscriptionsv2 中的 canceledStateContext.userInitiatedCancellation.cancelSurveyResult 传给开发者。
- Google Play 的取消调查有五个开发者可读的答案:使用不够多、技术问题、费用相关、找到了更好的应用,以及其他。只有用户选择“其他”时,reasonUserInput 中才会有自由文本。
- Google Play 的 SUBSCRIPTION_CANCELED 通知只携带购买令牌。取消原因必须通过 purchases.subscriptionsv2.get 获取,而且如果用户恢复订阅,Google 会清空所有取消相关字段。
- Apple 的 App Store Server API 没有取消调查。它的 expirationIntent 字段给出五个代码之一:客户取消、账单错误、未同意涨价或优惠转换、产品不可用,或其他。
- Apple 的 DID_CHANGE_RENEWAL_STATUS 通知若带有 AUTO_RENEW_DISABLED 子类型,可能表示客户申请退款后 App Store 关闭了自动续订,而不只是客户自己关掉了它。
- 取消原因从不会让开发者对退款拥有发言权。只有 Apple 的 CONSUMPTION_REQUEST(需在 12 小时内回复)和 Google Play 通过 orders.reviewrefund 发起的拒付审查(需在 24 小时内回复)会要求你提供证据。
- 因技术问题取消的 Google Play 订阅者最有可能想要退款。对于 2026 年 8 月 3 日之后下的订单,若对方拒付成功,开发者将损失购买价格减去 Play 服务费的部分,外加银行收取的费用。
大多数最终获得退款的订阅者都会先取消订阅。取消会附带一个原因,而两家商店给你的信息量差别很大。Google Play 会做一个简短的调查,并把答案(包括自由文本)传给你的服务器。Apple 只发送一个数字代码,说明订阅是怎么结束的,而不是客户为什么想离开。把两者都读懂,你就能在还来得及采取行动时,提前看到退款请求的到来。
Google Play 告诉你哪些订阅取消原因
用户在 Play 订阅中心取消时,Google 会询问原因。答案出现在你通过 purchases.subscriptionsv2.get 获取的订阅资源中,位于 canceledStateContext 之下。这个对象只在订阅处于已取消或已过期状态时存在,它会说明是谁结束了订阅:用户、Google 的系统、你,或者是替换操作(例如更换套餐)。
如果是用户取消的,userInitiatedCancellation 对象会包含两个字段。cancelTime 表示取消时间,cancelSurveyResult 保存调查答案。
| 调查原因 | Google 的描述 | 它通常意味着什么 |
|---|---|---|
| CANCEL_SURVEY_REASON_NOT_ENOUGH_USAGE | 订阅使用不够多 | 使用量低,账户上投入的成本很少 |
| CANCEL_SURVEY_REASON_TECHNICAL_ISSUES | 使用应用时遇到技术问题 | 最强烈的退款和拒付信号 |
| CANCEL_SURVEY_REASON_COST_RELATED | 费用相关问题 | 对价格敏感,有时是意料之外的续订 |
| CANCEL_SURVEY_REASON_FOUND_BETTER_APP | 用户找到了更好的应用 | 竞争对手胜出,很少引发争议 |
| CANCEL_SURVEY_REASON_OTHERS | 其他原因 | 查看 reasonUserInput,即用户自己的原话 |
| CANCEL_SURVEY_REASON_UNSPECIFIED | 未指定的取消调查原因 | 没有可用的答案 |
最右一列是我们的解读,不是 Google 的说法。前两列直接来自 API 参考文档。
通知本身不包含原因
订阅被取消会触发 SUBSCRIPTION_CANCELED 实时开发者通知,类型为 type 3。Google 明确表示,这类通知只告诉你购买状态发生了变化。负载中只有一个购买令牌,没有其他有用信息,所以你的处理程序必须调用 purchases.subscriptionsv2.get 才能读取调查答案。
Play Console 中的视图
查看汇总数据不需要写代码。Play Console 的订阅报告中有一张“取消”卡片,把流失分为自愿、非自愿和其他三类,还有一张“取消调查回复”卡片,展示选择作答的用户。选择“其他”的用户所填写的自由文本,可以从“其他回复”卡片下载为 CSV。下载需要 View financial data 权限,也就是你授予读取订单的工具的同一项权限。
Apple 告诉你什么,又省略了什么
Apple 没有任何会向开发者反馈结果的取消调查。最接近的字段是签名续订信息中的 expirationIntent,它描述的是订阅如何结束,而不是客户怎么想。
| expirationIntent | Apple 的含义 | 对应的 EXPIRED 子类型 |
|---|---|---|
| 1 | 客户取消了订阅 | VOLUNTARY |
| 2 | 账单错误,例如付款信息无效 | BILLING_RETRY |
| 3 | 未同意需要确认的涨价或优惠转换 | PRICE_INCREASE |
| 4 | 续订时该产品无法购买 | PRODUCT_NOT_FOR_SALE |
| 5 | 其他原因 | 无子类型 |
代码 1 是唯一描述客户主动选择的代码,但它完全不说明动机。因为应用频繁崩溃而离开的客户,和只是刚好完成了一个项目的客户,看起来一模一样。
藏在 AUTO_RENEW_DISABLED 里的信号
有一个 Apple 通知值得仔细看。带有 AUTO_RENEW_DISABLED 子类型的 DID_CHANGE_RENEWAL_STATUS 通常表示客户关闭了自动续订。Apple 的文档还补充了第二种情况:客户申请退款后,App Store 也会关闭自动续订。
因此在 iOS 上,一个关闭续订的事件可能是退款请求的第一个迹象。如果购买的是消耗型项目或自动续期订阅,随后可能会收到 CONSUMPTION_REQUEST,从那一刻起你有 12 小时发送消耗数据。如果关闭续订事件到来时,你的应用里看不到任何相应操作,就值得把它当作提醒,提前准备好该账户的使用数据。
Apple 与 Google 对比
| Google Play | Apple App Store | |
|---|---|---|
| 是否询问用户原因 | 是,有取消调查 | 没有开发者可见的调查 |
| 原因存放位置 | purchases.subscriptionsv2 中的 canceledStateContext | 续订信息中的 expirationIntent |
| 自由文本 | reasonUserInput,仅限“其他” | 无 |
| 谁取消的 | 用户、系统、开发者或替换操作 | 根据代码和子类型推断 |
| 通知 | SUBSCRIPTION_CANCELED,然后调用 get | DID_CHANGE_RENEWAL_STATUS、EXPIRED |
| 用户重新订阅后是否丢失 | 是,恢复时被清空 | 续订信息反映当前状态 |
| 控制台 | “取消调查回复”卡片 | 没有原因相关视图 |

取消在什么时候变成退款
取消会停止下一次扣款,但不会退回上一次的款项。钱能否退回,取决于客户接下来进入哪个退款流程,而这些流程大多不需要你参与。
- 在 Google Play 上,买家可以在购买后 48 小时内自行获得退款,由 Google 决定。
- 通过 Google 或 Apple 客服申请的退款,由商店决定。
- Apple 的 CONSUMPTION_REQUEST 会向你索取消耗数据,回复时限为 12 小时。
- Google Play 的拒付审查通过 orders.reviewrefund 向你索取证据,回复时限为 24 小时。银行对拒付的裁决是最终结果。
取消原因不会改变以上任何规则。它改变的是你能多早知道哪位客户不满意,以及当两个需要证据的流程之一到来时,你的证据是否已经准备好。
每种原因会让你付出多少
调查答案也能粗略反映一个账户已经花掉了多少服务成本,这正是退款后由你承担的那部分钱。
技术问题
这是代价最高的一种。客户用得足够多才会遇到问题,所以你已经为他们的会话支付了计算资源、为他们的请求支付了模型 API 调用、为他们创建的内容支付了存储,还可能要向他们使用过其内容的创作者支付分成。如果他们随后获得退款,货款会退回,而这些成本仍由你承担。如果他们在 Google Play 上找银行拒付,2026 年 8 月 3 日之后下的订单会让你损失购买价格减去 Play 服务费的部分,外加银行收取的费用。
费用相关
价格投诉的实际成本比看起来要低,除非背后藏着一次意料之外的续订。那正是典型的拒付情节,所以要检查取消是否紧跟在一次续订扣款之后,以及你的续订提醒是否已经发出。
使用不够多
使用量低意味着服务成本低,所以这类退款退回的大多是你没花多少成本就赚到的钱。如果 Apple 要求提供消耗数据,诚实的数据也能清楚展示客户用了什么、没用什么。
找到了更好的应用
这些客户很少提出争议,他们只是转投了竞争对手。这个原因对产品决策有用,但几乎帮不上退款防御。
读取取消原因的日常流程
- 每次收到 Google Play 的 SUBSCRIPTION_CANCELED,都调用 purchases.subscriptionsv2.get,并把 canceledStateContext 连同购买令牌和 cancelTime 一起保存。
- 当原因是“其他”时,原样保存 reasonUserInput。这是两家商店中唯一会给你的自由文本。
- 每次收到 Apple 带有 AUTO_RENEW_DISABLED 的 DID_CHANGE_RENEWAL_STATUS,都记录该事件并调出该账户的使用数据,以防随后收到 CONSUMPTION_REQUEST。
- 每次收到 Apple 的 EXPIRED 通知,都记录子类型和 expirationIntent,避免把自愿流失和账单失败混在一起。
- 对于以技术问题为由取消的 Google Play 订阅,检查该账户的错误日志,并在客户找银行之前主动联系对方。
- 保留每个账户的使用日志,这样 CONSUMPTION_REQUEST 能在 12 小时内得到回复,拒付审查能在 24 小时内得到回复。
常见问题解答
- 如何查看 Google Play 订阅者取消的原因?
- 调用 purchases.subscriptionsv2.get,读取 canceledStateContext.userInitiatedCancellation.cancelSurveyResult。其中的原因字段保存调查答案,用户选择“其他”时,reasonUserInput 保存自由文本。Play Console 也会在“取消调查回复”卡片中显示汇总数据。
- Apple 会告诉开发者客户为什么取消订阅吗?
- 不会。Apple 没有开发者可见的取消调查。expirationIntent 字段说明订阅如何结束,代码分别对应客户取消、账单错误、未同意涨价、产品不可用或其他,但不包含客户的动机。
- SUBSCRIPTION_CANCELED 通知包含取消原因吗?
- 不包含。Google Play 的实时开发者通知只告诉你购买状态发生了变化。你需要用 purchases.subscriptionsv2.get 获取订阅来读取原因,并立即保存,因为如果用户恢复订阅,Google 会清空取消相关字段。
- Apple 的 AUTO_RENEW_DISABLED 通知可能意味着退款请求吗?
- 可能。Apple 的文档说明,AUTO_RENEW_DISABLED 子类型既包括客户自己关闭自动续订,也包括客户申请退款后 App Store 关闭自动续订。
- 订阅者取消后,我可以撤销其访问权限吗?
- 在 Google Play 上,只要用户仍有权使用就不可以。Google 表示,已取消的订阅者在当前计费周期结束前保留访问权限,提前撤销访问违反 Google Play 的订阅政策。请根据 expiryTime 决定访问何时结束。
来源和延伸阅读
- 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
App Store 和 Google Play 退款自动驾驶
继续阅读
过多的退款申请,代价可能远超退款本身,因为两大应用商店都把它当作质量信号
Apple 审核指南 5.6.4 把过多的退款申请列为应用未达质量标准的迹象,Google 的开发者分发协议也允许它依据退款率下架新开发者的应用。本文梳理两家商店的原文、下架的真实成本,以及如何避开风险。
在处理退款之前先验证 App Store 服务器通知,因为任何人都能向你的 webhook URL 发送请求
在检查签名之前,一条退款通知只不过是一个 HTTPS 请求。本文介绍如何验证 App Store 服务器通知和 Google Play RTDN 消息、每个商店签名的内容,以及伪造的退款或被错误拒绝的退款会让你付出什么代价。