396 lines
19 KiB
C#
396 lines
19 KiB
C#
|
#if UNITY_PURCHASE
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using JetBrains.Annotations;
|
|||
|
using UnityEngine;
|
|||
|
|
|||
|
namespace WZ
|
|||
|
{
|
|||
|
|
|||
|
public class IAPOrderManager : D_MonoSingleton<IAPOrderManager>
|
|||
|
{
|
|||
|
private static string _purchaseOrderMap = "PurchaseOrderMap";
|
|||
|
private List<int> NotRetryCode = new List<int> { 0, 430, 900, 901, 902, 903, 904, 905 };
|
|||
|
private Dictionary<string, int> mRepeatCountDic;
|
|||
|
|
|||
|
#region 订单验证
|
|||
|
public void VerifyPurchase(IAPDataConfig args)
|
|||
|
{
|
|||
|
LoggerUtils.Debug("[iap] Start VerifyPurchase orderid:" + args.info["order_id"] + "status:" + args.state);
|
|||
|
if (args.state == IAPDataStateType.def)
|
|||
|
{
|
|||
|
// 记录订单处理次数,处理5次还是失败就不处理了
|
|||
|
if (mRepeatCountDic.ContainsKey(args.info["order_id"]))
|
|||
|
{
|
|||
|
mRepeatCountDic[args.info["order_id"]] = mRepeatCountDic[args.info["order_id"]] + 1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
mRepeatCountDic.Add(args.info["order_id"], 1);
|
|||
|
}
|
|||
|
|
|||
|
// 开始处理,设置正在处理的状态
|
|||
|
RefreshOrderStatue(args, IAPDataStateType.deal);
|
|||
|
args.info.TryGetValue("mGameExtraParam", out var gameExtraParam);
|
|||
|
|
|||
|
if (string.IsNullOrEmpty(gameExtraParam))
|
|||
|
{
|
|||
|
gameExtraParam = IAPPurchaseManager.Instance._gameExtraParam;
|
|||
|
}
|
|||
|
|
|||
|
LoggerUtils.Debug("[iap] IAP VerifyPurchase start");
|
|||
|
var requestArgs = args.info.ToDictionary(kv => kv.Key, kv => (object)kv.Value);
|
|||
|
ServerMgr.Instance.VerifyPurchase(requestArgs, (code, msg, data) =>
|
|||
|
{
|
|||
|
RefreshOrderStatue(args, IAPDataStateType.def);
|
|||
|
LoggerUtils.Debug("IAP VerifyPurchase CODE:" + code + " illegal:" + data.illegal_order + " env:" + data.environment);
|
|||
|
LoggerUtils.Debug("IAP VerifyPurchase msg:" + msg);
|
|||
|
|
|||
|
if ((code == 0 || code == 430) && data.illegal_order == 0)
|
|||
|
{
|
|||
|
// 将订阅商品信息存储到本地
|
|||
|
if (!IAPPurchaseManager.Instance._productArgs.ContainsKey(args.info["product_id"].ToString()) && args.info["product_type"].ToString().Equals("Subscription"))
|
|||
|
{
|
|||
|
IAPPurchaseManager.Instance._productArgs.Add(args.info["product_id"].ToString(), requestArgs);
|
|||
|
|
|||
|
}
|
|||
|
else if (IAPPurchaseManager.Instance._productArgs.ContainsKey(args.info["product_id"].ToString()) && args.info["product_type"].ToString().Equals("Subscription"))
|
|||
|
{
|
|||
|
IAPPurchaseManager.Instance._productArgs[args.info["product_id"].ToString()] = requestArgs;
|
|||
|
}
|
|||
|
RushSDKManager.Instance.OnPurchaseComplete?.Invoke(new PurchaseInfo(
|
|||
|
productName: args.info["product_name"],
|
|||
|
productID: args.info["product_id"],
|
|||
|
orderID: args.info["order_id"],
|
|||
|
currency: args.info["currency"],
|
|||
|
price: args.info["price"],
|
|||
|
gameExtra: gameExtraParam,
|
|||
|
failReason: "",
|
|||
|
orderAlreadyExists: code == 430,
|
|||
|
purchaseResult: code == 0,
|
|||
|
resultType: IAPResultType.PurchasingSuccess,
|
|||
|
productType: args.info["product_type"]
|
|||
|
));
|
|||
|
LogVerifySuccessOrder(args, data.environment, gameExtraParam, code == 430);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// 不需要重试的错误码
|
|||
|
if (!NotRetryCode.Contains(code))
|
|||
|
{
|
|||
|
SaveVerifyFailOrderId(args);
|
|||
|
}
|
|||
|
|
|||
|
// 主动删除过期订单
|
|||
|
if (!string.IsNullOrEmpty(data.illegal_msg) && args.info["product_type"].ToString().Equals("Subscription"))
|
|||
|
{
|
|||
|
if (data.illegal_msg.Contains("product expired"))
|
|||
|
{
|
|||
|
RemoveVerifySuccessOrder(args.info["product_id"]);
|
|||
|
}
|
|||
|
}
|
|||
|
if (code == -1)
|
|||
|
{
|
|||
|
RushSDKManager.Instance.OnPurchaseComplete?.Invoke(new PurchaseInfo(
|
|||
|
productName: args.info["product_name"],
|
|||
|
productID: args.info["product_id"],
|
|||
|
orderID: args.info["order_id"],
|
|||
|
currency: args.info["currency"],
|
|||
|
price: args.info["price"],
|
|||
|
gameExtra: gameExtraParam,
|
|||
|
failReason: "",
|
|||
|
orderAlreadyExists: code == 430,
|
|||
|
purchaseResult: false,
|
|||
|
resultType: IAPResultType.NULL,
|
|||
|
productType: args.info["product_type"]
|
|||
|
));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
RushSDKManager.Instance.OnPurchaseComplete?.Invoke(new PurchaseInfo(
|
|||
|
productName: args.info["product_name"],
|
|||
|
productID: args.info["product_id"],
|
|||
|
orderID: args.info["order_id"],
|
|||
|
currency: args.info["currency"],
|
|||
|
price: args.info["price"],
|
|||
|
gameExtra: gameExtraParam,
|
|||
|
failReason: "",
|
|||
|
orderAlreadyExists: code == 430,
|
|||
|
purchaseResult: false,
|
|||
|
resultType: code == 430 ? IAPResultType.PurchasingSuccess : IAPResultType.ServerAuthenticationFailed,
|
|||
|
productType: args.info["product_type"]
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
// msg 包含 illegal_order_0 说明是正常的缓存订单上报
|
|||
|
// 卸载重装后,一次性商品
|
|||
|
if (msg.Contains("illegal_order_0"))
|
|||
|
{
|
|||
|
LogVerifySuccessOrder(args, msg.Contains("sandbox") ? "sandbox" : "production", gameExtraParam, code == 430);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LogVerifyFailOrder(args, gameExtraParam, $"Purchase service code : {code} msg : {msg} dataMsg:{data.illegal_msg} illegal_order:{data.illegal_order} environment:{(msg.Contains("sandbox") ? "sandbox" : "production")}");
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void LogVerifySuccessOrder(IAPDataConfig _productInfo, string _enviroment, string _gameExtraParam, bool _orderAlreadyExists)
|
|||
|
{
|
|||
|
IAPEvent.LogPurchaseSuccess(new PurchaseInfo(
|
|||
|
productName: _productInfo.info["product_name"],
|
|||
|
productID: _productInfo.info["product_id"],
|
|||
|
orderID: _productInfo.info["order_id"],
|
|||
|
currency: _productInfo.info["currency"],
|
|||
|
price: _productInfo.info["price"],
|
|||
|
gameExtra: _gameExtraParam,
|
|||
|
failReason: "",
|
|||
|
orderAlreadyExists: _orderAlreadyExists,
|
|||
|
purchaseResult: !_orderAlreadyExists,
|
|||
|
resultType: IAPResultType.NULL,
|
|||
|
productType: _productInfo.info["product_type"]
|
|||
|
), _enviroment);
|
|||
|
}
|
|||
|
|
|||
|
private void LogVerifyFailOrder(IAPDataConfig _productInfo, string _gameExtraParam, string _errInfo)
|
|||
|
{
|
|||
|
|
|||
|
IAPEvent.LogPurchaseFail(new PurchaseInfo(
|
|||
|
productName: _productInfo.info["product_name"],
|
|||
|
productID: _productInfo.info["product_id"],
|
|||
|
orderID: _productInfo.info["order_id"],
|
|||
|
currency: _productInfo.info["currency"],
|
|||
|
price: _productInfo.info["price"],
|
|||
|
gameExtra: _gameExtraParam,
|
|||
|
failReason: _errInfo,
|
|||
|
orderAlreadyExists: false,
|
|||
|
purchaseResult: false,
|
|||
|
resultType: IAPResultType.NULL,
|
|||
|
productType: _productInfo.info["product_type"]
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 重置订单状态
|
|||
|
private void RefreshOrderStatue(IAPDataConfig _productInfo, IAPDataStateType state)
|
|||
|
{
|
|||
|
if (ES3.KeyExists("FailOrderCacheData"))
|
|||
|
{
|
|||
|
List<IAPDataConfig> list = ES3.Load(_purchaseOrderMap) as List<IAPDataConfig>;
|
|||
|
var value = _productInfo.info["order_id"];
|
|||
|
var want = list.Find(e => e.info.ContainsValue(value));
|
|||
|
if (want != null)
|
|||
|
{
|
|||
|
want.state = state;
|
|||
|
ES3.Save(_purchaseOrderMap, list);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 存储订单
|
|||
|
public void SaveOrder(IAPDataConfig _productInfo)
|
|||
|
{
|
|||
|
if (ES3.KeyExists(_purchaseOrderMap))
|
|||
|
{
|
|||
|
List<IAPDataConfig> list = ES3.Load(_purchaseOrderMap) as List<IAPDataConfig>;
|
|||
|
var want = list.Find(e => e.info.ContainsValue(_productInfo.info["order_id"]));
|
|||
|
if (want == null)
|
|||
|
{
|
|||
|
list.Add(_productInfo);
|
|||
|
ES3.Save(_purchaseOrderMap, list);
|
|||
|
IAPEvent.LogSaveOrderBySdk(new PurchaseInfo(
|
|||
|
productName: _productInfo.info["product_name"],
|
|||
|
productID: _productInfo.info["product_id"],
|
|||
|
orderID: _productInfo.info["order_id"],
|
|||
|
currency: _productInfo.info["currency"],
|
|||
|
price: _productInfo.info["price"],
|
|||
|
gameExtra: _productInfo.info["mGameExtraParam"],
|
|||
|
productType: _productInfo.info["product_type"],
|
|||
|
failReason: null,
|
|||
|
orderAlreadyExists: false,
|
|||
|
purchaseResult: false,
|
|||
|
resultType: IAPResultType.NULL
|
|||
|
));
|
|||
|
|
|||
|
LoggerUtils.Debug("[iap] IAP SaveOrder 已经有表了,添加订单,订单ID:" + _productInfo.info["order_id"] + " 未验证订单个数:" + list.Count);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
List<IAPDataConfig> tem = new List<IAPDataConfig>
|
|||
|
{
|
|||
|
_productInfo
|
|||
|
};
|
|||
|
ES3.Save(_purchaseOrderMap, tem);
|
|||
|
IAPEvent.LogSaveOrderBySdk(new PurchaseInfo(
|
|||
|
productName: _productInfo.info["product_name"],
|
|||
|
productID: _productInfo.info["product_id"],
|
|||
|
orderID: _productInfo.info["order_id"],
|
|||
|
currency: _productInfo.info["currency"],
|
|||
|
price: _productInfo.info["price"],
|
|||
|
gameExtra: _productInfo.info["mGameExtraParam"],
|
|||
|
productType: _productInfo.info["product_type"],
|
|||
|
failReason: null,
|
|||
|
orderAlreadyExists: false,
|
|||
|
purchaseResult: false,
|
|||
|
resultType: IAPResultType.NULL
|
|||
|
));
|
|||
|
LoggerUtils.Debug("[iap] IAP SaveOrder 第一次存储表 添加订单ID:" + _productInfo.info["order_id"] + " 商品类型:" + _productInfo.info["product_type"] + " 订单个数:" + tem.Count);
|
|||
|
}
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 保存验证失败订单
|
|||
|
private bool inDealFailOrder;
|
|||
|
private void SaveVerifyFailOrderId(IAPDataConfig _productInfo)
|
|||
|
{
|
|||
|
LoggerUtils.Debug("[iap] iap save verify fail order,orderId:" + _productInfo.info["order_id"] + " 商品类型:" + _productInfo.info["product_type"]);
|
|||
|
if (ES3.KeyExists(_purchaseOrderMap))
|
|||
|
{
|
|||
|
List<IAPDataConfig> list = ES3.Load(_purchaseOrderMap) as List<IAPDataConfig>;
|
|||
|
|
|||
|
var value = _productInfo.info["order_id"];
|
|||
|
var want = list.Find(e => e.info.ContainsValue(value));
|
|||
|
if (want == null)
|
|||
|
{
|
|||
|
list.Add(_productInfo);
|
|||
|
ES3.Save(_purchaseOrderMap, list);
|
|||
|
ReadFailOrderId();
|
|||
|
LoggerUtils.Debug("[iap] IAP SaveVerifyFailOrderId 已经有表了,添加失败订单,订单ID:" + _productInfo.info["order_id"] + " 商品类型:" + _productInfo.info["product_type"]);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (!inDealFailOrder)
|
|||
|
{
|
|||
|
inDealFailOrder = true;
|
|||
|
InvokeRepeating(nameof(ReadFailOrderId), 0, 30);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
List<IAPDataConfig> tem = new List<IAPDataConfig>
|
|||
|
{
|
|||
|
_productInfo
|
|||
|
};
|
|||
|
ES3.Save(_purchaseOrderMap, tem);
|
|||
|
ReadFailOrderId();
|
|||
|
LoggerUtils.Debug("[iap] IAP SaveVerifyFailOrderId 第一次存储表 添加失败订单 订单个数:" + tem.Count);
|
|||
|
}
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 再次验证订单
|
|||
|
public void ReadFailOrderId()
|
|||
|
{
|
|||
|
if (ES3.KeyExists(_purchaseOrderMap))
|
|||
|
{
|
|||
|
List<IAPDataConfig> list = ES3.Load(_purchaseOrderMap) as List<IAPDataConfig>;
|
|||
|
LoggerUtils.Debug("[iap] IAP ReadFailOrderId 读取失败订单列表,count:" + list.Count + " mRepeatCountDic" + mRepeatCountDic.Count);
|
|||
|
if (list.Count > 0)
|
|||
|
{
|
|||
|
// 当前进程每条订单校验5次,如果失败则不再处理
|
|||
|
// 重复校验时判断次数是否大于5,大于5则不再处理
|
|||
|
IAPDataConfig tempData = null;
|
|||
|
if (mRepeatCountDic.Count > 0)
|
|||
|
{
|
|||
|
foreach (var data in list)
|
|||
|
{
|
|||
|
LoggerUtils.Debug("[iap] iap start verify fail order with repeat dic" + data.info["order_id"] + " count:" + mRepeatCountDic[data.info["order_id"]]);
|
|||
|
if (mRepeatCountDic.ContainsKey(data.info["order_id"]) && mRepeatCountDic[data.info["order_id"]] <= 5)
|
|||
|
{
|
|||
|
tempData = data;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
// 如果还有次数不超过5次的订单,则继续校验,否则就不再处理
|
|||
|
if (tempData != null)
|
|||
|
{
|
|||
|
LoggerUtils.Debug("[iap] iap start verify fail order with repeat temp dic" + tempData.info["order_id"] + " count:" + mRepeatCountDic[tempData.info["order_id"]]);
|
|||
|
VerifyPurchase(tempData);
|
|||
|
IAPEvent.LogStarVerifyOrderBySdk(new PurchaseInfo(
|
|||
|
productName: tempData.info["product_name"],
|
|||
|
productID: tempData.info["product_id"],
|
|||
|
orderID: tempData.info["order_id"],
|
|||
|
currency: tempData.info["currency"],
|
|||
|
price: tempData.info["price"],
|
|||
|
gameExtra: tempData.info["mGameExtraParam"],
|
|||
|
productType: tempData.info["product_type"],
|
|||
|
failReason: null,
|
|||
|
orderAlreadyExists: false,
|
|||
|
purchaseResult: false,
|
|||
|
resultType: IAPResultType.NULL));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LoggerUtils.Debug("[iap] iap cancel verify fail order");
|
|||
|
CancelInvoke(nameof(ReadFailOrderId));
|
|||
|
inDealFailOrder = false;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
CancelInvoke(nameof(ReadFailOrderId));
|
|||
|
inDealFailOrder = false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 移除订单
|
|||
|
// 校验成功后移除订单
|
|||
|
public void RemoveVerifySuccessOrder(string orderId)
|
|||
|
{
|
|||
|
if (ES3.KeyExists(_purchaseOrderMap))
|
|||
|
{
|
|||
|
List<IAPDataConfig> list = ES3.Load(_purchaseOrderMap) as List<IAPDataConfig>;
|
|||
|
var want = list.Find(e => e.info.ContainsValue(orderId));
|
|||
|
if (want != null)
|
|||
|
{
|
|||
|
LoggerUtils.Debug("[iap] IAP RemoveVerifySuccessOrder 找到并移除订单,订单ID:" + orderId);
|
|||
|
list.Remove(want);
|
|||
|
IAPEvent.LogRemoveOrderBySdk(new PurchaseInfo(
|
|||
|
productName: want.info["product_name"],
|
|||
|
productID: want.info["product_id"],
|
|||
|
orderID: want.info["order_id"],
|
|||
|
currency: want.info["currency"],
|
|||
|
price: want.info["price"],
|
|||
|
gameExtra: want.info["mGameExtraParam"],
|
|||
|
productType: want.info["product_type"],
|
|||
|
failReason: null,
|
|||
|
orderAlreadyExists: false,
|
|||
|
purchaseResult: false,
|
|||
|
resultType: IAPResultType.NULL));
|
|||
|
ES3.Save(_purchaseOrderMap, list);
|
|||
|
}
|
|||
|
|
|||
|
LoggerUtils.Debug("[iap] IAP 订单还剩余:" + list.Count + " 条未校验");
|
|||
|
if (list.Count > 0)
|
|||
|
{
|
|||
|
ReadFailOrderId();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
inDealFailOrder = false;
|
|||
|
CancelInvoke(nameof(ReadFailOrderId));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
public class IAPDataConfig
|
|||
|
{
|
|||
|
public Dictionary<string, string> info;
|
|||
|
public IAPDataStateType state;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|