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

365 lines
14 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;
public class IAPTool : NormalSingleton<IAPTool>, IStoreListener
{
public event Action OnPurchaseBegin;
public event Action<string, bool> OnPurchaseDone;
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;
public void Initialize()
{
if (m_StoreController == null && m_StoreExtensionProvider == null)
InitUnityPurchase();
}
private bool IsInitialized()
{
return m_StoreController != null && m_StoreExtensionProvider != null;
}
public void AddProducts(Dictionary<string, ProductType> pInitProductDic)
{
mInitProductDic = pInitProductDic;
}
// 初始化IAP;
private void InitUnityPurchase()
{
if (IsInitialized()) return;
// 标准采购模块;
StandardPurchasingModule module = StandardPurchasingModule.Instance();
// 配置模式;
ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
// 注意ProductType的类型Consumable是可以无限购买(比如水晶)NonConsumable是只能购买一次(比如关卡)Subscription是每月订阅(比如VIP);
// 这里初始化没有添加平台信息因为平台信息有的时候还存在bug如果必须添加也可以添加没有问题确保平台信息添加正确就行了。
foreach (string tID in IAPProducts.ProductDic.Keys)
{
builder.AddProduct(tID, IAPProducts.ProductDic[tID]);
}
if (mInitProductDic != null && mInitProductDic.Count > 0)
{
foreach (string tID in mInitProductDic.Keys)
{
builder.AddProduct(tID, mInitProductDic[tID]);
}
}
//初始化;
UnityPurchasing.Initialize(this, builder);
}
#region Public Func
// 根据ID给购买商品;
public void BuyProductByID(string productId)
{
if (IsInitialized())
{
if (m_PurchaseInProgress == true) 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");
}
}
else
{
TKGDebugger.LogDebug("BuyProductID FAIL. Not initialized.");
}
}
// 确认购买产品成功;
public 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;
}
}
}
// 恢复购买;
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) => {
mIsRestoring = false;
OnRestoreDone?.Invoke(result);
// 返回一个bool值如果成功则会多次调用支付回调然后根据支付回调中的参数得到商品id最后做处理(ProcessPurchase);
TKGDebugger.LogDebug("RestorePurchases continuing: " + result + ". 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 上配置的商品;
ProductCollection products = m_StoreController.products;
Product[] all = products.all;
for (int i = 0; i < all.Length; i++)
{
TKGDebugger.LogDebug(all[i].metadata.localizedTitle + "|" + all[i].metadata.localizedPriceString + "|" + all[i].metadata.localizedDescription + "|" + all[i].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");
}
}
}
*/
}
// IAP初始化失败回掉函数没有网络的情况下并不会调起而是一直等到有网络连接再尝试初始化;
public void OnInitializeFailed(InitializationFailureReason error)
{
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;
}
// 支付成功处理函数;
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);
OnPurchaseDone?.Invoke(e.purchasedProduct.definition.id, true);
// 我们自己后台完毕的话,通过代码设置成功(如果是不需要后台设置直接设置完毕不要设置Pending);
return PurchaseProcessingResult.Complete;
}
// 支付失败回掉函数;
public void OnPurchaseFailed(Product item, PurchaseFailureReason r)
{
TKGDebugger.LogDebug("Purchase OK: " + item.definition.id);
m_PurchaseInProgress = false;
OnPurchaseDone?.Invoke(item.definition.id, false);
}
// 购买延迟提示(这个看自己项目情况是否处理);
public void OnDeferred(Product item)
{
TKGDebugger.LogDebug("Purchase deferred: " + item.definition.id);
OnPurchaseDone?.Invoke(item.definition.id, false);
}
// 恢复购买功能执行回掉函数;
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++)
{
TKGDebugger.LogDebug(tProducts[i].metadata.localizedTitle + "|" + tProducts[i].metadata.localizedPriceString + "|" + tProducts[i].metadata.localizedDescription + "|" + tProducts[i].metadata.isoCurrencyCode);
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 SubscriptionInfo GetSubscriptionInfo()
{
return mSubsInfo;
}
#endregion
}
#endif