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 商店都把它視為品質訊號
Apple 審查準則 5.6.4 把過多的退款申請列為 App 未達品質標準的跡象,Google 的開發人員發布協議也允許它依退款率下架新開發人員的 App。本文整理兩家商店的原文、下架的實際成本,以及如何避開風險。
在處理退款之前先驗證 App Store 伺服器通知,因為任何人都能向你的 webhook URL 發送請求
在檢查簽章之前,一則退款通知只不過是一個 HTTPS 請求。本文說明如何驗證 App Store 伺服器通知和 Google Play RTDN 訊息、各商店簽署的內容,以及偽造的退款或遭錯誤拒絕的退款會讓你付出什麼代價。