mergemilitary/Assets/TKGSDK/Common/SDKTools/IAPTool.cs

732 lines
33 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 USE_IAP
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;
using Touka;
using UnityEngine.Purchasing.Extension;
using Unity.Services.Core;
using Unity.Services.Core.Environments;
namespace Touka
{
public class IAPTool : NormalSingleton<IAPTool>, IDetailedStoreListener
{
/// <summary>
/// 获取到在AppStore和Google Play 上配置的商品时回调
/// 游戏可根据返回的商品列表进行商品UI的展示或隐藏
/// </summary>
public event Action<Product[]> OnGetProductsInfo;
/// <summary>
/// 拉起支付窗口,开始支付流程时回调
/// </summary>
public event Action OnPurchaseBegin;
private static string mProductName = "";
private static string mGameExtraParam = "";
/// <summary>
/// 恢复购买回调
/// </summary>
public event Action<bool> OnRestoreDone;
public bool IsRestoring => mIsRestoring;
private bool mIsRestoring = false;
private static IStoreController m_StoreController; // 存储商品信息;
private static IExtensionProvider m_StoreExtensionProvider; // IAP扩展工具;
private bool m_PurchaseInProgress = false; // 是否处于付费中;
private Dictionary<string, ProductType> mInitProductDic;
private SubscriptionInfo mSubsInfo = null;
private const string Environment = "production";
private bool mServiceInit = false;
private bool IsFetchingAdditionalProducts = false;
private Dictionary<string, ProductType> mAddProductsDic;
/// <summary>
/// 初始化UnityServices
/// </summary>
public void PreInitialize()
{
TKGDebugger.LogDebug("PreInitialize() mServiceInit: " + mServiceInit);
if (!mServiceInit)
{
InitializeUnityServices(OnSuccess, OnError);
}
}
/// <summary>
/// 初始化IAP
/// </summary>
public void Initialize()
{
TKGDebugger.LogDebug("IAP Initialize() m_StoreController:" + m_StoreController +
" m_StoreExtensionProvider: " + m_StoreExtensionProvider);
if (m_StoreController == null && m_StoreExtensionProvider == null)
InitUnityPurchase();
}
private bool IsInitialized()
{
return m_StoreController != null && m_StoreExtensionProvider != null;
}
/// <summary>
/// IAP初始化后可使用此接口添加新增商品id
/// </summary>
/// <param name="pInitProductDic">商品IDs</param>
/// <param name="onProductsResult">bool添加新增商品id结果 string附加信息</param>
[Obsolete(
"AddProducts(Dictionary<string, ProductType> products, Action<bool, string> onProductsResult = null) is deprecated, please use TKGSDKManager.Instance.BuyProductByID(string productId) instead.")]
public void AddProducts(Dictionary<string, ProductType> products, Action<bool, string> onProductsResult = null)
{
mInitProductDic = products;
FetchAdditionalProducts(products, onProductsResult);
}
/// <summary>
/// 初始化UnityServices
/// </summary>
/// <param name="onSuccess"></param>
/// <param name="onError"></param>
private void InitializeUnityServices(Action onSuccess, Action<string> onError)
{
try
{
var options = new InitializationOptions().SetEnvironmentName(Environment);
UnityServices.InitializeAsync(options).ContinueWith(task => onSuccess());
}
catch (Exception exception)
{
onError(exception.Message);
}
}
void OnSuccess()
{
var text = "Congratulations!\nUnity Gaming Services has been successfully initialized.";
TKGDebugger.LogDebug(text);
mServiceInit = true;
}
void OnError(string message)
{
var text = $"Unity Gaming Services failed to initialize with error: {message}.";
TKGDebugger.LogError(text);
}
// 初始化IAP;
private void InitUnityPurchase()
{
TKGDebugger.LogDebug("IAP InitUnityPurchase() IsInitialized: " + IsInitialized());
if (IsInitialized()) return;
// 标准采购模块;
StandardPurchasingModule module = StandardPurchasingModule.Instance();
// 配置模式;
ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
// 注意ProductType的类型Consumable是可以无限购买(比如水晶)NonConsumable是只能购买一次(比如关卡)Subscription是每月订阅(比如VIP);
// 这里初始化没有添加平台信息因为平台信息有的时候还存在bug如果必须添加也可以添加没有问题确保平台信息添加正确就行了。
int productsNum = 0;
foreach (string tID in IAPProducts.ProductDic.Keys)
{
productsNum++;
if (!string.IsNullOrEmpty(tID))
{
TKGDebugger.LogDebug($"Add IAPProducts IAPProducts: {tID}");
builder.AddProduct(tID, IAPProducts.ProductDic[tID]);
}
}
if (mInitProductDic != null && mInitProductDic.Count > 0)
{
foreach (string tID in mInitProductDic.Keys)
{
productsNum++;
if (!string.IsNullOrEmpty(tID))
{
TKGDebugger.LogDebug($"Add InitProductDic APProducts: {tID}");
builder.AddProduct(tID, mInitProductDic[tID]);
}
}
}
if (mAddProductsDic != null && mAddProductsDic.Count > 0)
{
foreach (string tID in mAddProductsDic.Keys)
{
productsNum++;
if (!string.IsNullOrEmpty(tID))
{
TKGDebugger.LogDebug($"Add AddProductsDic IAPProducts: {tID}");
builder.AddProduct(tID, mAddProductsDic[tID]);
}
}
}
if (productsNum > 0)
{
//初始化;
UnityPurchasing.Initialize(this, builder);
}
else
{
TKGDebugger.LogDebug(
"UnityPurchasing will not initialize.Products is empty,please invoke TKGSDKManager.Instance.BuyProductByID(string productId) add product.");
}
}
/// <summary>
/// 批量获取其他产品
/// </summary>
private void FetchAdditionalProducts(Dictionary<string, ProductType> ProductDic,
Action<bool, string> onProductsResult = null)
{
if (!IsInitialized())
{
mAddProductsDic = ProductDic;
TKGDebugger.LogDebug("IAP not init.Now InitUnityPurchase");
InitUnityPurchase();
return;
}
if (IsFetchingAdditionalProducts)
{
TKGDebugger.LogDebug("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;
TKGDebugger.LogDebug("Fetched successfully!");
if (OnGetProductsInfo != null)
{
OnGetProductsInfo(m_StoreController.products.all);
}
foreach (var product in m_StoreController.products.all)
{
TKGDebugger.LogDebug(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);
}
Debug.Log("Fetching failed for the specified reason: " + i + " msg: " + msg);
};
m_StoreController.FetchAdditionalProducts(additional, onSuccess, onFailure);
}
}
#region Public Func
/// <summary>
/// 根据商品ID 购买商品;
/// </summary>
/// <param name="productId">商品ID</param>
[Obsolete(
"BuyProductByID(string productId) is deprecated, please use TKGSDKManager.Instance.BuyProductByID(string productId) instead.")]
public void BuyProductByID(string productId, string productName, string gameExtraParam)
{
mProductName = productName;
mGameExtraParam = gameExtraParam;
TKGNativeInterface.Instance.LogIAPBtnClick(productName, productId);
// 设置监听购买二次验证的回调
TKGSDKCallback.SetSecondPurchaseCallback(TKGSDKManager.Instance.OnPurchaseDone);
if (IsInitialized())
{
if (m_PurchaseInProgress)
{
TKGDebugger.LogDebug("The payment is in progress, please do not initiate the payment repeatedly.");
return;
}
Product product = m_StoreController.products.WithID(productId);
if (product != null && product.availableToPurchase)
{
OnPurchaseBegin?.Invoke();
m_PurchaseInProgress = true;
TKGDebugger.LogDebug(
string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
m_StoreController.InitiatePurchase(product);
}
else
{
TKGDebugger.LogDebug(
"BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
TKGNativeInterface.Instance.ClientIAPFailed(mProductName, productId, "", "", "", "", mGameExtraParam, IAPClientFailReasonType.NotInit);
}
}
else
{
TKGDebugger.LogDebug("BuyProductID FAIL. IAP Not initialized or Not add product.");
//OnPurchaseDone?.Invoke(productId, false, "", "");
TKGDebugger.LogDebug("OnPurchaseFailed -> productId : " + productId + " , transactionID : " + "" + " , localizedPrice : " + "" + " , isoCurrencyCode : " + "");
TKGNativeInterface.Instance.ClientIAPFailed(mProductName, productId, "", "", "", "", mGameExtraParam, IAPClientFailReasonType.NotInit);
}
}
// 确认购买产品成功;
private void DoConfirmPendingPurchaseByID(string productId)
{
Product product = m_StoreController.products.WithID(productId);
if (product != null && product.availableToPurchase)
{
if (m_PurchaseInProgress)
{
m_StoreController.ConfirmPendingPurchase(product);
m_PurchaseInProgress = false;
}
}
}
/// <summary>
/// 恢复购买
/// </summary>
[Obsolete("RestorePurchases() is deprecated, please use TKGSDKManager.Instance.RestorePurchases() instead.")]
public void RestorePurchases()
{
if (!IsInitialized())
{
OnRestoreDone?.Invoke(false);
TKGDebugger.LogDebug("RestorePurchases FAIL. Not initialized.");
return;
}
if (Application.platform == RuntimePlatform.IPhonePlayer ||
Application.platform == RuntimePlatform.OSXPlayer)
{
TKGDebugger.LogDebug("RestorePurchases started ...");
mIsRestoring = true;
var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
apple.RestoreTransactions((result, msg) =>
{
mIsRestoring = false;
OnRestoreDone?.Invoke(result);
// 返回一个bool值如果成功则会多次调用支付回调然后根据支付回调中的参数得到商品id最后做处理(ProcessPurchase);
TKGDebugger.LogDebug("RestorePurchases continuing: " + result + "msg" + msg
+ " If no further messages, no purchases available to restore.");
});
}
else
{
TKGDebugger.LogDebug("RestorePurchases FAIL. Not supported on this platform. Current = "
+ Application.platform);
}
}
#endregion
#region IStoreListener Callback
// IAP初始化成功回调函数;
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
TKGDebugger.LogDebug("IAP initialize Succeed!");
m_StoreController = controller;
m_StoreExtensionProvider = extensions;
// 这里可以获取您在AppStore和Google Play 上配置的商品;
if (OnGetProductsInfo != null)
{
OnGetProductsInfo(m_StoreController.products.all);
}
foreach (var product in m_StoreController.products.all)
{
TKGDebugger.LogDebug(product.metadata.localizedTitle
+ "|" + product.metadata.localizedPriceString
+ "|" + product.metadata.localizedDescription
+ "|" + product.metadata.isoCurrencyCode);
}
#if UNITY_IOS
m_StoreExtensionProvider.GetExtension<IAppleExtensions>().RegisterPurchaseDeferredListener(OnDeferred);
#endif
/*
Dictionary<string, string> introductory_info_dict = null;
#if UNITY_IOS
introductory_info_dict = m_StoreExtensionProvider.GetExtension<IAppleExtensions>().GetIntroductoryPriceDictionary();
#endif
TKGDebugger.LogDebug("IAP - Available items:");
foreach (var item in controller.products.all)
{
if (item.availableToPurchase)
{
TKGDebugger.LogDebug("IAP - " + string.Join(" - ",
new[]
{
item.metadata.localizedTitle,
item.metadata.localizedDescription,
item.metadata.isoCurrencyCode,
item.metadata.localizedPrice.ToString(),
item.metadata.localizedPriceString,
item.transactionID,
item.receipt
}));
// this is the usage of SubscriptionManager class
if (item.receipt != null)
{
if (item.definition.type == ProductType.Subscription)
{
if (CheckIfProductIsAvailableForSubscriptionManagerC(item.receipt))
{
string intro_json = (introductory_info_dict == null || !introductory_info_dict.ContainsKey(item.definition.storeSpecificId)) ? null : introductory_info_dict[item.definition.storeSpecificId];
SubscriptionManager p = new SubscriptionManager(item, intro_json);
SubscriptionInfo info = p.getSubscriptionInfo();
mSubsInfo = info;
TKGDebugger.LogDebug("product id is: " + info.getProductId());
TKGDebugger.LogDebug("purchase date is: " + info.getPurchaseDate());
TKGDebugger.LogDebug("subscription next billing date is: " + info.getExpireDate());
TKGDebugger.LogDebug("is subscribed? " + info.isSubscribed().ToString());
TKGDebugger.LogDebug("is expired? " + info.isExpired().ToString());
TKGDebugger.LogDebug("is cancelled? " + info.isCancelled());
TKGDebugger.LogDebug("product is in free trial peroid? " + info.isFreeTrial());
TKGDebugger.LogDebug("product is auto renewing? " + info.isAutoRenewing());
TKGDebugger.LogDebug("subscription remaining valid time until next billing date is: " + info.getRemainingTime());
TKGDebugger.LogDebug("is this product in introductory price period? " + info.isIntroductoryPricePeriod());
TKGDebugger.LogDebug("the product introductory localized price is: " + info.getIntroductoryPrice());
TKGDebugger.LogDebug("the product introductory price period is: " + info.getIntroductoryPricePeriod());
TKGDebugger.LogDebug("the number of product introductory price period cycles is: " + info.getIntroductoryPricePeriodCycles());
}
else
{
TKGDebugger.LogDebug("This product is not available for SubscriptionManager class, only products that are purchase by 1.19+ SDK can use this class.");
}
}
else
{
TKGDebugger.LogDebug("the product is not a subscription product");
}
}
else
{
TKGDebugger.LogDebug("the product should have a valid receipt");
}
}
}
*/
}
public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
{
TKGDebugger.LogError(product.transactionID + "," + failureDescription.productId + "," +
failureDescription.message);
m_PurchaseInProgress = false;
//OnPurchaseDone?.Invoke(product.definition.id, false, "", "");
TKGDebugger.LogDebug("productId : " + failureDescription.productId + " , transactionID : " + product.transactionID + " , localizedPrice : " + product.metadata.localizedPriceString + " , isoCurrencyCode : " + product.metadata.isoCurrencyCode);
TKGNativeInterface.Instance.ClientIAPFailed(mProductName, failureDescription.productId, product.transactionID, "", product.metadata.isoCurrencyCode,product.metadata.localizedPrice.ToString(), mGameExtraParam, IAPClientFailReasonType.PurchaseFailed);
}
public void OnInitializeFailed(InitializationFailureReason error)
{
OnInitializeFailed(error, "");
}
// IAP初始化失败回掉函数没有网络的情况下并不会调起而是一直等到有网络连接再尝试初始化;
#pragma warning disable CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。
public void OnInitializeFailed(InitializationFailureReason error, string? message)
#pragma warning restore CS8632 // 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。
{
TKGDebugger.LogDebug("IAP OnInitializeFailed error" + error.ToString());
TKGDebugger.LogDebug("IAP OnInitializeFailed message" + message);
switch (error)
{
case InitializationFailureReason.AppNotKnown:
TKGDebugger.LogError("Is your App correctly uploaded on the relevant publisher console?");
break;
case InitializationFailureReason.PurchasingUnavailable:
TKGDebugger.LogDebug("Billing disabled! Ask the user if billing is disabled in device settings.");
break;
case InitializationFailureReason.NoProductsAvailable:
TKGDebugger.LogDebug(
"No products available for purchase! Developer configuration error; check product metadata!");
break;
}
}
private bool CheckIfProductIsAvailableForSubscriptionManagerC(string receipt)
{
var receipt_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(receipt);
if (!receipt_wrapper.ContainsKey("Store") || !receipt_wrapper.ContainsKey("Payload"))
{
TKGDebugger.LogDebug("The product receipt does not contain enough information");
return false;
}
var store = (string)receipt_wrapper["Store"];
var payload = (string)receipt_wrapper["Payload"];
if (payload != null)
{
switch (store)
{
case GooglePlay.Name:
{
var payload_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(payload);
if (!payload_wrapper.ContainsKey("json"))
{
TKGDebugger.LogDebug(
"The product receipt does not contain enough information, the 'json' field is missing");
return false;
}
var original_json_payload_wrapper =
(Dictionary<string, object>)MiniJson.JsonDecode((string)payload_wrapper["json"]);
if (original_json_payload_wrapper == null ||
!original_json_payload_wrapper.ContainsKey("developerPayload"))
{
TKGDebugger.LogDebug(
"The product receipt does not contain enough information, the 'developerPayload' field is missing");
return false;
}
var developerPayloadJSON = (string)original_json_payload_wrapper["developerPayload"];
var developerPayload_wrapper =
(Dictionary<string, object>)MiniJson.JsonDecode(developerPayloadJSON);
if (developerPayload_wrapper == null ||
!developerPayload_wrapper.ContainsKey("is_free_trial") ||
!developerPayload_wrapper.ContainsKey("has_introductory_price_trial"))
{
TKGDebugger.LogDebug(
"The product receipt does not contain enough information, the product is not purchased using 1.19 or later");
return false;
}
return true;
}
case AppleAppStore.Name:
case AmazonApps.Name:
case MacAppStore.Name:
{
return true;
}
default:
{
return false;
}
}
}
return false;
}
//
/// <summary>
/// 支付成功处理函数;
/// 在支持交易恢复功能的平台上(例如 Google Play 和通用 Windows 应用程序),
/// Unity IAP 会在重新安装后的第一次初始化期间自动恢复用户拥有的任何商品;
/// 系统将为每项拥有的商品调用 IStoreListener 的 ProcessPurchase 方法。
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
{
m_PurchaseInProgress = false;
TKGDebugger.LogDebug("Purchase OK: " + e.purchasedProduct.definition.id);
// 消息结构 : Receipt: {"Store":"fake","TransactionID":"9c5c16a5-1ae4-468f-806d-bc709440448a","Payload":"{ \"this\" : \"is a fake receipt\" }"};
TKGDebugger.LogDebug("Receipt: " + e.purchasedProduct.receipt);
TKGSDKManager.Instance.SetSegment(SegmentType.Purchase);
//获取并解析你需要上传的数据。解析成string类型
var wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(e.purchasedProduct.receipt);
// Corresponds to http://docs.unity3d.com/Manual/UnityIAPPurchaseReceipts.html
// 正在使用的商店的名称,例如 GooglePlay 或 AppleAppStore
var store = (string)wrapper["Store"];
TKGDebugger.LogDebug("Purchase OK: store" + store);
//下面的payload 验证商品信息的数据。即我们需要上传的部分。
// For Apple this will be the base64 encoded ASN.1 receipt
// 对于苹果来说这将是base64编码的ASN.1收据
var payload = (string)wrapper["Payload"];
TKGDebugger.LogDebug("Purchase OK: payload" + payload);
string token;
string orderId;
if (Application.platform == RuntimePlatform.Android)
{
// For GooglePlay payload contains more JSON
// 对于GooglePlay有效负载包含更多JSON
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"];
TKGDebugger.LogDebug("ClientIAPSuccess productId : " + e.purchasedProduct.definition.id + " , transactionID : " + orderId + " , token : " + token + " , localizedPrice : " + e.purchasedProduct.metadata.localizedPriceString + " , isoCurrencyCode : " + e.purchasedProduct.metadata.isoCurrencyCode);
TKGNativeInterface.Instance.ClientIAPSuccess(mProductName, e.purchasedProduct.definition.id, orderId, token, e.purchasedProduct.metadata.isoCurrencyCode, e.purchasedProduct.metadata.localizedPrice.ToString(), mGameExtraParam);
//if (orderId.StartsWith("GPA."))
//{
// TKGSDKManager.Instance.LogPurchasePrice(e.purchasedProduct.metadata.localizedPriceString,
// e.purchasedProduct.metadata.isoCurrencyCode); // todo delete android upload event
// OnPurchaseDone?.Invoke(e.purchasedProduct.definition.id, true, orderId, token);
//}
//else
//{
// //OnPurchaseDone?.Invoke(e.purchasedProduct.definition.id, false, "", "");
// TKGDebugger.LogDebug("productId : " + e.purchasedProduct.definition.id + " , transactionID : " + orderId + " , localizedPrice : " + e.purchasedProduct.metadata.localizedPriceString + " , isoCurrencyCode : " + e.purchasedProduct.metadata.isoCurrencyCode);
// TKGNativeInterface.Instance.ClientIAPFailed("", e.purchasedProduct.definition.id, orderId, token, e.purchasedProduct.metadata.localizedPriceString, e.purchasedProduct.metadata.isoCurrencyCode);
//}
}
else
{
//TKGSDKManager.Instance.LogPurchasePrice(e.purchasedProduct.metadata.localizedPriceString,
// e.purchasedProduct.metadata.isoCurrencyCode); // todo delete iOS upload event
//苹果验单直接传入 payload
token = "";
orderId = (string)wrapper["TransactionID"];
//OnPurchaseDone?.Invoke(e.purchasedProduct.definition.id, true, orderId, token);
TKGDebugger.LogDebug("ClientIAPSuccess productId : " + e.purchasedProduct.definition.id + " , transactionID : " + orderId + " , token : " + token + " , localizedPrice : " + e.purchasedProduct.metadata.localizedPriceString + " , isoCurrencyCode : " + e.purchasedProduct.metadata.isoCurrencyCode);
TKGNativeInterface.Instance.ClientIAPSuccess(mProductName, e.purchasedProduct.definition.id, orderId, token, e.purchasedProduct.metadata.isoCurrencyCode, e.purchasedProduct.metadata.localizedPrice.ToString(),mGameExtraParam);
}
return PurchaseProcessingResult.Complete;
}
// 支付失败回掉函数;
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureDescription)
{
TKGDebugger.LogDebug("Purchase OK: " + product.definition.id);
m_PurchaseInProgress = false;
//OnPurchaseDone?.Invoke(item.definition.id, false, "", "");
TKGDebugger.LogDebug("OnPurchaseFailed -> productId : " + product.definition.id + " , transactionID : " + product.transactionID + " , localizedPrice : " + product.metadata.localizedPriceString + " , isoCurrencyCode : " + product.metadata.isoCurrencyCode);
TKGNativeInterface.Instance.ClientIAPFailed(mProductName, product.definition.id, product.transactionID, "", product.metadata.isoCurrencyCode,product.metadata.localizedPrice.ToString(),mGameExtraParam, IAPClientFailReasonType.PurchaseFailed);
}
// 购买延迟提示(这个看自己项目情况是否处理);
// 在 Apple 平台上,我们需要处理由 Apple 的"购买前先询问"功能导致的延期购买。
// 在非 Apple 平台上,这将不起作用;永远不会调用 OnDeferred。
public void OnDeferred(Product product)
{
TKGDebugger.LogDebug("Purchase deferred: " + product.definition.id + " , maybe slow internet.");
//OnPurchaseDone?.Invoke(item.definition.id, false, "", "");
TKGNativeInterface.Instance.ClientIAPFailed(mProductName, product.definition.id, product.transactionID, "", product.metadata.isoCurrencyCode, product.metadata.localizedPrice.ToString(), mGameExtraParam, IAPClientFailReasonType.Deferred);
}
// 恢复购买功能执行回掉函数;
public void OnTransactionsRestored(bool success)
{
TKGDebugger.LogDebug("Transactions restored : " + success);
}
#endregion
#region custom functions
public string GetPriceByID(string pID)
{
if (m_StoreController == null && m_StoreExtensionProvider == null)
return "";
Product[] tProducts = m_StoreController.products.all;
for (int i = 0; i < tProducts.Length; i++)
{
Product tItem = tProducts[i];
if (tItem.definition.id.Equals(pID))
{
#if UNITY_ANDROID
return tItem.metadata.GetGoogleProductMetadata().localizedPriceString;
#else
return tItem.metadata.localizedPriceString;
#endif
}
}
return "";
}
public Product GetProductInfoByID(string pID)
{
if (m_StoreController == null && m_StoreExtensionProvider == null)
return null;
for (int i = 0; i < m_StoreController.products.all.Length; i++)
{
Product tItem = m_StoreController.products.all[i];
if (tItem.definition.id.Equals(pID))
{
return tItem;
}
}
return null;
}
public SubscriptionInfo GetSubscriptionInfo()
{
return mSubsInfo;
}
#endregion
}
}
#endif