用户申请退款找的是 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 服务器通知收不到怎么办。
| notificationType | Apple 的说明 | 怎么做 |
|---|---|---|
| CONSUMPTION_REQUEST | 用户"initiated a refund request"(发起了退款申请),App Store 请你提供消费数据。 | 用户同意过的,在 12 小时内调用 Send Consumption Information;没同意的,不要回复。 |
| REFUND | App Store 已成功退款,适用于消耗型、非消耗型、续期订阅(auto-renewable)和非续期订阅。 | 收回这笔交易给用户的内容。 |
| REFUND_DECLINED | "the App Store declined a refund request."(App Store 拒绝了退款申请。) | 不需要撤回什么。 |
| REFUND_REVERSED | App 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 日查阅。
- notificationType,App Store Server Notifications
- consumptionRequestReason,App Store Server Notifications
- Send Consumption Information,App Store Server API
- ConsumptionRequest,App Store Server API
- deliveryStatus、refundPreference、consumptionPercentage、sampleContentProvided,App Store Server API
- Get Refund History 和 RefundHistoryResponse,App Store Server API
- JWSTransactionDecodedPayload、revocationDate、revocationReason、revocationType,App Store Server API
- Handling refund notifications,StoreKit
- currentEntitlements,StoreKit
Apple 的退款规则和接口字段会变化,请以上述页面的最新内容为准。