所有文章
Deep dive阅读需 8 分钟

订阅退款与一次性退款的运作方式不同,而你能有多少话语权由所在的商店决定

订阅退款撤销的是整个计费周期,而不是单笔销售。在 App Store 上,由 Apple 做决定,你的服务器只会得知结果。在 Google Play 上,你自己选择全额退款还是按比例退款。下面介绍每家商店如何处理订阅退款,以及一笔退款会让你付出什么代价。

一张挂历上圈出了每月循环的计费日期,一张信用卡横放其上,几枚硬币被部分推回,说明订阅退款如何撤销一个计费周期

要点

  • 订阅退款和一次性退款不一样。被撤销的单位是一个计费周期,而且在两家商店上,对订阅订单退款都会取消订阅并切断访问权限。
  • 在 App Store 上,每一笔订阅退款都由 Apple 决定。客户在 reportaproblem.apple.com 向 Apple 提出申请,Apple 做出裁定,你的服务器只能通过 REFUND、REFUND_DECLINED 或 REFUND_REVERSED 通知得知结果。
  • 当 Apple 批准订阅退款时,该交易会获得一个 revocationDate,并且订阅的 autoRenewStatus 被设为 false,因此它会停止续订,客户失去访问权限。
  • Apple 按已过去的时间来衡量自动续订订阅,而不是按你的用量指标。某人在年度套餐中已过去六个月,就消耗了约 50%,GRANT_PRORATED 是你能为它表达的唯一退款偏好。
  • 在 Google Play 上,退款由你自己发起。purchases.subscriptionsv2.revoke 接受 fullRefund(退还整个当前周期)或 proratedRefund(只退还未使用的权益时间)。
  • 你在 Google Play 上对哪一笔订单退款会改变结果。对最近一笔订单退款,订阅会立即被移除,未来的续订被取消。对较早的一笔订单退款,订阅仍然有效。
  • Google Play 的限制很严格。超过 three years 的订单无法退款,部分退款仅适用于 March 2018 之后下的订单,不适用于付费应用,而且退款一经发放就无法撤销。

对订阅退款和对单笔应用内购买退款,感觉像是按同一个按钮。其实不是。一次性购买是你退还的一笔扣款。订阅是带有实时权益的循环扣款,因此订阅退款必须回答两个一次性退款从不会问的问题:当前周期你退还多少,以及订阅是否继续运行。App Store 和 Google Play 对这两个问题给出的答案正好相反,而这种差异决定了你能掌控多少,以及有多少钱离开你的账户。

这是简明的对照图。在 App Store 上,Apple 拥有决定权,你事后才得知。在 Google Play 上,你掌握控制权,自己选择全额还是按比例。本文会走一遍两家商店、每家使用的确切字段和通知、当订阅已多次计费时你该处理哪一笔订单,以及一旦把你已经花掉的算力算进去,订阅退款实际会让你付出什么。

订阅退款与一次性退款有何不同

两家商店都把退款视为撤销某一笔特定订单。对于一次性购买,只有一笔订单,因此没什么可选。订阅是一串订单,每个计费周期一笔,并附带一项处于激活或未激活状态的权益。正是这种结构,让订阅退款带有一次性退款所没有的选择。

被撤销的单位是一个计费周期

当你为订阅退款时,你并不是在退还一辈子的付款。你处理的是这串订单中的一笔,通常是最近的一笔,也就是单个计费周期。全额退款退还整个周期。按比例退款只退还客户尚未使用的那部分周期。无论哪种,你撤销的销售都是一个周期的价格,而不是客户在应用上的全部历史。

谁来决定才是真正的区别

更鲜明的分野在于权限。在 App Store 上,是否发生订阅退款完全由 Apple 决定,你顶多提供一些背景信息。在 Google Play 上,由你决定,API 严格照你说的执行。对客户来说结果一样,但开发者所处的位置完全不同,这也改变了你要为处理各自情况而构建的东西。

App Store 如何处理订阅退款

在 Apple 这边,你是个拿着小话筒的旁观者。客户与 Apple 沟通,Apple 做出裁定,你的服务器读取结果。

客户向 Apple 申请,由 Apple 裁定

App Store 客户在 reportaproblem.apple.com 申请订阅退款,而不是找你。Apple 依据自己的信号审核请求并决定。你不批准它,也没有哪个 Apple 控制台按钮能让你像在 Google Play 那样发放订阅退款。Apple 自购买起最多有 90 days 的时间来受理审核请求,而一旦它做出裁定,决定权就在 Apple。

退款到达时你的服务器会看到什么

你通过 App Store Server Notifications V2 得知结果。已批准的退款会以 REFUND 通知到达,该交易现在带有一个标记访问权应何时结束的 revocationDate。订阅的 autoRenewStatus 被设为 false,因此它停止续订,客户失去权益。如果 Apple 拒绝了请求,你会收到 REFUND_DECLINED;如果 Apple 后来撤销了它已经批准的退款,你会收到 REFUND_REVERSED,订阅会恢复。你的工作是读取这些通知并切换访问权,而不是投票。

通知含义你要做什么
REFUNDApple 批准了退款;该交易获得一个 revocationDate撤销访问权,将 autoRenewStatus 视为 false
REFUND_DECLINEDApple 拒绝了客户的退款请求一切不变;订阅继续有效
REFUND_REVERSEDApple 撤销了它已批准的退款恢复访问权和这笔销售

Apple 按时间而非你的指标来衡量订阅

对于消耗型商品,你可以通过消耗数据告诉 Apple 使用了多少。对于自动续订订阅,Apple 会自行根据已过去的时间计算消耗。Apple 自己的例子是一位在年度订阅中已过去六个月的客户,已消耗其中约 50%。所以你的用量指标并不决定订阅的这个数字。你唯一能拉动的杠杆是 refundPreference,而对于订阅,相关的值是 GRANT_PRORATED,它表示你希望 Apple 只退还未使用的部分。这是一种偏好,不是决定。仍然由 Apple 裁定。

Google Play 如何处理订阅退款

Google Play 把决定权交到你手里。有一个 Play Console 界面和一组 API 方法,让你直接为订阅退款、选择退多少,并决定是否结束访问权。这与 Apple 的姿态正好相反,也伴随着你必须做对的选择。

全额还是按比例,由你决定

当前退款并撤销订阅的方式是 purchases.subscriptionsv2.revoke,调用时带上一个 revocationContext。传入 fullRefund,Google Play 会终止订阅并退还当前计费周期的全部金额。传入 proratedRefund,Google Play 会终止订阅,只退还剩余的、未使用的权益时间。较旧的方法只能做全额退款,这正是使用较新方法的理由。按比例撤销是一个两行的请求体,一个包裹着 proratedRefund 的 revocationContext,你把它换成 fullRefund 就能退整个周期。任何一种撤销都会触发一条 SUBSCRIPTION_REVOKED 实时开发者通知,这就是你切断访问权的信号。

你对哪一笔订单退款会改变结果

Google Play 还允许你通过 orders.refund 在不撤销的情况下为订单退款,这里有一条微妙的规则会起作用。如果你对订阅中最近的一笔订单退款,该订单被退款,订阅会立即被移除,未来的续订被取消。如果你对这串订单中较早的一笔退款,那笔订单被退款,但订阅仍然有效。在 orders.refund 上,revoke 参数只在订单是最近一笔时才会撤销,因此要结束一个有效订阅的访问权,你应改用 purchases.subscriptionsv2.revoke。

操作被退款的订单订阅会怎样
对最近一笔订单退款最新的计费周期立即移除,未来续订被取消
对较早的一笔订单退款过去的一个计费周期仍然有效,继续续订
subscriptionsv2.revoke,全额或按比例当前周期立即终止,发送 SUBSCRIPTION_REVOKED
两部智能手机并排放在深色桌面上,显示着订阅付款界面,两者之间是一小堆退回的硬币,说明 App Store 和 Google Play 上全额退款与按比例退款的区别

让开发者栽跟头的那些限制

有几个硬性的边界会绊住人,而且没有一个位于顺利路径上。

  • 超过 three years 的订单根本无法退款。
  • 部分退款仅适用于 March 2018 之后下的订单,且永远不适用于付费应用购买。
  • 你可以在一笔订单上叠加多次部分退款,但总额不能达到或超过订单金额。
  • 退款一经发放就无法撤销。

Google Play 通常在一天内返回退款决定,不过也可能长达四天。当退款需要反映到你自己的访问控制中时,要为这段延迟做好准备。

订阅退款实际会让你付出什么

被退还的扣款从来都不是全部账单。订阅订单被交付的那一刻,你就已经为它花掉了真金白银:算力、模型 API 调用、存储,以及与该周期挂钩的任何分成支付。退款退还的是销售额。它不退还你为提供服务所花掉的东西。对订阅而言,损失的形态取决于你发放哪种退款。

全额还是按比例,是一个关于钱的决定

在 Google Play 上,全额退款退回整个当前周期,因此你损失整个周期的收入,同时保留你在该周期已经产生的每一笔成本。按比例退款只退回未使用的那一小块,因此你保留客户实际拥有的那部分收入。如果客户在一个 10 美元的月度套餐中已过完 60%,全额退款会让你损失整整 10 美元,而按比例退款只让你损失约 4 美元,即未使用的 40%。Apple 在确定订阅退款额度时,在它那一侧也采用同样的按已过时间计算的逻辑。

退款类型客户拿回什么你保留什么
全额退款整个当前周期,10.00本周期一无所留
按比例退款未使用的 40%,约 4.00已使用的 60%,约 6.00

那些永远回不来的成本

无论你发放哪种退款,该周期的下游成本都已经没了。你付费的一次模型 API 调用、你渲染的一段视频、你预置的存储、你已经发出的一笔创作者分成,当订阅退款结清时,这些都不会逆转。按客户实际使用的量来确定退款额度,永远不要按他们订阅了多久,因为已过去的时间不是成本,两家商店也都不把它当成成本。而且在 August 3, 2026 之后,如果一笔 Google Play 订阅扣款以银行拒付而非你发放的退款收场,你还要承担银行的拒付费用,而先由你主动发放退款则可以避免这笔费用。

如何在无人盯守的情况下处理订阅退款

这一切都不应该等着某人去看仪表盘。通知在任何时刻都会到达,访问权应在退款落地的那一刻切换,而不是等到第二天早上。

读取结果并在两家商店上撤销访问权

在 App Store 上,监听 REFUND、REFUND_DECLINED 和 REFUND_REVERSED,并利用 revocationDate 和 autoRenewStatus 自动结束或恢复访问权。在 Google Play 上,监听 SUBSCRIPTION_REVOKED 和已作废购买的信号,做同样的事。这就是 RefundHalt 为你运行的管道:通知进来,匹配账户,切换访问权,无人介入。

在争议发生前就备好证据

那些确实需要你输入的狭窄窗口,Apple 针对消耗型商品的 12 小时 CONSUMPTION_REQUEST 和 Google Play 的 24 小时拒付审核,只有在证据已经存在时才有用。给每一笔购买附上一个账户标识符,在 Apple 上是一个 appAccountToken UUID,在 Google Play 上是一个 64 characters 或更短的哈希 setObfuscatedAccountId,并记录每个付费周期交付了什么。这样当令牌到达时,案件已经建好,在窗口内作答就成了机器的活儿,而不是一场手忙脚乱。

常见问题解答

为订阅退款和为一次性购买退款不一样吗?
不一样。一次性购买是你退还的单笔订单。订阅是一串带有实时权益的订单,因此订阅退款作用于一个计费周期,同时还决定订阅是否继续运行。在 App Store 和 Google Play 上,对订阅订单退款都会取消订阅并结束访问权,而且你通常可以在退还整个周期还是只退未使用部分之间选择。
开发者能直接发放 App Store 订阅退款吗?
不能。App Store 订阅退款由 Apple 决定。客户在 reportaproblem.apple.com 提出申请,Apple 做出裁定,你通过 REFUND、REFUND_DECLINED 或 REFUND_REVERSED 通知得知结果。你能提供的唯一输入是一个 refundPreference,比如 GRANT_PRORATED,而 Apple 把它当作偏好,不是决定。
如何在 Google Play 上发放按比例的订阅退款?
调用 purchases.subscriptionsv2.revoke,将 revocationContext 设为 proratedRefund。Google Play 会终止订阅,只退还剩余的、未使用的权益时间,然后发送一条 SUBSCRIPTION_REVOKED 通知。改为传入 fullRefund 则退还整个当前周期。较旧的撤销方法只能做全额退款。
为什么我的 Google Play 退款没有取消订阅?
因为你退款的是这串订单中较早的一笔。在 Google Play 上,对最近一笔订单退款会立即移除订阅并取消未来的续订,但对较早的一笔订单退款只会退还那个周期,而订阅仍然有效。orders.refund 上的 revoke 参数只在订单是最新一笔时才撤销,所以要结束一个有效订阅,请使用 purchases.subscriptionsv2.revoke。
订阅订单最早可以追溯到多久前退款?
在 Google Play 上,超过 three years 的订单无法退款。部分退款仅适用于 March 2018 之后下的订单,且永远不适用于付费应用,退款一经发放也无法撤销。在 App Store 上,Apple 自购买起最多受理 90 days 的客户退款请求,并各自做出裁定。

来源和延伸阅读

RefundHalt

App Store 和 Google Play 退款自动驾驶

继续阅读

下一项退款请求已经在路上。

读完又一封关于未能申辩退款的支持邮件所需的时间,足够您完成 RefundHalt 设置。