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 小時計時,以及這個按鈕是否值得上線。