国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)

這篇具有很好參考價(jià)值的文章主要介紹了Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)

最近因?yàn)轫?xiàng)目需要,被分配來(lái)做項(xiàng)目SDK接入以及上架相關(guān)事宜。搞了好幾天關(guān)于Unity接入支付的SDK,接入很簡(jiǎn)單,卡的最久的就是服務(wù)器驗(yàn)單,google相關(guān)文檔也不是很全,走通之后覺(jué)得可以發(fā)出來(lái)共享一下,第一次寫(xiě)文章,有什么不足多多見(jiàn)諒

一.Unity接入In App Purchasing SDK

Unity已經(jīng)集成了Google Pay、Apple App Store的支付,Unity會(huì)根據(jù)不同的平臺(tái)喚起相應(yīng)的支付。

1.安裝IAP

通過(guò)Unity自帶的Package Manager安裝 Window > Package Manager
打開(kāi)后搜索In App Purchasing安裝

2.關(guān)于IAP在Unity內(nèi)的設(shè)置

參考文檔:
鏈接: https://docs.unity3d.com/530/Documentation/Manual/UnityIAP.html
安裝IAP后需要開(kāi)啟Unity內(nèi)IAP服務(wù)
Window-Services(Ctrl+0)在Services面板Link你的工程,啟用In-App Purchase
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
這里Options下需要填入GooglePlay開(kāi)發(fā)者后臺(tái)的Key
進(jìn)入開(kāi)發(fā)者后臺(tái)創(chuàng)建的應(yīng)用內(nèi),找到創(chuàng)收設(shè)置,把key復(fù)制過(guò)來(lái)
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)

3.GooglePlay商店設(shè)置

參考文檔:
鏈接: https://docs.unity3d.com/cn/current/Manual/UnityIAPGoogleConfiguration.html
①創(chuàng)建商品:
在Google開(kāi)發(fā)者后臺(tái)創(chuàng)建好應(yīng)用后添加應(yīng)用內(nèi)商品
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
②內(nèi)部測(cè)試
添加測(cè)試用戶
如果沒(méi)有添加測(cè)試用戶,即使應(yīng)用在測(cè)試軌道,測(cè)試人員仍舊需要購(gòu)買商品,所以需要在Google后臺(tái)將測(cè)試人員添加進(jìn)測(cè)試用戶內(nèi)
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
這里的測(cè)試用戶是指測(cè)試用戶可以通過(guò)下載鏈接在GooglePlay內(nèi)下載你的測(cè)試版本,如果想要內(nèi)購(gòu)免費(fèi)測(cè)試購(gòu)買還需要進(jìn)一步添加測(cè)試賬號(hào)
GooglePlayConsole返回所有應(yīng)用,找到許可測(cè)試,再次添加測(cè)試賬號(hào),并且將許可設(shè)置LICENSED
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
下載內(nèi)部測(cè)試版本
上傳內(nèi)部測(cè)試aab包后,在添加測(cè)試用戶界面最下方可以找到鏈接,分享給測(cè)試人員即可
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)

4.關(guān)于IAP初始化

參考文檔:
鏈接: https://docs.unity3d.com/cn/current/Manual/UnityIAPInitialization.html
直接上代碼
①開(kāi)始初始化,把Google的商品id添加進(jìn)來(lái)

public void Initialize()
{
     if (IsInitializd())  
         return;

     Log.Info($"開(kāi)始初始化IAP");
     var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
     // 消耗品添加
     var shopConfig = ShopCashConfigCategory.Instance.GetCashs();
     //這里根據(jù)自己項(xiàng)目情況添加,我這里是讀的配置表
     //添加商品 builder.AddProduct("xxx.xxx.xxx", ProductType.Consumable);
     for (int i = 0; i < shopConfig.Count; i++)
     {
         builder.AddProduct(shopConfig[i].IAP, ProductType.Consumable);
         Log.Info($"添加商品 :{shopConfig[i].IAP}");
     }
     // 非消耗品添加
     
     //初始化
     UnityPurchasing.Initialize(this,builder);
 }

②初始化成功

/// <summary>
/// Unity IAP 準(zhǔn)備好可以進(jìn)行購(gòu)買時(shí)調(diào)用。
/// IAP初始化成功回掉函數(shù)
/// </summary>
/// <param name="controller"></param>
/// <param name="extensions"></param>
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
    Log.Info("OnInitialized Succ !");
    // Overall Purchasing system, configured with products for this application.
    // 整體采購(gòu)系統(tǒng),為該應(yīng)用程序配置了產(chǎn)品。
    this.storeController = controller;
    // Store specific subsystem, for accessing device-specific store features.
    // 存儲(chǔ)特定子系統(tǒng),用于訪問(wèn)設(shè)備特定的存儲(chǔ)特性。
    this.storeExtensionProvider = extensions;
}

如果網(wǎng)絡(luò)不好沒(méi)有掛VPN,或者沒(méi)有安裝Google服務(wù)初始化不會(huì)成功
③根據(jù)ID購(gòu)買商品

public async ETTask BuyProductByID(int id)
{
    Log.Info($"是否存在訂單purchaseInProgress = {this.purchaseInProgress}");
    //嘗試初始化
    if (!this.IsInitializd())
    {
        this.Initialize();
    }
    //初始化失敗
    if (!this.IsInitializd())
    {
        // report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or retrying initiailization.
        // 報(bào)告采購(gòu)尚未成功初始化的事實(shí)??紤]等待更長(zhǎng)時(shí)間或重新嘗試初始化。
        Log.Info("沒(méi)有初始化UnityIAP或初始化失敗.");
        return;
    }
    //正在購(gòu)買中
    if (this.purchaseInProgress)
    {
        Log.Info($"未完成購(gòu)買商品:{this.productId}");  
        return;
    }
    // system's products collection.
    // 系統(tǒng)的產(chǎn)品集合。
    this.productId = id;
    var config = ShopGoodsConfigCategory.Instance.Get(this.productId);
    if (config == null)
    {
        Log.Info($"購(gòu)買商品:{this.productId} 失敗。沒(méi)有找到 ShopGoodsConfig");  
        return;
    }
    //將商品id添加進(jìn)Product 
    Product product = storeController.products.WithID(config.Price.IAP);
    if (product == null)
    {
        Log.Info($"購(gòu)買商品:{this.productId} 失敗。storeController.products.WithID return null");  
        return;
    }
    // If the look up found a product for this device's store and that product is ready to be sold ... 
    // 如果查找找到了該設(shè)備的商店的產(chǎn)品,該產(chǎn)品準(zhǔn)備出售…
    if (!product.availableToPurchase)
    {
        Log.Info($"購(gòu)買商品:{this.productId} 失敗。product.availableToPurchase is false");  
        return;
    }
    Log.Info($"開(kāi)始購(gòu)買商品: {this.productId} --> {product.definition.id} --> {product.transactionID}");  
    
    // ... buy the product. Expect a response either through ProcessPurchase or OnPurchaseFailed 
    //  ……購(gòu)買產(chǎn)品。期待通過(guò)ProcessPurchase或onpurchasfailed的響應(yīng)
    storeController.InitiatePurchase(product);  
    this.purchaseInProgress = true;
    await ETTask.CompletedTask;
}

④購(gòu)買成功后發(fā)貨(成功回調(diào))
這里可以選擇客戶端發(fā)貨或者服務(wù)器發(fā)貨,客戶端發(fā)貨,在成功回調(diào)時(shí)已經(jīng)進(jìn)行過(guò)一次驗(yàn)單,如果是服務(wù)器發(fā)貨,則需要將商品信息發(fā)送給服務(wù)器進(jìn)行二次驗(yàn)單

/// <summary>
/// 購(gòu)買完成時(shí)調(diào)用。
///
/// 可能在 OnInitialized() 之后的任何時(shí)間調(diào)用。
/// </summary>
/// <param name="purchaseEvent"></param>
/// <returns></returns>
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent)
{
    var product = purchaseEvent.purchasedProduct;
    Log.Info($"購(gòu)買成功 需要驗(yàn)單: {this.productId} --> {product.definition.id} --> {product.transactionID} --> receipt:{product.receipt}");
    // TODO::向服務(wù)器發(fā)送驗(yàn)單
    this.DoConfirmPendingPurchase(product).Coroutine();
    // //獲取并解析你需要上傳的數(shù)據(jù)。解析成string類型
    // var wrapper = (Dictionary<string, object>) MiniJson.JsonDecode (product.receipt);
    // // Corresponds to http://docs.unity3d.com/Manual/UnityIAPPurchaseReceipts.html
    // // 正在使用的商店的名稱,例如 GooglePlay 或 AppleAppStore
    // var store = (string)wrapper ["Store"];
    // //下面的payload 驗(yàn)證商品信息的數(shù)據(jù)。即我們需要上傳的部分。
    // // For Apple this will be the base64 encoded ASN.1 receipt
    // // 對(duì)于蘋(píng)果來(lái)說(shuō),這將是base64編碼的ASN.1收據(jù)
    // var payload = (string)wrapper ["Payload"]; 
    // //蘋(píng)果驗(yàn)單直接傳入 payload
    // #if UNITY_IPHONE
    //         //蘋(píng)果驗(yàn)單直接傳入 payload
    //         DoConfirmPendingPurchaseByID(purchaseEvent.purchasedProduct.definition.id,payload).Coroutine();
	//#endif
    // // For GooglePlay payload contains more JSON
    // // 對(duì)于GooglePlay有效負(fù)載包含更多JSON
    // if (Application.platform == RuntimePlatform.Android)
    // {
    //     var gpDetails = (Dictionary<string, object>)MiniJson.JsonDecode(payload);
    //     var gpJson = (string)gpDetails["json"];
    //     var gpSig = (string)gpDetails["signature"];
    //     //Google驗(yàn)證商品信息的數(shù)據(jù)包含在gpJson里面還需要在服務(wù)端進(jìn)行解析一下,對(duì)應(yīng)的鍵是"purchaseToken"。
    //     DoConfirmPendingPurchaseByID(purchaseEvent.purchasedProduct.definition.id,gpJson).Coroutine();
    // }
    return PurchaseProcessingResult.Pending;
}

這里直接將商品的所有信息全部都傳給了服務(wù)器,服務(wù)器自己進(jìn)行訂單信息的解析
注釋部分為客戶端解析訂單信息,然后再做下一步處理
注:如果不需要服務(wù)器驗(yàn)單,可以直接在這里處理購(gòu)買完成后的邏輯,這里的返回應(yīng)該是
return PurchaseProcessingResult.Complete;
需要服務(wù)器驗(yàn)單的話需要返回,等服務(wù)器驗(yàn)單后再結(jié)束訂單
return PurchaseProcessingResult.Pending;

// 驗(yàn)單 確認(rèn)購(gòu)買產(chǎn)品成功;
public async ETTask DoConfirmPendingPurchase(Product product)
{
    var response =  await ShopHelper.Buy(this.iapComponent.ZoneScene(), this.productId, getPayType(product), OrderStateType.PayFinish, product.transactionID, 	     product.receipt);
    if (response.Error == ErrorCode.ERR_Success)
    {
        Log.Info($"驗(yàn)單結(jié)束,購(gòu)買成功");
        storeController.ConfirmPendingPurchase(product);
    }
    this.purchaseInProgress = false;
    Log.Info($"訂單結(jié)束 purchaseInProgress = {purchaseInProgress}");
    await ETTask.CompletedTask;
}

⑤完整代碼

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Purchasing;

namespace ET
{
    public class UnityIAPHanlder : IIAPHandler, IStoreListener
    {
        private readonly UnityIAPComponent iapComponent;
        private IStoreController storeController;//存儲(chǔ)商品信息
        private IExtensionProvider storeExtensionProvider;//IAP擴(kuò)展工具
        private bool purchaseInProgress = false;//是否處于付費(fèi)中
        private int productId;

        public UnityIAPHanlder(UnityIAPComponent iapComponent)
        {
            this.iapComponent = iapComponent;
        }
        public void Initialize()
        {
            if (IsInitializd())  
                return;

            Log.Info($"開(kāi)始初始化IAP");
            var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
            // 消耗品添加
            var shopConfig = ShopCashConfigCategory.Instance.GetCashs();
            for (int i = 0; i < shopConfig.Count; i++)
            {
                builder.AddProduct(shopConfig[i].IAP, ProductType.Consumable);
                Log.Info($"添加商品 :{shopConfig[i].IAP}");
            }
            // 非消耗品添加
            
            UnityPurchasing.Initialize(this,builder);
        }

        public void Dispose()
        {
            this.purchaseInProgress = false;
            this.productId = 0;
            this.storeController = null;
            this.storeExtensionProvider = null;
        }

        public bool IsInitializd()
        {
            return storeController != null && storeExtensionProvider != null;  
        }
        public async ETTask BuyProductByID(int id)
        {
            Log.Info($"是否存在訂單purchaseInProgress = {this.purchaseInProgress}");
            //嘗試初始化
            if (!this.IsInitializd())
            {
                this.Initialize();
            }
            //初始化失敗
            if (!this.IsInitializd())
            {
                // report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or retrying initiailization.
                // 報(bào)告采購(gòu)尚未成功初始化的事實(shí)??紤]等待更長(zhǎng)時(shí)間或重新嘗試初始化。
                Log.Info("沒(méi)有初始化UnityIAP或初始化失敗.");
                return;
            }
            //正在購(gòu)買中
            if (this.purchaseInProgress)
            {
                Log.Info($"未完成購(gòu)買商品:{this.productId}");  
                return;
            }
            // system's products collection.
            // 系統(tǒng)的產(chǎn)品集合。
            this.productId = id;
            var config = ShopGoodsConfigCategory.Instance.Get(this.productId);
            if (config == null)
            {
                Log.Info($"購(gòu)買商品:{this.productId} 失敗。沒(méi)有找到 ShopGoodsConfig");  
                return;
            }
            Product product = storeController.products.WithID(config.Price.IAP);
            if (product == null)
            {
                Log.Info($"購(gòu)買商品:{this.productId} 失敗。storeController.products.WithID return null");  
                return;
            }
            // If the look up found a product for this device's store and that product is ready to be sold ... 
            // 如果查找找到了該設(shè)備的商店的產(chǎn)品,該產(chǎn)品準(zhǔn)備出售…
            if (!product.availableToPurchase)
            {
                Log.Info($"購(gòu)買商品:{this.productId} 失敗。product.availableToPurchase is false");  
                return;
            }
            Log.Info($"開(kāi)始購(gòu)買商品: {this.productId} --> {product.definition.id} --> {product.transactionID}");  
            // var response = await ShopHelper.Buy(UnityIAPComponent.Instance.ZoneScene(), productId);
            // if (response.Error != ErrorCode.ERR_Success)
            // {
            //     Log.Info($"購(gòu)買商品:{this.productId} 失敗。向邏輯服下單異常 error:{response.Error}");  
            //     return;
            // }
            // ... buy the product. Expect a response either through ProcessPurchase or OnPurchaseFailed 
            //  ……購(gòu)買產(chǎn)品。期待通過(guò)ProcessPurchase或onpurchasfailed的響應(yīng)
            storeController.InitiatePurchase(product);  
            this.purchaseInProgress = true;
            await ETTask.CompletedTask;
        }

        #region IStoreListener
        /// <summary>
        /// 購(gòu)買完成時(shí)調(diào)用。
        ///
        /// 可能在 OnInitialized() 之后的任何時(shí)間調(diào)用。
        /// </summary>
        /// <param name="purchaseEvent"></param>
        /// <returns></returns>
        public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent)
        {
            var product = purchaseEvent.purchasedProduct;
            Log.Info($"購(gòu)買成功 需要驗(yàn)單: {this.productId} --> {product.definition.id} --> {product.transactionID} --> receipt:{product.receipt}");
            // TODO::向服務(wù)器發(fā)送驗(yàn)單
            this.DoConfirmPendingPurchase(product).Coroutine();
            // //獲取并解析你需要上傳的數(shù)據(jù)。解析成string類型
            // var wrapper = (Dictionary<string, object>) MiniJson.JsonDecode (product.receipt);
            // // Corresponds to http://docs.unity3d.com/Manual/UnityIAPPurchaseReceipts.html
            // // 正在使用的商店的名稱,例如 GooglePlay 或 AppleAppStore
            // var store = (string)wrapper ["Store"];
            // //下面的payload 驗(yàn)證商品信息的數(shù)據(jù)。即我們需要上傳的部分。
            // // For Apple this will be the base64 encoded ASN.1 receipt
            // // 對(duì)于蘋(píng)果來(lái)說(shuō),這將是base64編碼的ASN.1收據(jù)
            // var payload = (string)wrapper ["Payload"]; 
             // //蘋(píng)果驗(yàn)單直接傳入 payload
		    // #if UNITY_IPHONE
		    //         //蘋(píng)果驗(yàn)單直接傳入 payload
		    //         DoConfirmPendingPurchaseByID(purchaseEvent.purchasedProduct.definition.id,payload).Coroutine();
			//#endif
            // // For GooglePlay payload contains more JSON
            // // 對(duì)于GooglePlay有效負(fù)載包含更多JSON
            // if (Application.platform == RuntimePlatform.Android)
            // {
            //     var gpDetails = (Dictionary<string, object>)MiniJson.JsonDecode(payload);
            //     var gpJson = (string)gpDetails["json"];
            //     var gpSig = (string)gpDetails["signature"];
            //     //Google驗(yàn)證商品信息的數(shù)據(jù)包含在gpJson里面還需要在服務(wù)端進(jìn)行解析一下,對(duì)應(yīng)的鍵是"purchaseToken"。
            //     DoConfirmPendingPurchaseByID(purchaseEvent.purchasedProduct.definition.id,gpJson).Coroutine();
            // }
            return PurchaseProcessingResult.Pending;
        }

        private int getPayType(Product product)
        {
            var wrapper = (Dictionary<string, object>) MiniJson.JsonDecode (product.receipt);
            // 正在使用的商店的名稱,例如 GooglePlay 或 AppleAppStore
            var store = (string)wrapper ["Store"];
            if (store == "GooglePlay")
                return PayType.Google;
            if (store == "AppleAppStore")
                return PayType.IOS;
            return PayType.Test;
        }
        // 驗(yàn)單 確認(rèn)購(gòu)買產(chǎn)品成功;
        public async ETTask DoConfirmPendingPurchase(Product product)
        {
            var response =  await ShopHelper.Buy(this.iapComponent.ZoneScene(), this.productId, getPayType(product), OrderStateType.PayFinish, product.transactionID, product.receipt);
            if (response.Error == ErrorCode.ERR_Success)
            {
                Log.Info($"驗(yàn)單結(jié)束,購(gòu)買成功");
                storeController.ConfirmPendingPurchase(product);
            }
            this.purchaseInProgress = false;
            Log.Info($"訂單結(jié)束 purchaseInProgress = {purchaseInProgress}");
            await ETTask.CompletedTask;
        }
        /// <summary>
        /// 購(gòu)買失敗時(shí)調(diào)用。
        /// </summary>
        /// <param name="product"></param>
        /// <param name="failureReason"></param>
        public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
        {
            this.purchaseInProgress = false;
            this.iapComponent.OnPurchaseFailed(this.productId, failureReason.ToString());
            Log.Info("購(gòu)買失敗" );
        }

        /// <summary>
        /// Unity IAP 準(zhǔn)備好可以進(jìn)行購(gòu)買時(shí)調(diào)用。
        /// IAP初始化成功回掉函數(shù)
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="extensions"></param>
        public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
        {
            Log.Info("OnInitialized Succ !");
            // Overall Purchasing system, configured with products for this application.
            // 整體采購(gòu)系統(tǒng),為該應(yīng)用程序配置了產(chǎn)品。
            this.storeController = controller;
            // Store specific subsystem, for accessing device-specific store features.
            // 存儲(chǔ)特定子系統(tǒng),用于訪問(wèn)設(shè)備特定的存儲(chǔ)特性。
            this.storeExtensionProvider = extensions;
        }
        /// <summary>
        /// Unity IAP 遇到不可恢復(fù)的初始化錯(cuò)誤時(shí)調(diào)用。
        /// IAP初始化失敗回掉函數(shù)(沒(méi)有網(wǎng)絡(luò)的情況下并不會(huì)調(diào)起,而是一直等到有網(wǎng)絡(luò)連接再嘗試初始化);
        /// 請(qǐng)注意,如果互聯(lián)網(wǎng)不可用,則不會(huì)調(diào)用此項(xiàng);
        /// 將嘗試初始化,直到互聯(lián)網(wǎng)變?yōu)榭捎谩?        /// </summary>
        /// <param name="error"></param>
        public void OnInitializeFailed(InitializationFailureReason error)
        {
            string errorDes = "";
            switch (error)
            {
                case InitializationFailureReason.AppNotKnown:
                    errorDes = "你的應(yīng)用是否正確上傳到相關(guān)發(fā)行商控制臺(tái)?";
                    break;
                case InitializationFailureReason.PurchasingUnavailable:
                    errorDes = "計(jì)費(fèi)禁用!用戶是否在設(shè)備設(shè)置中禁用了計(jì)費(fèi)。";
                    break;
                case InitializationFailureReason.NoProductsAvailable:
                    errorDes = "沒(méi)有可供購(gòu)買的產(chǎn)品!開(kāi)發(fā)者配置錯(cuò)誤;檢查產(chǎn)品配置數(shù)據(jù)!";
                    break;
                default:
                    errorDes = $"初始化未處理異常 {error}";
                    break;
            }
            Log.Info("Unity Iap 初始化失敗: "+errorDes);
            this.iapComponent.OnInitializeFailed(errorDes);
        }
        #endregion
        
        // 恢復(fù)購(gòu)買;
        public void RestorePurchases()  
        {  
            if (!this.IsInitializd())  
            {  
                Log.Info("沒(méi)有初始化UnityIAP或初始化失敗.");
                return;  
            }  
            if (Application.platform == RuntimePlatform.IPhonePlayer ||   
                Application.platform == RuntimePlatform.OSXPlayer)  
            {  
                Log.Info("開(kāi)始恢復(fù)購(gòu)買 ...");  
                var apple = storeExtensionProvider.GetExtension<IAppleExtensions>();  
                apple.RestoreTransactions((result) => 
                {  
                    // 返回一個(gè)bool值,如果成功,則會(huì)多次調(diào)用支付回調(diào),然后根據(jù)支付回調(diào)中的參數(shù)得到商品id,最后做處理(ProcessPurchase); 
                    Log.Info("恢復(fù)購(gòu)買中: " + result + ". 如果沒(méi)有進(jìn)一步的消息,則沒(méi)有可恢復(fù)的購(gòu)買.");
                    OnTransactionsRestored(result);
                });  
            }  
            else  
            {  
                Log.Info("恢復(fù)購(gòu)買失敗。當(dāng)前平臺(tái)不支持。當(dāng)前的平臺(tái) = " + Application.platform);  
            }  
        } 
        
        /// <summary>
        /// 恢復(fù)購(gòu)買功能執(zhí)行回掉函數(shù);
        /// </summary>
        /// <param name="success"></param>
        private void OnTransactionsRestored(bool success)
        {
            if (!success)
            {
                Log.Info("恢復(fù)購(gòu)買失敗或者沒(méi)有可恢復(fù)的商品");
                return;
            }
            //恢復(fù)購(gòu)買成功的處理
            
        }
    }
}

參考:
鏈接: https://blog.csdn.net/KindSuper_liu/article/details/123254052?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166936044716782388086288%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=166936044716782388086288&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-123254052-null-null.142v66control,201v3control_1,213v2t3_control2&utm_term=unity%20iap&spm=1018.2226.3001.4187

⑥不同平臺(tái)接入?yún)⒖?br> 鏈接: https://docs.unity3d.com/cn/current/Manual/UnityIAP.html

二.服務(wù)器驗(yàn)單

由于Google關(guān)于服務(wù)器驗(yàn)單相關(guān)的文檔很少,也沒(méi)有相關(guān)Demo,所以Googl官方相關(guān)的文檔只能部分參考,這里提供我跑通的一篇文章參考,流程基本一致,我補(bǔ)充一些容易忽略的細(xì)節(jié)(我跟我們的后端大佬就是栽在了這些細(xì)節(jié)上QAQ)
流程參考:
鏈接: https://www.jianshu.com/p/76416ebc0db0

1.創(chuàng)建API項(xiàng)目

Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)

2.開(kāi)啟Google Play Android Developer API

Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
搜索“Google Play Android Developer API”
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)

3.開(kāi)啟同意屏幕

Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
注:完善必填項(xiàng)即可

4.創(chuàng)建OAuth2客戶端ID

Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
注意:
1.這里選擇的應(yīng)用類型為Web應(yīng)用,不要被Android、IOS之類的選項(xiàng)迷惑
2.重定向URI是用來(lái)接收code的,當(dāng)?shù)顷懗晒笾匦露ㄏ?,重定向地址后帶有code,我們這里不需要服務(wù)端接收code,所以隨便填一個(gè)即可,這里我填的是http://127.0.0.1:51355/,如果有服務(wù)器接收code的需求,填寫(xiě)接收code的地址即可
創(chuàng)建成功后可以獲取到clientId和clientSecret
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)

5.GooglePay后臺(tái)關(guān)聯(lián)API項(xiàng)目

Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)

6.獲取code

地址:
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri={填寫(xiě)的重定向地址}&client_id={創(chuàng)建的clientId}

請(qǐng)求方式:瀏覽器中打開(kāi)

將上面的{XX}替換成創(chuàng)建api項(xiàng)目時(shí)填寫(xiě)的重定向地址,和clientId,然后將連接放到瀏覽器中打開(kāi),就會(huì)調(diào)起授權(quán)界面,使用你的開(kāi)發(fā)者賬號(hào)授權(quán)登錄

授權(quán)后會(huì)打開(kāi)一個(gè)網(wǎng)頁(yè),查看該網(wǎng)頁(yè)的地址即可看到code
Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
這里可以看到,重定向地址上有兩個(gè)參數(shù)code和scope,我們只需要code就行了,這里的code是urlencode后的,使用時(shí)需要decode

7.使用code換取refreshToken

地址:
https://accounts.google.com/o/oauth2/token
請(qǐng)求方式:post
參數(shù):grant_type=authorization_code
code=獲取到的code(需要看看code中是否有%號(hào),如果有需要urldecode)
client_id=創(chuàng)建api項(xiàng)目是的clientId(客戶端ID)
client_secret=創(chuàng)建api項(xiàng)目時(shí)的clientSecret(客戶端密鑰)
redirect_uri=創(chuàng)建api項(xiàng)目時(shí)的重定向地址

Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
注意:
①.code是一次性的?。。∫欢ㄒ⒁猓。?!
②.refreshToken一定要保存下來(lái),因?yàn)閏ode是一次性的,只能獲取一次。refreshToken可以作為一個(gè)常量保存起來(lái)
③.如果出現(xiàn)如下的返回,大概率這個(gè)code已經(jīng)不能用了

Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)
④.如果code已經(jīng)使用過(guò),并且沒(méi)有保存refreshToken。需要?jiǎng)h除之前創(chuàng)建的OAuth2客戶端,重新創(chuàng)建,并且重復(fù)6、7步,重新獲取refreshToken

8.使用refreshToken獲取accessToken

地址:
請(qǐng)求方式:post
https://accounts.google.com/o/oauth2/token
參數(shù):grant_type=refresh_token
refresh_token=剛剛獲取到的refreshToken
client_id=創(chuàng)建api項(xiàng)目是的clientId(客戶端ID)
client_secret=創(chuàng)建api項(xiàng)目時(shí)的clientSecret(客戶端密鑰)

Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)

9.查詢訂單狀態(tài)

請(qǐng)求方式:get
地址:
https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{token}?access_token={access_token}
packageName:app包名,必須是創(chuàng)建登錄api項(xiàng)目時(shí),創(chuàng)建android客戶端Id使用包名
productId:對(duì)應(yīng)購(gòu)買商品的商品ID
token:購(gòu)買成功后Purchase對(duì)象的getPurchaseToken()
access_token:上面獲取到的accessToken

返回值:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-410598.html

VerifyOrder json:
{
  "purchaseTimeMillis": "1670494175424",//購(gòu)買產(chǎn)品的時(shí)間,自紀(jì)元(1970 年 1 月 1 日)以來(lái)的毫秒數(shù)。
  "purchaseState": 0,//訂單的購(gòu)買狀態(tài)。可能的值為:0. 已購(gòu)買 1. 已取消 2. 待定
  "consumptionState": 0,//產(chǎn)品的消費(fèi)狀態(tài)。可能的值為: 0. 尚未消耗 1. 已消耗
  "developerPayload": "",
  "orderId": "GPA.3377-3702-0099-36461",//google訂單號(hào)
  "purchaseType": 0,
  "acknowledgementState": 0,
  "kind": "androidpublisher#productPurchase",//上面客戶支付時(shí)的透?jìng)髯侄?,google指導(dǎo)是用來(lái)存放用戶信息的,不能過(guò)長(zhǎng),否則客戶端不能支付
  "regionCode": "HK"
}

關(guān)于RefreshToken過(guò)期問(wèn)題

  • api項(xiàng)目-同意屏幕,發(fā)布狀態(tài)為測(cè)試(有效期7天)
  • RefreshToken 6個(gè)月都未使用,這個(gè)要維護(hù)accessToken的有效性,應(yīng)該可以不必考慮
  • 授權(quán)賬號(hào)改密碼了(未測(cè)試,修改開(kāi)發(fā)者賬號(hào)密碼是否會(huì)導(dǎo)致過(guò)期)
  • 授權(quán)超過(guò)50個(gè)刷新令牌,最先的刷新令牌就會(huì)失效(這里50個(gè)應(yīng)該夠用了,除了測(cè)試時(shí),可能會(huì)授權(quán)多個(gè))
  • 取消了授權(quán)
  • 屬于具有有效會(huì)話控制策略的 Google Cloud Platform 組織

到了這里,關(guān)于Unity接入IAP、服務(wù)器驗(yàn)單(Google Play)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Unity接入IAP內(nèi)購(gòu)(Android,IOS)最新流程,第一篇:內(nèi)購(gòu)接入

    Unity接入IAP內(nèi)購(gòu)(Android,IOS)最新流程,第一篇:內(nèi)購(gòu)接入

    你好! 這將是一個(gè)系列的文章 第一篇 介紹客戶端里支付的調(diào)起以及購(gòu)買。 第二篇 介紹后臺(tái)對(duì)購(gòu)買結(jié)果的驗(yàn)證以及發(fā)貨(IOS)。 第三篇 介紹后臺(tái)對(duì)購(gòu)買結(jié)果的驗(yàn)證以及發(fā)貨(Android)。 第四篇 介紹后臺(tái)對(duì)內(nèi)購(gòu)?fù)藛螁?wèn)題的處理(IOS欺詐檢測(cè)以及欺詐信息反饋)。 我們是用的

    2024年04月13日
    瀏覽(21)
  • 國(guó)內(nèi)手機(jī)安裝 Google Play 服務(wù) (GMS/Google Mobile Services)

    GMS(英語(yǔ): Google Mobile Services), 是 Google 應(yīng)用程序和 API 的集合。這些應(yīng)用程序可以跨設(shè)備的無(wú)縫協(xié)作, 給您的設(shè)備提供出色的用戶體驗(yàn)。 下載需注意 cpu 指令集架構(gòu), 如果不知道本機(jī)的可以下載通用架構(gòu)版本 (Universal)。 可以從可靠的網(wǎng)站下載軟件包, 到 www.apkmirror.com(需扶墻)下載

    2024年02月08日
    瀏覽(33)
  • ubuntu18 修改dns服務(wù)器地址為google

    域名解析被干擾的有點(diǎn)嚴(yán)重,直接使用谷歌dns服務(wù)器來(lái)解析ip 1、修改 /etc/systemd/resolved.conf 文件 這里我們可以看到這些參數(shù): 根據(jù)需要修改 resolved.conf 文件中的DNS,然后保存。 我的配置是 2、重啟 systemd-resolved 服務(wù) 3、設(shè)置開(kāi)機(jī)啟動(dòng) systemd-resolved 服務(wù) 4、備份 systemd-resolved 托

    2024年02月11日
    瀏覽(29)
  • BRAS(寬帶接入服務(wù)器)簡(jiǎn)介

    BRAS(寬帶接入服務(wù)器)簡(jiǎn)介

    寬帶接入服務(wù)器(Broadband Remote Access Server,簡(jiǎn)稱BRAS)是面向?qū)拵ЬW(wǎng)絡(luò)應(yīng)用的新型接入網(wǎng)關(guān),它位于骨干網(wǎng)的邊緣層,可以完成用戶帶寬的IP/ATM網(wǎng)的數(shù)據(jù)接入(接入手段主要基于xDSL/Cable Modem/高速以太網(wǎng)技術(shù)(LAN)/無(wú)線寬帶數(shù)據(jù)接入(WLAN)/FTTx等),實(shí)現(xiàn)商業(yè)樓宇及小區(qū)住戶的寬帶

    2024年04月26日
    瀏覽(39)
  • 利用Google Colab免費(fèi)使用GPU服務(wù)器詳細(xì)攻略

    利用Google Colab免費(fèi)使用GPU服務(wù)器詳細(xì)攻略

    目錄 前言 一、Colab限額、提供的GPU類型 二、Colab的使用步驟(如何使用免費(fèi)GPU資源) 1、添加Colaboratory 2、新建Colab、連接GPU、掛載Google Driver 3、項(xiàng)目上傳文件并運(yùn)行 三、快速下載/上傳Google Drive文件的方法(利用MultiCloud) 四、其他相關(guān)技巧 Google Colab是一個(gè)基于云端的免費(fèi)

    2024年02月09日
    瀏覽(36)
  • 【Unity】Attribute meta-data#com.google.android.play.billingclient.version 多版本庫(kù)沖突

    【Unity】Attribute meta-data#com.google.android.play.billingclient.version 多版本庫(kù)沖突

    1、Unity 2021.3.9f1 2、Max由6.0.1至最新版本6.1.0 錯(cuò)誤信息 Attribute meta-data#com.google.android.play.billingclient.version@value value=(6.1.0) from [com.android.billingclient:billing:6.1.0] AndroidManifest.xml:21:13-34 is also present at [:billing-5.2.1:] AndroidManifest.xml:25:13-34 value=(5.2.1). Suggestion: add ‘tools:replace=“android:val

    2024年01月18日
    瀏覽(35)
  • 最佳實(shí)踐 · 塔石串口服務(wù)器接入 MODBUS 物聯(lián)網(wǎng)平臺(tái)

    最佳實(shí)踐 · 塔石串口服務(wù)器接入 MODBUS 物聯(lián)網(wǎng)平臺(tái)

    串口服務(wù)器是為RS-232/RS-485/RS-422終端到TCP/IP之間完成數(shù)據(jù)轉(zhuǎn)換的通訊接口協(xié)議轉(zhuǎn)換器。提供RS-232終端與TCP/IP網(wǎng)絡(luò)的數(shù)據(jù)雙向透明傳輸,提供串口轉(zhuǎn)TCP/IP功能,RS-232/RS-485/RS-422轉(zhuǎn)TCP/IP的解決方案??梢宰孯S-232/RS-485/RS-422串口設(shè)備立即聯(lián)接網(wǎng)絡(luò)。 串口通訊服務(wù)器其實(shí)就是串口服務(wù)器

    2024年02月16日
    瀏覽(25)
  • 【Unity】Unity接入內(nèi)購(gòu)IAP,提示you are not authorized to set the license key

    【Unity】Unity接入內(nèi)購(gòu)IAP,提示you are not authorized to set the license key

    接入IAP的時(shí)候需要輸入谷歌的開(kāi)發(fā)者后臺(tái)key Unity2020之后有可能會(huì)提示:you are not authorized to set the license key 查閱相關(guān)內(nèi)容后(https://forum.unity.com/threads/purchase-you-are-not-authorized-to-set-the-license-key-google-play.954261/) Unity2020后不在Editor上面填寫(xiě)了,改成在Dashboard上輸入 打開(kāi)后輸入即

    2024年02月08日
    瀏覽(25)
  • 聯(lián)想Lenovo手機(jī)平板安裝谷歌服務(wù)框架Google, Play商店,安裝套件GMS

    聯(lián)想Lenovo手機(jī)平板安裝谷歌服務(wù)框架Google, Play商店,安裝套件GMS

    如果你的安卓手機(jī)或者平板升級(jí)了,11以上的系統(tǒng),比如是安卓11,12以上的系統(tǒng),那么安裝谷歌play商店就非常的艱難。這是因?yàn)榘沧?1以上的系統(tǒng)對(duì)權(quán)限加以了越來(lái)越多的限制。我就今天拿聯(lián)想的Z6 Pro測(cè)試,首先我到百度搜索了好幾個(gè)關(guān)于安裝谷歌套件的應(yīng)用。然后測(cè)試下來(lái)

    2024年02月16日
    瀏覽(28)
  • 速盾:服務(wù)器接入CDN后上傳圖片失敗的解決方案

    本文將探討當(dāng)服務(wù)器接入CDN后,上傳圖片失敗的常見(jiàn)原因,并提供解決方案以解決這些問(wèn)題。同時(shí),我們還將附上一些相關(guān)的問(wèn)題和解答,讓讀者更好地理解和應(yīng)對(duì)這些挑戰(zhàn)。 ?隨著互聯(lián)網(wǎng)的持續(xù)發(fā)展,網(wǎng)站的性能和速度對(duì)于用戶體驗(yàn)變得至關(guān)重要。為了提高網(wǎng)站的可訪問(wèn)性

    2024年01月24日
    瀏覽(36)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包