Forest_Client/Forest/Assets/Scripts/Gameplay/IAP/IAPHandle.cs

372 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using PhxhSDK;
using UnityEngine;
using Unity.Services.Core;
using UnityEngine.Purchasing;
using System.Collections.Generic;
using UnityEngine.Purchasing.Security;
using Unity.Services.Core.Environments;
public class IAPHandle : IStoreListener
{
private const string Environment = "production";
public Action<string> SendReceiptAction;
public Action<string> SetApplicationUseId;
public bool ClientVerify;
public List<string> ConsumableGoods;
public List<string> NonConsumableGoods;
private Dictionary<string, Product> _pendingDic = new Dictionary<string, Product>();
private IStoreController _controller;
private IExtensionProvider _extensions;
private IAppleExtensions _appleExtensions;
private IGooglePlayStoreExtensions _googlePlayStoreExtensions;
private Action<string> _onPurchaseSuccess;
private Action _onPurchaseFail;
public Action<string> OnRestorePurchase;
private bool _isInitialized;
/// <summary>
/// 当前购买商品
/// </summary>
private string _curProduct;
public IAPHandle()
{
try
{
InitUnityService();
}
catch (Exception exception)
{
DebugUtil.LogError("异常:" + exception);
}
}
#region 初始化
async void InitUnityService()
{
try
{
DebugUtil.Log("初始化Unity服务器");
var options = new InitializationOptions().SetEnvironmentName(Environment);
await UnityServices.InitializeAsync(options).ContinueWith(task => OnInitUnityServiceSuccess());
StandardPurchasingModule.Instance().useFakeStoreAlways = false;
}
catch (Exception exception)
{
OnInitUnityServiceError(exception.Message);
}
}
private void OnInitUnityServiceError(string message)
{
DebugUtil.LogError("unity server initialization failed, Error: " + message);
}
private void OnInitUnityServiceSuccess()
{
DebugUtil.Log("unity server initialization successful !!");
}
/// <summary>
/// 初始化商品
/// </summary>
public void InitIap()
{
if (ConsumableGoods == null && NonConsumableGoods == null)
return;
DebugUtil.Log("初始化IAP商品及 UnityPurchasing");
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
//Add Product
foreach (var consumableGoods in ConsumableGoods)
{
builder.AddProduct(consumableGoods, ProductType.Consumable);
}
foreach (var nonConsumableGoods in NonConsumableGoods)
{
builder.AddProduct(nonConsumableGoods, ProductType.NonConsumable);
}
UnityPurchasing.Initialize(this, builder);
}
/// <summary>
/// IStoreListener 初始化
/// </summary>
void IStoreListener.OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
_controller = controller;
_extensions = extensions;
_appleExtensions = extensions.GetExtension<IAppleExtensions>();
_googlePlayStoreExtensions = extensions.GetExtension<IGooglePlayStoreExtensions>();
if (string.IsNullOrEmpty(SDKManager.Instance.ApplicationUserName))
{
Guid uid = Guid.NewGuid();
SDKManager.Instance.ApplicationUserName = uid.ToString();
}
_appleExtensions.SetApplicationUsername(SDKManager.Instance.ApplicationUserName);
DebugUtil.Log("IAP initialization successful");
DebugUtil.Log("用户 UUID :" + SDKManager.Instance.ApplicationUserName);
_isInitialized = true;
//FetchGoodsList();
}
/// <summary>
/// IAP 初始化失败
/// </summary>
void IStoreListener.OnInitializeFailed(InitializationFailureReason error)
{
DebugUtil.LogError("Unity IAP遇到不可恢复的初始化错误调用请检查配置或设备设置中是否禁用ipa");
}
/// <summary>
/// IAP 初始化失败
/// </summary>
void IStoreListener.OnInitializeFailed(InitializationFailureReason error, string message)
{
DebugUtil.LogError("IAP OnInitializeFailed reason : " + message);
switch (error)
{
case InitializationFailureReason.AppNotKnown:
DebugUtil.LogError("IAP Is your App correctly uploaded on the relevant publisher console?");
break;
case InitializationFailureReason.PurchasingUnavailable:
DebugUtil.LogError("IAP Billing disabled!");
break;
case InitializationFailureReason.NoProductsAvailable:
DebugUtil.LogError("IAP No products available for purchase!");
break;
default:
throw new ArgumentOutOfRangeException(nameof(error), error, message);
}
}
#endregion
#region 购买
/// <summary>
/// IAP 购买商品
/// </summary>
public void BuyGoods(string productId, string actorId, Action<string> onPurchaseSuccess, Action onPurchaseFail)
{
DebugUtil.Log("购买商品: {0}", productId);
if (!_isInitialized || Application.internetReachability == NetworkReachability.NotReachable)
return;
_onPurchaseSuccess = onPurchaseSuccess;
_onPurchaseFail = onPurchaseFail;
_controller.InitiatePurchase(productId, actorId);
}
/// <summary>
/// IAP 购买成功
/// </summary>
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
{
#if UNITY_IOS||UNITY_ANDROID
var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
AppleTangle.Data(), Application.identifier);
DebugUtil.Log("Google Play Public Key: " + GooglePlayTangle.Data());
DebugUtil.Log("Apple Public Key: " + AppleTangle.Data());
try
{
var result = validator.Validate(e.purchasedProduct.receipt);
DebugUtil.Log("Receipt is valid. Contents:");
foreach (IPurchaseReceipt productReceipt in result)
{
DebugUtil.LogWarning("productID: {0}", productReceipt.productID);
DebugUtil.LogWarning("purchaseDate: {0}", productReceipt.purchaseDate);
DebugUtil.LogWarning("未解析: {0}", productReceipt.transactionID);
AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt;
if (null != apple)
{
_curProduct = productReceipt.productID;
DebugUtil.Log("iOS Receipt: {0}", apple.originalTransactionIdentifier);
if (!ClientVerify)
{
AddPendingOrder(apple.originalTransactionIdentifier, e.purchasedProduct);
SendReceipt(apple.originalTransactionIdentifier);
}
else
{
if (_onPurchaseSuccess != null)
{
_onPurchaseSuccess.Invoke(_curProduct);
}
else
{
OnRestorePurchase?.Invoke(_curProduct);
}
}
}
GooglePlayReceipt google = productReceipt as GooglePlayReceipt;
if (null != google)
{
_curProduct = productReceipt.productID;
// 这是 Google 的订单 ID。
// 请注意,在沙盒中测试时此项为 null因为 Google 的沙盒不提供订单 ID。
DebugUtil.LogWarning("Google transactionID: {0}", google.transactionID);
DebugUtil.LogWarning("Google purchaseState: {0}", google.purchaseState);
DebugUtil.LogWarning("Google purchaseToken: {0}", google.purchaseToken);
//TODO 谷歌服务器验证 待测试 待确定验证哪个参数
if (!ClientVerify)
{
AddPendingOrder(google.transactionID, e.purchasedProduct);
SendReceipt(google.transactionID);
}
else
{
if (_onPurchaseSuccess != null)
{
_onPurchaseSuccess.Invoke(_curProduct);
}
else
{
OnRestorePurchase?.Invoke(_curProduct);
}
}
}
}
}
catch (Exception exception)
{
DebugUtil.LogError("ProcessPurchase Result Error: " + exception);
}
if (ClientVerify)
return PurchaseProcessingResult.Complete;
return PurchaseProcessingResult.Pending;
#else
return PurchaseProcessingResult.Complete;
#endif
}
/// <summary>
/// 收集待验证凭证
/// </summary>
void AddPendingOrder(string appleReceipt, Product product)
{
DebugUtil.LogWarning("添加待验证收据:{0}", appleReceipt);
_pendingDic.TryAdd(appleReceipt, product);
}
/// <summary>
/// 向服务器发送收据验证
/// </summary>
private void SendReceipt(string receipt)
{
if (SendReceiptAction == null)
DebugUtil.LogError("SendReceiptAction 为空");
SendReceiptAction?.Invoke(receipt);
}
/// <summary>
/// 服务器验证完成移除待验证凭证
/// </summary>
public void RemovePendingOrder(string receipt)
{
if (_pendingDic.ContainsKey(receipt))
{
_controller.ConfirmPendingPurchase(_pendingDic[receipt]);
_pendingDic.Remove(receipt);
DebugUtil.LogWarning("移除待验证收据:{0}, 待验证凭证还有: {1}", receipt, _pendingDic.Count);
}
if (_pendingDic.Count == 0)
{
DebugUtil.LogWarning("订单 {0} 已完结, 购买的商品: {1}", receipt, _curProduct);
_onPurchaseSuccess?.Invoke(_curProduct);
}
}
/// <summary>
/// IAP 购买失败
/// </summary>
void IStoreListener.OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
if (failureReason == PurchaseFailureReason.PurchasingUnavailable)
{
DebugUtil.LogError("可能在设备设置中禁用了 IAP");
}
_curProduct = null;
_onPurchaseFail?.Invoke();
DebugUtil.LogError("Purchase of [{0}] failed for this reason: {1}", product.definition.id, failureReason);
}
#endregion
//TODO 待完善 测试
/// <summary>
/// 恢复交易
/// </summary>
public void RestorePurchase(Action<List<string>> onRestorePurchase)
{
if (!_isInitialized)
return;
var tempRestoreGoods = new List<string>();
_extensions.GetExtension<IAppleExtensions>().RestoreTransactions(result =>
{
if (result)
{
foreach (var product in _controller.products.all)
{
if (product.hasReceipt)
{
tempRestoreGoods.Add(product.definition.id);
DebugUtil.Log("Products to be restored: " + product.definition.id);
}
}
}
else
{
DebugUtil.LogWarning("Failure of recovery.");
}
onRestorePurchase?.Invoke(tempRestoreGoods);
});
}
void FetchGoodsList()
{
Action onSuccess = () =>
{
DebugUtil.Log("Successfully pulled to the store !!");
foreach (var product in _controller.products.all)
{
Debug.Log(product.definition.id);
}
};
Action<InitializationFailureReason> onFailure = (InitializationFailureReason i) =>
{
DebugUtil.LogError("Failed to pull store: " + i);
};
}
string GetAppReceipt()
{
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
string receipt = builder.Configure<IAppleConfiguration>().appReceipt;
return receipt;
}
bool CanMakePayments()
{
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
return builder.Configure<IAppleConfiguration>().canMakePayments;
}
}