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; using UnityEngine.Purchasing.Extension; public class IAPHandle : IDetailedStoreListener { private const string Environment = "production"; public Action SendReceiptAction; public Action SetApplicationUseId; public Action OnBreakPurchase; 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; private Action> _onRestore; private bool _isInitialized; /// /// 当前购买商品 /// private string _curProduct; public IAPHandle() { try { InitUnityService(); } catch (Exception exception) { DebugUtil.LogError("异常:" + exception); } } #region 初始化 async void InitUnityService() { try { Debug.Log("初始化Unity服务器"); var options = new InitializationOptions().SetEnvironmentName(Environment); await UnityServices.InitializeAsync(options).ContinueWith(task => OnInitUnityServiceSuccess()); StandardPurchasingModule.Instance().useFakeStoreAlways = true; } catch (Exception exception) { OnInitUnityServiceError(exception.Message); } } private void OnInitUnityServiceError(string message) { DebugUtil.LogError("unity server initialization failed, Error: " + message); } private void OnInitUnityServiceSuccess() { Debug.Log("unity server initialization successful !!"); } /// /// 初始化商品 /// public void InitIap() { if (ConsumableGoods == null && NonConsumableGoods == null) return; Debug.Log("初始化IAP商品及 UnityPurchasing"); var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance()); //Add Product foreach (var consumableGoods in ConsumableGoods) { Debug.Log($"添加消耗商品:{consumableGoods}"); builder.AddProduct(consumableGoods, ProductType.Consumable); } foreach (var nonConsumableGoods in NonConsumableGoods) { Debug.Log($"添加非消耗商品:{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); Debug.Log("IAP initialization successful"); Debug.Log("用户 UUID :" + SDKManager.Instance.ApplicationUserName); _isInitialized = true; } public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription) { DebugUtil.LogError("购买 {0} 失败, 原因: {1} ", failureDescription.productId, failureDescription.message); } /// /// 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) { Debug.Log("购买商品: " + productId); #if !UNITY_EDITOR if (!_isInitialized || Application.internetReachability == NetworkReachability.NotReachable) return; _onPurchaseSuccess = onPurchaseSuccess; _onPurchaseFail = onPurchaseFail; _controller.InitiatePurchase(productId, actorId); #else DebugUtil.Log("请在真实环境下测试"); #endif } /// /// 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) { if (!IAPManager.Instance.ProductDic.TryGetValue(productReceipt.productID, out var productID)) continue; Debug.Log($"productID: {productReceipt.productID}"); Debug.Log($"purchaseDate: {productReceipt.purchaseDate}"); AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt; if (null != apple) { _curProduct = productReceipt.productID; Debug.Log("iOS Receipt: " + apple.originalTransactionIdentifier); if (!ClientVerify) { if (!IAPManager.Instance.OwnedNonConsumableGood(_curProduct)) { AddPendingOrder(apple.originalTransactionIdentifier, e.purchasedProduct); SendReceipt(apple.originalTransactionIdentifier, _curProduct); } else { Debug.Log($"非消耗品商品{_curProduct}已购买过"); } } else { _onPurchaseSuccess?.Invoke(_curProduct); } } GooglePlayReceipt google = productReceipt as GooglePlayReceipt; if (null != google) { _curProduct = productReceipt.productID; // 这是 Google 的订单 ID。 // 请注意,在沙盒中测试时此项为 null,因为 Google 的沙盒不提供订单 ID。 Debug.Log($"Google transactionID: {google.transactionID}"); Debug.Log($"Google purchaseState: {google.purchaseState}"); Debug.Log($"Google purchaseToken: {google.purchaseToken}"); //TODO 谷歌服务器验证 待测试 待确定验证哪个参数 if (!ClientVerify) { if (!IAPManager.Instance.OwnedNonConsumableGood(_curProduct)) { AddPendingOrder(google.transactionID, e.purchasedProduct); SendReceipt(google.transactionID, _curProduct); } } else { _onPurchaseSuccess?.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) { Debug.Log("添加待验证收据:{appleReceipt}"); _pendingDic.TryAdd(appleReceipt, product); } /// /// 向服务器发送收据验证 /// private void SendReceipt(string receipt, string product) { if (SendReceiptAction == null) DebugUtil.LogError("SendReceiptAction 为空"); SendReceiptAction?.Invoke(receipt, product); } /// /// 服务器验证完成移除待验证凭证 /// public void RemovePendingOrder(string receipt) { if (_pendingDic.ContainsKey(receipt)) { _controller.ConfirmPendingPurchase(_pendingDic[receipt]); _pendingDic.Remove(receipt); Debug.Log($"移除待验证收据:{receipt}, 待验证凭证还有: {_pendingDic.Count}"); } if (_pendingDic.Count == 0) { Debug.Log($"订单 {receipt} 已完结, 购买的商品: {_curProduct}"); _onPurchaseSuccess?.Invoke(_curProduct); } } /// /// Debug /// public void ConfirmPending() { foreach (var goods in _pendingDic) { _controller.ConfirmPendingPurchase(goods.Value); Debug.Log($" 完成订单:{goods.Key}"); } _pendingDic.Clear(); } /// /// 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; _onRestore = onRestorePurchase; var apple = _extensions.GetExtension(); apple.RestoreTransactions(HandleRestored); } private void HandleRestored(bool result, string message) { var tempRestoreGoods = new List(); if (result) { Debug.Log("恢复购买成功"); foreach (var product in _controller.products.all) { if (product.hasReceipt) { tempRestoreGoods.Add(product.definition.id); Debug.Log("Products to be restored: " + product.definition.id); } } _onRestore?.Invoke(tempRestoreGoods); } else { DebugUtil.LogWarning("购买恢复失败: {0}", message); } } 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; } /// /// 根据ID获取商品价格 /// public string GetGoodPrice(string productID) { var product = _controller.products.WithID(productID); if (product != null) { string price = product.metadata.localizedPriceString; DebugUtil.Log("Product price: " + price); return price; } DebugUtil.LogError("Product not found"); return null; } }