在上架前聯調 IAP 時,我們遇到一個典型但非常迷惑的問題:
- 使用者點擊「繼續購買」
- 按鈕進入「處理中」
- 幾秒後恢復「繼續購買」
- 沒有彈出 App Store 購買確認框
這篇文章記錄完整排查路徑、關鍵日誌、根因以及可落地修復方案,適合發佈前做 StoreKit 風險檢查。
1. 問題現象
購買頁商品列表能正常載入,點擊購買按鈕後:
purchase()被呼叫- 無系統彈窗
- UI 最終回到未購買狀態
乍看像是「購買流程沒發起」,但日誌顯示並不這麼簡單。
2. 首輪排查:確認購買鏈路有沒有走通
我們先把購買鏈路關鍵節點打全日誌:
- 按鈕點擊參數
purchaseSelectedProduct()入口和 guard 分支product.purchase()前後Transaction.updatesrefreshEntitlements()Transaction.currentEntitlementsTransaction.latest(for:)
結果顯示:
purchase()返回了.success- 交易校驗成功、
transaction.finish()也執行了 - 但
entitledIDs仍為空
這說明:不是「沒發起購買」,而是「購買後權益沒被識別」。
3. 深挖後發現的關鍵事實
繼續補日誌後,出現了幾個決定性訊號:
Transaction.currentEntitlements迭代數為0Transaction.latest(for: monthly)有有效記錄,但已過期- 更關鍵的是:購買前
Transaction.unfinished中存在舊交易 - 本次
purchase()返回的也是舊交易鏈路中的交易,而非新的互動購買流程
這就解釋了為什麼「不彈窗」:
- StoreKit 先返回了可處理的舊交易(unfinished / 歷史鏈路)
- 你程式碼把它 finish 掉了
- 購買請求看起來「成功」,但沒有走到使用者期望的新購買確認彈窗路徑
4. 根因總結
本次問題不是單點,而是兩類因素疊加:
- 未完成交易(unfinished transaction)干擾購買流程
- 沙盒訂閱鏈路處於過期狀態(即使有 latest transaction,也可能不是 active entitlement)
最終表現就是:不彈窗、成功返回舊交易、權益仍為空。
5. 修復策略(實踐有效)
5.1 購買前主動清理 unfinished 交易
在啟動和點擊購買前,掃描 Transaction.unfinished,對 verified transaction 執行 finish(),避免舊交易攔截新購買流程。
5.2 保留並強化診斷日誌(上線前建議開啟)
關鍵日誌建議保留到上線前:
unfinished transaction快照subscription status(state、renewal info)currentEntitlements迭代數量latest(for:)的 transaction/expiration
5.3 權益判斷採用「雙通道」(可選增強)
若某些環境下 currentEntitlements 短暫為空,可用 latest(for:) 做回退判定(注意仍需校驗是否 active)。
6. 上線前 IAP 檢查清單
- 每次測試前確認 Sandbox 帳號訂閱狀態(是否已過期)
- 關鍵路徑列印
unfinished數量 - 測試「首次購買 / 過期後續訂 / 恢復購買」三類場景
- 檢查
currentEntitlements與latest是否一致 - 若出現「不彈窗」,先看是否被舊交易鏈路命中
7. 結語
「點擊購買不彈窗」不一定是 UI 或呼叫時機問題。
在 StoreKit 2 下,交易狀態、unfinished 佇列、訂閱生命週期都會影響最終表現。
上架前把這些日誌和清理動作補齊,能減少大量線上不可復現的 IAP 問題。