StoreKitで課金機能を実装してつまずいた話

StoreKitで課金機能を実装してつまずいた話 トラブルシューティング

レビューを増やす工夫を試している中で、アプリに買い切りの追加機能を用意してみることにし、初めてStoreKitでのアプリ内課金を実装しました。無料機能だけを作ってきた今までとは違う種類のつまずきが多く、今回はその記録です。

1. App Store Connect側の商品登録を忘れて延々とエラーが出た

コード側の実装を先に済ませてテストしようとしたら、購入対象の商品情報がいつまで経っても取得できませんでした。

let products = try await Product.products(for: ["com.example.app.premium"])
// products が空のまま

原因は単純で、App Store Connect側で該当の商品(In-App Purchase)を登録し、なおかつ「送信可能な状態」まで設定を進めていなかったことでした。コードのプロダクトIDだけ用意しても、Apple側の管理画面での登録が完了していないと商品情報自体が取得できないのだと、当たり前ですが実際にハマるまで意識していませんでした。

2. Sandbox環境での購入テストのはずが本番の確認画面が出て焦った

商品登録後、実機でテスト購入をしようとしたら、テスト用ではなく本物の支払い確認画面のような表示が出て一瞬本気で焦りました。

設定 > App Store > サンドボックスアカウント
にテスト用のApple IDでサインインしておく

実機テストの際は、実際のApple IDではなく、App Store Connectで発行したSandbox(テスト用)アカウントで購入する必要があると知りました。見た目は本番の課金画面とほとんど同じなので紛らわしいのですが、金額が引き落とされることはなく、あくまでテスト用の確認フローだと理解してからは安心して検証できるようになりました。

3. 「購入の復元」処理を実装し忘れていた

一通り購入処理が動くようになって満足していたのですが、後から「機種変更した場合はどうなるのか」と気づき、購入済みの権利を復元する処理をまったく用意していないことに気づきました。

func restorePurchases() async {
    try? await AppStore.sync()
    // 購入済み商品を再チェックする処理
}

購入ボタンだけを実装して満足しがちですが、審査ガイドライン上も「復元」のボタンや導線を用意することが求められています。新規購入のことだけを考えていて、既存ユーザーが機種変更や再インストールをするケースをすっかり見落としていました。

4. 課金状態の判定をローカルのUserDefaultsだけに頼っていた

購入が完了したらUserDefaultsにフラグを立てるだけの実装にしていたのですが、UserDefaultsとSwiftDataで迷った話のときと同様、この設計の甘さに後から気づきました。アプリを削除して再インストールすると、購入したはずの機能が使えなくなってしまったのです。

// ✕ ローカルのフラグだけに頼ると、再インストールで消える
UserDefaults.standard.set(true, forKey: "isPremiumUnlocked")

// ○ 起動時にApp Storeへ購入状態を問い合わせる処理も必要
for await result in Transaction.currentEntitlements {
    // 実際に有効な購入があるかを確認
}

購入状態はローカルに保存するだけでなく、起動時にApple側の実際の購入履歴(Transaction)を確認して同期する処理が必要だと学びました。課金機能は「買えたら終わり」ではなく、「買った状態をどう正しく維持するか」まで含めて設計する必要があるのだと痛感しました。

まとめ

  • コード側のプロダクトIDだけでなく、App Store Connectでの商品登録・設定完了が必須
  • 実機テストにはSandboxアカウントが必要。見た目は本番に近いが課金は発生しない
  • 「購入の復元」処理は新規購入と同じくらい重要。機種変更・再インストールのケースを見落とさない
  • 購入状態をローカルのフラグだけに頼ると、再インストールで消えてしまう。起動時にApple側の購入履歴と同期する処理が必要

無料機能の実装とは異なる緊張感があり、お金が絡む分だけ「動けば良い」では済まない設計の重要性を実感した実装でした。


本記事は生成AI(Claude)を活用して執筆・編集しています。内容は実体験に基づき、執筆者本人が確認・監修しています。

コメント

タイトルとURLをコピーしました