この記事はUnity IAP完全攻略への道:消耗型編(+コンビニ決済)を掘り下げた記事です
この記事では、基本的な処理は書かれているものの初心者にはわかりづらい点があります。
問題1・IAPクラスをどこに置くべきかわからない
「多分、UIやアイテム反映処理と別だよなあ…」←その通りです
問題2・「PurchaseProcessingResult.Pending」した購入プロセスがデバイスを終了させると無効化する
「Androidアプリのテストで遅延購入すると、反映されずに消費だけ行われる」ぞい…困った
※実は、購入ID(product.definition.id)の永続化が必要
問題3・遅延購入を有効化したら、思わぬ挙動が出た
初期化タイミングが重要です。
pendingしていた購入を有効化すると、その後すぐ購入処理が走ります。
ゲームデータが読み込まれていないのに購入処理をすると・・・どうなる?
以下解説します。
問題1・IAPクラスをどこに置くべきかわからない
常時利用できる場所に置く必要があります。
例えば、ゲーム開始画面にGameObjectを置きIAPをアタッチします。スクリプトは以下のようにシングルトンにできるようにしておくといいでしょう。
public class IAP : MonoBehaviour, IStoreListener { // -----------------------------------------// // 設定 // -----------------------------------------// // インスタンス public static PurchaseManager Instance { get; private set; } // コントローラー IStoreController storeController; IGooglePlayStoreExtensions googlePlayStoreExtensions; // -----------------------------------------// // 初期化 // -----------------------------------------// void Awake() { if(Instance == null) { Instance = this; Debug.Log("Purchase Manager Awake."); } else { Destroy(gameObject); } } // 以下その他処理 }
初期化は問題3で実装するため、とりあえずなしにしておきます。
※Start内で初期化すると問題が起こります…。
そして外部スクリプトから、呼び出せるような関数を作っておきます。
// IAP内 // 購入 public void PurchaseProduct(string dataId) { // ID設定 string productId = dataId; // 必要に応じてIDを変更 // チェック if(storeController != null && storeController.products.WithID(productId) != null) { storeController.InitiatePurchase(productId); // 購入処理を実行 Debug.Log($"Purchase Add:{productId}"); } else { Debug.LogError("Purchase failed: Product not found or not initialized."); } }
そして、アイテムショップから以下のコードで呼び出します。
IAP.Instance.PurchaseProduct(dataId);
IAPが初期化されていれば、上記のコードが実行可能となります。
上記のように、どこからでもアクセスできるようにしておきましょう!
問題2・3への対応も、この配置が良いようです。
問題2・「PurchaseProcessingResult.Pending」した購入プロセスがデバイスを終了させると無効化する
起動中はデバイスに購入待ちデータが保管されているんですが、永続化されていないんですよね。
そのため、起動中に購入待ち処理が完了すると問題なく購入が完了します。
(なお、読んでみても詳しく書いてないという…)
データの永続化には、以下のコードのように「購入したもののIDを、PendingまたはComplete時に保存(永続化)」が必要となります。
IDをリスト形式(List
保存するものはPlayFabでも、EasySaveでも何でも可能です。
// 購入プロセス開始 public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) { // プロダクト呼び出し var product = args.purchasedProduct; Debug.Log($"購入レシート処理開始: {product.definition.id}"); // コンビニ決済未完了 if(googlePlayStoreExtensions.IsPurchasedProductDeferred(product)) { Debug.Log("コンビニ決済未完了のためPendingとします"); // ここでproduct.definition.idを保存 return PurchaseProcessingResult.Pending; } // レシート送信開始 StartCoroutine(SendReceipt(product)); // レシート未送信時点ではPendingを返しトランザクションを張る Debug.Log("レシート未送信のためPendingとします"); // ここでproduct.definition.idを保存 return PurchaseProcessingResult.Pending; } // レシート送信 private IEnumerator SendReceipt(Product product) { Debug.Log($"レシート送信 Product:'{product.definition.id}'"); // サーバーに決済データを送信 var addPurchase = xxxxx; // 非同期処理を実装し、完了を待つ(例:Firebaseでデータを送る等) // ToDo yield return WaitForTask(addPurchase); if(addPurchase.IsCompletedSuccessfully) // 成功 { Debug.Log("レシート処理完了"); // ここでproduct.definition.idを削除 storeController.ConfirmPendingPurchase(product); } else // 失敗 { Debug.Log($"エラー: {addPurchase.Exception?.Message}"); } }
そして保存したIDを、デバイス再起動時に復活させる処理が初期化後に必要です。
// 初期化 public void InitializePurchasing() { // 初期化済み確認 if(storeController != null) return; Debug.Log("Initialize Purchasing"); // ビルダー立ち上げ var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance()); builder.Configure<IGooglePlayConfiguration>().SetDeferredPurchaseListener( product => Debug.Log("コンビニ決済開始") ); // 商品追加 foreach(XXXXX) { builder.AddProduct(dataId, ProductType.Consumable); } // 初期化 UnityPurchasing.Initialize(this, builder); // 保留の内容を確認 CheckPurchasePendings(); } // 保留中の内容を確認 void CheckPurchasePendings() { Debug.Log("Check Purchase Pendings:"); List<string> listId = xxx; // 読み込み // Pendingになっていたものリストを取得し、再確認 if(listId.Count > 0) { foreach(string productId in listId) { PurchaseProduct(productId); // 定義したPurchaseProductを実行し、復活 Debug.Log($"ProductId: {productId}"); } } else // else { Debug.Log("No Pendings."); } }
上記のように設定すると、遅延購入が可能となります!
問題3・遅延購入を有効化したら、思わぬ挙動が出た
ゲームデータの読み込みを少し遅延する方法(EasySaveやFirebase)で行うと、IAP初期化してすぐに購入処理が走るため困ったことが起こる場合があります。
かんたんに言うと、ゲームデータが消えます。
【思ってた読み込みフロー】
ゲームデータ読み込み
↓
IAP初期化
↓
遅延購入実行
↓
購入対応:ゲームデータ書き込み
【設定をミスったら起こる読み込みフロー】
IAP初期化
↓
遅延購入実行
↓
購入対応:ゲームデータ書き込み ← アイテムデータ等を読み取っていない段階で上書きをしてしまう
↓
ゲームデータ読み込み ← 遅延する
あっ・・・これはデータが消えますねえ…
そのため、ゲーム立ち上げ時にはIAPは初期化せずゲームデータの読み込みを行ってください。
その後、以下のコードでIAP初期化を行ってください。
IAP.Instance.InitializePurchasing()
まとめ
以上のように、IAPには思わぬ落とし穴があります。
注意して開発を進めましょう!
それでは~( ^^)/