update: 支付插件初始化修改, orders 上报缓存, IAP 数据中台接口改造
parent
277bf0bfba
commit
0c9b2a77fe
|
|
@ -136,6 +136,11 @@ namespace Guru
|
||||||
{
|
{
|
||||||
CacheUserProperty(Analytics.PropertyIDFV, idfv);
|
CacheUserProperty(Analytics.PropertyIDFV, idfv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SetIDFA(string idfa)
|
||||||
|
{
|
||||||
|
CacheUserProperty(Analytics.PropertyIDFA, idfa);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ namespace Guru
|
||||||
|
|
||||||
public static class AdjustService
|
public static class AdjustService
|
||||||
{
|
{
|
||||||
public const string Version = "1.6.0";
|
public const string Version = "1.6.1";
|
||||||
public static readonly string LOG_TAG = "Adjust";
|
public static readonly string LOG_TAG = "Adjust";
|
||||||
public static readonly float DelayTime = 1f; // 延迟启动时间
|
public static readonly float DelayTime = 1f; // 延迟启动时间
|
||||||
|
|
||||||
|
|
@ -28,6 +28,8 @@ namespace Guru
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string IDFA => Adjust.getIdfa();
|
||||||
|
|
||||||
private static string _adjustId = "";
|
private static string _adjustId = "";
|
||||||
public static string AdjustId
|
public static string AdjustId
|
||||||
{
|
{
|
||||||
|
|
@ -111,6 +113,9 @@ namespace Guru
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 关键属性上报
|
#region 关键属性上报
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ namespace Guru
|
||||||
public static readonly string PropertyHp = "hp"; // 生命值/体力
|
public static readonly string PropertyHp = "hp"; // 生命值/体力
|
||||||
public static readonly string PropertyAndroidID = "android_id"; // Android 平台 AndroidID
|
public static readonly string PropertyAndroidID = "android_id"; // Android 平台 AndroidID
|
||||||
public static readonly string PropertyIDFV = "idfv"; // iOS 平台 IDFV
|
public static readonly string PropertyIDFV = "idfv"; // iOS 平台 IDFV
|
||||||
|
public static readonly string PropertyIDFA = "idfa"; // iOS 平台 IDFA
|
||||||
public static readonly string PropertyPicture = "picture"; // 玩家在主线的mapid
|
public static readonly string PropertyPicture = "picture"; // 玩家在主线的mapid
|
||||||
public static readonly string PropertyNoAds = "no_ads"; // 玩家是否去广告
|
public static readonly string PropertyNoAds = "no_ads"; // 玩家是否去广告
|
||||||
public static readonly string PropertyATTStatus = "att_status"; // ATT 状态
|
public static readonly string PropertyATTStatus = "att_status"; // ATT 状态
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ namespace Guru
|
||||||
{
|
{
|
||||||
private static bool _hasGotFirebaseId; //已取得FirebaseId
|
private static bool _hasGotFirebaseId; //已取得FirebaseId
|
||||||
private static bool _hasGotAdId; // 已取得AdId
|
private static bool _hasGotAdId; // 已取得AdId
|
||||||
|
private static bool _hasGotIDFA; // 已取得IDFA
|
||||||
private static bool _hasGotAdjustId; // 已取得AdjustId
|
private static bool _hasGotAdjustId; // 已取得AdjustId
|
||||||
private static bool _hasGotDeviceId; // 已取得DeviceId
|
private static bool _hasGotDeviceId; // 已取得DeviceId
|
||||||
private static bool _hasGotUid; // 已取得UID
|
private static bool _hasGotUid; // 已取得UID
|
||||||
|
|
@ -21,6 +22,7 @@ namespace Guru
|
||||||
private static double _reportSuccessInterval; // 上报频率
|
private static double _reportSuccessInterval; // 上报频率
|
||||||
|
|
||||||
private const string VALUE_NOT_FOR_IOS = "not_support_for_ios";
|
private const string VALUE_NOT_FOR_IOS = "not_support_for_ios";
|
||||||
|
private const string VALUE_ONLY_FOR_IOS = "idfa_only_for_ios";
|
||||||
|
|
||||||
public static bool IsDebug { get; set; } = false;
|
public static bool IsDebug { get; set; } = false;
|
||||||
|
|
||||||
|
|
@ -148,6 +150,10 @@ namespace Guru
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置FirebaseId
|
/// 设置FirebaseId
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -200,6 +206,24 @@ namespace Guru
|
||||||
{
|
{
|
||||||
GuruAnalytics.SetIDFV(DeviceIDHelper.IDFV);
|
GuruAnalytics.SetIDFV(DeviceIDHelper.IDFV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void SetIDFA()
|
||||||
|
{
|
||||||
|
if(_hasGotIDFA) return;
|
||||||
|
var idfa = AdjustService.IDFA;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(idfa))
|
||||||
|
{
|
||||||
|
// Debug.Log($"---[ANA] ADID: {adId}");
|
||||||
|
IPMConfig.ADJUST_IDFA = idfa;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(IPMConfig.ADJUST_IDFA))
|
||||||
|
{
|
||||||
|
GuruAnalytics.SetIDFA(IPMConfig.ADJUST_IDFA);
|
||||||
|
_hasGotIDFA = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -231,6 +255,7 @@ namespace Guru
|
||||||
#if UNITY_IOS
|
#if UNITY_IOS
|
||||||
SetATTStatus();
|
SetATTStatus();
|
||||||
SetIDFV();
|
SetIDFV();
|
||||||
|
SetIDFA();
|
||||||
#endif
|
#endif
|
||||||
ReportEventSuccessRate();
|
ReportEventSuccessRate();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,13 @@ namespace Guru
|
||||||
get => PlayerPrefs.GetString(nameof(ADJUST_ADID), "");
|
get => PlayerPrefs.GetString(nameof(ADJUST_ADID), "");
|
||||||
set => PlayerPrefs.SetString(nameof(ADJUST_ADID), value);
|
set => PlayerPrefs.SetString(nameof(ADJUST_ADID), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static string ADJUST_IDFA
|
||||||
|
{
|
||||||
|
get => PlayerPrefs.GetString(nameof(ADJUST_IDFA), "");
|
||||||
|
set => PlayerPrefs.SetString(nameof(ADJUST_IDFA), value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,30 +1,37 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Guru
|
namespace Guru
|
||||||
{
|
{
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class AppleOrderData
|
public class AppleOrderData
|
||||||
{
|
{
|
||||||
|
public int orderType;
|
||||||
|
public string productId;
|
||||||
public string bundleId;
|
public string bundleId;
|
||||||
public string receipt;
|
public string receipt;
|
||||||
public string country;
|
public string country;
|
||||||
|
public int level;
|
||||||
public Dictionary<string, object> userInfo;
|
public Dictionary<string, object> userInfo;
|
||||||
|
public EventConfig eventConfig;
|
||||||
|
|
||||||
public AppleOrderData(string receipt, int level)
|
public AppleOrderData(int orderType, string productId, string receipt, int level)
|
||||||
{
|
{
|
||||||
|
this.orderType = orderType;
|
||||||
|
this.productId = productId;
|
||||||
this.receipt = receipt;
|
this.receipt = receipt;
|
||||||
|
this.level = level;
|
||||||
bundleId = GuruSettings.Instance.GameIdentifier;
|
bundleId = GuruSettings.Instance.GameIdentifier;
|
||||||
country = IPMConfig.IPM_COUNTRY_CODE;
|
country = IPMConfig.IPM_COUNTRY_CODE;
|
||||||
userInfo = new Dictionary<string, object>
|
userInfo = new Dictionary<string, object> { ["level"] = level };
|
||||||
{
|
eventConfig = EventConfig.Build();
|
||||||
["level"] = level
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{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}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
namespace Guru
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class EventConfig
|
||||||
|
{
|
||||||
|
public string firebaseAppInstanceId; // firebase实例id,用于标识一个用户
|
||||||
|
public string idfa; // adjust广告id(ios)
|
||||||
|
public string adid; // adjust设备id(ios)
|
||||||
|
public string gpsAdid; // adjust广告id
|
||||||
|
|
||||||
|
|
||||||
|
public static EventConfig Build()
|
||||||
|
{
|
||||||
|
var config = new EventConfig()
|
||||||
|
{
|
||||||
|
firebaseAppInstanceId = IPMConfig.FIREBASE_ID,
|
||||||
|
idfa = IPMConfig.ADJUST_IDFA,
|
||||||
|
adid = IPMConfig.ADJUST_ID,
|
||||||
|
gpsAdid = IPMConfig.ADJUST_ADID,
|
||||||
|
};
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0b44b9252ff741f58c6489223c0d5dca
|
||||||
|
timeCreated: 1710411579
|
||||||
|
|
@ -6,27 +6,56 @@ namespace Guru
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class GoogleOrderData
|
public class GoogleOrderData
|
||||||
{
|
{
|
||||||
public int orderType;
|
public int orderType; // 订单类型,可选值:0:IAP订单 1: 订阅订单
|
||||||
public string packageName;
|
public string packageName; //应用包名
|
||||||
public string productId;
|
public string productId = ""; // 商品ID 当orderType=0时,传递该参数
|
||||||
public string token;
|
public string subscriptionId = ""; // 订阅ID // 当orderType=1时,传递该参数
|
||||||
public Dictionary<string, object> userInfo;
|
public string token; // 应用商店里面的购买token
|
||||||
|
public int level;
|
||||||
|
public Dictionary<string, object> userInfo; // 当前用户信息。目前包含: level: 用户属性中的"b_level"的值
|
||||||
|
public string basePlanId; // 订阅商品的planId
|
||||||
|
public string offerId; // 订阅商品的offerId
|
||||||
|
public EventConfig eventConfig; // 事件打点所需信息
|
||||||
|
|
||||||
public GoogleOrderData(int orderType, string productId, string token, int level)
|
public GoogleOrderData(int orderType, string productId, string subscriptionId, string token, int level,
|
||||||
|
string offerId = "", string basePlanId = "")
|
||||||
{
|
{
|
||||||
this.orderType = orderType;
|
this.orderType = orderType;
|
||||||
this.packageName = GuruSettings.Instance.GameIdentifier;
|
this.packageName = GuruSettings.Instance.GameIdentifier;
|
||||||
this.productId = productId;
|
this.productId = productId;
|
||||||
|
this.subscriptionId = subscriptionId;
|
||||||
this.token = token;
|
this.token = token;
|
||||||
userInfo = new Dictionary<string, object>
|
this.offerId = offerId;
|
||||||
{
|
this.basePlanId = basePlanId;
|
||||||
["level"] = level
|
this.level = level;
|
||||||
};
|
userInfo = new Dictionary<string, object> { ["level"] = level };
|
||||||
|
eventConfig = EventConfig.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GoogleOrderData(ProductInfo productInfo, string token, int level,
|
||||||
|
string offerId = "", string basePlanId = "")
|
||||||
|
{
|
||||||
|
this.orderType = productInfo.Type == "subscription" ? 1 : 0;
|
||||||
|
if (orderType == 1)
|
||||||
|
{
|
||||||
|
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(token)}: {token}";
|
return $"{nameof(orderType)}: {orderType}, {nameof(packageName)}: {packageName}, {nameof(productId)}: {productId}, {nameof(subscriptionId)}: {subscriptionId}, {nameof(token)}: {token}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9e4562ba60204b85bca8777b771c73e5
|
||||||
|
timeCreated: 1710410036
|
||||||
|
|
@ -11,23 +11,34 @@ namespace Guru
|
||||||
public string productId;
|
public string productId;
|
||||||
public string receipt;
|
public string receipt;
|
||||||
public int level;
|
public int level;
|
||||||
|
public AppleOrderData orderData;
|
||||||
|
|
||||||
public AppleOrderRequest(){}
|
public AppleOrderRequest(){}
|
||||||
public AppleOrderRequest(int orderType, string productId, string receipt, int level)
|
public static AppleOrderRequest Build(int orderType, string productId, string receipt, int level)
|
||||||
{
|
{
|
||||||
this.orderType = orderType;
|
var request = new AppleOrderRequest()
|
||||||
this.productId = productId;
|
{
|
||||||
this.receipt = receipt;
|
orderType = orderType,
|
||||||
this.level = level;
|
productId = productId,
|
||||||
|
receipt = receipt,
|
||||||
|
level = level,
|
||||||
|
};
|
||||||
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AppleOrderRequest Build(AppleOrderData orderData)
|
||||||
|
{
|
||||||
|
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()
|
||||||
{
|
{
|
||||||
AppleOrderData appleOrderData = new AppleOrderData(receipt, level);
|
this.Log($"send orderData:{orderData}");
|
||||||
this.Log($"send orderData:{appleOrderData}");
|
|
||||||
var request = new UnityWebRequest(RequestURL, "POST");
|
var request = new UnityWebRequest(RequestURL, "POST");
|
||||||
request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(JsonMapper.ToJson(appleOrderData)));
|
request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(JsonMapper.ToJson(orderData)));
|
||||||
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);
|
||||||
|
|
@ -41,11 +52,17 @@ namespace Guru
|
||||||
ResponseData<OrderResponse> responseData = JsonUtility.FromJson<ResponseData<OrderResponse>>(response);
|
ResponseData<OrderResponse> responseData = JsonUtility.FromJson<ResponseData<OrderResponse>>(response);
|
||||||
if (responseData != null && responseData.data != null)
|
if (responseData != null && responseData.data != null)
|
||||||
{
|
{
|
||||||
// Analytics.Tch001IAPRev(responseData.data.usdPrice);
|
|
||||||
double usdPrice = responseData.data.usdPrice;
|
double usdPrice = responseData.data.usdPrice;
|
||||||
Analytics.Tch001IAPRev(usdPrice);
|
Analytics.Tch001IAPRev(usdPrice);
|
||||||
Analytics.Tch02IAPRev(usdPrice);
|
Analytics.Tch02IAPRev(usdPrice);
|
||||||
AdjustService.TrackIAPPurchase(usdPrice, productId);
|
if (orderType == 0)
|
||||||
|
{
|
||||||
|
AdjustService.TrackIAPPurchase(usdPrice, productId);
|
||||||
|
}
|
||||||
|
else if (orderType == 1)
|
||||||
|
{
|
||||||
|
AdjustService.TrackSubPurchase(usdPrice, productId);
|
||||||
|
}
|
||||||
Analytics.IAPPurchase(usdPrice, productId);
|
Analytics.IAPPurchase(usdPrice, productId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,20 @@ namespace Guru
|
||||||
{
|
{
|
||||||
public class GoogleOrderRequest : RequestBase
|
public class GoogleOrderRequest : RequestBase
|
||||||
{
|
{
|
||||||
public int orderType;
|
public int orderType; // 订单类型,可选值:0:IAP订单 1: 订阅订单
|
||||||
public string productId;
|
public string productId;
|
||||||
public string subscriptionId;
|
public string subscriptionId;
|
||||||
public string token;
|
public string token;
|
||||||
public string packageName;
|
public string packageName;
|
||||||
public int level;
|
public int level;
|
||||||
|
public string basePlanId;
|
||||||
|
public string offerId;
|
||||||
|
public GoogleOrderData orderData;
|
||||||
|
|
||||||
public GoogleOrderRequest(){}
|
public GoogleOrderRequest(){}
|
||||||
|
|
||||||
public GoogleOrderRequest(int orderType, string productId, string subscriptionId,
|
public GoogleOrderRequest(int orderType, string productId, string subscriptionId,
|
||||||
string token, int level)
|
string token, int level, string offerId = "", string basePlanId = "")
|
||||||
{
|
{
|
||||||
this.orderType = orderType;
|
this.orderType = orderType;
|
||||||
this.packageName = Application.identifier;
|
this.packageName = Application.identifier;
|
||||||
|
|
@ -25,12 +28,14 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
protected override string RequestURL => IPMConfig.IPM_URL + "order/api/v1/orders/android";
|
protected override string RequestURL => IPMConfig.IPM_URL + "order/api/v1/orders/android";
|
||||||
protected override UnityWebRequest CreateRequest()
|
protected override UnityWebRequest CreateRequest()
|
||||||
{
|
{
|
||||||
GoogleOrderData googleOrderData = new GoogleOrderData(orderType, productId, token, level);
|
GoogleOrderData googleOrderData = orderData;
|
||||||
this.Log($"send orderData:{googleOrderData}");
|
this.Log($"send orderData:{googleOrderData}");
|
||||||
var request = new UnityWebRequest(RequestURL, "POST");
|
var request = new UnityWebRequest(RequestURL, "POST");
|
||||||
request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(JsonMapper.ToJson(googleOrderData)));
|
request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(JsonMapper.ToJson(googleOrderData)));
|
||||||
|
|
@ -51,27 +56,59 @@ namespace Guru
|
||||||
double usdPrice = responseData.data.usdPrice;
|
double usdPrice = responseData.data.usdPrice;
|
||||||
Analytics.Tch001IAPRev(usdPrice);
|
Analytics.Tch001IAPRev(usdPrice);
|
||||||
Analytics.Tch02IAPRev(usdPrice);
|
Analytics.Tch02IAPRev(usdPrice);
|
||||||
AdjustService.TrackIAPPurchase(usdPrice, productId);
|
|
||||||
Analytics.IAPPurchase(usdPrice, productId);
|
string pid = "";
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(productId))
|
||||||
|
{
|
||||||
|
pid = productId;
|
||||||
|
AdjustService.TrackIAPPurchase(usdPrice, productId); // 上报 IAP 支付事件
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(subscriptionId))
|
||||||
|
{
|
||||||
|
pid = subscriptionId;
|
||||||
|
AdjustService.TrackSubPurchase(usdPrice, productId); // 上报 订阅 支付事件
|
||||||
|
}
|
||||||
|
|
||||||
|
Analytics.IAPPurchase(usdPrice, pid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(response))
|
|
||||||
{
|
|
||||||
if (response.Contains("500") || response.Contains("Internal Server Error"))
|
|
||||||
{
|
|
||||||
Debug.LogError(
|
|
||||||
$"[IAP] GoogleOrderRequest failed: contact with guru backend server team. Make sure they'v connect order report to this project!");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.LogError($"[IAP] GoogleOrderRequest failed: {response}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.LogError($"[IAP] GoogleOrderRequest failed: response is null or empty");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static GoogleOrderRequest Build(int orderType, string productId, string subscriptionId,
|
||||||
|
string token, 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,
|
||||||
|
token = token,
|
||||||
|
level = level,
|
||||||
|
};
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GoogleOrderRequest Build(GoogleOrderData data)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Guru
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
public class IAPModel
|
||||||
|
{
|
||||||
|
public static readonly float SaveInterval = 3;
|
||||||
|
|
||||||
|
public static readonly string PlatformAndroid = "android";
|
||||||
|
public static readonly string PlatformIOS = "ios";
|
||||||
|
|
||||||
|
public int buyCount = 0;
|
||||||
|
public List<string> androidTokens;
|
||||||
|
public List<string> iosReceipts;
|
||||||
|
public List<GoogleOrderData> googleOrders;
|
||||||
|
public List<AppleOrderData> appleOrders;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否还有未上报的 Google Order
|
||||||
|
/// </summary>
|
||||||
|
public bool HasUnreportedGoogleOrder => (googleOrders?.Count ?? 0) > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否还有未上报的 Apple Order
|
||||||
|
/// </summary>
|
||||||
|
public bool HasUnreportedAppleOrder => (appleOrders?.Count ?? 0) > 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public IAPModel()
|
||||||
|
{
|
||||||
|
androidTokens = new List<string>(20);
|
||||||
|
iosReceipts = new List<string>(20);
|
||||||
|
googleOrders = new List<GoogleOrderData>(20);
|
||||||
|
appleOrders = new List<AppleOrderData>(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IAPModel Load()
|
||||||
|
{
|
||||||
|
IAPModel model = null;
|
||||||
|
var json = LoadModelData();
|
||||||
|
if (!string.IsNullOrEmpty(json))
|
||||||
|
{
|
||||||
|
model = JsonConvert.DeserializeObject<IAPModel>(json);
|
||||||
|
}
|
||||||
|
if(null != model) return model;
|
||||||
|
return new IAPModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
SaveModelData(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static string LoadModelData()
|
||||||
|
{
|
||||||
|
return PlayerPrefs.GetString(nameof(IAPModel), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SaveModelData(IAPModel model)
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(model);
|
||||||
|
PlayerPrefs.SetString(nameof(IAPModel), json);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Receipt
|
||||||
|
|
||||||
|
public void AddToken(string token)
|
||||||
|
{
|
||||||
|
if (androidTokens == null) androidTokens = new List<string>(20);
|
||||||
|
if(string.IsNullOrEmpty(token)) return;
|
||||||
|
androidTokens.Add(token);
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加收据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="receipt"></param>
|
||||||
|
/// <param name="platform"></param>
|
||||||
|
public void AddReceipt(string receipt)
|
||||||
|
{
|
||||||
|
if (iosReceipts == null) iosReceipts = new List<string>(20);
|
||||||
|
if(string.IsNullOrEmpty(receipt)) return;
|
||||||
|
iosReceipts.Add(receipt);
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsTokenExists(string token)
|
||||||
|
{
|
||||||
|
if (androidTokens == null) return false;
|
||||||
|
return androidTokens.Contains(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReceiptExist(string receipt)
|
||||||
|
{
|
||||||
|
if (iosReceipts == null) return false;
|
||||||
|
return iosReceipts.Contains(receipt);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Orders
|
||||||
|
|
||||||
|
public void AddGoogleOrder(GoogleOrderData order)
|
||||||
|
{
|
||||||
|
googleOrders.Add(order);
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveGoogleOrder(GoogleOrderData order)
|
||||||
|
{
|
||||||
|
googleOrders.Remove(order);
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
public void ClearGoogleOrders()
|
||||||
|
{
|
||||||
|
googleOrders.Clear();
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddAppleOrder(AppleOrderData order)
|
||||||
|
{
|
||||||
|
appleOrders.Add(order);
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAppleOrder(AppleOrderData order)
|
||||||
|
{
|
||||||
|
appleOrders.Remove(order);
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearAppleOrders()
|
||||||
|
{
|
||||||
|
appleOrders.Clear();
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 52e82348c38f470c8d9f247451ca5ce5
|
||||||
|
timeCreated: 1710408457
|
||||||
|
|
@ -12,6 +12,9 @@ namespace Guru
|
||||||
public abstract class IAPServiceBase<T>: IStoreListener where T: IAPServiceBase<T> , new()
|
public abstract class IAPServiceBase<T>: IStoreListener where T: IAPServiceBase<T> , new()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public static readonly int OrderRequestTimeout = 10;
|
||||||
|
public static readonly int OrderRequestRetryTimes = 3;
|
||||||
|
|
||||||
#region 属性定义
|
#region 属性定义
|
||||||
|
|
||||||
private const string Tag = "[IAP]";
|
private const string Tag = "[IAP]";
|
||||||
|
|
@ -33,7 +36,8 @@ namespace Guru
|
||||||
public bool IsInitialized => _storeController != null && _storeExtensionProvider != null;
|
public bool IsInitialized => _storeController != null && _storeExtensionProvider != null;
|
||||||
|
|
||||||
private Product _curPurchasingProduct = null;
|
private Product _curPurchasingProduct = null;
|
||||||
|
|
||||||
|
private IAPModel _model;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否是首次购买
|
/// 是否是首次购买
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -129,6 +133,7 @@ namespace Guru
|
||||||
{
|
{
|
||||||
_googlePublicKey = googlePublicKey;
|
_googlePublicKey = googlePublicKey;
|
||||||
_appleRootCert = appleRootCert;
|
_appleRootCert = appleRootCert;
|
||||||
|
InitModel();
|
||||||
Initialize(showLog);
|
Initialize(showLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -681,7 +686,7 @@ namespace Guru
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 支付上报逻辑
|
#region 支付上报逻辑
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 支付结果上报
|
/// 支付结果上报
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -716,15 +721,13 @@ namespace Guru
|
||||||
string subscriptionID = orderType == 1 ? args.purchasedProduct.definition.id : "";
|
string subscriptionID = orderType == 1 ? args.purchasedProduct.definition.id : "";
|
||||||
|
|
||||||
LogI($"{Tag} --- Report Android IAP Order -> orderType:{orderType} productID:{productID} blevel:{blevel}");
|
LogI($"{Tag} --- Report Android IAP Order -> orderType:{orderType} productID:{productID} blevel:{blevel}");
|
||||||
|
|
||||||
Debug.Log($"############ --- result.Count: {result.Length}");
|
|
||||||
|
|
||||||
foreach (var productReceipt in result)
|
foreach (var productReceipt in result)
|
||||||
{
|
{
|
||||||
if (productReceipt is GooglePlayReceipt google)
|
if (productReceipt is GooglePlayReceipt google)
|
||||||
{
|
{
|
||||||
Debug.Log($"{Tag} --- Report Android IAP Order -> orderType:{orderType} productID:{productID} blevel:{blevel}");
|
Debug.Log($"{Tag} --- Report Android IAP Order -> orderType:{orderType} productID:{productID} blevel:{blevel}");
|
||||||
new GoogleOrderRequest(orderType, productID, subscriptionID, google.purchaseToken, blevel).SetTimeOut(5).Send();
|
// new GoogleOrderRequest(orderType, productID, subscriptionID, google.purchaseToken, blevel).SetTimeOut(5).Send();
|
||||||
|
ReportGoogleOrder(orderType, productID, subscriptionID, google.purchaseToken, blevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#elif UNITY_IOS
|
#elif UNITY_IOS
|
||||||
|
|
@ -737,7 +740,8 @@ namespace Guru
|
||||||
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);
|
||||||
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}");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -821,6 +825,187 @@ namespace Guru
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region 数据初始化
|
||||||
|
|
||||||
|
|
||||||
|
private void InitModel()
|
||||||
|
{
|
||||||
|
_model = IAPModel.Load(); // 初始化 Model
|
||||||
|
|
||||||
|
// 启动时查询
|
||||||
|
if(_orderRequests == null)
|
||||||
|
_orderRequests = new Queue<RequestBase>(20);
|
||||||
|
#if UNITY_ANDROID
|
||||||
|
|
||||||
|
if (_model.HasUnreportedGoogleOrder)
|
||||||
|
{
|
||||||
|
foreach (var o in _model.googleOrders)
|
||||||
|
{
|
||||||
|
ReportGoogleOrder(o);
|
||||||
|
}
|
||||||
|
_model.ClearGoogleOrders();
|
||||||
|
}
|
||||||
|
#elif UNITY_IOS
|
||||||
|
if (_model.HasUnreportedAppleOrder)
|
||||||
|
{
|
||||||
|
foreach (var o in _model.appleOrders)
|
||||||
|
{
|
||||||
|
ReportAppleOrder(o);
|
||||||
|
}
|
||||||
|
_model.ClearAppleOrders();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 订单上报队列
|
||||||
|
|
||||||
|
private bool isOrderSending = false;
|
||||||
|
private Queue<RequestBase> _orderRequests = new Queue<RequestBase>(20);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上报 Google Order Request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="orderType"></param>
|
||||||
|
/// <param name="productId"></param>
|
||||||
|
/// <param name="subscriptionId"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <param name="level"></param>
|
||||||
|
/// <param name="offerId"></param>
|
||||||
|
/// <param name="basePlanId"></param>
|
||||||
|
private void ReportGoogleOrder(int orderType, string productId, string subscriptionId, string token, int level,
|
||||||
|
string offerId = "", string basePlanId = "")
|
||||||
|
{
|
||||||
|
var request = GoogleOrderRequest.Build(orderType, productId, subscriptionId, token, level, offerId, basePlanId);
|
||||||
|
ReportNextOrder(request);
|
||||||
|
}
|
||||||
|
private void ReportGoogleOrder(GoogleOrderData data)
|
||||||
|
{
|
||||||
|
var request = GoogleOrderRequest.Build(data);
|
||||||
|
ReportNextOrder(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportAppleOrder(int orderType, string productId, string receipt, int level)
|
||||||
|
{
|
||||||
|
var request = AppleOrderRequest.Build(orderType, productId, receipt, level);
|
||||||
|
ReportNextOrder(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportAppleOrder(AppleOrderData data)
|
||||||
|
{
|
||||||
|
var request = AppleOrderRequest.Build(data);
|
||||||
|
ReportNextOrder(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportNextOrder(RequestBase request)
|
||||||
|
{
|
||||||
|
_orderRequests.Enqueue(request);
|
||||||
|
|
||||||
|
if(isOrderSending) return;
|
||||||
|
isOrderSending = true;
|
||||||
|
|
||||||
|
OnSendNextOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上报下一个 Google 订单
|
||||||
|
/// </summary>
|
||||||
|
private void OnSendNextOrder()
|
||||||
|
{
|
||||||
|
if (_orderRequests != null && _orderRequests.Count > 0)
|
||||||
|
{
|
||||||
|
isOrderSending = true;
|
||||||
|
var request = _orderRequests.Dequeue();
|
||||||
|
GoogleOrderRequest go = request as GoogleOrderRequest;
|
||||||
|
AppleOrderRequest ao = request as AppleOrderRequest;
|
||||||
|
|
||||||
|
if (go != null && _model.IsTokenExists(go.token))
|
||||||
|
{
|
||||||
|
OnSendNextOrder();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ao != null && _model.IsReceiptExist(ao.receipt))
|
||||||
|
{
|
||||||
|
OnSendNextOrder();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.SetTimeOut(OrderRequestTimeout)
|
||||||
|
.SetRetryTimes(OrderRequestRetryTimes)
|
||||||
|
.SetSuccessCallBack(() =>
|
||||||
|
{
|
||||||
|
if (go != null)
|
||||||
|
{
|
||||||
|
_model.AddToken(go.token);
|
||||||
|
}
|
||||||
|
else if (ao != null)
|
||||||
|
{
|
||||||
|
_model.AddReceipt(ao.receipt);
|
||||||
|
}
|
||||||
|
OnSendNextOrder();
|
||||||
|
})
|
||||||
|
.SetFailCallBack(() =>
|
||||||
|
{
|
||||||
|
if (go != null)
|
||||||
|
{
|
||||||
|
_model.AddGoogleOrder(go.orderData);
|
||||||
|
ReportGoogleOrderLost(go.orderData);
|
||||||
|
}
|
||||||
|
else if (ao != null)
|
||||||
|
{
|
||||||
|
_model.AddAppleOrder(ao.orderData);
|
||||||
|
ReportAppleOrderLost(ao.orderData);
|
||||||
|
}
|
||||||
|
|
||||||
|
OnSendNextOrder();
|
||||||
|
})
|
||||||
|
.Send();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isOrderSending = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportGoogleOrderLost(GoogleOrderData data)
|
||||||
|
{
|
||||||
|
Analytics.LogEvent("google_order_lost", new Dictionary<string, dynamic>()
|
||||||
|
{
|
||||||
|
["data"] = data.ToString(),
|
||||||
|
}, new Analytics.EventSetting()
|
||||||
|
{
|
||||||
|
EnableFirebaseAnalytics = true,
|
||||||
|
EnableFacebookAnalytics = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportAppleOrderLost(AppleOrderData data)
|
||||||
|
{
|
||||||
|
Analytics.LogEvent("apple_order_lost", new Dictionary<string, dynamic>()
|
||||||
|
{
|
||||||
|
["data"] = data.ToString(),
|
||||||
|
}, new Analytics.EventSetting()
|
||||||
|
{
|
||||||
|
EnableFirebaseAnalytics = true,
|
||||||
|
EnableFacebookAnalytics = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue