From 5a52fc0987b38cb348682c396c23e38120a4d8f8 Mon Sep 17 00:00:00 2001 From: huyufei Date: Thu, 9 May 2024 20:25:13 +0800 Subject: [PATCH] + Opt IAPService OrderData, add orderId, payedDate, productId to tch001 EVENT. --- .../Analytics/Analytics.TemplateDefine.cs | 44 +++- .../GuruCore/Runtime/ExtensionKit/TimeUtil.cs | 6 + .../IPM/Scripts/RequestData/AppleOrderData.cs | 41 ++-- .../IPM/Scripts/RequestData/BaseOrderData.cs | 59 ++++++ .../Scripts/RequestData/BaseOrderData.cs.meta | 3 + .../Scripts/RequestData/GoogleOrderData.cs | 51 +++-- .../IPM/Scripts/Requests/AppleOrderRequest.cs | 29 ++- .../Scripts/Requests/GoogleOrderRequest.cs | 76 ++----- .../GuruIAP/Runtime/Code/IAPServiceBase.cs | 189 +++++++++++------- package.json | 2 +- 10 files changed, 292 insertions(+), 208 deletions(-) create mode 100644 Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/BaseOrderData.cs create mode 100644 Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/BaseOrderData.cs.meta diff --git a/Runtime/GuruCore/Runtime/Analytics/Analytics.TemplateDefine.cs b/Runtime/GuruCore/Runtime/Analytics/Analytics.TemplateDefine.cs index ba35100..f151c53 100644 --- a/Runtime/GuruCore/Runtime/Analytics/Analytics.TemplateDefine.cs +++ b/Runtime/GuruCore/Runtime/Analytics/Analytics.TemplateDefine.cs @@ -308,25 +308,34 @@ namespace Guru } } public static bool EnableTch02Event { get; set; } = false; // 是否使用太极02事件(请手动开启) - + /// /// 太极001 IAP收入 /// 每发生一次iap收入,触发一次该事件,value值为本次iap的收入值; /// /// 中台返回地收入值 - public static void Tch001IAPRev(double value) + /// + /// + /// + /// + public static void Tch001IAPRev(double value, string productId, string orderId, string orderType, string timestamp) { - TchRevEvent(EventTchAdRev001Impression, IAPPlatform, value); + TchRevEvent(EventTchAdRev001Impression, IAPPlatform, value, orderType, productId, orderId, timestamp); if(EnableTch02Event) return; // 如果使用了太极02 则不做FB上报 FBPurchase(value, USD, "iap", IAPPlatform); } - + /// /// 太极02 IAP 收入打点 /// 发生一次iap收入,触发一次该事件,value值为本次iap的收入值; /// /// + /// + /// + /// + /// + // public static void Tch02IAPRev(double value, string productId, string orderId, string orderType, string timestamp) public static void Tch02IAPRev(double value) { if (!EnableTch02Event) return; @@ -371,23 +380,38 @@ namespace Guru //FB标准购买事件 FBPurchase(value, USD, "ads", AdMAX); } - + /// /// 太极事件点位上报 /// /// /// /// - private static void TchRevEvent(string evtName, string platform, double value) + /// + /// + /// + /// + private static void TchRevEvent(string evtName, string platform, double value, + string orderType = "", string productId = "", string orderId = "", string timestamp = "") { - LogEvent(evtName, new Dictionary() + var data = new Dictionary() { { ParameterAdPlatform, platform }, { ParameterCurrency, USD }, { ParameterValue, value }, - }); + }; + + //--------- Extra data for IAP receipt --------------- + if(!string.IsNullOrEmpty(orderType)) data.Add("order_type", orderType); + if(!string.IsNullOrEmpty(productId)) data.Add("product_id", productId); + if(!string.IsNullOrEmpty(orderId)) data.Add("order_id", orderId); + if(!string.IsNullOrEmpty(timestamp)) data.Add("payed_date", timestamp); + //--------- Extra data for IAP receipt --------------- + + LogEvent(evtName, data); } + /// /// Facebook 支付上报 /// @@ -528,7 +552,7 @@ namespace Guru /// /// 新用户首次 IAP 付费成功上报 (仅限应用内付费商品,不包含订阅等其它情况)【买量打点】 /// - /// product_id 商品ID + /// productId 商品ID /// 付费总金额 /// 币种 public static void FirstIAP(string itemName, double value, string currency) @@ -545,7 +569,7 @@ namespace Guru /// 商品购买成功上报【买量打点】 /// /// 商品名称(商品ID一样) - /// product_id 商品ID + /// productId 商品ID /// 付费总金额 /// 币种 public static void ProductIAP(string productName, string itemName, double value, string currency) diff --git a/Runtime/GuruCore/Runtime/ExtensionKit/TimeUtil.cs b/Runtime/GuruCore/Runtime/ExtensionKit/TimeUtil.cs index 36d7313..2526ed6 100644 --- a/Runtime/GuruCore/Runtime/ExtensionKit/TimeUtil.cs +++ b/Runtime/GuruCore/Runtime/ExtensionKit/TimeUtil.cs @@ -33,6 +33,12 @@ namespace Guru { return dateTime.Ticks / TICK_TO_MILLISECOND - STAMP_1970_1_1; } + + public static string GetTimeStampString(DateTime dateTime) + { + return GetTimeStamp(dateTime).ToString(); + } + /// /// 时间戳转日期 diff --git a/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/AppleOrderData.cs b/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/AppleOrderData.cs index 55d0aac..2a15496 100644 --- a/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/AppleOrderData.cs +++ b/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/AppleOrderData.cs @@ -1,49 +1,44 @@ +using PlasticPipe.PlasticProtocol.Messages; + namespace Guru { using System; using System.Collections.Generic; using Newtonsoft.Json; + /// + /// 该数据用于向服务端发送对应的消息 + /// 同时兼具缓存用户数据 + /// 详见 https://github.com/castbox/backend-dev/blob/main/saas/%E4%B8%AD%E5%8F%B0%E6%9C%8D%E5%8A%A1%E6%8E%A5%E5%85%A5%E6%89%8B%E5%86%8C.md#553apple-store%E8%AE%A2%E5%8D%95%E4%B8%8A%E6%8A%A5 + /// [Serializable] - public class AppleOrderData + public class AppleOrderData : BaseOrderData { - public string guid; - public int orderType; - public string productId; - public string bundleId; - public string receipt; - public string country; - public int level; - public Dictionary userInfo; - public EventConfig eventConfig; - - public AppleOrderData(int orderType, string productId, string receipt, int level) + + public string bundleId; //应用包名 + public string receipt; // Apple Store返回的Receipt数据 + public string country; // 用户商店的国家2字大写代码 + + public AppleOrderData(int orderType, string productId, string receipt, string orderId, + string date, int level, string offerId = "", string basePlanId = "") : base(orderType, productId, orderId, date, level, offerId, basePlanId) { - guid = Guid.NewGuid().ToString(); - this.orderType = orderType; - this.productId = productId; this.receipt = receipt; - this.level = level; bundleId = GuruSettings.Instance.GameIdentifier; country = IPMConfig.IPM_COUNTRY_CODE; - userInfo = new Dictionary { ["level"] = level }; - eventConfig = EventConfig.Build(); } public bool Equals(AppleOrderData other) { - if (string.IsNullOrEmpty(guid)) guid = Guid.NewGuid().ToString(); + if (string.IsNullOrEmpty(guid)) guid = GetGuid(); return guid.Equals(other.guid); } - - - public string GetId() => productId; + public override string ToString() { - return $"{nameof(orderType)}: {orderType}, {nameof(productId)}: {productId}, {nameof(bundleId)}: {bundleId}, {nameof(receipt)}: {receipt}, {nameof(country)}: {country}"; + return "AppleOrderData: " + base.ToString() + $", {nameof(bundleId)}: {bundleId}, {nameof(receipt)}: {receipt}, {nameof(country)}: {country}"; } public string ToJson() => JsonConvert.SerializeObject(this); diff --git a/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/BaseOrderData.cs b/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/BaseOrderData.cs new file mode 100644 index 0000000..8878b5a --- /dev/null +++ b/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/BaseOrderData.cs @@ -0,0 +1,59 @@ + + + + + + +namespace Guru +{ + using System; + using System.Collections.Generic; + using Newtonsoft.Json; + + public class BaseOrderData + { + [JsonIgnore] + public string guid; // 唯一标识 + public string productId; // 商品ID 当orderType=0时,传递该参数 + [JsonIgnore] + public int level; // 关卡 ID + + public int orderType; // 订单类型,可选值:0:IAP 订单 1:SUB 订阅订单 + public Dictionary userInfo; // 当前用户信息。目前包含: level: 用户属性中的"b_level"的值 + public EventConfig eventConfig; // 事件打点所需信息 + + //---- Offer Data ------- + [JsonIgnore] + public string orderId; // 订单的 OrderID + [JsonIgnore] + public string payedDate; // 支付时间 (13位时间戳) + public string basePlanId; // 订阅商品的planId + public string offerId; // 订阅商品的offerId + + + public BaseOrderData(int orderType, string productId, string orderId, + string payedDate, int level, string offerId = "", string basePlanId = "") + { + guid = GetGuid(); + this.orderType = orderType; + this.productId = productId; + this.orderId = orderId; + this.payedDate = payedDate; + this.basePlanId = basePlanId; + this.offerId = offerId; + this.level = level; + userInfo = new Dictionary { ["level"] = level }; + eventConfig = EventConfig.Build(); + } + + protected string GetGuid() => Guid.NewGuid().ToString(); + + public string OrderType() => orderType == 0 ? "IAP" : "SUB"; + + public override string ToString() + { + return $"{nameof(orderType)}: {orderType}, {nameof(productId)}: {productId}, {nameof(orderId)}: {orderId}, {nameof(payedDate)}: {payedDate}"; + } + + } +} \ No newline at end of file diff --git a/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/BaseOrderData.cs.meta b/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/BaseOrderData.cs.meta new file mode 100644 index 0000000..0d260de --- /dev/null +++ b/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/BaseOrderData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e576b9cd108047ec91aac82aa9aca5c6 +timeCreated: 1715252456 \ No newline at end of file diff --git a/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/GoogleOrderData.cs b/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/GoogleOrderData.cs index 6d3acda..a0d8dfd 100644 --- a/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/GoogleOrderData.cs +++ b/Runtime/GuruCore/Runtime/IPM/Scripts/RequestData/GoogleOrderData.cs @@ -5,35 +5,30 @@ namespace Guru using System.Collections.Generic; using Newtonsoft.Json; + /// + /// 该数据用于向服务端发送对应的消息 + /// 同时兼具缓存用户数据 + /// 详见 https://github.com/castbox/backend-dev/blob/main/saas/%E4%B8%AD%E5%8F%B0%E6%9C%8D%E5%8A%A1%E6%8E%A5%E5%85%A5%E6%89%8B%E5%86%8C.md#552google%E8%AE%A2%E5%8D%95%E4%B8%8A%E6%8A%A5 + /// [Serializable] - public class GoogleOrderData + public class GoogleOrderData : BaseOrderData { - public string guid; - public int orderType; // 订单类型,可选值:0:IAP订单 1: 订阅订单 + private string _productId; + public string subscriptionId; // 订阅道具名称 public string packageName; //应用包名 - public string productId = ""; // 商品ID 当orderType=0时,传递该参数 - public string subscriptionId = ""; // 订阅ID // 当orderType=1时,传递该参数 public string token; // 应用商店里面的购买token - public int level; - public Dictionary userInfo; // 当前用户信息。目前包含: level: 用户属性中的"b_level"的值 - public string basePlanId = ""; // 订阅商品的planId - public string offerId = ""; // 订阅商品的offerId - public EventConfig eventConfig; // 事件打点所需信息 - - public GoogleOrderData(int orderType, string productId, string subscriptionId, string token, int level, - string offerId = "", string basePlanId = "") + + public GoogleOrderData(int orderType, string productId, string token, string orderId, + string date, int level, string offerId = "", string basePlanId = "") : base(orderType, productId, orderId, date, level, offerId, basePlanId) { - guid = Guid.NewGuid().ToString(); - this.orderType = orderType; + _productId = productId; this.packageName = GuruSettings.Instance.GameIdentifier; - this.productId = productId; - this.subscriptionId = subscriptionId; this.token = token; - this.offerId = offerId; - this.basePlanId = basePlanId; - this.level = level; - userInfo = new Dictionary { ["level"] = level }; - eventConfig = EventConfig.Build(); + if (orderType == 1) + { + this.subscriptionId = productId; + this.productId = ""; + } } public bool Equals(GoogleOrderData other) @@ -41,12 +36,14 @@ namespace Guru if (string.IsNullOrEmpty(guid)) guid = Guid.NewGuid().ToString(); return guid == other.guid; } - - public override string ToString() - { - return $"{nameof(orderType)}: {orderType}, {nameof(packageName)}: {packageName}, {nameof(productId)}: {productId}, {nameof(subscriptionId)}: {subscriptionId}, {nameof(token)}: {token}"; - } public string ToJson() => JsonConvert.SerializeObject(this); + + public string RealProductId => _productId; + + public override string ToString() + { + return "GoogleOrderData: " + base.ToString() + $",{nameof(subscriptionId)}: {subscriptionId}, {nameof(packageName)}: {packageName}, {nameof(token)}: {token}"; + } } } \ No newline at end of file diff --git a/Runtime/GuruCore/Runtime/IPM/Scripts/Requests/AppleOrderRequest.cs b/Runtime/GuruCore/Runtime/IPM/Scripts/Requests/AppleOrderRequest.cs index e30c6cd..e0cb832 100644 --- a/Runtime/GuruCore/Runtime/IPM/Scripts/Requests/AppleOrderRequest.cs +++ b/Runtime/GuruCore/Runtime/IPM/Scripts/Requests/AppleOrderRequest.cs @@ -13,26 +13,29 @@ namespace Guru public int orderType; public string productId; public string receipt; - public int level; public AppleOrderData orderData; public AppleOrderRequest(){} - public static AppleOrderRequest Build(int orderType, string productId, string receipt, int level) + public static AppleOrderRequest Build(int orderType, string productId, string receipt, string orderId, string date, int level) { var request = new AppleOrderRequest() { orderType = orderType, productId = productId, receipt = receipt, - level = level, - orderData = new AppleOrderData(orderType, productId, receipt, level), + orderData = new AppleOrderData(orderType, productId, receipt, orderId, date, level), }; return request; } public static AppleOrderRequest Build(AppleOrderData orderData) { - return Build(orderData.orderType, orderData.productId, orderData.receipt, orderData.level); + return Build(orderData.orderType, + orderData.productId, + orderData.receipt, + orderData.orderId, + orderData.payedDate, + orderData.level); } protected override string RequestURL => IPMConfig.IPM_URL + "order/api/v1/orders/ios"; @@ -57,18 +60,12 @@ namespace Guru if (responseData != null && responseData.data != null) { double usdPrice = responseData.data.usdPrice; - Analytics.Tch001IAPRev(usdPrice); + + Analytics.Tch001IAPRev(usdPrice, productId, orderData.orderId, orderData.OrderType(), orderData.payedDate); Analytics.Tch02IAPRev(usdPrice); - if (orderType == 0) - { - AdjustService.TrackIAPPurchase(usdPrice, productId); - Analytics.IAPPurchase(usdPrice, productId); - } - else if (orderType == 1) - { - AdjustService.TrackSubPurchase(usdPrice, productId); - Analytics.SubPurchase(usdPrice, productId); - } + + AdjustService.TrackSubPurchase(usdPrice, productId); + Analytics.SubPurchase(usdPrice, productId); } } catch (Exception ex) diff --git a/Runtime/GuruCore/Runtime/IPM/Scripts/Requests/GoogleOrderRequest.cs b/Runtime/GuruCore/Runtime/IPM/Scripts/Requests/GoogleOrderRequest.cs index 9b48d43..d801c55 100644 --- a/Runtime/GuruCore/Runtime/IPM/Scripts/Requests/GoogleOrderRequest.cs +++ b/Runtime/GuruCore/Runtime/IPM/Scripts/Requests/GoogleOrderRequest.cs @@ -1,44 +1,31 @@ -using System; -using System.Text; -using Guru.LitJson; -using UnityEngine; -using UnityEngine.Networking; namespace Guru { + using System; + using System.Text; + using UnityEngine; + using UnityEngine.Networking; + public class GoogleOrderRequest : RequestBase { - public int orderType; // 订单类型,可选值:0:IAP订单 1: 订阅订单 - public string productId; - public string subscriptionId; public string token; - public string packageName; - public int level; - public string basePlanId; - public string offerId; public GoogleOrderData orderData; public GoogleOrderRequest(){} - public GoogleOrderRequest(int orderType, string productId, string subscriptionId, - string token, int level, string offerId = "", string basePlanId = "") + public GoogleOrderRequest(int orderType, string productId, string subscriptionId, string token, string orderId, + string date, int level, string offerId = "", string basePlanId = "") { - this.orderType = orderType; - this.packageName = Application.identifier; - this.productId = productId; - this.subscriptionId = subscriptionId; this.token = token; - this.level = level; - orderData = new GoogleOrderData(orderType, productId, subscriptionId, token, level, offerId, basePlanId); + orderData = new GoogleOrderData(orderType, productId, token, orderId, date, level, offerId, basePlanId); } protected override string RequestURL => IPMConfig.IPM_URL + "order/api/v1/orders/android"; protected override UnityWebRequest CreateRequest() { - GoogleOrderData googleOrderData = orderData; - this.Log($"send orderData:{googleOrderData}"); + this.Log($"send orderData:{orderData}"); var request = new UnityWebRequest(RequestURL, "POST"); - request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(JsonMapper.ToJson(googleOrderData))); + request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(orderData.ToJson())); request.downloadHandler = new DownloadHandlerBuffer(); request.SetRequestHeader(IPMConfig.Header_Param_ContentType, IPMConfig.Header_Value_ContentType); request.SetRequestHeader(IPMConfig.Header_Param_APPID, IPMConfig.IPM_X_APP_ID); @@ -56,24 +43,15 @@ namespace Guru { // Analytics.Tch001IAPRev(responseData.data.usdPrice); double usdPrice = responseData.data.usdPrice; - Analytics.Tch001IAPRev(usdPrice); - Analytics.Tch02IAPRev(usdPrice); - - string pid = ""; - - if (!string.IsNullOrEmpty(productId)) - { - pid = productId; - AdjustService.TrackIAPPurchase(usdPrice, productId); // 上报 IAP 支付事件 - Analytics.IAPPurchase(usdPrice, pid); - } - - if (!string.IsNullOrEmpty(subscriptionId)) - { - pid = subscriptionId; - AdjustService.TrackSubPurchase(usdPrice, productId); // 上报 订阅 支付事件 - Analytics.SubPurchase(usdPrice, productId); - } + string productId = orderData.RealProductId; + + Analytics.Tch001IAPRev(usdPrice, productId, orderData.orderId, orderData.OrderType(), orderData.payedDate); // TCH 001 + // Analytics.Tch02IAPRev(usdPrice, productId, orderId, orderTypeString, timestamp); + Analytics.Tch02IAPRev(usdPrice); // TCH 020 + + // Adjust Track IAP Purchase + AdjustService.TrackIAPPurchase(usdPrice, productId); // 上报 IAP 支付事件 + Analytics.IAPPurchase(usdPrice, productId); } } catch (Exception ex) @@ -83,18 +61,13 @@ namespace Guru } - public static GoogleOrderRequest Build(int orderType, string productId, string subscriptionId, - string token, int level, string offerId = "", string basePlanId = "") + public static GoogleOrderRequest Build(int orderType, string productId, + string token, string orderId, string date, int level, string offerId = "", string basePlanId = "") { var request = new GoogleOrderRequest() { - orderData = new GoogleOrderData(orderType, productId, subscriptionId, token, level, offerId, basePlanId), - orderType = orderType, - packageName = Application.identifier, - productId = productId, - subscriptionId = subscriptionId, + orderData = new GoogleOrderData(orderType, productId, token, orderId, date, level, offerId, basePlanId), token = token, - level = level, }; return request; } @@ -104,12 +77,7 @@ namespace Guru var request = new GoogleOrderRequest() { orderData = data, - orderType = data.orderType, - packageName = data.packageName, - productId = data.productId, - subscriptionId = data.subscriptionId, token = data.token, - level = data.level, }; return request; } diff --git a/Runtime/GuruIAP/Runtime/Code/IAPServiceBase.cs b/Runtime/GuruIAP/Runtime/Code/IAPServiceBase.cs index daf7920..fcd6ba5 100644 --- a/Runtime/GuruIAP/Runtime/Code/IAPServiceBase.cs +++ b/Runtime/GuruIAP/Runtime/Code/IAPServiceBase.cs @@ -126,10 +126,11 @@ namespace Guru _showLog = showLog; InitPurchasing(); } - + /// /// 带有校验器的初始化 /// + /// /// /// /// @@ -213,12 +214,12 @@ namespace Guru /// public void OnInitialized(IStoreController controller, IExtensionProvider extensions) { - LogI($"--- IAP Initialized Success"); _storeController = controller; _storeExtensionProvider = extensions; - if (string.IsNullOrEmpty(_userId)) _userId = IPMConfig.IPM_UID; - + + LogI($"--- IAP Initialized Success. With UID: {_userId}"); + #if UNITY_IOS _appleExtensions = extensions.GetExtension(); _appleExtensions.SetApplicationUsername(_userId); // SetUp UID @@ -712,72 +713,90 @@ namespace Guru /// /// 支付结果上报 /// - protected virtual void ReportPurchaseResult(PurchaseEventArgs args) + protected virtual bool ReportPurchaseResult(PurchaseEventArgs args) { - int blevel = GetBLevel(); - int orderType = args.purchasedProduct.definition.type == ProductType.Subscription ? 1 : 0; - - - // Debug.Log($"############ ReportPurchaseResult: _validator:{_validator }"); - + // 验证器判定 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; + return false; } + int level = GetBLevel(); + int orderType = args.purchasedProduct.definition.type == ProductType.Subscription ? 1 : 0; + string productId = args.purchasedProduct.definition.id; + string appleReceiptString = ""; + // ----- 支付后的b_level上报逻辑 + 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 { - // ----- 支付后的b_level上报逻辑 - LogI($"--- Report b_level:[{blevel}] with product id:{args.purchasedProduct.definition.id} "); - - -#if UNITY_EDITOR - // Editor 不做上报逻辑 -#elif UNITY_ANDROID - // Android 订单验证, 上报打点信息 - var result = _validator.Validate(args.purchasedProduct.receipt); - string productID = orderType == 0 ? args.purchasedProduct.definition.id : ""; - string subscriptionID = orderType == 1 ? args.purchasedProduct.definition.id : ""; - - LogI($"{Tag} --- Report Android IAP Order -> orderType:{orderType} productID:{productID} blevel:{blevel}"); - foreach (var productReceipt in result) - { - if (productReceipt is GooglePlayReceipt google) - { - Debug.Log($"{Tag} --- Report Android IAP Order -> orderType:{orderType} productID:{productID} blevel:{blevel}"); - // new GoogleOrderRequest(orderType, productID, subscriptionID, google.purchaseToken, blevel).SetTimeOut(5).Send(); - ReportGoogleOrder(orderType, productID, subscriptionID, google.purchaseToken, blevel); - } - } -#elif UNITY_IOS - // iOS 订单验证, 上报打点信息 +#if UNITY_IOS + // ---- iOS 订单验证, 上报打点信息 ---- var jsonData = JsonConvert.DeserializeObject(args.purchasedProduct.receipt); - if (jsonData.TryGetValue("Payload", out var receipt)) + if (jsonData != null && jsonData.TryGetValue("Payload", out var recp)) { - ReportAppleOrder(orderType, args.purchasedProduct.definition.id, receipt.ToString(),blevel); - Debug.Log($"{Tag} --- Report iOS IAP Order -> orderType:{orderType} productID:{args.purchasedProduct.definition.id} blevel:{blevel}"); + appleReceiptString = recp.ToString(); + LogI($"--- [{productId}] iOS receipt: {appleReceiptString}"); + } +#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, offerId, basePlanId); + } + else if (receipt is AppleInAppPurchaseReceipt appleReceipt) + { + ReportAppleOrder(orderType, + appleReceipt.productID, + appleReceiptString, + appleReceipt.transactionID, + appleReceipt.purchaseDate, level); + } + else + { + string exmsg = $"--- [{productId}] :: Unknown Receipt Type: {receipt.GetType()} can't report order."; + Analytics.LogCrashlytics(exmsg); + LogE(exmsg); + } + } - // string receipt = jsonData["Payload"].ToString(); - // if (HasReceipt(receipt)) - // { - // Debug.Log($"[IAP] Receipt has already reported: {receipt}"); - // return; - // } - // AddReceipt(receipt); - // new AppleOrderRequest(orderType, args.purchasedProduct.definition.id, receipt,blevel).Send(); -#endif } - catch (Exception e) + catch (Exception ex) { - LogE($" [IAPManager.RevenueUpload] got Exception: {e.Message}"); - Analytics.LogCrashlytics(new Exception($"[IAP] Unity report purchase data with b_level={blevel} got error: {e.Message}")); + 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 @@ -903,8 +922,8 @@ namespace Guru private bool isOrderSending = false; private Queue _orderRequests = new Queue(20); - - + + /// /// 上报 Google Order Request /// @@ -912,13 +931,16 @@ namespace Guru /// /// /// + /// /// /// /// - private void ReportGoogleOrder(int orderType, string productId, string subscriptionId, string token, int level, - string offerId = "", string basePlanId = "") + /// + private void ReportGoogleOrder(int orderType, string productId, string token, + string orderId, DateTime date, int level, string offerId = "", string basePlanId = "") { - var request = GoogleOrderRequest.Build(orderType, productId, subscriptionId, token, level, offerId, basePlanId); + var payedDate = TimeUtil.GetTimeStampString(date); + var request = GoogleOrderRequest.Build(orderType, productId, token, orderId, payedDate, level, offerId, basePlanId); ReportNextOrder(request); } private void ReportGoogleOrder(GoogleOrderData data) @@ -927,9 +949,22 @@ namespace Guru ReportNextOrder(request); } - private void ReportAppleOrder(int orderType, string productId, string receipt, int level) + /// + /// 上报 Apple Order Request + /// + /// + /// + /// + /// + /// + /// + /// + /// + private void ReportAppleOrder(int orderType, string productId, string receipt, + string orderId, DateTime date,int level, string offerId = "", string basePlanId = "") { - var request = AppleOrderRequest.Build(orderType, productId, receipt, level); + var payedDate = TimeUtil.GetTimeStampString(date); + var request = AppleOrderRequest.Build(orderType, productId, receipt, orderId, payedDate, level); ReportNextOrder(request); } @@ -968,26 +1003,26 @@ namespace Guru return; } - GoogleOrderRequest go = request as GoogleOrderRequest; - AppleOrderRequest ao = request as AppleOrderRequest; + GoogleOrderRequest googleReq = request as GoogleOrderRequest; + AppleOrderRequest appReq = request as AppleOrderRequest; - if (go != null) + if (googleReq != null) { - if (_model.IsTokenExists(go.token)) + if (_model.IsTokenExists(googleReq.token)) { OnSendNextOrder(); // 跳过上报过的 Google 订单 return; } - _model.AddGoogleOrder(go.orderData); // 缓存当前 orderData 等待上报后再消除 + _model.AddGoogleOrder(googleReq.orderData); // 缓存当前 orderData 等待上报后再消除 } - else if( ao != null) + else if( appReq != null) { - if (_model.IsReceiptExist(ao.receipt)) + if (_model.IsReceiptExist(appReq.receipt)) { OnSendNextOrder(); // 跳过上报过的 Apple 订单 return; } - _model.AddAppleOrder(ao.orderData); // 缓存当前 orderData 等待上报后再消除 + _model.AddAppleOrder(appReq.orderData); // 缓存当前 orderData 等待上报后再消除 } request.SetTimeOut(OrderRequestTimeout) @@ -995,28 +1030,28 @@ namespace Guru .SetSuccessCallBack(() => { //---------------- Success ------------------------ - if (go != null) + if (googleReq != null) { - _model.AddToken(go.token); // 记录当前的 Google 订单 - _model.RemoveGoogleOrder(go.orderData); // 成功后清除缓存 orderData + _model.AddToken(googleReq.token); // 记录当前的 Google 订单 + _model.RemoveGoogleOrder(googleReq.orderData); // 成功后清除缓存 orderData } - else if (ao != null) + else if (appReq != null) { - _model.AddReceipt(ao.receipt); // 记录当前的 Apple 订单 - _model.RemoveAppleOrder(ao.orderData); // 成功后清除缓存 orderData + _model.AddReceipt(appReq.receipt); // 记录当前的 Apple 订单 + _model.RemoveAppleOrder(appReq.orderData); // 成功后清除缓存 orderData } OnSendNextOrder(); // NEXT Order }) .SetFailCallBack(() => { //---------------- Fail ------------------------ - if (go != null) + if (googleReq != null) { - ReportGoogleOrderLost(go.orderData); // 上报 Google 订单缺失打点 + ReportGoogleOrderLost(googleReq.orderData); // 上报 Google 订单缺失打点 } - else if (ao != null) + else if (appReq != null) { - ReportAppleOrderLost(ao.orderData); // 上报 Apple 订单缺失打点 + ReportAppleOrderLost(appReq.orderData); // 上报 Apple 订单缺失打点 } OnSendNextOrder(); // NEXT Order }) diff --git a/package.json b/package.json index b77516b..38e393d 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "Guru SDK core for Unity developers", "unity": "2021.3", "author":{ - "name": "Guru Games" + "name": "Guru Game" }, "license": "MIT", "category": ["game","tool","development", "Guru"],