#if USE_IAP using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Purchasing; using Touka; public class IAPTool : NormalSingleton, IStoreListener { public event Action OnPurchaseBegin; public event Action OnPurchaseDone; public event Action OnRestoreDone; public bool IsRestoring => mIsRestoring; private bool mIsRestoring = false; private static IStoreController m_StoreController; // 存储商品信息; private static IExtensionProvider m_StoreExtensionProvider; // IAP扩展工具; private bool m_PurchaseInProgress = false; // 是否处于付费中; private Dictionary mInitProductDic; private SubscriptionInfo mSubsInfo = null; public void Initialize() { if (m_StoreController == null && m_StoreExtensionProvider == null) InitUnityPurchase(); } private bool IsInitialized() { return m_StoreController != null && m_StoreExtensionProvider != null; } public void AddProducts(Dictionary pInitProductDic) { mInitProductDic = pInitProductDic; } // 初始化IAP; private void InitUnityPurchase() { if (IsInitialized()) return; // 标准采购模块; StandardPurchasingModule module = StandardPurchasingModule.Instance(); // 配置模式; ConfigurationBuilder builder = ConfigurationBuilder.Instance(module); // 注意ProductType的类型,Consumable是可以无限购买(比如水晶),NonConsumable是只能购买一次(比如关卡),Subscription是每月订阅(比如VIP); // 这里初始化没有添加平台信息,因为平台信息有的时候还存在bug,如果必须添加,也可以添加,没有问题,确保平台信息添加正确就行了。 foreach (string tID in IAPProducts.ProductDic.Keys) { builder.AddProduct(tID, IAPProducts.ProductDic[tID]); } if (mInitProductDic != null && mInitProductDic.Count > 0) { foreach (string tID in mInitProductDic.Keys) { builder.AddProduct(tID, mInitProductDic[tID]); } } //初始化; UnityPurchasing.Initialize(this, builder); } #region Public Func // 根据ID给购买商品; public void BuyProductByID(string productId) { if (IsInitialized()) { if (m_PurchaseInProgress == true) return; Product product = m_StoreController.products.WithID(productId); if (product != null && product.availableToPurchase) { OnPurchaseBegin?.Invoke(); m_PurchaseInProgress = true; TKGDebugger.LogDebug(string.Format("Purchasing product asychronously: '{0}'", product.definition.id)); m_StoreController.InitiatePurchase(product); } else { TKGDebugger.LogDebug("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase"); } } else { TKGDebugger.LogDebug("BuyProductID FAIL. Not initialized."); } } // 确认购买产品成功; public void DoConfirmPendingPurchaseByID(string productId) { Product product = m_StoreController.products.WithID(productId); if (product != null && product.availableToPurchase) { if (m_PurchaseInProgress) { m_StoreController.ConfirmPendingPurchase(product); m_PurchaseInProgress = false; } } } // 恢复购买; public void RestorePurchases() { if (!IsInitialized()) { OnRestoreDone?.Invoke(false); TKGDebugger.LogDebug("RestorePurchases FAIL. Not initialized."); return; } if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.OSXPlayer) { TKGDebugger.LogDebug("RestorePurchases started ..."); mIsRestoring = true; var apple = m_StoreExtensionProvider.GetExtension(); apple.RestoreTransactions((result) => { mIsRestoring = false; OnRestoreDone?.Invoke(result); // 返回一个bool值,如果成功,则会多次调用支付回调,然后根据支付回调中的参数得到商品id,最后做处理(ProcessPurchase); TKGDebugger.LogDebug("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore."); }); } else { TKGDebugger.LogDebug("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform); } } #endregion #region IStoreListener Callback // IAP初始化成功回掉函数; public void OnInitialized(IStoreController controller, IExtensionProvider extensions) { TKGDebugger.LogDebug("IAP initialize Succeed!"); m_StoreController = controller; m_StoreExtensionProvider = extensions; // 这里可以获取您在AppStore和Google Play 上配置的商品; ProductCollection products = m_StoreController.products; Product[] all = products.all; for (int i = 0; i < all.Length; i++) { TKGDebugger.LogDebug(all[i].metadata.localizedTitle + "|" + all[i].metadata.localizedPriceString + "|" + all[i].metadata.localizedDescription + "|" + all[i].metadata.isoCurrencyCode); } #if UNITY_IOS m_StoreExtensionProvider.GetExtension().RegisterPurchaseDeferredListener(OnDeferred); #endif /* Dictionary introductory_info_dict = null; #if UNITY_IOS introductory_info_dict = m_StoreExtensionProvider.GetExtension().GetIntroductoryPriceDictionary(); #endif TKGDebugger.LogDebug("IAP - Available items:"); foreach (var item in controller.products.all) { if (item.availableToPurchase) { TKGDebugger.LogDebug("IAP - " + string.Join(" - ", new[] { item.metadata.localizedTitle, item.metadata.localizedDescription, item.metadata.isoCurrencyCode, item.metadata.localizedPrice.ToString(), item.metadata.localizedPriceString, item.transactionID, item.receipt })); // this is the usage of SubscriptionManager class if (item.receipt != null) { if (item.definition.type == ProductType.Subscription) { if (CheckIfProductIsAvailableForSubscriptionManagerC(item.receipt)) { string intro_json = (introductory_info_dict == null || !introductory_info_dict.ContainsKey(item.definition.storeSpecificId)) ? null : introductory_info_dict[item.definition.storeSpecificId]; SubscriptionManager p = new SubscriptionManager(item, intro_json); SubscriptionInfo info = p.getSubscriptionInfo(); mSubsInfo = info; TKGDebugger.LogDebug("product id is: " + info.getProductId()); TKGDebugger.LogDebug("purchase date is: " + info.getPurchaseDate()); TKGDebugger.LogDebug("subscription next billing date is: " + info.getExpireDate()); TKGDebugger.LogDebug("is subscribed? " + info.isSubscribed().ToString()); TKGDebugger.LogDebug("is expired? " + info.isExpired().ToString()); TKGDebugger.LogDebug("is cancelled? " + info.isCancelled()); TKGDebugger.LogDebug("product is in free trial peroid? " + info.isFreeTrial()); TKGDebugger.LogDebug("product is auto renewing? " + info.isAutoRenewing()); TKGDebugger.LogDebug("subscription remaining valid time until next billing date is: " + info.getRemainingTime()); TKGDebugger.LogDebug("is this product in introductory price period? " + info.isIntroductoryPricePeriod()); TKGDebugger.LogDebug("the product introductory localized price is: " + info.getIntroductoryPrice()); TKGDebugger.LogDebug("the product introductory price period is: " + info.getIntroductoryPricePeriod()); TKGDebugger.LogDebug("the number of product introductory price period cycles is: " + info.getIntroductoryPricePeriodCycles()); } else { TKGDebugger.LogDebug("This product is not available for SubscriptionManager class, only products that are purchase by 1.19+ SDK can use this class."); } } else { TKGDebugger.LogDebug("the product is not a subscription product"); } } else { TKGDebugger.LogDebug("the product should have a valid receipt"); } } } */ } // IAP初始化失败回掉函数(没有网络的情况下并不会调起,而是一直等到有网络连接再尝试初始化); public void OnInitializeFailed(InitializationFailureReason error) { switch (error) { case InitializationFailureReason.AppNotKnown: TKGDebugger.LogError("Is your App correctly uploaded on the relevant publisher console?"); break; case InitializationFailureReason.PurchasingUnavailable: TKGDebugger.LogDebug("Billing disabled! Ask the user if billing is disabled in device settings."); break; case InitializationFailureReason.NoProductsAvailable: TKGDebugger.LogDebug("No products available for purchase! Developer configuration error; check product metadata!"); break; } } private bool CheckIfProductIsAvailableForSubscriptionManagerC(string receipt) { var receipt_wrapper = (Dictionary)MiniJson.JsonDecode(receipt); if (!receipt_wrapper.ContainsKey("Store") || !receipt_wrapper.ContainsKey("Payload")) { TKGDebugger.LogDebug("The product receipt does not contain enough information"); return false; } var store = (string)receipt_wrapper ["Store"]; var payload = (string)receipt_wrapper ["Payload"]; if (payload != null ) { switch (store) { case GooglePlay.Name: { var payload_wrapper = (Dictionary)MiniJson.JsonDecode(payload); if (!payload_wrapper.ContainsKey("json")) { TKGDebugger.LogDebug("The product receipt does not contain enough information, the 'json' field is missing"); return false; } var original_json_payload_wrapper = (Dictionary)MiniJson.JsonDecode((string)payload_wrapper["json"]); if (original_json_payload_wrapper == null || !original_json_payload_wrapper.ContainsKey("developerPayload")) { TKGDebugger.LogDebug("The product receipt does not contain enough information, the 'developerPayload' field is missing"); return false; } var developerPayloadJSON = (string)original_json_payload_wrapper["developerPayload"]; var developerPayload_wrapper = (Dictionary)MiniJson.JsonDecode(developerPayloadJSON); if (developerPayload_wrapper == null || !developerPayload_wrapper.ContainsKey("is_free_trial") || !developerPayload_wrapper.ContainsKey("has_introductory_price_trial")) { TKGDebugger.LogDebug("The product receipt does not contain enough information, the product is not purchased using 1.19 or later"); return false; } return true; } case AppleAppStore.Name: case AmazonApps.Name: case MacAppStore.Name: { return true; } default: { return false; } } } return false; } // 支付成功处理函数; public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e) { m_PurchaseInProgress = false; TKGDebugger.LogDebug("Purchase OK: " + e.purchasedProduct.definition.id); // 消息结构 : Receipt: {"Store":"fake","TransactionID":"9c5c16a5-1ae4-468f-806d-bc709440448a","Payload":"{ \"this\" : \"is a fake receipt\" }"}; TKGDebugger.LogDebug("Receipt: " + e.purchasedProduct.receipt); OnPurchaseDone?.Invoke(e.purchasedProduct.definition.id, true); // 我们自己后台完毕的话,通过代码设置成功(如果是不需要后台设置直接设置完毕,不要设置Pending); return PurchaseProcessingResult.Complete; } // 支付失败回掉函数; public void OnPurchaseFailed(Product item, PurchaseFailureReason r) { TKGDebugger.LogDebug("Purchase OK: " + item.definition.id); m_PurchaseInProgress = false; OnPurchaseDone?.Invoke(item.definition.id, false); } // 购买延迟提示(这个看自己项目情况是否处理); public void OnDeferred(Product item) { TKGDebugger.LogDebug("Purchase deferred: " + item.definition.id); OnPurchaseDone?.Invoke(item.definition.id, false); } // 恢复购买功能执行回掉函数; public void OnTransactionsRestored(bool success) { TKGDebugger.LogDebug("Transactions restored : " + success); } #endregion #region custom functions public string GetPriceByID(string pID) { if (m_StoreController == null && m_StoreExtensionProvider == null) return ""; Product[] tProducts = m_StoreController.products.all; for (int i = 0; i < tProducts.Length; i++) { TKGDebugger.LogDebug(tProducts[i].metadata.localizedTitle + "|" + tProducts[i].metadata.localizedPriceString + "|" + tProducts[i].metadata.localizedDescription + "|" + tProducts[i].metadata.isoCurrencyCode); Product tItem = tProducts[i]; if (tItem.definition.id.Equals(pID)) { #if UNITY_ANDROID return tItem.metadata.GetGoogleProductMetadata().localizedPriceString; #else return tItem.metadata.localizedPriceString; #endif } } return ""; } public SubscriptionInfo GetSubscriptionInfo() { return mSubsInfo; } #endregion } #endif