namespace Guru { using System; using System.Linq; using UnityEngine; using UnityEngine.Purchasing; using UnityEngine.Purchasing.Security; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; public abstract class IAPServiceBase: IStoreListener where T: IAPServiceBase , new() { public static readonly int OrderRequestTimeout = 10; public static readonly int OrderRequestRetryTimes = 3; #region 属性定义 private const string Tag = "[IAP]"; private const string DefaultCategory = "Store"; private static bool _showLog; private ConfigurationBuilder _configBuilder; // 商店配置创建器 private IStoreController _storeController; private IExtensionProvider _storeExtensionProvider; private IAppleExtensions _appleExtensions; private IGooglePlayStoreExtensions _googlePlayStoreExtensions; private CrossPlatformValidator _validator; private Dictionary _products; protected Dictionary Products => _products; public bool IsInitialized => _storeController != null && _storeExtensionProvider != null && _model != null; private ProductInfo _curProductInfo = null; private string _curProductCategory = ""; public string CurrentBuyingProductId { get { if (_curProductInfo != null) { return _curProductInfo.Id; } return ""; } } private IAPModel _model; /// /// 是否是首次购买 /// public int PurchaseCount { get => _model.PurchaseCount; set => _model.PurchaseCount = value; } /// /// 是否是首个IAP /// public bool IsFirstIAP => PurchaseCount == 0; private byte[] _googlePublicKey; private byte[] _appleRootCert; private string _uid; private string _uuid; /// /// 服务初始化回调 /// public event Action OnInitResult; /// /// 恢复购买回调 /// public event Action OnRestored; public event Action OnBuyStart; public event Action OnBuyEnd; public event Action OnBuyFailed; public event Action OnGetProductReceipt; #if UNITY_IOS /// /// AppStore 支付, 处理苹果支付延迟反应 /// /// public Action OnAppStorePurchaseDeferred; #endif #endregion #region 单利模式 protected static T _instance; private static object _locker = new object(); public static T Instance { get { if (null == _instance) { lock (_locker) { _instance = Activator.CreateInstance(); _instance.OnCreatedInit(); } } return _instance; } } /// /// 组件创建初始化 /// protected virtual void OnCreatedInit() { Debug.Log("--- IAPService Init"); } #endregion #region 初始化 /// /// 初始化支付服务 /// public virtual void Initialize(string uid, bool showLog = false) { if (string.IsNullOrEmpty(uid)) uid = IPMConfig.IPM_UID; _uid = uid; _showLog = showLog; InitPurchasing(); } /// /// 带有校验器的初始化 /// /// /// /// /// public virtual void InitWithKeys(string uid, byte[] googlePublicKey, byte[] appleRootCert, bool showLog = false) { _googlePublicKey = googlePublicKey; _appleRootCert = appleRootCert; InitModel(); Initialize(uid, showLog); } public void SetUID(string uid) { if (_configBuilder != null && !string.IsNullOrEmpty(uid)) { _uid = uid; _configBuilder.Configure().SetObfuscatedAccountId(uid); Debug.Log($"[IAP] --- Set UID: {uid}"); } } public void SetUUID(string uuid) { if (_appleExtensions != null && !string.IsNullOrEmpty(uuid)) { _uuid = uuid; _appleExtensions.SetApplicationUsername(uuid); Debug.Log($"[IAP] --- Set UUID: {uuid}"); } } /// /// 初始化支付插件 /// protected virtual void InitPurchasing() { _configBuilder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance()); // 注入初始商品产品列表 var settings = GetProductSettings(); if (null != settings) { int len = settings.Length; if(_products != null) _products.Clear(); _products = new Dictionary(len); ProductSetting item; IDs ids; bool emptyIDs = false; for (int i = 0; i < len; i++) { item = settings[i]; ids = new IDs(); if (!string.IsNullOrEmpty(item.GooglePlayProductId)) { ids.Add(item.GooglePlayProductId, GooglePlay.Name); } else { #if UNITY_ADNROID emptyIDs = true; LogE($"[IAP] --- GoogleProductId is empty, please check the product setting: {item.ProductName}"); #endif } if (!string.IsNullOrEmpty(item.AppStoreProductId)) { ids.Add(item.AppStoreProductId, AppleAppStore.Name); } else { #if UNITY_IOS emptyIDs = true; LogE($"[IAP] --- AppleProductId is empty, please check the product setting: {item.ProductName}"); #endif } if (emptyIDs) { continue; } _configBuilder.AddProduct(item.ProductId, item.Type, ids); // 添加商品 // 建立本地的商品信息列表 if (string.IsNullOrEmpty(item.Category)) item.Category = DefaultCategory; _products[item.ProductId] = new ProductInfo(item); } } // 调用插件初始化 UnityPurchasing.Initialize(this, _configBuilder); } /// /// 初始化成功 /// /// /// /// public void OnInitialized(IStoreController controller, IExtensionProvider extensions) { _storeController = controller; _storeExtensionProvider = extensions; var uuid = IPMConfig.IPM_UUID; if(string.IsNullOrEmpty(_uid)) _uid = IPMConfig.IPM_UID; if (!string.IsNullOrEmpty(_uid) && string.IsNullOrEmpty(uuid)) { uuid = IDHelper.GenUUID(_uid); } LogI($"--- IAP Initialized Success. With UID: {_uid} UUID: {uuid} DeviceId: {IPMConfig.IPM_DEVICE_ID}"); #if UNITY_IOS _appleExtensions = extensions.GetExtension(); _appleExtensions.SetApplicationUsername(uuid); // SetUp UUID (8)-(4)-(4)-(12): xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // On Apple platforms we need to handle deferred purchases caused by Apple's Ask to Buy feature. // On non-Apple platforms this will have no effect; OnDeferred will never be called. _appleExtensions.RegisterPurchaseDeferredListener(item => { LogI("Purchase deferred: " + item.definition.id); OnAppStorePurchaseDeferred?.Invoke(item); }); var appReceipt = _configBuilder.Configure().appReceipt; if (!string.IsNullOrEmpty(appReceipt)) { LogI($"[IAP] --- AppReceipt: {appReceipt}"); } #elif UNITY_ANDROID _configBuilder.Configure().SetObfuscatedAccountId(_uid); // SetUp UID _googlePlayStoreExtensions = extensions.GetExtension(); //添加安装游戏后第一次初试化进行恢复购买的回调 只有安卓才有 _googlePlayStoreExtensions.RestoreTransactions(OnRestoreHandle); #endif foreach (var product in _storeController.products.all) { if (!product.availableToPurchase) { continue; } if (_products.ContainsKey(product.definition.id)) { _products[product.definition.id].SetProduct(product); } } InitValidator(); // 初始化订单验证器 OnInitResult?.Invoke(true); } /// /// 初始化失败 /// /// /// public void OnInitializeFailed(InitializationFailureReason error) { LogE($"--- IAP Initialized Fail: {error}"); OnInitResult?.Invoke(false); } /// /// 初始化失败 /// /// /// /// public void OnInitializeFailed(InitializationFailureReason error, string message) { LogE($"--- IAP Initialized Fail: {error} msg: {message}"); OnInitResult?.Invoke(false); } #endregion #region 数据查询 // /// 获取商品Info /// /// 商品名称 /// public ProductInfo GetInfo(string productName) { if(null == Products || Products.Count == 0 ) return null; return Products.Values.FirstOrDefault(c => c.Name == productName); } /// /// 通过商品ID获取对应的信息 /// /// 商品ID /// public ProductInfo GetInfoById(string productId) { if(null == Products || Products.Count == 0 ) return null; return Products.Values.FirstOrDefault(c => c.Id == productId); } /// /// 获取道具价格 /// /// /// public double GetProductPrice(string name) { if (_storeController == null || _storeController.products == null) { return Fallback(); } ProductInfo info = GetInfo(name); var product = _storeController.products.WithID(info.Id); if (product == null) return Fallback(); return (double)product.metadata.localizedPrice; double Fallback() { ProductInfo info = GetInfo(name); return info?.Price ?? 0.0; } } /// /// 获取道具价格(带单位 $0.01) /// /// /// public string GetProductPriceString(string name) { if (_storeController == null || _storeController.products == null) { return Fallback(); } ProductInfo info = GetInfo(name); var product = _storeController.products.WithID(info.Id); if (product == null) return Fallback(); return product.metadata.localizedPriceString; string Fallback() { ProductInfo info = GetInfo(name); var pr = info?.Price ?? 0.0; return "$" + pr; } } /// /// 获取 IAP 内置商品 /// /// /// public Product GetProduct(string productName) { if (_storeController != null && _storeController.products != null) { var info = GetInfo(productName); if (info != null) { return _storeController.products.WithID(info.Id); } Debug.LogError($"[IAP] --- can't find with name {productName}"); } // 商品不存在则返回 NULL Debug.LogError($"[IAP] --- _storeController is null or products is null or products.all.Length == 0"); return null; } /// /// 当前的商品是否已经持有收据了 /// /// /// public bool IsProductHasReceipt(string productName) { var product = GetProduct(productName); if (product != null) return product.hasReceipt; return false; } #endregion #region 订单验证器 /// /// 是否支持订单校验 /// /// private bool IsCurrentStoreSupportedByValidator() => IsGooglePlayStoreSelected() || IsAppleAppStoreSelected(); /// /// Google 商店支持 /// /// private bool IsGooglePlayStoreSelected() { var currentAppStore = StandardPurchasingModule.Instance().appStore; return currentAppStore == AppStore.GooglePlay; } /// /// Apple 商店支持 /// /// private bool IsAppleAppStoreSelected() { var currentAppStore = StandardPurchasingModule.Instance().appStore; return currentAppStore == AppStore.AppleAppStore || currentAppStore == AppStore.MacAppStore; } /// /// 初始化订单校验器 /// protected virtual void InitValidator() { if (IsCurrentStoreSupportedByValidator()) { try { if (_googlePublicKey != null && _appleRootCert != null) { _validator = new CrossPlatformValidator(_googlePublicKey, _appleRootCert, Application.identifier); } else { Analytics.LogCrashlytics(new Exception($"[IAP] Init Validator failed -> googlePublicKey: {_googlePublicKey} appleRootCert: {_appleRootCert}")); } } catch (NotImplementedException exception) { LogE("Cross Platform Validator Not Implemented: " + exception); } } } #endregion #region 恢复购买 /// /// 恢复购买 /// /// /// protected virtual void OnRestoreHandle(bool success, string msg) { LogI($"--- Restore complete: {success}: msg:{msg}" ); if (success) { bool isIAPUser = false; // 扫描所有商品, 追加用户属性 for (int i = 0; i < _storeController.products.all.Length; i++) { var product = _storeController.products.all[i]; if (product.hasReceipt) { isIAPUser = true; } } if(isIAPUser) SetIsIAPUser(true); } OnRestored?.Invoke(success, msg); } /// /// 恢复购买道具 /// public virtual void Restore() { if (!IsInitialized) return; #if UNITY_IOS _appleExtensions.RestoreTransactions(OnRestoreHandle); #elif UNITY_ANDROID _googlePlayStoreExtensions.RestoreTransactions(OnRestoreHandle); #endif } #endregion #region 购买流程 /// /// 购买商品 /// /// public virtual T Buy(string productName, string category = "") { if (!IsInitialized) { LogE("Buy FAIL. Not initialized."); OnBuyEnd?.Invoke(productName, false); return (T)this; } ProductInfo info = GetInfo(productName); if (info == null) { LogE($"Buy FAIL. No product with name: {productName}"); OnBuyEnd?.Invoke(productName, false); return (T)this; } Product product = _storeController.products.WithID(info.Setting.ProductId); if (product != null && product.availableToPurchase) { if (string.IsNullOrEmpty(category)) category = info.Category; _storeController.InitiatePurchase(product); _curProductInfo = info; _curProductCategory = category; // Analytics.IAPClick(_curProductCategory, product.definition.id); // Analytics.IAPImp(_curProductCategory, product.definition.id); // <--- Client should report this Event OnBuyStart?.Invoke(productName); return (T)this; } // 找不到商品 LogE($"Can't find product by name: {productName}, pay canceled."); OnPurchaseOver(false, productName); OnBuyEnd?.Invoke(productName, false); return (T)this; } /// /// 处理支付流程 /// /// /// /// public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent) { string productId = purchaseEvent.purchasedProduct.definition.id; ProductInfo info = GetInfoById(productId); bool success = false; string productName = ""; if (null != info) { success = true; productName = info.Name; SetIsIAPUser(success); // 设置用户属性标记 // 只有实际发生购买后才会有订单上报. 启动时的 Restore 操作自动调用支付成功. 这里做一个判定, 过滤掉订单的物品 if (_curProductInfo != null) { ReportPurchaseResult(purchaseEvent); // 订单上报 // 真实购买后上报对应的事件 if (IsFirstIAP) Analytics.FirstIAP(info.Id, info.Price, info.CurrencyCode); // 上报首次支付打点 Analytics.ProductIAP(info.Id,info.Id, info.Price, info.CurrencyCode); } var pp = purchaseEvent.purchasedProduct; if ( pp == null || string.IsNullOrEmpty(pp.receipt)) { string msg = $"{Tag} --- Purchased product is null or has no receipt!!"; Debug.LogError(msg); Analytics.LogCrashlytics(new Exception(msg)); } else { OnGetProductReceipt?.Invoke(pp.definition.id, pp.receipt, pp.appleProductIsRestored); } LogI($"{Tag} --- OnPurchaseSuccess :: purchase count: {PurchaseCount} productName: {productName}"); PurchaseCount++; // 记录支付次数 } else { string msg = $"{Tag} --- Purchased end, but can't find ProductInfo with ID: {productId}"; Debug.LogError(msg); Analytics.LogCrashlytics(new Exception(msg)); } LogI($"{Tag} --- Call OnBuyEnd [{productName}] :: {OnBuyEnd}"); OnBuyEnd?.Invoke(productName, success); OnPurchaseOver(success, productName); // 支付成功处理逻辑 ClearCurPurchasingProduct(); // 清除购买缓存 return PurchaseProcessingResult.Complete; // 直接Consume 掉当前的商品 } /// /// 支付失败 /// /// /// /// public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason) { string productId = product.definition.id; ProductInfo info = GetInfoById(productId); //上报点位,用户购买失败的原因 Analytics.IAPRetFalse(_curProductCategory, product.definition.id, failureReason.ToString()); LogI($"{Tag} --- OnPurchaseFailed :: failureReason = {failureReason}"); // 失败的处理逻辑 OnPurchaseOver(false, info.Name); OnBuyEnd?.Invoke(info.Name, false); // 失败原因 OnBuyFailed?.Invoke(info.Name, failureReason.ToString()); ClearCurPurchasingProduct(); // 清除购买缓存 } private void ClearCurPurchasingProduct() { _curProductInfo = null; _curProductCategory = ""; } /// /// 获取商品的本地化价格字符串 /// 如果商品不存在或者 IAP 尚未初始化完成则显示 "Loading" 字样 /// /// /// public string GetLocalizedPriceString(string productName) { return GetInfo(productName)?.LocalizedPriceString ?? "Loading"; } #endregion #region Log 输出 /// /// 日志输出 /// /// private static void LogI(object msg) { if (_showLog) Debug.Log($"{Tag} {msg}"); } private static void LogE(object msg) { if (_showLog) Debug.LogError($"{Tag} {msg}"); } private static void LogW(object msg) { if (_showLog) Debug.LogWarning($"{Tag} {msg}"); } #endregion #region 实现接口 /// /// 需要游戏侧继承并完成Blevel的取值上报 /// /// protected abstract int GetBLevel(); /// /// 获取商品品配置列表 /// /// protected virtual ProductSetting[] GetProductSettings() => GuruSettings.Instance.Products; /// /// 支付回调 /// /// 是否成功 /// 商品名称 protected abstract void OnPurchaseOver(bool success, string productName); #endregion #region 支付上报逻辑 /// /// 支付结果上报 /// protected virtual bool ReportPurchaseResult(PurchaseEventArgs args) { // 验证器判定 if (_validator == null) { // Debug.Log($"############ --- Validator is null"); LogE($"{Tag} --- Validator is null. Report Order failed."); Analytics.LogCrashlytics(new Exception($"IAPService can not report order because Validator is null!")); return false; } //---------------- All Report Information -------------------- int level = GetBLevel(); int orderType = args.purchasedProduct.definition.type == ProductType.Subscription ? 1 : 0; string productId = args.purchasedProduct.definition.id; // string appleReceiptString = ""; string userCurrency = args.purchasedProduct.metadata.isoCurrencyCode; double payPrice = decimal.ToDouble(args.purchasedProduct.metadata.localizedPrice); string scene = _curProductCategory ?? "Store"; bool isFree = false; if (orderType == 1) isFree = IsSubscriptionFreeTrailById(productId); string appleReceiptString = ""; //---------------- All Report Information -------------------- LogI($"--- Report b_level:[{level}] with product id:{args.purchasedProduct.definition.id} "); #if UNITY_EDITOR // // Editor 不做上报逻辑 LogI($"--- Editor Validate Success. But Editor can't report order."); return true; #endif IPurchaseReceipt[] allReceipts = null; try { #if UNITY_IOS // ---- iOS 订单验证, 上报打点信息 ---- var jsonData = JsonConvert.DeserializeObject(args.purchasedProduct.receipt); if (jsonData != null && jsonData.TryGetValue("Payload", out var recp)) { appleReceiptString = recp.ToString(); LogI($"--- [{productId}] iOS receipt: {appleReceiptString}"); } Debug.Log($"[IAP] --- Full receipt: \n{args.purchasedProduct.receipt}"); #endif allReceipts = _validator.Validate(args.purchasedProduct.receipt); // ---- Android 订单验证, 上报打点信息 ---- string offerId = ""; string basePlanId = ""; foreach (var receipt in allReceipts) { if (receipt == null) continue; if (receipt is GooglePlayReceipt googleReceipt) { ReportGoogleOrder(orderType, googleReceipt.productID, googleReceipt.purchaseToken, googleReceipt.orderID, googleReceipt.purchaseDate, level, userCurrency, payPrice, scene, isFree, offerId, basePlanId); } else if (receipt is AppleInAppPurchaseReceipt appleReceipt) { ReportAppleOrder(orderType, appleReceipt.productID, appleReceiptString, appleReceipt.transactionID, appleReceipt.purchaseDate, level, userCurrency, payPrice, scene, isFree); } else { string exmsg = $"--- [{productId}] :: Unknown Receipt Type: {receipt.GetType()} can't report order."; Analytics.LogCrashlytics(exmsg); LogE(exmsg); } } } catch (Exception ex) { LogE($" [IAPManager.RevenueUpload] got Exception: {ex.Message}"); Analytics.LogCrashlytics(new Exception($"[IAP] Unity report purchase data with b_level={level} got error: {ex.Message}")); return false; } LogI("--- All Receipt is valid ---"); return true; } #endregion #region IOS Orders Collection private HashSet iOSReceipts; public HashSet IOSReceiptCollection { get { // 读取订单信息 if (iOSReceipts == null) { iOSReceipts = new HashSet(); string raw = PlayerPrefs.GetString(nameof(IOSReceiptCollection), ""); if (!string.IsNullOrEmpty(raw)) { var arr = raw.Split(','); for (int i = 0; i < arr.Length; i++) { iOSReceipts.Add(arr[i]); } } } return iOSReceipts; } set { // 保存订单信息 iOSReceipts = value; PlayerPrefs.SetString(nameof(IOSReceiptCollection), string.Join(",", iOSReceipts)); PlayerPrefs.Save(); } } /// /// 添加订单信息 /// /// public void AddReceipt(string receipt) { if (!HasReceipt(receipt)) { IOSReceiptCollection.Add(receipt); } } /// /// 是否包含订单 /// /// /// public bool HasReceipt(string receipt) { return IOSReceiptCollection.Contains(receipt); } #endregion #region 用户标志位设置 /// /// 标记是否为付费用户 /// /// public static void SetIsIAPUser(bool value = true) { if (Instance != null && Instance._model != null && value) { Instance._model.SetIsIapUser(value); // 用户属性 } Analytics.SetUserProperty(Analytics.PropertyIsIAPUser, value ? "true" : "false"); } #endregion #region 数据初始化 private void InitModel() { _model = IAPModel.Load(); // 初始化 Model // 启动时查询 if(_orderRequests == null) _orderRequests = new Queue(20); // #if UNITY_EDITOR // Debug.Log($"----- IAP Model init -----"); // #elif UNITY_ANDROID #if UNITY_ANDROID if (_model.HasUnreportedGoogleOrder) { int i = 0; while (_model.googleOrders.Count > 0 && i < _model.googleOrders.Count) { var o = _model.googleOrders[i]; ReportGoogleOrder(o); i++; } } #elif UNITY_IOS if (_model.HasUnreportedAppleOrder) { int i = 0; while (_model.appleOrders.Count > 0 && i < _model.appleOrders.Count) { var o = _model.appleOrders[i]; ReportAppleOrder(o); i++; } } #endif } #endregion #region 订单上报队列 private bool isOrderSending = false; private Queue _orderRequests = new Queue(20); /// /// 上报 Google Order Request /// /// /// /// /// /// /// /// /// /// private void ReportGoogleOrder(int orderType, string productId, string token, string orderId, DateTime date, int level, string userCurrency, double payPrice, string scene, bool isFree = false, string offerId = "", string basePlanId = "") { var payedDate = TimeUtil.GetTimeStampString(date); var request = GoogleOrderRequest.Build(orderType, productId, token, orderId, payedDate, level, userCurrency, payPrice, scene, isFree, offerId, basePlanId); ReportNextOrder(request); } private void ReportGoogleOrder(GoogleOrderData data) { var request = GoogleOrderRequest.Build(data); ReportNextOrder(request); } /// /// 上报 Apple Order Request /// /// /// /// /// /// /// /// /// /// /// /// /// private void ReportAppleOrder(int orderType, string productId, string receipt, string orderId, DateTime date,int level, string userCurrency, double payPrice, string scene, bool isFree = false, string offerId = "", string basePlanId = "") { var payedDate = TimeUtil.GetTimeStampString(date); var request = AppleOrderRequest.Build(orderType, productId, receipt, orderId, payedDate, level, userCurrency, payPrice, scene, isFree, offerId, basePlanId); ReportNextOrder(request); } private void ReportAppleOrder(AppleOrderData data) { var request = AppleOrderRequest.Build(data); ReportNextOrder(request); } private void ReportNextOrder(RequestBase request) { if(_orderRequests == null) _orderRequests = new Queue(20); _orderRequests.Enqueue(request); if(isOrderSending) return; isOrderSending = true; OnSendNextOrder(); } /// /// 上报下一个订单数据 /// private void OnSendNextOrder() { if (_orderRequests != null && _orderRequests.Count > 0) { // 如果上报队列不为空, 则尝试上报 isOrderSending = true; var request = _orderRequests.Dequeue(); if (request == null) { // 跳过空请求 OnSendNextOrder(); return; } GoogleOrderRequest googleReq = request as GoogleOrderRequest; AppleOrderRequest appReq = request as AppleOrderRequest; if (googleReq != null) { if (_model.IsTokenExists(googleReq.token)) { OnSendNextOrder(); // 跳过上报过的 Google 订单 return; } _model.AddGoogleOrder(googleReq.orderData); // 缓存当前 orderData 等待上报后再消除 } else if( appReq != null) { if (_model.IsReceiptExist(appReq.receipt)) { OnSendNextOrder(); // 跳过上报过的 Apple 订单 return; } _model.AddAppleOrder(appReq.orderData); // 缓存当前 orderData 等待上报后再消除 } request.SetTimeOut(OrderRequestTimeout) .SetRetryTimes(OrderRequestRetryTimes) .SetSuccessCallBack(() => { //---------------- Success ------------------------ if (googleReq != null) { _model.AddToken(googleReq.token); // 记录当前的 Google 订单 _model.RemoveGoogleOrder(googleReq.orderData); // 成功后清除缓存 orderData } else if (appReq != null) { _model.AddReceipt(appReq.receipt); // 记录当前的 Apple 订单 _model.RemoveAppleOrder(appReq.orderData); // 成功后清除缓存 orderData } OnSendNextOrder(); // NEXT Order }) .SetFailCallBack(() => { //---------------- Fail ------------------------ if (googleReq != null) { ReportGoogleOrderLost(googleReq.orderData); // 上报 Google 订单缺失打点 } else if (appReq != null) { ReportAppleOrderLost(appReq.orderData); // 上报 Apple 订单缺失打点 } OnSendNextOrder(); // NEXT Order }) .Send(); } else { isOrderSending = false; } } private void ReportGoogleOrderLost(GoogleOrderData data) { Analytics.LogEvent("google_order_lost", new Dictionary() { ["data"] = data.ToString(), }, new Analytics.EventSetting() { EnableFirebaseAnalytics = true, EnableFacebookAnalytics = true, }); } private void ReportAppleOrderLost(AppleOrderData data) { Analytics.LogEvent("apple_order_lost", new Dictionary() { ["data"] = data.ToString(), }, new Analytics.EventSetting() { EnableFirebaseAnalytics = true, EnableFacebookAnalytics = true, }); } #endregion #region Subscription public static DateTime DefaultSubscriptionDate = new DateTime(1970, 1,1,0,0,0); private SubscriptionManager GetSubManager(string productName) { var product = GetProduct(productName); if (product != null && product.definition.type == ProductType.Subscription) { return new SubscriptionManager(product, null); } return null; } private SubscriptionManager GetSubManagerById(string productId) { var product = _storeController.products.WithID(productId); if (product != null && product.definition.type == ProductType.Subscription) { return new SubscriptionManager(product, null); } return null; } public bool IsSubscriptionFreeTrail(string productName) { if(!IsInitialized) return false; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().isFreeTrial() == Result.True; } return false; } public bool IsSubscriptionFreeTrailById(string productId) { if(!IsInitialized) return false; var smgr = GetSubManagerById(productId); if (smgr != null) { return smgr.getSubscriptionInfo().isFreeTrial() == Result.True; } return false; } public bool IsSubscriptionCancelled(string productName) { if(!IsInitialized) return false; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().isCancelled() == Result.True; } return false; } public bool IsSubscriptionAvailable(string productName) { if(!IsInitialized) return false; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().isSubscribed() == Result.True; } return false; } public bool IsSubscriptionExpired(string productName) { if(!IsInitialized) return false; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().isExpired() == Result.True; } return false; } public bool IsSubscriptionAutoRenewing(string productName) { if(!IsInitialized) return false; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().isAutoRenewing() == Result.True; } return false; } /// /// IntroductioryPrice Period /// /// /// public bool IsSubscriptionIntroductoryPricePeriod(string productName) { if(!IsInitialized) return false; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().isIntroductoryPricePeriod() == Result.True; } return false; } public DateTime GetSubscriptionExpireDate(string productName) { if(!IsInitialized) return DefaultSubscriptionDate; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo()?.getExpireDate() ?? DateTime.Now; } return DefaultSubscriptionDate; } public DateTime GetSubscriptionPurchaseDate(string productName) { if(!IsInitialized) return DefaultSubscriptionDate; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().getPurchaseDate(); } return DefaultSubscriptionDate; } public DateTime GetSubscriptionCancelDate(string productName) { if(!IsInitialized) return DefaultSubscriptionDate; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().getCancelDate(); } return DefaultSubscriptionDate; } public TimeSpan GetSubscriptionRemainingTime(string productName) { if(!IsInitialized) return TimeSpan.Zero; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().getRemainingTime(); } return TimeSpan.Zero; } public TimeSpan GetSubscriptionIntroductoryPricePeriod(string productName) { if(!IsInitialized) return TimeSpan.Zero; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().getIntroductoryPricePeriod(); } return TimeSpan.Zero; } public TimeSpan GetSubscriptionFreeTrialPeriod(string productName) { if(!IsInitialized) return TimeSpan.Zero; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().getFreeTrialPeriod(); } return TimeSpan.Zero; } public string GetSubscriptionInfoJsonString(string productName) { if(!IsInitialized) return ""; var smgr = GetSubManager(productName); if (smgr != null) { return smgr.getSubscriptionInfo().getSubscriptionInfoJsonString(); } return ""; } #endregion } }