Apple 的 Send Consumption Information 现在只要求五个字段,而非十二个,以下逐一解读
当客户向 Apple 申请退款时,Send Consumption Information 载荷就是你的回应。Apple 已将它从十二个字段精简为五个,其中三个必填、两个可选。以下是每个字段、各自接受的取值,以及你必须在其中发送的 12 小时窗口。

要点
- Apple 的 Send Consumption Information 载荷现在包含五个字段,较此前的十二个有所减少。三个为必填,customerConsented、deliveryStatus 和 sampleContentProvided,两个为可选,consumptionPercentage 和 refundPreference。
- customerConsented 是一道硬性门槛。Apple 自己的指引是,如果客户未同意共享消耗数据,你就完全不发送该载荷。
- deliveryStatus 承载着你最有力的信号。DELIVERED 表示购买正常生效,而四个 UNDELIVERED 取值则告诉 Apple 客户从未拿到可用的产品。
- consumptionPercentage 用一个以 milliunits 为单位的精确数字,取代了旧有的四档 consumptionStatus 枚举,其中 100,000 milliunits 表示商品已被完全消耗。
- refundPreference 现在是字符串,而非数字。三个取值为 DECLINE、GRANT_FULL 和 GRANT_PRORATED,它表达的是一种倾向,而非决定。最终仍由 Apple 裁定。
- 你通过向 Send Consumption Information 端点发送 PUT 请求来提交载荷,以交易 id 为键,Apple 返回 202 Accepted 且响应体为空。窗口是 CONSUMPTION_REQUEST 之后的 12 小时。
- 对于自动续期订阅,Apple 会根据已过时间自行计算消耗量,因此 consumptionPercentage 适用于消耗型和非续期购买。
当客户想要退款时,Apple 向你索取的事实清单大大缩短了。Send Consumption Information 载荷,也就是开发者被允许送入 App Store 退款裁决的那唯一一次回应,过去有十二个字段,现在只有五个。Apple 在 WWDC24 前后进行了精简,把大部分旧有的账户画像式问题收拢为两个简单问题外加一个数字。字段减少并不是一处小改动。它改变了 Apple 权衡哪些证据,也改变了你绝不能填错的是哪些字段。
为什么这值得你留意,而不只是一条 schema 备注,原因在此。这份载荷是 Apple 退款流程中,你这一方的说法唯一能触及裁决的时刻。一笔消耗型退款会冲销你已经实实在在花钱交付的收入,而这五个字段正是你用来告诉 Apple 产品已交付并被使用的方式。错过 12 小时窗口,或者填错某个字段,Apple 就只凭客户一方的主张来裁定。
What Send Consumption Information is
Send Consumption Information 是 App Store Server API 的一个端点,你在 Apple 向你的服务器发送 CONSUMPTION_REQUEST 通知后调用它。该通知意味着某位客户就一笔应用内购买向 Apple 申请退款,而 Apple 想在裁定前听取你的意见。你的回应方式,是向该端点 PUT 一小段 JSON 体,即 ConsumptionRequest。Apple 读取它,与客户的历史一并权衡,然后作出决定。退款从来不由你决定。你提供事实。
这个 body 就是全部接口。没有另外的表单,没有申诉,也没有第二次能算数的提交。你在那一次载荷里发送的一切,就是你的全部陈述,因此每个字段的含义,比字段数量所暗示的更为重要。
The five fields Apple asks for now
当前的 ConsumptionRequest 有五个成员。三个必填,两个可选。Apple 过去就客户账户所问的一切,他们的账龄、终身消费、终身退款、游玩时长,都已从你所发送的内容中移除。
| 字段 | 是否必填 | 类型 | 承载内容 |
|---|---|---|---|
| customerConsented | 是 | Boolean | 客户是否同意向 Apple 共享消耗数据 |
| deliveryStatus | 是 | String | 你的应用是否交付了可用的产品 |
| sampleContentProvided | 是 | Boolean | 你是否在购买前提供了免费样品或试用 |
| consumptionPercentage | 否 | Integer | 购买内容被消耗了多少,以 milliunits 计 |
| refundPreference | 否 | String | 你对该退款请求偏好的结果 |
customerConsented is the gate
customerConsented 是一个 Boolean,也是决定你是否发送任何内容的字段。它记录客户是否同意让你向 Apple 共享消耗数据。Apple 的指引很直接:如果客户未同意,就不要发送消耗信息。所以这不是一个你为了强化己方论据而翻成 true 的字段。它反映的是一个你必须早已握有的真实是与否,这里为 false,就意味着载荷的其余部分都不应发送。
deliveryStatus is the field that moves refunds
deliveryStatus 是一个字符串枚举,也是你手中最有力的杠杆。它告诉 Apple 你的应用是否真的交付了可用的应用内购买。一个取值表示是。另外四个表示否,各自对应不同原因,而每一个都告诉 Apple 客户有正当的不满。
| 取值 | 它告诉 Apple 什么 |
|---|---|
| DELIVERED | 应用交付了可用的应用内购买 |
| UNDELIVERED_QUALITY_ISSUE | 因质量问题未能交付购买内容 |
| UNDELIVERED_WRONG_ITEM | 客户收到了错误的商品 |
| UNDELIVERED_SERVER_OUTAGE | 服务器中断阻止了交付 |
| UNDELIVERED_OTHER | 因其他原因未能交付购买内容 |
sampleContentProvided answers a fairness question
sampleContentProvided 是一个 Boolean。它记录你是否在客户购买前给过他们免费样品、试用,或关于该购买作用的明确说明。这里为 true 是一个小小的公平性信号:客户有机会了解自己在买什么。它本身不决定任何事,但它是仅有的三个必填字段之一,可见 Apple 明确希望每次回应中都有它。
consumptionPercentage is a number now, not a status
这是变化最大的字段。旧载荷有 consumptionStatus,一个四档枚举:UNDECLARED、NOT_CONSUMED、PARTIALLY_CONSUMED、FULLY_CONSUMED。新载荷用 consumptionPercentage 取而代之,这是一个以 milliunits 计量的整数。100,000 milliunits 表示商品已被完全消耗,那么 50,000 就是一半,0 就是分毫未动。一个精确的数字胜过四选一的分档,因为它让你能说客户用掉了某个额度包的百分之九十,而不是向下取整成部分消耗。
有一个常让人栽跟头的注意事项。对于自动续期订阅,Apple 会根据已过时间自行算出消耗量,因此 consumptionPercentage 适用于消耗型和非续期购买。在适用之处发送它,在不适用之处交由 Apple 推导。

refundPreference states a preference, not a verdict
refundPreference 是一个可选字符串,你在这里告诉 Apple 你偏好的结果。它的形态也变了。旧字段是一个数字,取值类似 prefer-grant、prefer-decline 和 no-preference。新字段是一个具名字符串,带三个取值。
| 取值 | 你所请求的内容 |
|---|---|
| GRANT_FULL | 你希望 Apple 给予全额退款 |
| GRANT_PRORATED | 你希望按已使用部分给予部分退款 |
| DECLINE | 你希望 Apple 拒绝退款 |
What Apple dropped, and why it matters
旧的 ConsumptionRequest 有十二个字段。其中七个已从你所发送的内容中消失。accountTenure、lifetimeDollarsPurchased、lifetimeDollarsRefunded、playTime、userStatus、platform 和 appAccountToken 构成载荷中账户画像的那一半,这些字段要求你按客户拥有账户的时长、花了多少钱、被退了多少款、使用应用多久来给他们分档。
Apple 删掉它们的理由值得一提。那些字段要求开发者交出一份客户画像,而大多数开发者要么把它们留作未声明,要么靠猜。留下的五个关乎购买与交付,是你真正能从自己系统中核实的事实。这一转变,是从客户是谁,转向这笔具体购买发生了什么。
| 旧载荷 | 当前载荷 | |
|---|---|---|
| 字段总数 | 12 | 5 |
| 必填字段 | 实际上未强制任何 | 3 |
| 消耗信号 | consumptionStatus,四档 | consumptionPercentage,精确 milliunits |
| 退款倾向 | 数字枚举 | 具名字符串,3 个取值 |
| 账户画像 | 账龄、终身消费、退款、游玩时长、状态 | 已移除 |
The endpoint and the clock
你通过 HTTP PUT 向 Send Consumption Information 端点发送载荷,以争议购买的交易 id 为键:PUT /inApps/v1/transactions/consumption/{transactionId}。成功会返回 202 Accepted,且响应体为空。那个空回应是意料之中,而非 bug。它确认 Apple 已将你的数据排入队列,却不告诉你最终结果如何,那要稍后以 REFUND 或 REFUND_DECLINED 通知的形式到来。
时限是你无法拉长的部分。Apple 给你从 CONSUMPTION_REQUEST 起算的 12 小时来回应。只有你的第一次回应会被采用,所以第一份载荷就必须是完整且正确的那一份。Apple 可能就同一笔购买多次发送 CONSUMPTION_REQUEST,但每一次的截止时间都是固定的,而人工审核在跨时区和周末的情况下,很难塞进 12 小时之内。
What a mishandled field costs you
The money left the building before the refund did
一笔消耗型退款不是干净利落的冲销。等到客户就一包额度或一批 AI 生成申请退钱时,你早已为交付它花了钱:每次请求上的 GPU 推理、按 token 计费的第三方模型 API 调用、为你所产出内容付出的存储,以及与该使用挂钩的任何创作者或合作方分成。商店售价退回给客户。你的交付成本却不会退回给你。所以一笔本可申辩的退款,不是一次不赔不赚的事件,而是你为服务该账户所付出的一切净亏损。
The five fields are how you avoid paying twice
deliveryStatus 设为 DELIVERED,加上较高的 consumptionPercentage,是告诉 Apple 客户已收到并使用了产品的两项事实。它们是你证明算力、API 调用和存储都各尽其责的证据。若把载荷留着不发,Apple 就永远听不到这些。它会凭客户的主张来裁定,退款更有可能通过,而你要同时吞下被冲销的收入和其背后的交付成本。
Chargebacks are the worse door, and silence points to it
一位无法通过 Apple 退款流程得到满意结果的客户,仍可向其银行就该笔扣款发起争议。银行卡拒付是最终的,它带有一笔固定的争议费,并把决定权从 Apple 和你手中一并夺走。妥善回应 CONSUMPTION_REQUEST,能让争议留在 Apple 的体系之内,你在那里还有发言权。对它置之不理,则会把处于两可之间的案子推向你毫无发言权的那唯一渠道。
How RefundHalt handles it
这五个字段看着简单,直到你必须在 12 小时之内、对每一次 CONSUMPTION_REQUEST、以正确的交易为键,把它们填对。RefundHalt 捕获通知,读取你自己关于那笔购买的交付与使用记录,并在窗口关闭前自动发送载荷。deliveryStatus 反映你日志真正显示的情况,consumptionPercentage 来自真实使用而非猜测,refundPreference 遵循你一次设定好的政策。你得到的是一笔以证据受审的可申辩退款,而非一个错过的截止时间和一份没有你参与就作出的决定。
常见问题解答
- Apple 的 Send Consumption Information 现在有多少个字段?
- 五个。三个必填,customerConsented、deliveryStatus 和 sampleContentProvided,两个可选,consumptionPercentage 和 refundPreference。该载荷的上一版本有十二个字段,Apple 移除了其中账户画像类的字段,例如 accountTenure、lifetimeDollarsPurchased 和 userStatus。
- 消耗请求中的 deliveryStatus 是什么意思?
- deliveryStatus 告诉 Apple 你的应用是否交付了可用的应用内购买。DELIVERED 表示交付了。四个 UNDELIVERED 取值,UNDELIVERED_QUALITY_ISSUE、UNDELIVERED_WRONG_ITEM、UNDELIVERED_SERVER_OUTAGE 和 UNDELIVERED_OTHER,各自以某个明确原因表示未交付。它是载荷中最有力的信号,因此必须与你自己的日志相符。
- consumptionPercentage 是百分比还是原始数字?
- 它是一个以 milliunits 计量的整数,而非普通百分比。100,000 milliunits 表示客户完全消耗了该购买,那么 50,000 是一半,0 是分毫未动。它取代了旧的 consumptionStatus 枚举,后者只有从未消耗到完全消耗的四个分档。
- 把 refundPreference 设为 DECLINE 会阻止退款吗?
- 不会。refundPreference 表达的是你偏好的结果,它不决定任何事。DECLINE 告诉 Apple 你宁愿它不退款,GRANT_FULL 或 GRANT_PRORATED 则告诉它相反的意思,但 Apple 会把你的倾向与客户的历史及其自身政策一并权衡,然后作出最终裁定。
- 如果客户未同意共享消耗数据怎么办?
- 那你就不应发送该载荷。customerConsented 是一个必填的 Boolean,而 Apple 的指引是,如果客户未同意共享消耗数据,你就完全不回应 CONSUMPTION_REQUEST。同意与否是一个你必须早已握有的真实是与否,而不是一个你为帮助己方论据而设成 true 的值。
- 我有多长时间来发送消耗信息?
- 从 Apple 发送 CONSUMPTION_REQUEST 通知起算的 12 小时。你以向 Send Consumption Information 端点发送 PUT 来回应,成功会返回 202 Accepted 且响应体为空。只有你的第一次回应会被采用,所以第一份载荷必须完整,而人工流程很难塞进这个窗口之内。
来源和延伸阅读
- Apple Developer: ConsumptionRequest
- Apple Developer: Send Consumption Information
- Apple Developer: Send Consumption Information V1
- Apple Developer: deliveryStatus
- Apple Developer: consumptionPercentage
- Apple Developer: refundPreference
- Apple Developer: Explore App Store server APIs for In-App Purchase (WWDC24)
RefundHalt
App Store 和 Google Play 退款自动驾驶
继续阅读
有一个接口能返回某位客户在 App Store 的全部退款记录,本文说清楚它到底交还了什么
Apple 的 Get Refund History 接口会以签名交易的形式,返回某位客户在 App Store 的完整退款记录。本文讲清每一个字段、revision 令牌如何翻页、为什么它是按客户而非按 App 维度,以及你漏掉一笔退款要付出的代价。
你的 App 可以直接弹出应用内退款申请表,客户点击提交后 Apple 会这样处理
Apple 的应用内退款申请让客户无需离开你的 App 就能申请退款,表单由 Apple 构建并审核。本文讲清 beginRefundRequest 返回什么、它会在你的服务器上启动哪些 CONSUMPTION_REQUEST 和 48 小时计时,以及这个按钮是否值得上线。