using System; using PhxhSDK; using UnityEngine; using Unity.Services.Core; using UnityEngine.Purchasing; using System.Collections.Generic; using UnityEngine.Purchasing.Security; using Unity.Services.Core.Environments; public class IAPHandle : IStoreListener { private const string Environment = "production"; public Action SendReceiptAction; public Action SetApplicationUseId; public bool ClientVerify; public List ConsumableGoods; public List NonConsumableGoods; private Dictionary _pendingDic = new Dictionary(); private IStoreController _controller; private IExtensionProvider _extensions; private IAppleExtensions _appleExtensions; private IGooglePlayStoreExtensions _googlePlayStoreExtensions; private Action _onPurchaseSuccess; private Action _onPurchaseFail; public Action OnRestorePurchase; private bool _isInitialized; /// /// 当前购买商品 /// private string _curProduct; public IAPHandle() { try { InitUnityService(); } catch (Exception exception) { DebugUtil.LogError("异常:" + exception); } } #region 初始化 async void InitUnityService() { try { DebugUtil.Log("初始化Unity服务器"); var options = new InitializationOptions().SetEnvironmentName(Environment); await UnityServices.InitializeAsync(options).ContinueWith(task => OnInitUnityServiceSuccess()); StandardPurchasingModule.Instance().useFakeStoreAlways = false; } catch (Exception exception) { OnInitUnityServiceError(exception.Message); } } private void OnInitUnityServiceError(string message) { DebugUtil.LogError("unity server initialization failed, Error: " + message); } private void OnInitUnityServiceSuccess() { DebugUtil.Log("unity server initialization successful !!"); } /// /// 初始化商品 /// public void InitIap() { if (ConsumableGoods == null && NonConsumableGoods == null) return; DebugUtil.Log("初始化IAP商品及 UnityPurchasing"); var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance()); //Add Product foreach (var consumableGoods in ConsumableGoods) { builder.AddProduct(consumableGoods, ProductType.Consumable); } foreach (var nonConsumableGoods in NonConsumableGoods) { builder.AddProduct(nonConsumableGoods, ProductType.NonConsumable); } UnityPurchasing.Initialize(this, builder); } /// /// IStoreListener 初始化 /// void IStoreListener.OnInitialized(IStoreController controller, IExtensionProvider extensions) { _controller = controller; _extensions = extensions; _appleExtensions = extensions.GetExtension(); _googlePlayStoreExtensions = extensions.GetExtension(); if (string.IsNullOrEmpty(SDKManager.Instance.ApplicationUserName)) { Guid uid = Guid.NewGuid(); SDKManager.Instance.ApplicationUserName = uid.ToString(); } _appleExtensions.SetApplicationUsername(SDKManager.Instance.ApplicationUserName); DebugUtil.Log("IAP initialization successful"); DebugUtil.Log("用户 UUID :" + SDKManager.Instance.ApplicationUserName); _isInitialized = true; //FetchGoodsList(); } /// /// IAP 初始化失败 /// void IStoreListener.OnInitializeFailed(InitializationFailureReason error) { DebugUtil.LogError("Unity IAP遇到不可恢复的初始化错误调用,请检查配置或设备设置中是否禁用ipa"); } /// /// IAP 初始化失败 /// void IStoreListener.OnInitializeFailed(InitializationFailureReason error, string message) { DebugUtil.LogError("IAP OnInitializeFailed reason : " + message); switch (error) { case InitializationFailureReason.AppNotKnown: DebugUtil.LogError("IAP Is your App correctly uploaded on the relevant publisher console?"); break; case InitializationFailureReason.PurchasingUnavailable: DebugUtil.LogError("IAP Billing disabled!"); break; case InitializationFailureReason.NoProductsAvailable: DebugUtil.LogError("IAP No products available for purchase!"); break; default: throw new ArgumentOutOfRangeException(nameof(error), error, message); } } #endregion #region 购买 /// /// IAP 购买商品 /// public void BuyGoods(string productId, string actorId, Action onPurchaseSuccess, Action onPurchaseFail) { DebugUtil.Log("购买商品: {0}", productId); if (!_isInitialized || Application.internetReachability == NetworkReachability.NotReachable) return; _onPurchaseSuccess = onPurchaseSuccess; _onPurchaseFail = onPurchaseFail; _controller.InitiatePurchase(productId, actorId); } /// /// IAP 购买成功 /// public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e) { #if UNITY_IOS||UNITY_ANDROID var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.identifier); DebugUtil.Log("Google Play Public Key: " + GooglePlayTangle.Data()); DebugUtil.Log("Apple Public Key: " + AppleTangle.Data()); try { var result = validator.Validate(e.purchasedProduct.receipt); DebugUtil.Log("Receipt is valid. Contents:"); foreach (IPurchaseReceipt productReceipt in result) { DebugUtil.LogWarning("productID: {0}", productReceipt.productID); DebugUtil.LogWarning("purchaseDate: {0}", productReceipt.purchaseDate); DebugUtil.LogWarning("未解析: {0}", productReceipt.transactionID); AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt; if (null != apple) { _curProduct = productReceipt.productID; DebugUtil.Log("iOS Receipt: {0}", apple.originalTransactionIdentifier); if (!ClientVerify) { AddPendingOrder(apple.originalTransactionIdentifier, e.purchasedProduct); SendReceipt(apple.originalTransactionIdentifier); } else { if (_onPurchaseSuccess != null) { _onPurchaseSuccess.Invoke(_curProduct); } else { OnRestorePurchase?.Invoke(_curProduct); } } } GooglePlayReceipt google = productReceipt as GooglePlayReceipt; if (null != google) { _curProduct = productReceipt.productID; // 这是 Google 的订单 ID。 // 请注意,在沙盒中测试时此项为 null,因为 Google 的沙盒不提供订单 ID。 DebugUtil.LogWarning("Google transactionID: {0}", google.transactionID); DebugUtil.LogWarning("Google purchaseState: {0}", google.purchaseState); DebugUtil.LogWarning("Google purchaseToken: {0}", google.purchaseToken); //TODO 谷歌服务器验证 待测试 待确定验证哪个参数 if (!ClientVerify) { AddPendingOrder(google.transactionID, e.purchasedProduct); SendReceipt(google.transactionID); } else { if (_onPurchaseSuccess != null) { _onPurchaseSuccess.Invoke(_curProduct); } else { OnRestorePurchase?.Invoke(_curProduct); } } } } } catch (Exception exception) { DebugUtil.LogError("ProcessPurchase Result Error: " + exception); } if (ClientVerify) return PurchaseProcessingResult.Complete; return PurchaseProcessingResult.Pending; #else return PurchaseProcessingResult.Complete; #endif } /// /// 收集待验证凭证 /// void AddPendingOrder(string appleReceipt, Product product) { DebugUtil.LogWarning("添加待验证收据:{0}", appleReceipt); _pendingDic.TryAdd(appleReceipt, product); } /// /// 向服务器发送收据验证 /// private void SendReceipt(string receipt) { if (SendReceiptAction == null) DebugUtil.LogError("SendReceiptAction 为空"); SendReceiptAction?.Invoke(receipt); } /// /// 服务器验证完成移除待验证凭证 /// public void RemovePendingOrder(string receipt) { if (_pendingDic.ContainsKey(receipt)) { _controller.ConfirmPendingPurchase(_pendingDic[receipt]); _pendingDic.Remove(receipt); DebugUtil.LogWarning("移除待验证收据:{0}, 待验证凭证还有: {1}", receipt, _pendingDic.Count); } if (_pendingDic.Count == 0) { DebugUtil.LogWarning("订单 {0} 已完结, 购买的商品: {1}", receipt, _curProduct); _onPurchaseSuccess?.Invoke(_curProduct); } } /// /// IAP 购买失败 /// void IStoreListener.OnPurchaseFailed(Product product, PurchaseFailureReason failureReason) { if (failureReason == PurchaseFailureReason.PurchasingUnavailable) { DebugUtil.LogError("可能在设备设置中禁用了 IAP"); } _curProduct = null; _onPurchaseFail?.Invoke(); DebugUtil.LogError("Purchase of [{0}] failed for this reason: {1}", product.definition.id, failureReason); } #endregion //TODO 待完善 测试 /// /// 恢复交易 /// public void RestorePurchase(Action> onRestorePurchase) { if (!_isInitialized) return; var tempRestoreGoods = new List(); _extensions.GetExtension().RestoreTransactions(result => { if (result) { foreach (var product in _controller.products.all) { if (product.hasReceipt) { tempRestoreGoods.Add(product.definition.id); DebugUtil.Log("Products to be restored: " + product.definition.id); } } } else { DebugUtil.LogWarning("Failure of recovery."); } onRestorePurchase?.Invoke(tempRestoreGoods); }); } void FetchGoodsList() { Action onSuccess = () => { DebugUtil.Log("Successfully pulled to the store !!"); foreach (var product in _controller.products.all) { Debug.Log(product.definition.id); } }; Action onFailure = (InitializationFailureReason i) => { DebugUtil.LogError("Failed to pull store: " + i); }; } string GetAppReceipt() { var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance()); string receipt = builder.Configure().appReceipt; return receipt; } bool CanMakePayments() { var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance()); return builder.Configure().canMakePayments; } }