Apple 可以撤回一筆已經核准的退款,而你的伺服器忽略的已撤回退款,會把付了錢的客戶鎖在門外
當 App Store 撤回一筆已核准的退款時,它期望你的伺服器恢復你先前收回的存取權。以下說明退款、退款遭拒與退款撤回這幾種通知在 App Store 與 Google Play 上如何運作,以及當你忽略每一種時各會付出什麼代價。

重點摘要
- 當 App Store 因為客戶提出爭議而撤回一筆先前核准的退款時,它會送出 REFUND_REVERSED 通知,而 Apple 的指示很明確:如果你的 app 收回了內容或服務,就必須把它們恢復。
- REFUND 通知表示 App Store 已經為該筆交易退款,所以你的伺服器應該收回權益。REFUND_DECLINED 通知表示 Apple 拒絕了該請求,客戶同時保有存取權與該筆扣款。
- 如果你在退款時收回權益,卻從不處理撤回,扣款被恢復的客戶就會一直被鎖在門外。那會變成一張客服工單、一則一星評論,而在 Google Play 上,這種不滿可能演變成如今會讓你賠錢的 chargeback。
- Apple 的 revocationReason 會告訴你退款發生的原因:value 1 表示 Apple 因為你 app 內實際存在或被認為存在的問題而退款,value 0 表示其他原因,例如誤購。
- Google Play 沒有撤回通知。當一筆購買被作廢時,它會送出 VoidedPurchaseNotification,並針對 chargeback 送出另一則 PendingRefundReviewNotification,其餘的則由你透過 Voided Purchases API 的拉取模型來對帳。
- Google Play 給你 24 小時,透過呼叫 orders.reviewrefund 來回應 PendingRefundReviewNotification,而且它只記錄你的第一次呼叫。從 2026 年 8 月 3 日起,一筆敗訴的 chargeback 會讓開發者付出的代價是售價減去 Google 的服務費,再加上銀行的手續費。
- 以冪等方式處理 App Store 的退款通知。重複送達很正常,所以把每次收回與恢復都以 transaction id 為鍵,並讓重複的通知成為 no-op。
退款不一定是最後定論。在客戶提出爭議之後,App Store 可以撤回一筆已經核准的退款,而當這筆已撤回的退款送到你的伺服器時,它帶著一個指示:把存取權還回去。大多數團隊只接上一般的 REFUND 通知、切斷客戶,然後就到此為止。他們從不建立另一半。所以當撤回到來時,什麼都不會執行,而一位正在再次付款的客戶,卻被鎖在自己所購買的東西之外。以下說明整組退款通知在 App Store 與 Google Play 上如何運作,以及當你忽略每一種時各會付出什麼代價。
App Store 送出三種退款通知,不是一種
大多數退款處理是為單一事件而建:錢退回去了,切斷客戶。App Store Server Notifications V2 的資料流其實帶著三種各自獨立的退款結果,而它們各自要求不同的事。其中兩種會改變客戶能存取什麼。另一種則撤銷第一種。以下是完整的一組,用 Apple 自己的話來說。
| 通知 | 它代表什麼 | 你的伺服器要做什麼 |
|---|---|---|
| CONSUMPTION_REQUEST | 客戶要求退款,Apple 想要用量資料 | 在 12 hours 內送出 consumption payload |
| REFUND | App Store 已為該筆交易退款 | 收回該筆交易的權益 |
| REFUND_DECLINED | App Store 拒絕了退款請求 | 什麼都不用做;客戶保有存取權與扣款 |
| REFUND_REVERSED | App Store 撤回了它已核准的退款 | 恢復你收回的內容或服務 |
REFUND,每個團隊都會處理的那一種
當 App Store 處理一筆退款時,它會送出 REFUND 通知到你設定的 URL,而 Apple 的定義很直白:它「表示 App Store 已成功為一筆消耗型 In-App Purchase、非消耗型 In-App Purchase、自動續訂訂閱,或非續訂訂閱的交易完成退款。」你儲存這筆已退款的交易、收回它所購買的東西,而 Apple 要求你在 app 內用符合情境的訊息告訴客戶有什麼改變。這是大家最先接上的通知,也往往是唯一的一個。
REFUND_DECLINED,完全不需要你動手的那一種
REFUND_DECLINED 的意思正如字面:「App Store 拒絕了一筆退款請求。」客戶提出要求,Apple 說不,交易維持有效。客戶的存取權沒有任何改變,所以你的權益邏輯在這裡什麼都不做。這則通知的價值在於記帳。它為你可能以 CONSUMPTION_REQUEST 回應過的退款請求收尾,並確認客戶仍然擁有他所付費的東西。把它當成一筆記錄,而不是一個動作。
REFUND_REVERSED,讓團隊措手不及的那一種
這是大多數退款流程從不處理的通知。Apple 的定義毫不含糊:REFUND_REVERSED「表示 App Store 因為客戶提出的爭議,撤回了先前核准的退款。如果你的 app 因為相關退款而收回了內容或服務,就必須把它們恢復。」把這句讀兩遍。Apple 給了客戶退款,你收回存取權,接著 Apple 判定這筆退款不應成立並收回它。扣款再次生效。客戶付了錢,而如果你的伺服器只知道怎麼收回,他就依然被鎖在門外。已撤回的退款是唯一會把存取權加回去的退款事件,也是幾乎沒有人為它建立處理的那一個。
一筆已撤回的退款實際上讓你付出多少代價
漏掉一次撤回並不是四捨五入的誤差。把錢往兩個方向都走一遍,因為任一半做錯都有代價。
漏掉撤回,你就會讓一位付費客戶一直被鎖在門外。Apple 已經恢復了扣款,所以客戶再次破財,而你的 app 卻拒絕給他所購買的東西。眼前的代價是客服時間,以及一筆你現在可能得自己發出的善意退款,這一次沒有商店抽成退回來替你緩衝。較慢浮現的代價是評論與流失,而在 Google Play 上,同樣這種被鎖在門外的不滿,正是會演變成 chargeback 的東西。
漏掉原始退款,你就會繼續服務一位一毛錢都沒付的客戶。相對的錯誤是根本從不收回。一位已退款的客戶若仍在生成圖片、呼叫你的 API、佔用你的儲存空間,就會針對一筆已撤回的銷售累積真實成本。運算、第三方呼叫與儲存都是你已經花掉的錢,而它們沒有一樣會隨著退款回來。
- 客服成本:由真人回覆一張工單,處理的是你自己的程式碼移除、卻從未還原的存取權。
- 善意退款:把錢重新發給一位你錯誤鎖在門外的客戶,而這種手動舉動不會有商店抽成退回。
- 浪費的支出:由一個你從未切斷的已退款帳號所消耗的運算、API 呼叫與儲存。
- chargeback 風險:在 Google Play 上,一位覺得自己被重複收費的客戶可以提出爭議,而如今敗訴的爭議會落到你身上。
已撤回的退款與一般退款是同一條指向相反方向的 webhook 資料流。處理其中一種而略過另一種,你就會兩頭都付出代價。

Apple 為什麼撤回退款,以及如何解讀 revocationReason
撤回並非隨機。Apple 把它與「客戶提出的爭議」連在一起,也就是客戶事後對退款決定提出質疑。當退款最初被核准時,該筆交易帶有 revocationDate 與 revocationReason,而在下游任何東西據以行動之前,這個原因值得先讀一讀。
- revocationReason 1:App Store「因為你 app 內實際存在或被認為存在的問題」而退款。那是關於你產品的訊號,不只是關於這一位客戶。
- revocationReason 0:App Store「基於其他原因,例如誤購」而退款。沒有附帶任何關於 app 品質的訊號。
當那筆交易的 REFUND_REVERSED 到來時,撤銷正在被還原。你的恢復邏輯應該查出原始的 transaction id、確認你曾收回它,並把權益原原本本地放回去。
Google Play 不送出撤回,所以你改用對帳
Google Play 的模型不同,而如果你用一個 webhook 處理器同時跑兩家商店,這個差異就很重要。沒有與 REFUND_REVERSED 對應的 Google 版本。Google 的 Real-time developer notifications 把退款事件拆成兩則訊息,而撤回是靠對帳處理,不是靠推送。
作廢購買通知
當一筆 Google Play 購買被作廢時,你的伺服器會收到 VoidedPurchaseNotification。它會列出 purchaseToken 與 orderId、一個為 subscription 或 one-time 的 productType,以及一個為完全作廢、或針對多數量購買按數量計的部分退款的 refundType。Google 說這些資料足以找到正確的購買並調整權益。若需要更多,它會指引你使用 Voided Purchases API,這是一個拉取模型,會列出你所查詢的時間戳範圍內的已作廢訂單。
chargeback 審核,以及它的 24 小時時鐘
chargeback 透過另一則訊息傳來,也就是 PendingRefundReviewNotification。當客戶向銀行對一筆扣款提出爭議時,Google Play 會送出這則通知並啟動一個時鐘。你有 24 hours 可以呼叫 orders.reviewrefund,附上退款偏好與任何用量證據,好讓 Google 能代表你抗辯一筆不正當的 chargeback。Google 記錄你的第一次呼叫,忽略其餘的。這是 Google 這邊對應 Apple CONSUMPTION_REQUEST 的機制,是你在爭議中的一方唯一算數的那個窗口。
因為沒有撤回推送,一筆 Google 抗辯並勝訴的 chargeback,不會以一個乾淨俐落的恢復事件送達。你要拿它去和 Voided Purchases API 以及你自己的記錄對帳。教訓和 App Store 上一樣:一筆已作廢的訂單不一定是永久的,而你的權益狀態必須能夠往回移動,不能只會往前。
| 退款事件 | App Store | Google Play |
|---|---|---|
| 退款已核准 | REFUND notification | VoidedPurchaseNotification |
| 退款遭拒 | REFUND_DECLINED notification | 沒有另外的訊息 |
| 退款已撤回 | REFUND_REVERSED notification | 沒有推送;透過 Voided Purchases API 對帳 |
| 爭議證據窗口 | CONSUMPTION_REQUEST, 12 hours | PendingRefundReviewNotification, 24 hours |
| 誰可以發出退款 | 只有 Apple | Google,或你從 Orders 分頁 |
如何處理每一種退款通知,卻不把任何人鎖在門外
你不需要每家商店各一條獨立的流程。你需要一個處理器,它能把權益往兩個方向移動,並把每則訊息都當成可能重複的來對待。
- 建立恢復,而不只是收回。對於每一條在 REFUND 時移除存取權的路徑,都寫出在 REFUND_REVERSED 時把它還原的反向路徑,並以同一個 transaction id 為鍵。
- 讓它冪等。兩家商店都可能不只一次送達同一則通知,所以把每次收回與恢復都以 transaction 或 order id 為鍵,並讓重複成為 no-op。
- 動手之前先讀原因。用 revocationReason 分辨一筆 app 品質退款與一筆誤購退款,並把 app 品質那類轉給負責產品品質的人。
- 準時回應證據窗口。在 CONSUMPTION_REQUEST 之後 12 hours 內送出 Apple 的用量資料,並在 PendingRefundReviewNotification 之後 24 hours 內呼叫 orders.reviewrefund。
- 儲存每一個事件。保留 REFUND_DECLINED 與原始通知,好讓稍後到來的撤回能夠對應到它所撤銷的那筆退款。
這一切都不會改變退款是否發生。它改變的是,站在一筆已撤回退款另一端的客戶,會不會察覺到你的伺服器搞錯了。
常見問題解答
- App Store 上的 REFUND_REVERSED 通知是什麼?
- 那是 App Store 告訴你的伺服器,它撤回了一筆先前核准的退款,因為客戶提出了爭議。Apple 的指示很明確:如果你的 app 因為那筆退款而收回了內容或服務,就必須把它們恢復。扣款再次生效,所以客戶應該把存取權拿回去。
- 當我收到 REFUND_DECLINED 通知時該怎麼做?
- 對客戶的存取權什麼都不用做。REFUND_DECLINED 表示 App Store 拒絕了退款請求,所以交易維持有效,客戶保有他所付費的東西。把它當成一筆為退款請求收尾的記錄,而那筆請求往往是你以 CONSUMPTION_REQUEST 回應過的。
- 當退款或 chargeback 被撤回時,Google Play 會送出通知嗎?
- 不會。Google Play 沒有與 Apple REFUND_REVERSED 對應的機制。當購買被作廢時,它會送出 VoidedPurchaseNotification,並針對 chargeback 送出 PendingRefundReviewNotification,但一筆 Google 抗辯並勝訴的 chargeback 不會被推送回你這邊。你要用 Voided Purchases API 與你自己的記錄來對帳。
- 我有多久時間可以回應 Google Play 的 chargeback?
- 24 小時。當 Google Play 送出 PendingRefundReviewNotification 時,你有 24 小時可以呼叫 orders.reviewrefund,附上退款偏好與用量證據。Google 只記錄你的第一次呼叫。從 2026 年 8 月 3 日起,一筆敗訴的 chargeback 會讓開發者付出的代價是售價減去 Google 的服務費,再加上銀行的手續費。
- 在一筆已退款的 App Store 交易上,revocationReason 告訴我什麼?
- 它告訴你 Apple 為什麼退款。Value 1 表示 Apple 因為你 app 內實際存在或被認為存在的問題而退款,那是一個產品訊號。Value 0 表示其他原因,例如誤購。讀它能讓你把指向 bug 的退款和例行退款區分開來。
資料來源與延伸閱讀
- Apple Developer: App Store Server Notifications V2 notificationType
- Apple Developer: Handling refund notifications
- Apple Developer: revocationReason (App Store Server API)
- Android Developers: Real-time developer notifications reference
- Google Play Developer API: Method orders.reviewrefund
- Play Console Help: Updates to refund protection and chargeback cost responsibility
- Google Play Developer: Voided Purchases API
RefundHalt
App Store 與 Google Play 的退款自動駕駛
繼續閱讀
你應用上的退款主要由你買單,但你損失的並不是你以為的那筆佣金
當客戶獲得退款時,Apple 和 Google 都會把各自的佣金退還給你,所以你損失的並不是應用商店的抽成。本文講清楚誰為你應用上的退款買單,你的撥款裡實際流走了什麼,以及為什麼拒付比普通退款代價更高。
取消訂閱和取得退款是兩回事,只有其中一種會把錢退還給你的客戶
取消訂閱只是讓商店停止下一次扣款,客戶在本期結束前仍保留存取權限,沒有任何款項流動。退款則會撤銷一筆已經完成的付款,並連同存取權限一起收回。本文講清兩者的分界點、各自讓你付出什麼代價,以及為什麼只有退款才會抵達你的伺服器。