実現したいこと
UnityIAPを使用してiOS用のアプリ内課金を実装したい。
前提
https://hirokuma.blog/?p=4552
のサイトを参考にさせていただきながらUnityでUnityIAPを使用してiOS用のアプリ内課金を実装したいが、以下のようなエラーが発生してしまっていて、先に進めないので、ご教示をお願いします。
発生している問題・エラーメッセージ
Purchase not correctly processed for product "PRODUCT-ID". Add an active IAPButton to process this purchase, or add an IAPListener to receive any unhandled purchase events.
該当のソースコード
C#
1using System; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.Events; 5using UnityEngine.Purchasing; 6using UnityEngine.Purchasing.Security; 7using UnityEngine.UI; 8using TMPro; 9using UnityEngine.Networking; 10 11public class IAPManager2nd : MonoBehaviour, IStoreListener 12{ 13 14 static IStoreController storeController; // Purchasing システムの参照 15 static IExtensionProvider storeExtensionProvider; // 拡張した場合のPurchasing サブシステムの参照 16 static string productIDNonConsumable = "PRODUCT-ID"; static string productNameAppleNonConsumable = "PRODUCT-ID"; // Apple App Store identifier for the non-consumable product. 17 18 19 public GameObject BuySuccessPanel; //購入成功パネル 20 public GameObject BuyFaildPanel; //購入失敗パネル 21 public GameObject BuyButton1; //購入ボタン 22 public GameObject BuyButtonText1; //購入ボタンのテキスト 23 public GameObject WaitPanel; //購入処理待機中 24 25 26 public enum PURCHASE_STATE 27 { 28 NOT_PURCHASED = 0, 29 PURCHASED = 1, 30 PENDING = 2, 31 }; 32 bool isInitialized = false; 33 34 void Awake() 35 { 36 if (storeController == null) { 37 // 初期化 38 InitializePurchasing(); 39 } 40 } 41 42 public void InitializePurchasing() 43 { 44 // If we have already connected to Purchasing ... 45 if (IsInitialized()) { 46 // ... we are done here. 47 return; 48 } 49 var module = StandardPurchasingModule.Instance(); 50 var builder = ConfigurationBuilder.Instance(module); 51 builder.AddProduct(productIDNonConsumable, ProductType.NonConsumable, new IDs() 52 { 53 { productNameAppleNonConsumable, AppleAppStore.Name }, 54 }); 55 UnityPurchasing.Initialize(this, builder); 56 } 57 58 private bool IsInitialized() 59 { 60 return storeController != null && storeExtensionProvider != null; 61 } 62 63 public void BuyNonConsumable() 64 { 65 BuyProductID(productIDNonConsumable); 66 } 67 68 public void BuyProductID(string productId) 69 { 70 try { 71 if (IsInitialized()) { 72 Product product = storeController.products.WithID(productId); 73 Debug.Log(product); 74 if (product != null && product.availableToPurchase) { 75 Debug.Log(string.Format("Purchasing product asychronously: '{0}' - '{1}'", product.definition.id, product.definition.storeSpecificId)); 76 WaitPanel.SetActive(true); 77 storeController.InitiatePurchase(product); 78 } 79 // Otherwise ... 80 else { 81 Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase"); 82 } 83 } else { 84 Debug.Log("BuyProductID FAIL. Not initialized."); 85 } 86 } catch (Exception e) { 87 Debug.Log("BuyProductID: FAIL. Exception during purchase. " + e); 88 } 89 } 90 public void RestorePurchases() 91 { 92 if (!IsInitialized()) { 93 Debug.Log("RestorePurchases FAIL. Not initialized."); 94 return; 95 } 96 // RestorePurchases started 97 Debug.Log("RestorePurchases started ..."); 98 // Fetch the Apple store-specific subsystem. 99 var apple = storeExtensionProvider.GetExtension<IAppleExtensions>(); 100 // Begin the asynchronous process of restoring purchases. Expect a confirmation response in the Action<bool> below, and ProcessPurchase if there are previously purchased products to restore. 101 apple.RestoreTransactions((result,errmsg) => { 102 // The first phase of restoration. If no more responses are received on ProcessPurchase then no purchases are available to be restored. 103 Debug.Log("RestorePurchases continuing: " + result + ":" + errmsg + ". If no further messages, no purchases available to restore."); 104 }); 105 } 106 PURCHASE_STATE checkAppleReceipt(string receipt) 107 { 108 PURCHASE_STATE resultstate = PURCHASE_STATE.NOT_PURCHASED; 109 var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), 110 AppleTangle.Data(), Application.identifier); 111 112 try { 113 var result = validator.Validate(receipt); 114 Debug.Log("Receipt is valid. Contents:"); 115 foreach (IPurchaseReceipt productReceipt in result) { 116 Debug.Log(productReceipt.productID); 117 Debug.Log(productReceipt.purchaseDate); 118 Debug.Log(productReceipt.transactionID); 119 AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt; 120 if (null != apple) { 121 Debug.Log(apple.originalTransactionIdentifier); 122 Debug.Log(apple.subscriptionExpirationDate); 123 Debug.Log(apple.cancellationDate); 124 Debug.Log(apple.quantity); 125 resultstate = PURCHASE_STATE.PURCHASED; 126 } else { 127 resultstate = PURCHASE_STATE.NOT_PURCHASED; 128 } 129 } 130 } catch (IAPSecurityException) { 131 Debug.Log("Invalid receipt, not unlocking content"); 132 resultstate = PURCHASE_STATE.NOT_PURCHASED; 133 } 134 return resultstate; 135 } 136 137 public PURCHASE_STATE GetPurchaseState() 138 { 139 PURCHASE_STATE resultstate; 140 if (storeController != null) { 141 if (storeController.products.all[0].hasReceipt) { 142 resultstate = checkAppleReceipt(storeController.products.all[0].receipt); 143 } else { 144 resultstate = PURCHASE_STATE.NOT_PURCHASED; 145 } 146 } else { 147 resultstate = PURCHASE_STATE.NOT_PURCHASED; 148 } 149 return resultstate; 150 } 151 152 // 153 // --- IStoreListener 154 // 155 public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason) 156 { 157 Debug.Log("Purchase failed: " + failureReason); 158// Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason)); 159 BuyFaildPanel.SetActive(true); 160 Debug.Log("BuyFaildPanel : ON"); 161 WaitPanel.SetActive(false); 162 Debug.Log("WaitPanel : OFF"); 163 } 164 public void OnPurchaseFailed(PurchaseFailureReason failureReason,string errmsg) 165 { 166 Debug.Log("Purchase failed: " + failureReason + ":e:" + errmsg); 167 BuyFaildPanel.SetActive(true); 168 WaitPanel.SetActive(false); 169 } 170 public string GetlocalizedPriceString() 171 { 172 string retstr = ""; 173 if (storeController != null) { 174 byte[] bytesData = System.Text.Encoding.UTF8.GetBytes(storeController.products.all[0].metadata.localizedPriceString); 175 if (bytesData[0] == 0xC2 && bytesData[1] == 0xA5) { 176 retstr = "\\"; 177 retstr += storeController.products.all[0].metadata.localizedPriceString.Substring(1, storeController.products.all[0].metadata.localizedPriceString.Length - 1); 178 retstr += "(" + storeController.products.all[0].metadata.isoCurrencyCode + ")"; 179 } else { 180 retstr = storeController.products.all[0].metadata.localizedPriceString; 181 retstr += "(" + storeController.products.all[0].metadata.isoCurrencyCode + ")"; 182 } 183 } else { 184 retstr = null; 185 186 } 187 return retstr; 188 } 189 public void OnInitializeFailed(InitializationFailureReason error) 190 { 191 Debug.Log("OnInitializeFailed InitializationFailureReason:" + error); 192 } 193 public void OnInitializeFailed(InitializationFailureReason error,string message) 194 { 195 Debug.Log("Initialization failed: " + error + "("+ message +")"); 196 } 197 198 199 public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) 200 { 201 if (String.Equals(args.purchasedProduct.definition.id, productIDNonConsumable, StringComparison.Ordinal)) { 202 Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id)); 203 checkAppleReceipt(args.purchasedProduct.receipt); 204 //オリジナルの購入完了処理 205 BuyButtonText1.GetComponent<TextMeshProUGUI>().text = "購入済み"; 206 BuyButton1.GetComponent<Button>().interactable = false; 207 BuySuccessPanel.SetActive(true); 208 } else { 209 Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id)); 210 } 211 WaitPanel.SetActive(false); 212 return PurchaseProcessingResult.Complete; 213 } 214 215 public void OnInitialized(IStoreController controller, IExtensionProvider extensions) 216 { 217 PURCHASE_STATE checkRes; 218 Debug.Log("OnInitialized: PASS"); 219 storeController = controller; 220 storeExtensionProvider = extensions; 221 var apple = storeExtensionProvider.GetExtension<IAppleExtensions>(); 222 223 if (storeController.products.all[0].hasReceipt) { 224 checkRes = checkAppleReceipt(storeController.products.all[0].receipt); 225 } else { 226 checkRes = PURCHASE_STATE.NOT_PURCHASED; 227 isInitialized = true; 228 } 229 230 if(checkRes==PURCHASE_STATE.PURCHASED){ 231 BuyButtonText1.GetComponent<TextMeshProUGUI>().text = "購入済み"; 232 BuyButton1.GetComponent<Button>().interactable = false; 233 } 234 } 235}
試したこと
実機で試した時は購入確認画面が表示され、キャンセルするとWaitPanelが閉じない状態だったので、155行目の所でエラーが発生しているものと思いデバッグメッセージを変更してみたりしたが、変わらなかった。
そこで、とりあえずUnity上で見てみようと思い、シミュレータで実行したら、上記のエラーとなった。
今度は、購入確認画面すら表示されないので、68行目からのBuyProducIDが怪しいと、デバッグメッセージをいくつか置いてもみた。
75行目のメッセージ表示と、76行目のパネルは表示されたので、77行目でエラーになっていることが判明はした。
InitiatePurchaseなので、引数を直接ProductIdにしてみたりもしたが、同じエラーが表示されてしまった。
エラーの内容からコードレスIAPと認識されている可能性があるとは思うが、参考にさせていただいたサイトでは特にコードレスについて言及していないため、コードありきでのものと認識している。
補足情報(FW/ツールのバージョンなど)
Unity 2021.3.18f1
UnityIAP 4.7.0
Xcode 14.3.1
確認用実機 iPhone7 iOS15.7.6?

回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。