IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity接入Google Play 内购结算库 漏单补发 -> 正文阅读

[游戏开发]Unity接入Google Play 内购结算库 漏单补发

话不多说? 直接上代码

1 using引用

using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine.Purchasing;
using Google.Play.Billing.Internal;
using Google.Play.Billing;

?2变量定义

    private static IStoreController m_StoreController;         
    private static IExtensionProvider m_ExtensionProvider;    
    
    private AndroidJavaObject _billingClient;
    private JniUtils _jniUtils;

2 初始化(登录之后调用)

    /// <summary>
    /// 判断是否初始化过
    /// </summary>
    /// <returns></returns>
    private bool IsInitialized()
    {
        return m_StoreController != null && m_ExtensionProvider != null;
    }

    /// <summary>
    /// 初始化方法 游戏登录后调用
    /// </summary>
    /// <param name="productids"> 此参数为Google后台配置的商品id列表  没有传入的商品id不能购买 </param>
    public void InitializePurchasing(List<string> productids)
    {
        if (AssetLoadPath.GetRunPlatformType() == AssetLoadPath.RunPlatformType.Editor)
        {
            return;
        }
        if (IsInitialized())
        {
            return;
        }

        ConfigurationBuilder builder;
        if (Application.platform == RuntimePlatform.Android)
        {
            builder = ConfigurationBuilder.Instance(GooglePlayStoreModule.Instance());
        }
        else
        {
            builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
        }

        //设置商品
        for (int i = 0; i < productids.Count; i++)
        {
            builder.AddProduct(productids[i], ProductType.Consumable, new IDs { { productids[i], GooglePlay.Name } });
        }
        

        //  BillingClient 连接的回调 
        BillingClientStateListener _billingClientStateListener = new BillingClientStateListener();
        _billingClientStateListener.OnBillingServiceDisconnected += () =>
        {
           Debug.Log("OnBillingServiceDisconnected Service disconnected, retrying connection...");
        };
        _billingClientStateListener.OnBillingSetupFinished += (billingResult) =>
        {
            Debug.Log("OnBillingSetupFinished ");
        };


        //  BillingClient SetUp方法的回调
        PurchasesUpdatedListener purchaseUpdatedListener = new PurchasesUpdatedListener();
        purchaseUpdatedListener.OnPurchasesUpdated += ParsePurchaseResult;

        // 这里连接 BillingClient
        _billingClient = new AndroidJavaObject(Constants.BillingClient, Constants.Version);
        _billingClient.Call(Constants.BillingClientSetUpMethod, JniUtils.GetApplicationContext(), purchaseUpdatedListener, false);
        _billingClient.Call(Constants.BillingClientStartConnectionMethod, _billingClientStateListener);

        // 实例一个 jniUtils 里面有一些公关方法可以用
        var gameObject = new GameObject("GooglePlayBillingUtil");
        GooglePlayBillingUtil _util = gameObject.AddComponent<GooglePlayBillingUtil>();
        _jniUtils = new JniUtils(_util);

        UnityPurchasing.Initialize(this, builder);
    }



    /// <summary>
    /// BillingClient SetUp方法的回调
    /// </summary>
    /// <param name="billingResult"></param>
    /// <param name="javaPurchasesList"></param>
    private void ParsePurchaseResult(AndroidJavaObject billingResult, AndroidJavaObject javaPurchasesList)
    {
        var responseCode = _jniUtils.GetResponseCodeFromBillingResult(billingResult);
        var debugMessage = JniUtils.GetDebugMessageFromBillingResult(billingResult);
        Debug.Log(" 确认购买的回调  ParsePurchaseResult  11111 " + responseCode + debugMessage);
        if (responseCode == BillingResponseCode.Ok)
        {

        }
        else
        {
            Debug.LogError(string.Format("ParsePurchaseResult  Purchase failed with error code '{0}' and debug message: '{1}'", responseCode, debugMessage));
        }
    }

    /// <summary>
    ///  初始化完成的回调
    /// </summary>
    /// <param name="controller"></param>
    /// <param name="extensions"></param>
    void IStoreListener.OnInitialized(IStoreController controller, IExtensionProvider extensions)
    {
        m_StoreController = controller;
        m_ExtensionProvider = extensions;

        //下面的三行代码看需求是否需要 具体作用可以查看官方接入文档
        var _playStoreExtensions = extensions.GetExtension<Google.Play.Billing.IGooglePlayStoreExtensions>();
        _playStoreExtensions.SetObfuscatedAccountId("accountId");  //向 Google Play 传递经过混淆处理的帐号 ID
        _playStoreExtensions.SetObfuscatedProfileId("profileId");  //向 Google Play 传递经过混淆处理的个人资料 ID
    }

    /// <summary>
    /// 初始化失败的回调
    /// </summary>
    /// <param name="error"></param>
    void IStoreListener.OnInitializeFailed(InitializationFailureReason error)
    {
        Debug.LogError("Google Billing IAP 初始化失败 :" + error);
    }

4拉起支付

    /// <summary>
    /// 购买商品调用
    /// </summary>
    /// <param name="productID"> Google后台配置的商品id </param>
    public void BuyProduct(string productID)
    {
        if (AssetLoadPath.GetRunPlatformType() == AssetLoadPath.RunPlatformType.Editor)
        {
            return;
        }

        if (IsInitialized())
        {
            Product produdt = m_StoreController.products.WithID(productID);
            if (produdt != null && produdt.availableToPurchase)
            {
                m_StoreController.InitiatePurchase(produdt);
                Debug.Log(produdt.metadata.localizedPrice);
            }
            else
            {
                Debug.Log("BuyProduct Fail");
            }
        }
        else
        {
            Debug.Log("BuyProductID FAIL. Not initialized.");
        }
    }



    /// <summary>
    /// 购买成功的回调 
    /// </summary>
    /// <param name="args"></param>
    /// <returns></returns>
    PurchaseProcessingResult IStoreListener.ProcessPurchase(PurchaseEventArgs args)
    {

        string productId = args.purchasedProduct.definition.id;
        Dictionary<string, object> wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(args.purchasedProduct.receipt);
        string store = (string)wrapper["Store"];
        string payload = (string)wrapper["Payload"];

        Dictionary<string, object> gpDetails = (Dictionary<string, object>)MiniJson.JsonDecode(payload);
        string gpJson = (string)gpDetails["json"];
        string gpSig = (string)gpDetails["signature"];

        // TODO  
        // 这里向服务器发包,将 gpJson 和 gpSig 发给服务器做验证
        
        return PurchaseProcessingResult.Complete;
    }

    /// <summary>
    /// 购买失败的回调
    /// </summary>
    /// <param name="product"></param>
    /// <param name="failureReason"></param>
    void IStoreListener.OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    {
        Debug.LogError(string.Format("Google Billing IAP 购买失败. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    }

5消耗商品

    /// <summary>
    /// 确认购买 消耗商品  此方法要在服务器认证成功,并且成功发货之后调用
    /// 两个参数均可在购买成功的回调json内解析出
    /// </summary>
    /// <param name="skuId">商品id</param>
    /// <param name="token">订单token</param>
    public void ConsumeAsync(string productId, string token )
    {
        var consumeParamsBuilder = new AndroidJavaObject(Constants.ConsumeParamsBuilder);
        consumeParamsBuilder.Call<AndroidJavaObject>( Constants.ConsumeParamsBuilderSetPurchaseTokenMethod, token);
        var consumeResponseListener = new ConsumeResponseListener(productId);
        consumeResponseListener.OnConsumeResponse += ProcessConsumePurchaseResult;
        _billingClient.Call(Constants.BillingClientConsumePurchaseMethod, consumeParamsBuilder.Call<AndroidJavaObject>(Constants.BuildMethod), consumeResponseListener);
    }

    /// <summary>
    /// 消耗商品的回调
    /// </summary>
    /// <param name="productId"></param>
    /// <param name="billingResult"></param>
    private void ProcessConsumePurchaseResult(string productId, AndroidJavaObject billingResult)
    {
        var responseCode = _jniUtils.GetResponseCodeFromBillingResult(billingResult);
        if (responseCode == BillingResponseCode.Ok)
        {
          
        }
        else
        {
            var debugMessage = JniUtils.GetDebugMessageFromBillingResult(billingResult);
            Debug.LogError(string.Format( "Failed to finish the transaction with error code {0} and debug message: {1}. " , responseCode, debugMessage));
        }

    }

6漏单检查

    /// <summary>
    /// 漏单补发  游戏登录后调用  InitializePurchasing
    /// </summary>
    public void RestoreTransactions ()
    {
        var inAppPurchasesResult = _billingClient.Call<AndroidJavaObject>(Constants.QueryPurchasesMethod, SkuType.InApp.ToString());
        var subsPurchasesResult = _billingClient.Call<AndroidJavaObject>(Constants.QueryPurchasesMethod, SkuType.Subs.ToString());
        
        if (_jniUtils.GetResponseCodeFromQueryPurchasesResult(inAppPurchasesResult) !=  BillingResponseCode.Ok ||
                 _jniUtils.GetResponseCodeFromQueryPurchasesResult(subsPurchasesResult) != BillingResponseCode.Ok)
        {
            return;
        }

        IEnumerable<Purchase> allPurchases = _jniUtils.ParseQueryPurchasesResult(inAppPurchasesResult).Concat(_jniUtils.ParseQueryPurchasesResult(subsPurchasesResult)).ToList();
        //代码执行到这里会自动调用到购买成功的回调  在回调里处理好与服务器的通信即可

        foreach (Purchase purchase in allPurchases)
        {
            Debug.Log(" 漏单 purchase.ProductId  : " + purchase.ProductId);
            Debug.Log(" 漏单 purchase.JsonReceipt  : " + purchase.JsonReceipt);
            Debug.Log(" 漏单 purchase.PurchaseState  : " + purchase.PurchaseState);
        }
    }

流程说明 :

登录后调用 1初始化方法?

需要拉起充值时调用 4拉起支付

充值成功之后向服务器发包验证? 服务器发货

服务器发货之后? 调用 5消耗商品

至于漏单检查,推荐也放在登录之后,在初始化之后调用?

漏单检查的作用是在支付完成之后? 如果出现断网 或者服务器关闭等情况,则服务器不会发货,这样的话,玩家是付了钱,但没收到货? 当然,用户可以自己操作后台退款,但显然这并不是最好的处理 方式

所以才有漏单检查,在登录之后检查到有未消费的订单,直接帮他消费掉就好了

希望此贴对您的开发有所帮助??

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-07-23 11:09:28  更:2021-07-23 11:11:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/4 19:29:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码