732 lines
33 KiB
C#
732 lines
33 KiB
C#
#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 products,don't call repeatedly");
|
||
if (onProductsResult != null)
|
||
{
|
||
onProductsResult(false, "Now fetching additional products,don'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 |