Unity接入Google Play原生支付

Unity导出Android工程加支付逻辑

添加引用

    	//内购
    	implementation 'com.android.billingclient:billing:2.1.0'    	

AndroidMainfest文件添加权限

 <uses-permission android:name="com.android.vending.BILLING"/>
GooglePayUtil.java 实现 PurchasesUpdatedListener 接口
执行顺序:
  • 初始化:初始化完成后->查询未消耗订单(走正常支付成功后流程)
  • 购买商品:购买商品->支付完成->传给服务器验证订单->成功:发送奖励并调用Google消耗方法
  • 支付失败:购买商品->支付失败(BillingResponseCode = 7)存在未消耗订单->查询未消耗订单(走正常支付成功后流程)
IGooglePayUpdateListener主要监听事件
	public interface IGooglePayUpdateListener{
	        //失败
	        void onFail(String tag, String msg);
	        //响应Code
	        void onResponseCode(int code);
	        //初始化成功
	        void onGooglePayClientSetupFinished();
	        //购买成功回调
	        void onGooglePayPurchaseSuccess(Purchase purchase);
	        //消耗成功
	        void onGooglePayConsumeSuccess(BillingResult result, String token);
	}
	private boolean _IsServiceConnected;    //服务器连接状态

    private Activity _activity;
    private BillingClient _billingClient;
    private IGooglePayUpdateListener _listener;
    private Set<String> _tokensToBeConsumed = new HashSet<>();
    private Map<String,Purchase> _mapTokenPurchase = new HashMap<>();

_tokensToBeConsumed 储存消耗token
_mapTokenPurchase token对应Purchase

构造方法
    public GooglePayUtil(Activity activity, IGooglePayUpdateListener listener){
        this._activity = activity;
        this._listener = listener;

        //构建client
        _billingClient = BillingClient.newBuilder(_activity).setListener(this).enablePendingPurchases().build();

        Log.i(TAG, "Starting setup.");

        //连接GooglePay服务
        startServiceConnection(new Runnable() {
            @Override
            public void run() {
                //监听启动就绪
                _listener.onGooglePayClientSetupFinished();

                Log.i(TAG, "GooglePay服务已就位,进行查询订单.");

                queryPurchases();
            }
        });
    }

queryPurchases 查询订单方法

链接服务
    //启动服务链接
    private void startServiceConnection(final Runnable executeOnSuccess){
        _billingClient.startConnection(new BillingClientStateListener() {
            //启动完成
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                _listener.onResponseCode(billingResult.getResponseCode());
                Log.i(TAG, "Setup finished. Response code: "+ billingResult.getResponseCode());
                if(billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK){
                    Log.i(TAG, "Success 连接成功!");
                    _IsServiceConnected = true;
                    //可以执行扩展方法
                    if(executeOnSuccess != null) executeOnSuccess.run();
                }
            }
            //连接断开
            @Override
            public void onBillingServiceDisconnected() {
                _listener.onFail(TAG, "Disconnected 断开链接!");
                _IsServiceConnected = false;
            }
        });
    }
添加服务请求方法自动重连
	private void executeServiceRequest(Runnable runnable){
        if(_IsServiceConnected) runnable.run(); else startServiceConnection(runnable);
    }
初始化完成查询未消耗订单
	//查询
    private void queryPurchases(){
        Runnable queryToExecute = new Runnable() {
            @Override
            public void run() {
                long time = System.currentTimeMillis();
                Purchase.PurchasesResult purchasesResult = _billingClient.queryPurchases(BillingClient.SkuType.INAPP);
                Log.i(TAG, String.format("查询内购消耗时间: %s ms",System.currentTimeMillis() - time));
                Log.i(TAG, String.format("查询内购返回 code: %s size: %s",purchasesResult.getResponseCode(),purchasesResult.getPurchasesList().size()));
                //检查是否支持订阅
                if(checkAreSubscriptionsSupported()){
                    time = System.currentTimeMillis();
                    //查询订阅
                    Purchase.PurchasesResult subscriptionResult = _billingClient.queryPurchases(BillingClient.SkuType.SUBS);
                    Log.i(TAG, String.format("查询订阅消耗时间: %s ms",System.currentTimeMillis() - time));
                    Log.i(TAG, String.format("查询订阅返回 code: %s size: %s",subscriptionResult.getResponseCode(),subscriptionResult.getPurchasesList().size()));

                    if(subscriptionResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
                        purchasesResult.getPurchasesList().addAll(subscriptionResult.getPurchasesList());
                    else
                        _listener.onFail(TAG, String.format("查询订阅返回错误响应 code: %s",subscriptionResult.getResponseCode()));
                } else if(purchasesResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
                    Log.i(TAG, "未查询到订阅信息已跳过");
                else
                    _listener.onFail(TAG, String.format("查询订阅返回错误响应 code: %s",purchasesResult.getResponseCode()));

                //查询完成执行
                onQueryPurchasesFinished(purchasesResult);
            }
        };

        executeServiceRequest(queryToExecute);
    }
    //检查当前客户端是否支持订阅
    private boolean checkAreSubscriptionsSupported(){
        BillingResult featureSupported = _billingClient.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS);
        if(featureSupported.getResponseCode() != BillingClient.BillingResponseCode.OK)
            _listener.onFail(TAG, "checkAreSubscriptionsSupported()  got an error response: " + featureSupported.getResponseCode());

        return featureSupported.getResponseCode() == BillingClient.BillingResponseCode.OK;
    }
    //查询完成
    private void onQueryPurchasesFinished(Purchase.PurchasesResult result){
        if(_billingClient == null || result.getResponseCode() != BillingClient.BillingResponseCode.OK){
            _listener.onResponseCode(result.getResponseCode());
            _listener.onFail(TAG, String.format("billing == null 或 返回失败code: %s",result.getResponseCode()));
            return;
        }
        Log.i(TAG, "查询成功!");
        _mapTokenPurchase.clear();
        //更新购买
        onPurchasesUpdated(result.getBillingResult(),result.getPurchasesList());
    }
购买回调
//购买回调
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        _listener.onResponseCode(billingResult.getResponseCode());
        switch (billingResult.getResponseCode())
        {
            case BillingClient.BillingResponseCode.OK:
                for (Purchase purchase : purchases) {
                    handlePurchase(purchase);
                }
                break;
            case BillingClient.BillingResponseCode.USER_CANCELED:
                Log.i(TAG, "onPurchasesUpdated() - 用户取消购买流程");
                break;
            case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED:
                //TODO: 上次购买物品未消耗
                _listener.onFail(TAG, String.format("onPurchasesUpdated() - 上次购买物品未消耗 code: %s 重新 queryPurchases",billingResult.getResponseCode()));
                queryPurchases();
                break;
            default:
                _listener.onFail(TAG, String.format("onPurchasesUpdated() - 返回未知错误 code: %s",billingResult.getResponseCode()));
                break;
        }
    }

handlePurchase 处理购买方法
BillingResponseCode == BillingResponseCode.ITEM_ALREADY_OWNED 存在未消耗的订单这时需要执行查询订单方法,查询到未消耗的订单会自动走购买消耗流程

    //处理购买
    private void handlePurchase(Purchase purchase){
        if(purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED){
            //TODO: 购买完成
            _mapTokenPurchase.put(purchase.getPurchaseToken(),purchase);
            _listener.onGooglePayPurchaseSuccess(purchase);
        } else if(purchase.getPurchaseState() == Purchase.PurchaseState.PENDING){
            //TODO: 未完成-挂起
            Log.i(TAG, "未完成购买-已挂起");
        }
    }
消耗商品(Google不调用消耗方法无法再次购买)
	//消耗商品  只有消费成功之后,才能真正到账,否则3天之后,会执行退款处理 测试阶段只有5分钟
    public void ConsumeAsync(final String token){

        if(!CheckToken(token)){
            _listener.onFail(TAG,String.format("检查订单未购买: %s",token));
            return;
        }
        Purchase purchase = _mapTokenPurchase.get(token);
        if(_tokensToBeConsumed.contains(purchase.getPurchaseToken())){
            Log.i(TAG, "消耗商品已使用,正在跳过...");
            return;
        }
        _tokensToBeConsumed.add(purchase.getPurchaseToken());

        //监听
        final ConsumeResponseListener onConsumeListener = new ConsumeResponseListener() {
            @Override
            public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
                if(billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
                    Log.i(TAG, "消耗成功.");
                else
                    _listener.onFail(TAG, String.format("onConsumeResponse 消耗失败: code = %s , msg = %s , purchaseToken = %s", billingResult.getResponseCode(), billingResult.getDebugMessage(), purchaseToken));
                _listener.onGooglePayConsumeSuccess(billingResult,purchaseToken);
            }
        };

        //执行
        Runnable consumeRequest = new Runnable() {
            @Override
            public void run() {
                ConsumeParams consumeParams = ConsumeParams.newBuilder()
                        .setDeveloperPayload(purchase.getDeveloperPayload())
                        .setPurchaseToken(purchase.getPurchaseToken()).build();
                _billingClient.consumeAsync(consumeParams,onConsumeListener);
            }
        };

        executeServiceRequest(consumeRequest);
    }

    //*******

    //查询是此账单是否已付款
    public boolean CheckToken(String token){
        return _mapTokenPurchase.containsKey(token);
    }
    //获取Sku
    public String GetSku(String token){
        return _mapTokenPurchase.get(token).getSku();
    }

    //*******
购买
    //异步查询商品信息在后台是否配置过
    public void QuerySkuDetailsAsync(@BillingClient.SkuType final String itemType, final List<String> skuList, final SkuDetailsResponseListener listener){
        Runnable queryRequest = new Runnable() {
            @Override
            public void run() {
                SkuDetailsParams.Builder parans = SkuDetailsParams.newBuilder().setType(itemType).setSkusList(skuList);
                _billingClient.querySkuDetailsAsync(parans.build(), listener);
            }
        };

        executeServiceRequest(queryRequest);
    }

当查询到Sku后调用Google 支付窗口

    //启动购买或订阅流程
    public void Pay(final SkuDetails skuDetails ){
        Runnable purchaseFlowRequest = new Runnable() {
            @Override
            public void run() {
                BillingFlowParams flowParams = BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build();
                _billingClient.launchBillingFlow(_activity,flowParams);
                Log.i(TAG, "launchBillingFlow , 调用窗口...");
            }
        };

        executeServiceRequest(purchaseFlowRequest);
    }
GooglePayHelper 实现 IGooglePayUpdateListener 方法

主要与Unity通信

    public GooglePayHelper(Activity activity){
        _googlePayUtil = new GooglePayUtil(activity,this);
    }

IGooglePayUpdateListener

 @Override
    public void onFail(String tag, String msg) {
        UnityPlayer.UnitySendMessage("AndroidImpl","OnPayFail",String.format("%s : %s",tag,msg));
    }
    @Override
    public void onResponseCode(int code){
        UnityPlayer.UnitySendMessage("AndroidImpl","OnPayResponseCode",String.valueOf(code));
    }

    //初始化完成
    @Override
    public void onGooglePayClientSetupFinished() {
        UnityPlayer.UnitySendMessage("AndroidImpl","OnPayClientSetupFinished","");
    }
    //购买回调 * 需要给服务器验证
    @Override
    public void onGooglePayPurchaseSuccess(Purchase purchase) {
        //TODO:购买成功
        Log.i(TAG, String.format("购买成功物品: %s , 订单: %s",purchase.getSku(),purchase.getOrderId()));
        UnityPlayer.UnitySendMessage("AndroidImpl","OnPayPurchaseSuccess",purchase.getPurchaseToken());
    }
    //消耗完成
    @Override
    public void onGooglePayConsumeSuccess(BillingResult result, String token) {
        Log.i(TAG, String.format("onGooglePayConsumeSuccess() : 消耗token - %s ,结果 - %s", token, result.getResponseCode()));
        onResponseCode(result.getResponseCode());
        if(result.getResponseCode() == BillingClient.BillingResponseCode.OK){
            //TODO:消耗成功通知Unity
            UnityPlayer.UnitySendMessage("AndroidImpl","OnPayConsumeSuccess","");
            Log.i(TAG, "消耗成功物品");
        }else {
            Log.e(TAG, String.format("消耗物品返回error code: %s",result.getResponseCode()));
        }
    }

Unity 主要调用方法

    public void PayItem(final String sku){
        Log.i(TAG, String.format("PayItem 订单id: %s",sku));
        List<String> skuList = new ArrayList<>();
        skuList.add(sku);
        _googlePayUtil.QuerySkuDetailsAsync(BillingClient.SkuType.INAPP, skuList, new SkuDetailsResponseListener() {
            @Override
            public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
                Log.i(TAG,String.format("PayItem BillingResponseCode : %s",billingResult.getResponseCode()));
                if(billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
                    onResponseCode(billingResult.getResponseCode());
                    Log.e(TAG, String.format("查询%s失败。错误代码:%s", sku, billingResult.getResponseCode()));
                    onFail(TAG, String.format("查询%s失败。错误代码:%s", sku, billingResult.getResponseCode()));
                } else if(skuDetailsList != null && skuDetailsList.size() > 0){
                    //获取sku
                    for (SkuDetails details : skuDetailsList) {
                        if(sku.equals(details.getSku())){
                            _googlePayUtil.Pay(details);
                        }
                    }
                }
            }
        });
    }

    //服务器验证通过 * 消耗物品
    public void ConsumeItem(final String token){
        _googlePayUtil.ConsumeAsync(token);
    }

查询成功后调用Google 支付窗口

主要代码就这些,订阅我这边没有这个需求所以没处理

如果项目中有多个SDK 有的SDK需要v4 的支持可能会报 androidx 与 v4 冲突可以直接对支付api进行降级

		//内购
    	implementation 'com.android.billingclient:billing:2.0.3'
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值