公開前のIAP結合テスト中に、典型的ですが非常に紛らわしい問題に遭遇しました。
- ユーザーが「購入を続ける」をタップ
- ボタンが「処理中」になる
- 数秒後に「購入を続ける」に戻る
- App Storeの購入確認ダイアログが表示されない
この記事では、調査の全手順、重要ログ、根本原因、そして公開前に実施できる修正方針をまとめます。
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. 根本原因のまとめ
今回の問題は単一要因ではなく、次の2点の重なりでした。
- 未完了取引(
unfinished transaction)が購入フローを妨げる - Sandboxのサブスクリプションチェーンが期限切れ状態(
latest transactionがあっても、必ずしもアクティブな entitlement ではない)
最終的な見え方は「ポップアップなし・成功に見える・権限は空」です。
5. 修正方針(実運用で有効)
5.1 購入前に unfinished 取引を能動的に掃除する
アプリ起動時と購入ボタン押下前に Transaction.unfinished を走査し、検証済み取引へ finish() を実行して、古い取引が新しい購入フローを横取りしないようにします。
5.2 診断ログを保持・強化する(公開前は有効化推奨)
公開前まで残すべき主要ログ:
unfinished transactionのスナップショットsubscription status(state, renewal info)currentEntitlementsの反復件数latest(for:)の transaction/expiration 情報
5.3 権限判定を「二重経路」にする(任意の強化)
環境によって currentEntitlements が一時的に空になる場合、latest(for:) をフォールバック判定に利用できます(ただしアクティブ状態の検証は必須)。
6. 公開前IAPチェックリスト
- 各テスト前にSandboxアカウントの購読状態(期限切れかどうか)を確認する
- 重要経路で
unfinished件数をログ出力する - 「初回購入 / 期限切れ後の更新 / 購入復元」の3シナリオをテストする
currentEntitlementsとlatestの整合性を確認する- 「ポップアップが出ない」ときは、まず古い取引経路にヒットしていないかを確認する
7. まとめ
「購入をタップしてもポップアップが出ない」問題は、UIや呼び出しタイミングだけが原因とは限りません。
StoreKit 2では、取引状態・unfinishedキュー・サブスクリプションのライフサイクルが最終挙動に直接影響します。
公開前にこれらのログと掃除処理を入れておくことで、本番で再現しにくいIAP不具合を大幅に減らせます。