update: 优化 IAP 接口, 优化数据保存逻辑

deeplink
胡宇飞 2024-03-15 11:23:39 +08:00
parent 0c9b2a77fe
commit f9acc5785b
8 changed files with 128 additions and 136 deletions

View File

@ -1,13 +1,14 @@
namespace Guru namespace Guru
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json;
[Serializable] [Serializable]
public class AppleOrderData public class AppleOrderData
{ {
public string guid;
public int orderType; public int orderType;
public string productId; public string productId;
public string bundleId; public string bundleId;
@ -19,6 +20,7 @@ namespace Guru
public AppleOrderData(int orderType, string productId, string receipt, int level) public AppleOrderData(int orderType, string productId, string receipt, int level)
{ {
guid = Guid.NewGuid().ToString();
this.orderType = orderType; this.orderType = orderType;
this.productId = productId; this.productId = productId;
this.receipt = receipt; this.receipt = receipt;
@ -29,9 +31,21 @@ namespace Guru
eventConfig = EventConfig.Build(); eventConfig = EventConfig.Build();
} }
public bool Equals(AppleOrderData other)
{
if (string.IsNullOrEmpty(guid)) guid = Guid.NewGuid().ToString();
return guid.Equals(other.guid);
}
public string GetId() => productId;
public override string ToString() public override string ToString()
{ {
return $"{nameof(orderType)}: {orderType}, {nameof(productId)}: {productId}, {nameof(bundleId)}: {bundleId}, {nameof(receipt)}: {receipt}, {nameof(country)}: {country}"; return $"{nameof(orderType)}: {orderType}, {nameof(productId)}: {productId}, {nameof(bundleId)}: {bundleId}, {nameof(receipt)}: {receipt}, {nameof(country)}: {country}";
} }
public string ToJson() => JsonConvert.SerializeObject(this);
} }
} }

View File

@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
namespace Guru namespace Guru
{ {
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
[Serializable] [Serializable]
public class GoogleOrderData public class GoogleOrderData
{ {
public string guid;
public int orderType; // 订单类型可选值0:IAP订单 1: 订阅订单 public int orderType; // 订单类型可选值0:IAP订单 1: 订阅订单
public string packageName; //应用包名 public string packageName; //应用包名
public string productId = ""; // 商品ID 当orderType=0时传递该参数 public string productId = ""; // 商品ID 当orderType=0时传递该参数
@ -13,13 +16,14 @@ namespace Guru
public string token; // 应用商店里面的购买token public string token; // 应用商店里面的购买token
public int level; public int level;
public Dictionary<string, object> userInfo; // 当前用户信息。目前包含: level: 用户属性中的"b_level"的值 public Dictionary<string, object> userInfo; // 当前用户信息。目前包含: level: 用户属性中的"b_level"的值
public string basePlanId; // 订阅商品的planId public string basePlanId = ""; // 订阅商品的planId
public string offerId; // 订阅商品的offerId public string offerId = ""; // 订阅商品的offerId
public EventConfig eventConfig; // 事件打点所需信息 public EventConfig eventConfig; // 事件打点所需信息
public GoogleOrderData(int orderType, string productId, string subscriptionId, string token, int level, public GoogleOrderData(int orderType, string productId, string subscriptionId, string token, int level,
string offerId = "", string basePlanId = "") string offerId = "", string basePlanId = "")
{ {
guid = Guid.NewGuid().ToString();
this.orderType = orderType; this.orderType = orderType;
this.packageName = GuruSettings.Instance.GameIdentifier; this.packageName = GuruSettings.Instance.GameIdentifier;
this.productId = productId; this.productId = productId;
@ -31,31 +35,18 @@ namespace Guru
userInfo = new Dictionary<string, object> { ["level"] = level }; userInfo = new Dictionary<string, object> { ["level"] = level };
eventConfig = EventConfig.Build(); eventConfig = EventConfig.Build();
} }
public GoogleOrderData(ProductInfo productInfo, string token, int level, public bool Equals(GoogleOrderData other)
string offerId = "", string basePlanId = "")
{ {
this.orderType = productInfo.Type == "subscription" ? 1 : 0; if (string.IsNullOrEmpty(guid)) guid = Guid.NewGuid().ToString();
if (orderType == 1) return guid == other.guid;
{
subscriptionId = productInfo.Id;
}
else
{
productId = productInfo.Id;
}
this.packageName = GuruSettings.Instance.GameIdentifier;
this.token = token;
this.offerId = offerId;
this.basePlanId = basePlanId;
userInfo = new Dictionary<string, object> { ["level"] = level };
eventConfig = EventConfig.Build();
} }
public override string ToString() public override string ToString()
{ {
return $"{nameof(orderType)}: {orderType}, {nameof(packageName)}: {packageName}, {nameof(productId)}: {productId}, {nameof(subscriptionId)}: {subscriptionId}, {nameof(token)}: {token}"; return $"{nameof(orderType)}: {orderType}, {nameof(packageName)}: {packageName}, {nameof(productId)}: {productId}, {nameof(subscriptionId)}: {subscriptionId}, {nameof(token)}: {token}";
} }
public string ToJson() => JsonConvert.SerializeObject(this);
} }
} }

View File

@ -1,74 +0,0 @@
using UnityEngine;
namespace Guru
{
using System;
using System.Collections.Generic;
[Serializable]
public class IAPOrderData
{
public string platform; // 平台类型 android 或 ios
public int orderType = 0; // 订单类型可选值0:IAP订单 1: 订阅订单
public string productId = ""; // 商品ID 当orderType=0时传递该参数
public string subscriptionId = ""; // 订阅ID // 当orderType=1时传递该参数
public string receipt = ""; // 应用商店里面的购买token
public int level = 0; // 用户属性中的"b_level"的值
public string packageName = ""; // 应用包名
public string offerId = ""; // 订阅商品的offerId (Android)
public string basePlanId = ""; // 订阅商品的planId (Android)
public Dictionary<string, object> userInfo;
public EventConfig eventConfig;
/// <summary>
/// 构建订单数据
/// </summary>
/// <param name="platform"></param>
/// <param name="orderType"></param>
/// <param name="receipt"></param>
/// <param name="level"></param>
/// <param name="productId"></param>
/// <param name="subscriptionId"></param>
/// <param name="offerId"></param>
/// <param name="basePlanId"></param>
/// <returns></returns>
public static IAPOrderData Build(string platform, int orderType = 0, string receipt= "", int level = 0,
string productId = "", string subscriptionId = "",
string offerId = "", string basePlanId = "")
{
IAPOrderData orderData = new IAPOrderData
{
platform = platform,
orderType = orderType,
productId = productId,
subscriptionId = subscriptionId,
receipt = receipt,
level = level,
packageName = Application.identifier,
offerId = offerId,
basePlanId = basePlanId,
userInfo = new Dictionary<string, object>()
{
{"level", level}
},
eventConfig = new EventConfig()
{
firebaseAppInstanceId = IPMConfig.FIREBASE_ID,
idfa = IPMConfig.ADJUST_IDFA,
adid = IPMConfig.ADJUST_ID,
gpsAdid = IPMConfig.ADJUST_ADID,
}
};
return orderData;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 9e4562ba60204b85bca8777b771c73e5
timeCreated: 1710410036

View File

@ -1,10 +1,11 @@
using System.Text;
using Guru.LitJson;
using UnityEngine;
using UnityEngine.Networking;
namespace Guru namespace Guru
{ {
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
public class AppleOrderRequest : RequestBase public class AppleOrderRequest : RequestBase
{ {
public int orderType; public int orderType;
@ -22,6 +23,7 @@ namespace Guru
productId = productId, productId = productId,
receipt = receipt, receipt = receipt,
level = level, level = level,
orderData = new AppleOrderData(orderType, productId, receipt, level),
}; };
return request; return request;
} }
@ -30,15 +32,13 @@ namespace Guru
{ {
return Build(orderData.orderType, orderData.productId, orderData.receipt, orderData.level); return Build(orderData.orderType, orderData.productId, orderData.receipt, orderData.level);
} }
protected override string RequestURL => IPMConfig.IPM_URL + "order/api/v1/orders/ios"; protected override string RequestURL => IPMConfig.IPM_URL + "order/api/v1/orders/ios";
protected override UnityWebRequest CreateRequest() protected override UnityWebRequest CreateRequest()
{ {
this.Log($"send orderData:{orderData}"); this.Log($"send orderData:{orderData}");
var request = new UnityWebRequest(RequestURL, "POST"); var request = new UnityWebRequest(RequestURL, "POST");
request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(JsonMapper.ToJson(orderData))); request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(orderData.ToJson()));
request.downloadHandler = new DownloadHandlerBuffer(); request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader(IPMConfig.Header_Param_APPID, IPMConfig.IPM_X_APP_ID); request.SetRequestHeader(IPMConfig.Header_Param_APPID, IPMConfig.IPM_X_APP_ID);
request.SetRequestHeader(IPMConfig.Header_Param_UID, IPMConfig.IPM_UID); request.SetRequestHeader(IPMConfig.Header_Param_UID, IPMConfig.IPM_UID);

View File

@ -28,7 +28,6 @@ namespace Guru
this.subscriptionId = subscriptionId; this.subscriptionId = subscriptionId;
this.token = token; this.token = token;
this.level = level; this.level = level;
orderData = new GoogleOrderData(orderType, productId, subscriptionId, token, level, offerId, basePlanId); orderData = new GoogleOrderData(orderType, productId, subscriptionId, token, level, offerId, basePlanId);
} }

View File

@ -114,14 +114,38 @@ namespace Guru
public void AddGoogleOrder(GoogleOrderData order) public void AddGoogleOrder(GoogleOrderData order)
{ {
if (HasGoogleOrder(order)) return;
googleOrders.Add(order); googleOrders.Add(order);
Save(); Save();
} }
public void RemoveGoogleOrder(GoogleOrderData order) public bool HasGoogleOrder(GoogleOrderData order)
{ {
googleOrders.Remove(order); if(googleOrders == null || googleOrders.Count == 0) return false;
Save(); for (int i = 0; i < googleOrders.Count; i++)
{
var o = googleOrders[i];
if (o.Equals(order))
{
return true;
}
}
return false;
}
public bool RemoveGoogleOrder(GoogleOrderData order)
{
for (int i = 0; i < googleOrders.Count; i++)
{
var o = googleOrders[i];
if (o.Equals(order))
{
googleOrders.RemoveAt(i);
Save();
return true;
}
}
return false;
} }
public void ClearGoogleOrders() public void ClearGoogleOrders()
{ {
@ -131,14 +155,38 @@ namespace Guru
public void AddAppleOrder(AppleOrderData order) public void AddAppleOrder(AppleOrderData order)
{ {
if (HasAppleOrder(order)) return;
appleOrders.Add(order); appleOrders.Add(order);
Save(); Save();
} }
public void RemoveAppleOrder(AppleOrderData order) public bool HasAppleOrder(AppleOrderData order)
{ {
appleOrders.Remove(order); if(appleOrders == null || appleOrders.Count == 0) return false;
Save(); for (int i = 0; i < appleOrders.Count; i++)
{
var o = appleOrders[i];
if (o.Equals(order))
{
return true;
}
}
return false;
}
public bool RemoveAppleOrder(AppleOrderData order)
{
for (int i = 0; i < appleOrders.Count; i++)
{
var o = appleOrders[i];
if (o.Equals(order))
{
googleOrders.RemoveAt(i);
Save();
return true;
}
}
return false;
} }
public void ClearAppleOrders() public void ClearAppleOrders()
@ -147,8 +195,24 @@ namespace Guru
Save(); Save();
} }
#endregion #endregion
#region Params
public int PurchaseCount
{
get => buyCount;
set
{
buyCount = value;
Save();
}
}
#endregion
} }

View File

@ -43,8 +43,8 @@ namespace Guru
/// </summary> /// </summary>
public int PurchaseCount public int PurchaseCount
{ {
get => PlayerPrefs.GetInt(nameof(PurchaseCount), 0); get => _model.PurchaseCount;
set => PlayerPrefs.SetInt(nameof(PurchaseCount), value); set => _model.PurchaseCount = value;
} }
/// <summary> /// <summary>
@ -714,7 +714,7 @@ namespace Guru
#if UNITY_EDITOR #if UNITY_EDITOR
// Editor 不做上报逻辑 // Editor 不做上报逻辑
#elif UNITY_ANDROID // #elif UNITY_ANDROID
// Android 订单验证, 上报打点信息 // Android 订单验证, 上报打点信息
var result = _validator.Validate(args.purchasedProduct.receipt); var result = _validator.Validate(args.purchasedProduct.receipt);
string productID = orderType == 0 ? args.purchasedProduct.definition.id : ""; string productID = orderType == 0 ? args.purchasedProduct.definition.id : "";
@ -730,16 +730,16 @@ namespace Guru
ReportGoogleOrder(orderType, productID, subscriptionID, google.purchaseToken, blevel); ReportGoogleOrder(orderType, productID, subscriptionID, google.purchaseToken, blevel);
} }
} }
#elif UNITY_IOS // #elif UNITY_IOS
// iOS 订单验证, 上报打点信息 // iOS 订单验证, 上报打点信息
var jsonData = JsonMapper.ToObject(args.purchasedProduct.receipt); var jsonData = JsonMapper.ToObject(args.purchasedProduct.receipt);
string receipt = jsonData["Payload"].ToString(); string receipt = jsonData["Payload"].ToString();
if (HasReceipt(receipt)) // if (HasReceipt(receipt))
{ // {
Debug.Log($"[IAP] Receipt has already reported: {receipt}"); // Debug.Log($"[IAP] Receipt has already reported: {receipt}");
return; // return;
} // }
AddReceipt(receipt); // AddReceipt(receipt);
// new AppleOrderRequest(orderType, args.purchasedProduct.definition.id, receipt,blevel).Send(); // new AppleOrderRequest(orderType, args.purchasedProduct.definition.id, receipt,blevel).Send();
ReportAppleOrder(orderType, args.purchasedProduct.definition.id, receipt,blevel); ReportAppleOrder(orderType, args.purchasedProduct.definition.id, receipt,blevel);
Debug.Log($"{Tag} --- Report iOS IAP Order -> orderType:{orderType} productID:{args.purchasedProduct.definition.id} blevel:{blevel}"); Debug.Log($"{Tag} --- Report iOS IAP Order -> orderType:{orderType} productID:{args.purchasedProduct.definition.id} blevel:{blevel}");
@ -836,15 +836,18 @@ namespace Guru
// 启动时查询 // 启动时查询
if(_orderRequests == null) if(_orderRequests == null)
_orderRequests = new Queue<RequestBase>(20); _orderRequests = new Queue<RequestBase>(20);
#if UNITY_ANDROID
// #if UNITY_EDITOR
// Debug.Log($"----- IAP Model init -----");
// #elif UNITY_ANDROID
#if UNITY_ANDROID
if (_model.HasUnreportedGoogleOrder) if (_model.HasUnreportedGoogleOrder)
{ {
foreach (var o in _model.googleOrders) foreach (var o in _model.googleOrders)
{ {
ReportGoogleOrder(o); ReportGoogleOrder(o);
} }
_model.ClearGoogleOrders();
} }
#elif UNITY_IOS #elif UNITY_IOS
if (_model.HasUnreportedAppleOrder) if (_model.HasUnreportedAppleOrder)
@ -853,7 +856,6 @@ namespace Guru
{ {
ReportAppleOrder(o); ReportAppleOrder(o);
} }
_model.ClearAppleOrders();
} }
#endif #endif
@ -861,10 +863,6 @@ namespace Guru
} }
#endregion #endregion
#region 订单上报队列 #region 订单上报队列
@ -949,11 +947,14 @@ namespace Guru
if (go != null) if (go != null)
{ {
_model.AddToken(go.token); _model.AddToken(go.token);
_model.RemoveGoogleOrder(go.orderData);
} }
else if (ao != null) else if (ao != null)
{ {
_model.AddReceipt(ao.receipt); _model.AddReceipt(ao.receipt);
_model.RemoveAppleOrder(ao.orderData);
} }
OnSendNextOrder(); OnSendNextOrder();
}) })
.SetFailCallBack(() => .SetFailCallBack(() =>