SDK_UnityMoney/Assets/Script/SDKManager/Purchase/IAPPurchaseManager.cs

625 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#if UNITY_PURCHASE
using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework.Constraints;
using Unity.Services.Core;
using Unity.Services.Core.Environments;
using UnityEngine;
using UnityEngine.Purchasing;
using UnityEngine.Purchasing.Extension;
using static WZ.IAPOrderManager;
namespace WZ
{
public class IAPPurchaseManager : D_MonoSingleton<IAPPurchaseManager>, IDetailedStoreListener
{
#region API
public void BuyProductByID(string productId, string productName, string gameExtraParam)
{
#if UNITY_EDITOR
RushSDKManager.Instance.OnPurchaseComplete(new PurchaseInfo(
productName: productName,
productID: productId,
orderID: "",
currency: "USD",
price: "",
gameExtra: gameExtraParam,
failReason: "",
orderAlreadyExists: false,
purchaseResult: true,
resultType: IAPResultType.PurchasingSuccess,
productType: ""
));
#else
_productName = productName;
_gameExtraParam = gameExtraParam;
Product m_p = GetProductInfoByID(productId);
var currencyCode = "";
var localizedPrice = "";
if (m_p != null)
{
currencyCode = m_p.metadata.isoCurrencyCode;
localizedPrice = m_p.metadata.localizedPrice.ToString();
}
IAPEvent.LogIAPButtonClick(new PurchaseInfo(
productName: _productName,
productID: productId,
orderID: "",
currency: currencyCode,
price: localizedPrice,
gameExtra: _gameExtraParam,
failReason: "",
orderAlreadyExists: false,
purchaseResult: false,
resultType: IAPResultType.NULL,
productType: ""));
if (IsInitialized())
{
if (_inPurchaseProgress)
{
LoggerUtils.Debug("[iap] The payment is in progress, please do not initiate the payment repeatedly.");
return;
}
Product product = _storeController.products.WithID(productId);
if (product != null && product.availableToPurchase)
{
_inPurchaseProgress = true;
LoggerUtils.Debug(
string.Format("[iap] Purchasing product asychronously: '{0}'", product.definition.id));
if (_googlePlayConfiguration != null)
{
if (!string.IsNullOrEmpty(productName))
{
_googlePlayConfiguration.SetObfuscatedAccountId(productName);
}
_googlePlayConfiguration.SetObfuscatedProfileId(gameExtraParam);
LoggerUtils.Debug($"[iap] [BuyProductByID] 设置成功 userId = {productName} profileId = {gameExtraParam}");
}
_storeController.InitiatePurchase(product);
}
else
{
IAPEvent.LogPurchaseFail(new PurchaseInfo(
productName: _productName,
productID: productId,
orderID: "",
currency: currencyCode,
price: localizedPrice,
gameExtra: _gameExtraParam,
failReason: "BuyProductID FAIL. Not purchasing product, either is not found or is not available for purchase",
orderAlreadyExists: false,
purchaseResult: false,
resultType: IAPResultType.NULL,
productType: product.definition.type.ToString()));
LoggerUtils.Debug("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
}
}
else
{
RushSDKManager.Instance.OnPurchaseComplete(new PurchaseInfo(
productName: productName,
productID: productId,
orderID: "",
currency: "",
price: "",
gameExtra: _gameExtraParam,
failReason: "BuyProductID FAIL. IAP Not initialized or Not add product.",
orderAlreadyExists: false,
purchaseResult: false,
resultType: IAPResultType.PurchasingUnavailable,
productType: ""));
IAPEvent.LogPurchaseFail(new PurchaseInfo(
productName: _productName,
productID: productId,
orderID: "",
currency: currencyCode,
price: localizedPrice,
gameExtra: _gameExtraParam,
failReason: "BuyProductID FAIL. Not purchasing product, either is not found or is not available for purchase",
orderAlreadyExists: false,
purchaseResult: false,
resultType: IAPResultType.NULL,
productType: ""));
LoggerUtils.Debug("[iap] BuyProductID FAIL. IAP Not initialized or Not add product.");
LoggerUtils.Debug("[iap] OnPurchaseFailed -> productId : " + productId + " , transactionID : " + "" + " , localizedPrice : " + "" + " , isoCurrencyCode : " + "");
}
#endif
}
public string GetProductPriceByID(string pID)
{
if (_storeController == null && _storeExtensionProvider == null)
return "";
Product[] tProducts = _storeController.products.all;
for (int i = 0; i < tProducts.Length; i++)
{
Product tItem = tProducts[i];
if (tItem.definition.id.Equals(pID))
{
return tItem.metadata.GetGoogleProductMetadata().localizedPriceString;
}
}
return "";
}
public Product GetProductInfoByID(string pID)
{
if (_storeController == null && _storeExtensionProvider == null)
return null;
for (int i = 0; i < _storeController.products.all.Length; i++)
{
Product tItem = _storeController.products.all[i];
if (tItem.definition.id.Equals(pID))
{
return tItem;
}
}
return null;
}
public void AddProductsDynamic(Dictionary<string, ProductType> products, Action<bool, string> onProductsResult = null)
{
_initProductDic = products;
FetchAdditionalProducts(products, onProductsResult);
}
public void DoConfirmPendingPurchaseByID(string productId)
{
Product product = _storeController.products.WithID(productId);
if (null == product) return;
if (string.IsNullOrEmpty(product.transactionID)) return;
if (product != null && product.availableToPurchase)
{
_storeController.ConfirmPendingPurchase(product);
_inPurchaseProgress = false;
}
}
#endregion
#region
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent)
{
_inPurchaseProgress = false;
LoggerUtils.Debug("[iap] Purchase OK: " + purchaseEvent.purchasedProduct.definition.id);
var wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(purchaseEvent.purchasedProduct.receipt);
var payload = (string)wrapper["Payload"];
var profileId = _gameExtraParam;
var _productName = "";
var payloadObj = (Dictionary<string, object>)MiniJson.JsonDecode(payload);
var o = (string)payloadObj["json"];
var payloadData = (Dictionary<string, object>)MiniJson.JsonDecode(o);
if (payloadData.TryGetValue("obfuscatedAccountId", out var obfuscatedAccountIdValue))
{
var obfuscatedAccountId = (string)obfuscatedAccountIdValue;
if (!string.IsNullOrEmpty(obfuscatedAccountId))
{
_productName = obfuscatedAccountId;
}
}
if (payloadData.TryGetValue("obfuscatedProfileId", out var value))
{
var obfuscatedProfileId = (string)value;
if (!string.IsNullOrEmpty(obfuscatedProfileId))
{
profileId = obfuscatedProfileId;
}
}
LoggerUtils.Debug("[iap] productName" + _productName + " profileId:" + profileId);
string token = "";
string orderId = "";
if (Application.platform == RuntimePlatform.Android)
{
var gpDetails = (Dictionary<string, object>)MiniJson.JsonDecode(payload);
var gpJson = (string)gpDetails["json"];
var tokenJson = (Dictionary<string, object>)MiniJson.JsonDecode(gpJson);
token = (string)tokenJson["purchaseToken"];
orderId = (string)tokenJson["orderId"];
LoggerUtils.Debug("[iap] ClientIAPSuccess productId : " + purchaseEvent.purchasedProduct.definition.id
+ " , transactionID : " + orderId
+ " , token : " + token
+ " , localizedPrice : " + purchaseEvent.purchasedProduct.metadata.localizedPriceString
+ " , isoCurrencyCode : " + purchaseEvent.purchasedProduct.metadata.isoCurrencyCode);
}
IAPDataConfig newData = new IAPDataConfig();
newData.info = new Dictionary<string, string>
{
{ "price", purchaseEvent.purchasedProduct.metadata.localizedPrice.ToString() },
{ "product_id", purchaseEvent.purchasedProduct.definition.id },
{ "product_name", (string)_productName },
{ "purchase_token", token },
{ "order_id", orderId },
{ "currency", purchaseEvent.purchasedProduct.metadata.isoCurrencyCode },
{ "payment_method", "googleplay" },
{ "product_type", purchaseEvent.purchasedProduct.definition.type.ToString() },
{ "mGameExtraParam", (string)profileId }
};
newData.state = IAPDataStateType.def;
IAPEvent.LogClientComplete(new PurchaseInfo(
productName: (string)_productName,
productID: purchaseEvent.purchasedProduct.definition.id,
orderID: orderId,
currency: purchaseEvent.purchasedProduct.metadata.isoCurrencyCode,
price: purchaseEvent.purchasedProduct.metadata.localizedPrice.ToString(),
gameExtra: _gameExtraParam,
failReason: "",
orderAlreadyExists: false,
purchaseResult: true,
resultType: IAPResultType.PurchasingSuccess,
productType: purchaseEvent.purchasedProduct.definition.type.ToString()
));
IAPOrderManager.Instance.SaveOrder(newData);
IAPOrderManager.Instance.VerifyPurchase(newData);
return PurchaseProcessingResult.Complete;
}
#endregion
#region
private void FetchAdditionalProducts(Dictionary<string, ProductType> ProductDic, Action<bool, string> onProductsResult = null)
{
if (!IsInitialized())
{
_addProductsDic = ProductDic;
LoggerUtils.Debug("[iap] IAP not init.Now InitUnityPurchase");
InitUnityPurchase();
return;
}
if (_isFetchingAdditionalProducts)
{
LoggerUtils.Debug("[iap] Now fetching additional productsdon't call repeatedly");
if (onProductsResult != null)
{
onProductsResult(false, "Now fetching additional productsdon't call repeatedly");
}
return;
}
_isFetchingAdditionalProducts = true;
if (ProductDic != null)
{
var additional = new HashSet<ProductDefinition>();
foreach (string tID in ProductDic.Keys)
{
additional.Add(new ProductDefinition(tID, ProductDic[tID]));
}
Action onSuccess = () =>
{
_isFetchingAdditionalProducts = false;
LoggerUtils.Debug("[iap] Fetched successfully!");
RushSDKManager.Instance.OnGetProductsInfo?.Invoke(_storeController.products.all);
foreach (var product in _storeController.products.all)
{
LoggerUtils.Debug("[iap]" + product.metadata.localizedTitle
+ "|" + product.metadata.localizedPriceString
+ "|" + product.metadata.localizedDescription
+ "|" + product.metadata.isoCurrencyCode);
}
if (onProductsResult != null)
{
onProductsResult(true, "Fetched successfully!");
}
};
Action<InitializationFailureReason, string> onFailure = (InitializationFailureReason i, string msg) =>
{
_isFetchingAdditionalProducts = false;
if (onProductsResult != null)
{
onProductsResult(true, "Fetching failed for the specified reason: " + i + " msg: " + msg);
}
LoggerUtils.Debug("[iap] Fetching failed for the specified reason: " + i + " msg: " + msg);
};
_storeController.FetchAdditionalProducts(additional, onSuccess, onFailure);
}
}
#endregion
#region
public void PreInitialize()
{
LoggerUtils.Debug("[iap] PreInitialize() mServiceInit: " + _serviceInit);
if (!_serviceInit)
{
InitializeUnityServices(OnSuccess, OnError);
}
}
private void InitializeUnityServices(Action onSuccess, Action<string> onError)
{
try
{
var options = new InitializationOptions().SetEnvironmentName("production");
UnityServices.InitializeAsync(options).ContinueWith(task => onSuccess());
}
catch (Exception exception)
{
onError(exception.Message);
}
}
private void OnSuccess()
{
LoggerUtils.Debug("[iap] Congratulations!\nUnity Gaming Services has been successfully initialized.");
_serviceInit = true;
IAPEvent.LogPurchaseInit();
}
private void OnError(string message)
{
LoggerUtils.Debug($"[iap] Unity Gaming Services failed to initialize with error: {message}.");
}
/// <summary>
/// 初始化IAP
/// </summary>
public void Initialize()
{
LoggerUtils.Debug("[iap] IAP Initialize() _storeController.Debug:" + _storeController + " m_StoreExtensionProvider: " + _storeExtensionProvider);
if (_storeController == null && _storeExtensionProvider == null)
InitUnityPurchase();
}
private void InitUnityPurchase()
{
LoggerUtils.Debug("[iap] IAP InitUnityPurchase() IsInitialized: " + IsInitialized());
if (IsInitialized()) return;
_repeatCountDic = new Dictionary<string, int>();
_module ??= StandardPurchasingModule.Instance();
// 配置模式;
_builder ??= ConfigurationBuilder.Instance(_module);
int productsNum = 0;
foreach (string tID in StaticProductDic.Keys)
{
productsNum++;
if (!string.IsNullOrEmpty(tID))
{
LoggerUtils.Debug($"[iap] Add IAPProducts IAPProducts: {tID}");
_builder.AddProduct(tID, StaticProductDic[tID]);
}
}
if (_initProductDic != null && _initProductDic.Count > 0)
{
foreach (string tID in _initProductDic.Keys)
{
productsNum++;
if (!string.IsNullOrEmpty(tID))
{
LoggerUtils.Debug($"[iap] Add InitProductDic APProducts: {tID}");
_builder.AddProduct(tID, _initProductDic[tID]);
}
}
}
if (_addProductsDic != null && _addProductsDic.Count > 0)
{
foreach (string tID in _addProductsDic.Keys)
{
productsNum++;
if (!string.IsNullOrEmpty(tID))
{
LoggerUtils.Debug($"[iap] Add AddProductsDic IAPProducts: {tID}");
_builder.AddProduct(tID, _addProductsDic[tID]);
}
}
}
_googlePlayConfiguration = _builder.Configure<IGooglePlayConfiguration>();
if (productsNum > 0)
{
UnityPurchasing.Initialize(this, _builder);
}
else
{
LoggerUtils.Debug(
"[iap] UnityPurchasing will not initialize.Products is empty,please add product.");
}
}
private bool IsInitialized()
{
return _storeController != null && _storeExtensionProvider != null;
}
#endregion
#region
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
LoggerUtils.Debug("[iap] IAP initialize Succeed!");
_storeController = controller;
_storeExtensionProvider = extensions;
foreach (var product in _storeController.products.all)
{
LoggerUtils.Debug("[iap] " + product.metadata.localizedTitle
+ "|" + product.metadata.localizedPriceString
+ "|" + product.metadata.localizedDescription
+ "|" + product.metadata.isoCurrencyCode
+ "|" + product.definition.id
+ "|" + product.definition.type);
}
RushSDKManager.Instance.OnGetProductsInfo?.Invoke(_storeController.products.all);
RushSDKManager.Instance.OnPurchaseInitComplete?.Invoke(true, "");
IAPEvent.LogPurchaseInit(true);
IAPSubscribeManager.Instance.CheckSubscribeReceipt();
IAPOrderManager.Instance.RefreshOrderStatue();
}
#endregion
#region
public void OnInitializeFailed(InitializationFailureReason error)
{
OnInitializeFailed(error, "");
RushSDKManager.Instance.OnPurchaseInitComplete?.Invoke(false, error.ToString());
}
public void OnInitializeFailed(InitializationFailureReason error, string message)
{
LoggerUtils.Debug("[iap] IAP OnInitializeFailed error" + error.ToString() + " msg:" + message);
switch (error)
{
case InitializationFailureReason.AppNotKnown:
LoggerUtils.Debug("[iap] Is your App correctly uploaded on the relevant publisher console?");
break;
case InitializationFailureReason.PurchasingUnavailable:
LoggerUtils.Debug("[iap] Billing disabled! Ask the user if billing is disabled in device settings.");
break;
case InitializationFailureReason.NoProductsAvailable:
LoggerUtils.Debug("[iap] No products available for purchase! Developer configuration error; check product metadata!");
break;
}
IAPEvent.LogPurchaseInit(false, error.ToString());
}
#endregion
#region
public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
{
_inPurchaseProgress = false;
IAPOrderManager.Instance.ProgressCacheOrder();
LoggerUtils.Debug("[iap] OnPurchaseFailed productId : " + failureDescription.productId
+ " , transactionID : " + product.transactionID
+ " , localizedPrice : " + product.metadata.localizedPriceString
+ " , isoCurrencyCode : " + product.metadata.isoCurrencyCode
+ "failureDescription" + failureDescription.message);
// 失败打点
IAPEvent.LogPurchaseFail(new PurchaseInfo(
productName: product.metadata.localizedTitle,
productID: product.definition.id,
orderID: "",
currency: product.metadata.isoCurrencyCode,
price: product.metadata.localizedPriceString,
gameExtra: _gameExtraParam,
failReason: failureDescription.message,
orderAlreadyExists: false,
purchaseResult: false,
resultType: (IAPResultType)failureDescription.reason,
productType: product.definition.type.ToString()
));
// 购买失败回调
RushSDKManager.Instance.OnPurchaseComplete(new PurchaseInfo(
productName: product.metadata.localizedTitle,
productID: product.definition.id,
orderID: "",
currency: product.metadata.isoCurrencyCode,
price: product.metadata.localizedPriceString,
gameExtra: _gameExtraParam,
failReason: failureDescription.message,
orderAlreadyExists: false,
purchaseResult: false,
resultType: (IAPResultType)failureDescription.reason,
productType: product.definition.type.ToString()
));
}
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
_inPurchaseProgress = false;
IAPOrderManager.Instance.ProgressCacheOrder();
LoggerUtils.Debug("[iap] OnPurchaseFailed productId -> : " + product.definition.id
+ " , transactionID : " + product.transactionID
+ " , localizedPrice : " + product.metadata.localizedPriceString
+ " , isoCurrencyCode : " + product.metadata.isoCurrencyCode
+ " , failureReason : " + failureReason.ToString());
// 失败打点
IAPEvent.LogPurchaseFail(new PurchaseInfo(
productName: product.metadata.localizedTitle,
productID: product.definition.id,
orderID: "",
currency: product.metadata.isoCurrencyCode,
price: product.metadata.localizedPriceString,
gameExtra: _gameExtraParam,
productType: "",
failReason: failureReason.ToString(),
orderAlreadyExists: false,
purchaseResult: false,
resultType: (IAPResultType)failureReason)
);
// 购买失败回调
RushSDKManager.Instance.OnPurchaseComplete(new PurchaseInfo(
productName: product.metadata.localizedTitle,
productID: product.definition.id,
orderID: "",
currency: product.metadata.isoCurrencyCode,
price: product.metadata.localizedPriceString,
gameExtra: _gameExtraParam,
failReason: failureReason.ToString(),
orderAlreadyExists: false,
purchaseResult: false,
resultType: (IAPResultType)failureReason,
productType: product.definition.type.ToString()
));
}
#endregion
#region
public static Dictionary<string, ProductType> StaticProductDic = new Dictionary<string, ProductType>()
{
};
#endregion
#region Properties
private StandardPurchasingModule _module;
private ConfigurationBuilder _builder;
public IStoreController _storeController;
private static IExtensionProvider _storeExtensionProvider;
public string _gameExtraParam = "";
private static string _productName = "";
private bool _inPurchaseProgress = false;
private bool _serviceInit = false;
private Dictionary<string, ProductType> _addProductsDic;
private Dictionary<string, ProductType> _initProductDic;
public Dictionary<string, int> _repeatCountDic;
private IGooglePlayConfiguration _googlePlayConfiguration;
private bool _isFetchingAdditionalProducts = false;
#endregion
}
}
#endif