跳到正文
返回博客
·约 3 分钟读完

App Store 退款:REFUND 通知与消费信息请求

App Store 退款如何通知开发者:REFUND、REFUND_DECLINED、REFUND_REVERSED 和 CONSUMPTION_REQUEST,12 小时内回复消费信息、退款历史查询和收回权限。

用户申请退款找的是 Apple,不是你。由 Apple 决定是否退款,你的服务器通过 App Store Server Notifications V2 得知结果:用户申请退款、Apple 想要你的数据时,会收到 CONSUMPTION_REQUEST;退款批准后收到 REFUND,被拒绝收到 REFUND_DECLINED,已批准的退款后来被撤销则收到 REFUND_REVERSED。你可以用 Send Consumption Information 接口回复消费信息请求,但前提是用户同意,而且 Apple 要求"within 12 hours"(12 小时内)。退款完成后,交易会带上 revocationDate,这就是你收回权限的依据。如果漏收了通知,Get Refund History 能查到某位用户所有已批准的退款。

用户怎么申请退款

Apple 列出了几种途径,例如联系 Apple 客服("Contacting Apple Customer Support and asking for a refund")、登录 Apple 的自助网站 reportaproblem.apple.com,或向支付方式的发卡机构申请("Asking their payment method issuer for a refund")。App 也可以用 StoreKit 的 beginRefundRequest 方法在 App 内发起退款申请,但申请仍然交给 Apple,由 Apple 决定。

相关通知

以下通知都会发到你在 App Store Connect 里设置的 App Store Server Notifications V2 地址。如果一直收不到,见 App Store 服务器通知收不到怎么办。

notificationTypeApple 的说明怎么做
CONSUMPTION_REQUEST用户"initiated a refund request"(发起了退款申请),App Store 请你提供消费数据。用户同意过的,在 12 小时内调用 Send Consumption Information;没同意的,不要回复。
REFUNDApp Store 已成功退款,适用于消耗型、非消耗型、续期订阅(auto-renewable)和非续期订阅。收回这笔交易给用户的内容。
REFUND_DECLINED"the App Store declined a refund request."(App Store 拒绝了退款申请。)不需要撤回什么。
REFUND_REVERSEDApp Store 因用户提出的争议,撤销了之前批准的退款。"If your app revoked content or services as a result of the related refund, it needs to reinstate them."(如果因为退款收回过内容或服务,需要恢复。)
REVOKE用户通过家人共享(或机构分配)获得的购买"is no longer available"(不再可用),包括购买者获得退款的情况。收回该家庭成员的权限。

Apple 通知列表里还有三点相关说明:

  • 用户在你的 App 里通过退款申请 API 发起退款时,Apple 会关掉订阅的续订,你会收到 DID_CHANGE_RENEWAL_STATUS,子类型为 AUTO_RENEW_DISABLED。
  • Apple 的用例表把 REFUND_DECLINED 描述为针对"the customer initiated in the app, using the request refund API"(用户在 App 内通过退款申请 API 发起)的退款。Apple 没有说明 App 外发起的申请被拒时是否也会发送。
  • 续期订阅收到 REFUND_REVERSED 时,"the renewal date remains unchanged"(续期日期不变)。

通知内容是签名的 JWS。调试时可以粘贴到 App Store JWS 解码器里查看,解码只在浏览器中进行。

哪些商品会收到 CONSUMPTION_REQUEST

截至 2026 年 11 月 1 日,Apple 的几个页面说法不完全一致。notificationType 参考页和 consumptionRequestReason 页说的是"a consumable In-App Purchase or auto-renewable subscription"(消耗型 App 内购买或续期订阅)。Send Consumption Information 页面则说用户对"any product type (consumable, non-consumable, non-renewing subscription, or auto-renewable subscription)"(任何商品类型)发起退款时都会发送,请求体页面也说"You can provide consumption information for any type of product"(任何商品类型都可以提供消费信息)。实际处理时,你卖的每种商品类型都应能处理这个通知。

通知里带有 consumptionRequestReason,即用户填写的退款原因:

值含义
UNINTENDED_PURCHASE用户并非有意购买。
FULFILLMENT_ISSUE用户在收到或使用购买内容时遇到问题。
UNSATISFIED_WITH_PURCHASE用户对购买内容不满意。
LEGAL用户基于法律原因申请退款。
OTHER其他原因。

Send Consumption Information

PUT https://api.storekit.apple.com/inApps/v2/transactions/consumption/{transactionId} (沙盒环境:api.storekit-sandbox.apple.com)

transactionId 用 CONSUMPTION_REQUEST 通知里的那个。Apple 的规定:

  • 时限。 "Respond within 12 hours of receiving the CONSUMPTION_REQUEST notification."(收到 CONSUMPTION_REQUEST 通知后 12 小时内回复。)Apple 没有说明超时回复会怎样。
  • 先取得同意。 用户同意了,就调用这个接口发送消费数据;没同意,"don't respond to the CONSUMPTION_REQUEST notification"(不要回复这个通知)。取得同意是你的责任:"You, the developer, are solely responsible for obtaining valid consent"(开发者独自负责取得有效同意)。
  • 不能用追踪授权弹窗。 "Don't use the App Tracking Transparency prompt to obtain consent"(不要用 App 追踪透明度弹窗来取得这项同意),Apple 说两者"distinct and unrelated"(互不相关)。
  • 隐私标签。 使用这个接口的,Apple 要求在 App 隐私问题里披露收集了哪些数据、用于什么。
  • 作用。 "The App Store uses the consumption information you provide to inform its refund decisions."(App Store 会参考你提供的消费信息做退款决定。)它只是"a variety of factors"(多种因素)之一。

调用成功返回 202 Accepted。Apple 列出的其他响应:400(请求无效)、401(JWT 无效)、404(找不到交易)、429(超出频率限制)、500(稍后重试)。

请求体(ConsumptionRequest)

字段必填内容
customerConsented是用户同意共享消费数据时为 true。不是 true 的请求会被拒绝,返回 HTTP 400 InvalidCustomerConsentedError。
deliveryStatus是DELIVERED、UNDELIVERED_QUALITY_ISSUE、UNDELIVERED_WRONG_ITEM、UNDELIVERED_SERVER_OUTAGE 或 UNDELIVERED_OTHER。
sampleContentProvided是购买前提供过免费样品或免费试用,或介绍过内容及其用法的,为 true。
consumptionPercentage否用户用掉了多少,单位是千分之一个百分点(milliunits):40% 写 40000,100% 写 100000。
refundPreference否DECLINE、GRANT_FULL 或 GRANT_PRORATED。

会让请求失败或改变含义的规则:

  • 未交付就填 0。 deliveryStatus 不是 DELIVERED 时,consumptionPercentage 必须为 0,否则返回 HTTP 400 UndeliveredConsumptionPercentageNonZeroError。
  • 续期订阅不填百分比。 填了会返回 HTTP 400 ConsumptionPercentageAutoRenewableSubscriptionError;订阅的消费比例由 Apple 按"elapsed time"(已过去的时间)自行计算。
  • GRANT_PRORATED 用于消耗型、非消耗型和非续期订阅时,consumptionPercentage 要"greater than 0 and less than 100000"(大于 0 且小于 100000)。GRANT_FULL 和 DECLINE 适用于所有商品类型。
  • Apple 的例子:一笔交易买了两个消耗型商品,其中一个已用完,consumptionPercentage 填 50000。

截至 2026 年 11 月 1 日,Apple 的 ConsumptionRequest 页面只列出这五个字段。

Get Refund History

GET https://api.storekit.apple.com/inApps/v2/refund/lookup/{anyTransactionId}

  • 传入该用户在你 App 里的任意 originalTransactionId、transactionId 或 appTransactionId。
  • 返回所有商品类型的"App Store-approved refunds"(App Store 已批准的退款),每页最多 20 条,按 revocationDate 升序排列。hasMore 为 true 时,带上 ?revision= 和上次响应里的令牌再请求一次。
  • Apple 说返回的信息"is the same as the information in one or more REFUND notifications"(与 REFUND 通知中的信息相同),并建议用它找回漏收的通知,"such as during a server outage"(例如服务器宕机期间)。
  • Apple 建议保存最后一页的 revision 令牌,以后再查时只会拿到新的退款。
  • signedTransactions 为空数组,表示该用户没有已批准的退款。

被拒绝的申请和被撤销的退款不在这个接口的说明范围内;Apple 只说它返回已批准的退款。

收回权限

退款后的交易,签名内容(JWSTransactionDecodedPayload)里会有这些字段:

  • revocationDate:App Store 退款或从家人共享中收回该交易的时间,UNIX 毫秒时间戳。
  • revocationReason:0 表示"for other reasons, for example, an accidental purchase"(其他原因,例如误购);1 表示"due to an actual or perceived issue within your app"(因为 App 里实际存在或用户认为存在的问题)。
  • revocationType:REFUND_FULL、REFUND_PRORATED、FAMILY_REVOKE 或 ASSIGNMENT_REVOKE。
  • revocationPercentage:按比例退款时,已退款或收回的比例,单位同样是 milliunits。

原则很简单:交易带有 revocationDate,它就不再提供原来的权益。在设备上,StoreKit 已经替你处理了一处:"Products that the App Store has refunded or revoked don't appear in the current entitlements."(已退款或被收回的商品不会出现在当前权益中。)所以每次启动都检查 Transaction.currentEntitlements 的 App,不用额外代码就会去掉已退款的非消耗型商品或订阅。消耗型商品本来就不在其中,金币、点数这类保存在你服务器上的余额,要在收到 REFUND 时自己扣回。Apple 还建议在 App 里告诉用户你因退款采取了什么措施。

你控制不了的事

  • 退款决定。 由 Apple 决定。你的消费数据和 refundPreference 只是参考,"The final refund percentage may differ from the value you provide."(最终退款比例可能与你提供的不同。)
  • 用户从哪里申请。 用户可以直接找 Apple 客服、reportaproblem.apple.com 或发卡机构,不经过你。
  • 退款被撤销。 用户提出争议后,退款可能被撤销,这时你要恢复权限。
  • 退款原因。 事前你只能拿到 consumptionRequestReason,事后是 revocationReason。

Apple 的页面没有说明退款决定需要多久、能否申诉,也没有说明退款金额具体如何从你的收入中扣除。它们也没有承诺每一次退款申请都会产生 CONSUMPTION_REQUEST。

常见问题

必须回复 CONSUMPTION_REQUEST 吗?

不必须。用户没有同意的,Apple 要求你不要回复。如果回复,Apple 要求在 12 小时内。

没有用户同意能发送消费数据吗?

不能。customerConsented 不是 true 的请求会被 App Store 以 HTTP 400 拒绝。

漏收了几条 REFUND 通知,怎么补?

用该用户的任意交易 ID 调用 Get Refund History。Apple 正是建议用它找回"such as during a server outage"期间漏掉的通知。

在设备上怎么知道交易被退款了?

它会从 Transaction.currentEntitlements 中消失,交易的 revocationDate 也会有值。

退款被撤销后需要处理吗?

需要。如果收到 REFUND 后收回了权限,Apple 说收到 REFUND_REVERSED 后要恢复。

资料来源

均于 2026 年 11 月 1 日查阅。

Apple 的退款规则和接口字段会变化,请以上述页面的最新内容为准。

作者

独立开发者,在 Obelisk Club 做本地优先的 Mac 与 iOS 效率应用。 关于作者 →