漏收退款通知,可能讓你失去唯一能申辯的退款機會,好在兩大商店都讓你及時找回
伺服器漏收退款通知時,兩個需要你提交證據的退款流程會在你缺席的情況下結案。本文說明 Apple 和 Google Play 如何重試、漏收的退款通知最多能追回多久以前的,以及復原機制的缺口在哪裡。

重點摘要
- App Store Server Notifications V2 傳送失敗時,Apple 會重試五次,分別在上一次嘗試後的 1、12、24、48 和 72 小時,因此第二次重試大約在第一次傳送 13 小時後才抵達。
- Apple 要求開發者在收到 CONSUMPTION_REQUEST 後 12 小時內回應。如果伺服器錯過了第一次傳送和 1 小時後的重試,等它看到這筆請求時,回應期限很可能已經過了。
- Apple 的 Get Notification History 端點可傳回正式環境過去 180 天內的 V2 通知,每頁 20 筆。onlyFailures 參數可將結果限定為從未送達你伺服器的通知,以及仍在重試中的通知。
- Google Play 只透過 PendingRefundReviewNotification 傳送退單審查,而在 24 小時內呼叫 orders.reviewrefund 所需的 pendingRefundToken 就包含在這則通知裡。Google 沒有提供任何可列出待審查項目的獨立 API。
- Google Cloud Pub/Sub 預設保留未確認訊息 7 天,最長 31 天。訂閱若連續 31 天沒有訂閱端活動就會被刪除,除非你變更了它的到期時間。
- Google Play Voided Purchases API 可以找回過去 30 天內的退款、撤銷和退單,配額為每天 6,000 次查詢、任意 30 秒內 30 次查詢,但它無法找回你從未回應的退單審查。
- Apple 沙盒環境每則通知只傳送一次,不會重試。所以只在沙盒裡跑過的復原流程,從來沒有經過 Apple 真實重試時間表的考驗。
漏收退款通知,會讓開發者在不知不覺中失去僅有的兩次能夠參與的退款決定。Apple 的 CONSUMPTION_REQUEST 和 Google Play 的退單審查都從一則通知開始,期限也都很短:Apple 是 12 小時,Google 是 24 小時。如果這則訊息抵達時你的伺服器當機、回應太慢或設定有誤,商店不會等你,而是根據手上已有的資訊直接做出決定。
好消息是,兩家商店都不會在第一次失敗後就丟掉訊息。Apple 會重試,並保留可查詢的 180 天歷史紀錄。Google 透過 Pub/Sub 發布,未確認的訊息會保留好幾天。壞消息是,重試時間和回應期限對不上,而且兩個流程中有一個完全沒有復原用的 API。
兩大商店如何傳送退款通知
Apple 會把 App Store Server Notifications 直接傳送到你在 App Store Connect 中設定的 HTTPS 網址。Google 不會直接呼叫你的伺服器,而是把即時開發人員通知發布到一個 Google Cloud Pub/Sub 主題,你在該主題上的訂閱要麼把通知推送到你的端點,要麼等你的伺服器來提取。
| 問題 | App Store | Google Play |
|---|---|---|
| 傳送管道 | 對你的網址發出 HTTPS POST | Cloud Pub/Sub 主題,推送或提取 |
| 怎樣算成功 | HTTP 200 至 206 | 確認 Pub/Sub 訊息,或推送端點傳回成功狀態碼 |
| 重試 | V2 重試 5 次,分別在上一次嘗試後的 1、12、24、48 和 72 小時 | Pub/Sub 持續重新傳送,直到被確認或保留期結束 |
| 漏收的訊息能保留多久 | 在 Get Notification History 中保留 180 天 | 訂閱中預設 7 天,最長 31 天 |
| 需要你提交證據的退款 | CONSUMPTION_REQUEST,12 小時內回應 | PendingRefundReviewNotification,24 小時內回應 |
| 已核准退款的提取式備援 | Get Notification History、Get Refund History | Voided Purchases API,過去 30 天 |
| 沙盒或測試行為 | 只嘗試一次,不重試 | 在 Play Console 中使用 Send Test Message |
Apple 如何判定失敗
Apple 的文件寫得很清楚。傳回 200 至 206 之間的任何狀態碼,傳送就算完成。傳回 40x 或 50x,Apple 會重試。其他任何狀態碼 Apple 也一律視為失敗。每次嘗試都會記錄一個 sendAttemptResult,它的值幾乎就是一份真實故障清單:TIMED_OUT、TLS_ISSUE、CIRCULAR_REDIRECT、NO_RESPONSE、PREMATURE_CLOSE、UNSUCCESSFUL_HTTP_RESPONSE_CODE 等等。針對 TIMED_OUT,Apple 自己的建議是檢查伺服器是否在請求當下同步處理訊息。
Google 如何判定失敗
在推送訂閱中,你的端點傳回成功狀態碼就等於確認。在提取訂閱中,你的伺服器必須確認提取到的每則訊息,否則 Pub/Sub 會再傳送一次。預設的確認期限是 10 秒,最長 600 秒。Google 的 RTDN 指南還有一條值得再提的規則:通知只告訴你有東西變了,你仍然要呼叫 Google Play Developer API 取得完整的購買狀態。
為什麼 Apple 的重試時間表配合不上 12 小時期限
Apple 的重試間隔是從上一次嘗試開始算,而不是從第一次嘗試開始。把它們加總起來,就會得到一則持續傳送失敗的 V2 通知的時間軸。
| 嘗試 | 距上一次嘗試的等待時間 | 距第一次嘗試的小時數 |
|---|---|---|
| 第一次傳送 | 無 | 0 |
| 第 1 次重試 | 1 小時 | 1 |
| 第 2 次重試 | 12 小時 | 13 |
| 第 3 次重試 | 24 小時 | 37 |
| 第 4 次重試 | 48 小時 | 85 |
| 第 5 次重試 | 72 小時 | 157 |
Apple 要求你在收到 CONSUMPTION_REQUEST 後 12 小時內回應。如果第一次傳送和 1 小時後的重試時你的伺服器都當機,下一次嘗試要到第一次傳送 13 小時後才會抵達。Apple 沒有說明在重試中才第一次收到的請求,是否會重新給 12 小時。保守的解讀是把第一次嘗試當成計時起點,也就是說,如果你錯過請求超過 1 小時,光靠重試是救不回來的。
傳回 HTTP 400 後 Apple 會重試嗎?
依照文件,會。Apple 列出會觸發重試的狀態碼是 40x 和 50x。2026 年 3 月,有開發者在 Apple 論壇上回報,傳回 400 後重試似乎停止了,之後又說一兩天後傳送恢復正常,Apple 則請對方提交 Feedback Assistant 報告。請把文件中的行為當作規則,把你自己的監控當作證據。
用 Get Notification History 找回漏收的 App Store 通知
Get Notification History 會傳回在你指定的時間範圍內,Apple 嘗試傳送到你伺服器的第 2 版通知。每筆紀錄都含有完整的簽署酬載,所以你可以把它交給處理即時通知的同一個處理程式。每筆紀錄也含有一個 sendAttempts 陣列,最多六個項目,分別對應第一次嘗試和五次重試。
- startDate 和 endDate 為必填,startDate 在正式環境中必須落在過去 180 天內,在沙盒中必須落在過去 30 天內。
- 將 onlyFailures 設為 true,只取得尚未送達你伺服器的通知,以及 Apple 仍在重試的通知。
- 可以依單一 notificationType 篩選,例如 CONSUMPTION_REQUEST 或 REFUND,也可以依單一顧客的 transactionId 篩選,但同一個請求中不能兩者並用。
- 每次呼叫最多傳回 20 筆紀錄。只要 hasMore 為 true,就帶著 paginationToken 繼續呼叫,而且每次都送出相同的請求內容。
對退款來說,有一點特別重要。Apple 表示,歷史紀錄反映的是通知第一次傳送時的購買狀態,而不是現在的狀態。所以找回來的 REFUND 是過去的事實。撤銷存取權限之前,請先透過交易或訂閱狀態端點確認目前狀態。
V1 通知無法用這種方式找回
Get Notification History 只涵蓋第 2 版。Apple 對 V1 通知重試三次,分別在上一次嘗試後的 6、24 和 48 小時,如果全部失敗,就沒有可以重播的歷史紀錄。這也是還在用 V1 的 App 應該改用 V2 的另一個理由。
找回漏收的 Google Play 退款通知
Google 的安全網就是 Pub/Sub 本身。只要你的訂閱還在,沒人確認的訊息就會在保留期內一直留在那裡,預設 7 天,調高後最長 31 天。你的端點恢復後,傳送也會恢復。不過有兩個設定可能會悄悄讓這層保護失效。
- 無效信件主題會在傳送失敗達到設定次數後,把訊息移出你的訂閱。預設次數是 5,在預設的立即重新傳送政策下,五次嘗試可能很快就用完。如果你有使用,請把無效信件主題當成第二個收件匣來盯。
- 沒有訂閱端活動的訂閱,例如沒有開啟的連線、沒有進行中的提取或成功的推送,預設會在 31 天後到期。一個壞了一個月的端點,可能連訂閱本身和其中等待的所有訊息都一起賠掉。
已作廢的退款有 30 天的備援
對於已經發生的退款,Voided Purchases API 就是提取式備援。它會列出過去 30 天內的作廢購買,每頁最多 1,000 筆,每個 App 的配額為每天 6,000 次查詢、任意 30 秒內 30 次查詢。傳入 type 1 就能在一次性產品之外一併納入訂閱。每天執行一次,就能補上通知流程漏掉的任何退款,讓權益和營收數字保持正確。
退單審查沒有備援
PendingRefundReviewNotification 就不一樣了。它帶有你呼叫 orders.reviewrefund 時必須傳入的 pendingRefundToken,從收到通知起,你有 24 小時可以提交退款偏好和使用證據。Google 只記錄你的第一次呼叫,之後的都會忽略。Google 的文件中沒有任何列出待審查項目的端點,所以一旦這則 Pub/Sub 訊息遺失,你為這筆退單準備的證據也跟著消失。
漏收一則退款通知的金錢代價
如果漏收的是已經確定的退款通知,代價只是記帳工作。你之後會在 Voided Purchases API 或 Get Notification History 中發現它,依你的政策需要撤銷存取權限,報表也會跟著補齊。麻煩,但成本不高。
如果漏收的是兩個可申辯流程中的通知,就會造成實實在在的金錢損失,因為商店會在沒有你證據的情況下做出決定。在 Apple,這代表退款決定是在沒有你的使用量資料下做出的。在 Google Play,對於 2026 年 8 月 3 日之後成立的訂單,輸掉一筆退單要承擔購買價格扣除 Play 服務費後的金額,再加上買家銀行收取的退單手續費。Google 只承擔它自己的服務費。
而你為了交付這筆購買所花的錢,在爭議開始之前就已經花掉了。
- 運算:買家已經用掉的每一次生成、算圖或模型回應背後的 GPU 和伺服器時間。
- API 呼叫:第三方請求在送出的那一刻就已計費,通常按次收費。
- 儲存空間:買家建立的檔案和媒體,仍然算在你的主機帳單裡。
- 分潤:這筆銷售中已經支付給創作者、推廣夥伴或合作夥伴的營收分成。
退款一旦成立,這些都拿不回來。你在那 12 或 24 小時內提交的證據,是你唯一能用的籌碼,而漏收一則通知就會讓你失去它。
一套經得起考驗的復原流程
- 先確認。儲存原始酬載,傳回成功狀態碼,再放進佇列處理。
- 每隔幾分鐘用 onlyFailures 輪詢 Apple 的 Get Notification History,查找 CONSUMPTION_REQUEST,這樣一次故障只會損失 12 小時期限中的幾分鐘。
- 每天用 onlyFailures 針對過去幾天的所有類型做一次 Get Notification History 全面檢查,並重播找到的通知。
- 每天為每個 Google Play App 以 type 1 呼叫一次 Voided Purchases API,並和你已記錄的退款進行核對。
- 在 Pub/Sub 上,把訊息保留期設為 7 天以上,如果有無效信件主題就針對它設定警示,並在未傳送的訊息累積時發出警示。
- 也要在正式環境測試整條路徑。Apple 沙盒每則通知只傳送一次,不會重試,而 Request a Test Notification 搭配 Get Test Notification Status 可以清楚顯示你的伺服器是怎麼回應的。

RefundHalt 替你接收兩大商店的通知,在期限內自動回應 CONSUMPTION_REQUEST 和退單審查,你不必再親自盯著時間。
常見問題解答
- App Store 伺服器通知傳送失敗後,Apple 會持續重試多久?
- 第 2 版大約六天半。Apple 會重試五次,分別在上一次嘗試後的 1、12、24、48 和 72 小時,所以最後一次重試出現在第一次傳送 157 小時後。第 1 版通知重試三次,分別在上一次嘗試後的 6、24 和 48 小時。
- 漏收的 App Store 伺服器通知最多能追回多久以前的?
- 正式環境為 180 天。Apple 的 Get Notification History 端點會傳回過去 180 天內的第 2 版通知,沙盒則為過去 30 天,每次呼叫傳回 20 筆紀錄,onlyFailures 參數可將結果限定為從未送達你伺服器的通知。
- 如果我的伺服器傳回 HTTP 400,Apple 會重試通知嗎?
- 依照 Apple 的文件,會。伺服器傳回 40x 或 50x 狀態碼時 Apple 會重試,200 至 206 視為成功。其他任何狀態碼也都算失敗。
- 漏收的 Google Play 退單審查還能找回嗎?
- 只有在 Pub/Sub 訊息還存在時才可以。呼叫 orders.reviewrefund 所需的 pendingRefundToken 會隨 PendingRefundReviewNotification 一起送達,而 Google 沒有提供列出待審查項目的 API,所以訂閱中預設保留 7 天的未確認訊息就是你唯一的副本。
- 要怎麼找出伺服器漏掉的 Google Play 退款?
- 呼叫 Voided Purchases API。它會傳回過去 30 天內的作廢購買,包括退款、撤銷和退單,每頁最多 1,000 筆結果,每個 App 每天的配額為 6,000 次查詢。傳入 type 1 即可納入訂閱。
資料來源與延伸閱讀
- Apple Developer: Responding to App Store Server Notifications
- Apple Developer: Get Notification History
- Apple Developer: NotificationHistoryRequest
- Apple Developer: Send Consumption Information
- Android Developers: Real-time developer notifications reference
- Android Developers: Help Google dispute chargebacks
- Google Play Developer API: Voided Purchases API
- Google Cloud: Pub/Sub subscription properties
- Play Console Help: Updates to refund protection and chargeback cost responsibility
RefundHalt
App Store 與 Google Play 的退款自動駕駛
繼續閱讀
過多的退款申請,代價可能遠超退款本身,因為兩大 App 商店都把它視為品質訊號
Apple 審查準則 5.6.4 把過多的退款申請列為 App 未達品質標準的跡象,Google 的開發人員發布協議也允許它依退款率下架新開發人員的 App。本文整理兩家商店的原文、下架的實際成本,以及如何避開風險。
在處理退款之前先驗證 App Store 伺服器通知,因為任何人都能向你的 webhook URL 發送請求
在檢查簽章之前,一則退款通知只不過是一個 HTTPS 請求。本文說明如何驗證 App Store 伺服器通知和 Google Play RTDN 訊息、各商店簽署的內容,以及偽造的退款或遭錯誤拒絕的退款會讓你付出什麼代價。