はじめに
こんにちは、東京スタジオでクライアントエンジニアをしている山城です。
前回の記事ではUnityIAPの導入と実装を紹介しましたが課金フロー全体の流れを解説できていなかったため、今回は実際のフローについてシーケンス図を用いて解説していこうと思います。
シーケンスの構成
シーケンスは大きく分けて「ストア初期化・課金処理実行フェーズ」と「レシート検証・商品付与フェーズ」に分けて解説していきます。
今回解説するシーケンスには以下のオブジェクトが登場します。
- MyInAppPurchase
UnityIAPの制御を行うオブジェクト
IStoreListenerを継承した処理も含める - UnityIAP
Unityから提供されている課金関連処理
UnityPurchasingやIStoreController、IExtensionProvider、CrossPlatformValidatorの処理 - StoreServer
GooglePlayやAppStoreのサーバ
決済時やレシート検証時にアクセスする - MyServer
自前のサーバ
レシート検証や商品付与を行う
ストア初期化・課金処理実行フェーズ
以下がUnityIAPでの初期化〜プロダクトの購入成功のシーケンス図になります。
レシート検証はアプリの形態によって変わるため、別途シーケンス図を分けています。
大まかに分けて初期化とその成功・失敗、課金処理の開始と成功・失敗の2つの流れがあります。
- 1-1. 初期化開始
IStoreListenerを継承したクラスのインスタンスと、取得したいプロダクトIDのリストをUnityPurchasing.Initializeに登録します - 1-2. 初期化・プロダクト情報取得
UnityIAPがUnityPurchasing.Initializeに渡されたプロダクトIDのリストを元に、ストアに登録されているプロダクト情報を取得します - 1-3. 初期化失敗
初期化失敗の原因がIStoreListener.OnInitializeFailedに送られます - 1-4. 初期化・プロダクト情報取得成功
課金成功のイベントをIStoreListener.OnInitializedに返します - 2-1. 課金処理開始
購入するプロダクトIDをIStoreController.InitiatePurchaseに渡します - 2-2. 課金処理
ストアのモーダルダイアログが表示されます - 2-3. 決済処理
ユーザーが課金を承認した場合、ストアの決済処理が実行されます - 2-4. 購入失敗
決済の失敗やキャンセル等で購入に失敗した原因がIStoreListener.OnPurchaseFailedに送られます - 2-5. 決済成功・ストアトランザクション開始
購入に成功したプロダクト情報がIStoreListener.ProcessPurchaseに渡されます
レシート検証・商品付与フェーズ
UnityIAPにより返された結果であれ、ハッキングによって改ざんされた購入情報が渡される可能性があります。
そのため、何かしらの方法でレシート情報の検証を行う必要があります。
レシート検証にはUnityIAPの機能であるCrossPlatformValidatorを使用したローカル検証と、自前のサーバ経由でストアのAPIを使用するリモート検証がありますので、開発されるアプリの形態によって実装を行ってください。
ローカルでのレシート検証
サーバを介さないアプリであれば、CrossPlatformValidatorを使用したローカルでのレシート検証を行います。
実装の詳細は公式ドキュメントを参照してください。
https://docs.unity3d.com/ja/2019.4/Manual/UnityIAPValidatingReceipts.html
このシーケンスではすべてIStoreListener.ProcessPurchase内で処理しています。
- 3a-1. レシート検証開始
CrossPlatformValidator.Validateにレシートを設定します - 3a-2. レシート検証
UnityIAPがGooglePlayやAppStoreを通してレシート検証を行います - 3a-3. レシート検証失敗
IAPSecurityExceptionがthrowされます - 3a-4. エラー表示等の処理
IAPSecurityExceptionのエラー内容の表示等を行います
エラー原因によっては問い合わせへ誘導するなどしたほうがよいと思います - 3a-5 Pendingをreturnする
レシート検証に失敗したので、ストアトランザクションを継続させます - 3a-6. レシート検証成功
検証に成功したレシート情報が戻されます
GooglePlayとAppStoreで戻されたレシート情報の構成が異なることに注意してください - 3a-7. 商品の付与
購入された商品を付与してください - 3a-8. Completeをreturnする
ストアトランザクションを完了させます
リモートでのレシート検証
レシート検証は自前のサーバからGooglePlayやAppStoreのAPIを通すことでも行うことができます。
その場合サーバの応答待ちを行うことになるため、IStoreLitener.ProcessPurchaseでPendingを返しておき通信が完了し次第IStoreController.ConfirmPendingPurshaseを呼び出す必要があります。
自前のサーバでのレシート検証については各種ドキュメントを参照してください。
GooglePlayでのレシート検証については弊社デベロッパーズブログでも以前取り上げています。
https://developers.wonderpla.net/entry/2021/06/04/140228
一度ストアトランザクションを完了させず、サーバ側でのレシート検証成功後にIStoreController.ConfirmPendingPurchaseを呼び出して完了させる流れになります。
- 3b-1. ProcessPurchaseでPendingをreturnする
ストアトランザクションを継続させます - 3b-2. レシート情報の送信
自前のサーバにレシートを送信します - 3b-3. レシート検証
自前のサーバでGooglePlayやAppStoreのAPIを通してレシート検証を行います。 - 3b-4. レシート検証失敗
失敗理由を返します - 3b-5. エラー表示等の処理
失敗理由に応じて必要な対応を行ってください。
失敗理由には「処理済みレシート」や「不正なレシート」、「ストアサーバの応答なし・タイムアウト」等があります - 3b-6. ConfirmPendingPurchaseの呼び出し
処理済みレシートの場合はIStoreController.ConfirmPendingPurchaseを呼び出し、ストアトランザクションを完了させます
それ以外のエラーの場合は呼び出さず、ストアトランザクションを継続させます - 3b-7. 商品の付与
購入された商品を付与してください - b3-8 レシート検証・商品付与成功
商品付与の結果を返します - 3b-9. ConfirmPendingPurchaseの呼び出し
IStoreController.ConfirmPendingPurchaseを呼び出し、ストアトランザクションを完了させます
リストア処理
ストアでの決済後、IStoreLitener.ProcessPurchaseでCompleteをreturnするかIStoreController.ConfirmPendingPurchaseを呼び出さない限り、ストアトランザクションが完了にならず同じ商品を購入する時にエラーになります。
また、サーバを介さないアプリでも決済直後にクラッシュする等のトラブルでリストア処理が必要になることはあります。
その対応のためにも、初期化成功後のどこかのタイミングでリストア処理を実装してください。
リストアのフロー自体は至ってシンプルです。
- 4-1. RestoreTransactions
IExtensionProvider.GetExtensionでIAppleExtensionsかIGooglePlayStoreExtensionsを取得し、RestoreTransactionsを呼び出します
引数のコールバックはリストアが終了した時に呼び出されます。 - 4-2. IStoreListener.ProcessPurchase
リストア状態のプロダクトがある場合、IStoreListener.ProcessPurchaseが呼び出されます - 4-3. リストアの終了
リストア状態だったプロダクトのストアトランザクションが完了した、もしくはリストアするプロダクトがなかった場合に引数がtrueで呼び出されます
まとめ
今回は以前解説した実装内容をもとに、課金シーケンスを解説してみました。
課金処理はレシート検証やリストア処理など独特なシーケンスを要求する部分があるため、本記事が全体の流れを掴む一助になれれば幸いです。