Android Google billing储值接入(干货)

接下来给大家介绍下Google billing储值库的接入,以及如何避坑!!

Google billing接入文档:https://developer.android.com/google/play/billing/integrate?hl=zh-cn

下面将以billing5.2.1版本示例

一、调用流程

1.配置

dependencies {
    def billing_version = "5.2.1"

    implementation "com.android.billingclient:billing:$billing_version"
}

如果需要手动集成,不想使用maven方式,可以使用 fat-aar 把库拉下来

2.创建 PurchasesUpdatedListener

	private static PurchasesUpdatedListener getListener(final Context context) {
        return new PurchasesUpdatedListener() {
            @Override
            public void onPurchasesUpdated(final BillingResult billingResult, @Nullable final List<Purchase> list) {
                LogUtil.i(TAG, "onPurchasesUpdated: " + billingResult.getDebugMessage());
                ((Activity) context).runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        
                            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                                final ArrayList<Pur> results = new ArrayList<>();
                                if (list != null && !list.isEmpty()) {
                                    for (Purchase purchase : list) {
                                        try {
                                            Pur pur = new Pur(purchase.getOriginalJson(), purchase.getSignature());
                                            results.add(pur);
                                        } catch (JSONException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }

                                if (!results.isEmpty()) {
                                    ArrayList<QueryProductDetailsParams.Product> productArrayList = new ArrayList<>();
                                    for (Pur p : results) {
                                        productArrayList.add(QueryProductDetailsParams.Product.newBuilder()
                                                .setProductId(p.getSku())
                                                .setProductType(BillingClient.ProductType.INAPP)
                                                .build());
                                    }

                                    QueryProductDetailsParams productDetailsParams = QueryProductDetailsParams.newBuilder()
                                            .setProductList(productArrayList)
                                            .build();

                                    mBillingClient.queryProductDetailsAsync(productDetailsParams, new ProductDetailsResponseListener() {
                                        @Override
                                        public void onProductDetailsResponse(final BillingResult billingResult, final List<ProductDetails> list) {
                                            LogUtil.i(TAG, "onProductDetailsResponse: " + billingResult.getDebugMessage());
                                            ((Activity) context).runOnUiThread(new Runnable() {
                                                @Override
                                                public void run() {
                                                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                                                        if (list != null && !list.isEmpty()) {
                                                            for (ProductDetails details : list) {
                                                                for (Pur p : results) {
                                                                	//处理储值数据
                                                                    
                                                                    
                                                                }
                                                            }
                                                            
                                                        } else {
                                                            
                                                        }
                                                    } else {
                                                        
                                                    }
                                                }
                                            });
                                        }
                                    });

                                } else {
                                    
                                }
                            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
                                LogUtil.w(TAG, "USER_CANCELED");
                                
                            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_UNAVAILABLE) {
                                LogUtil.w(TAG, "ITEM_UNAVAILABLE");
                                
                            } else {
                                LogUtil.e(TAG, "onPurchasesUpdated not OK: " + billingResult.getResponseCode());
                                
                            }
                        
                    }
                });
            }
        };
    }

3.初始化 BillingClient
connection.connected();
connection.disConnection(str);
回调接口interface请自行实现

private static BillingClient mBillingClient;

private static void connection(Context context,回调){
			if (mListener == null) {
                mListener = getListener(context);
            }
            mBillingClient = BillingClient.newBuilder(context).enablePendingPurchases().setListener(mListener).build();
            mBillingClient.startConnection(new BillingClientStateListener() {
                @Override
                public void onBillingSetupFinished(final BillingResult billingResult) {
                    LogUtil.i(TAG, "onBillingSetupFinished: " + billingResult.getDebugMessage());
                    ((Activity) context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                                
                                connection.connected();
                            } else {
                                
                                LogUtil.w(TAG, "onBillingSetupFinished: connection result is not OK");
                                LogUtil.w(TAG, "onBillingSetupFinished ResponseCode: " + billingResult.getResponseCode());
                                connection.disConnection("onBillingSetupFinished ResponseCode: " + billingResult.getResponseCode());
                            }
                        }
                    });
                }

                @Override
                public void onBillingServiceDisconnected() {
                    ((Activity) context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            
                            LogUtil.w(TAG, "onBillingServiceDisconnected: Billing Service Disconnected");
                            connection.disConnection("Billing Service Disconnected");
                        }
                    });
                }
            });
            }

4.查询应用内商品详情

	connection(context, new Connection() {
            @Override
            public void connected() {

                ArrayList<QueryProductDetailsParams.Product> productList = new ArrayList<>();
                for (int i = 0; i < skuList.size(); i++) {
                    productList.add(QueryProductDetailsParams.Product.newBuilder()
                            .setProductId(skuList.get(i))
                            .setProductType(BillingClient.ProductType.INAPP)
                            .build());
                }

                QueryProductDetailsParams productDetailsParams = QueryProductDetailsParams.newBuilder()
                        .setProductList(productList)
                        .build();

                mBillingClient.queryProductDetailsAsync(productDetailsParams, new ProductDetailsResponseListener() {
                            @Override
                            public void onProductDetailsResponse(final BillingResult billingResult, final List<ProductDetails> productDetailsList) {
                                LogUtil.i(TAG, "onSkuDetailsResponse: " + billingResult.getDebugMessage());
                                ((Activity) context).runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                                            ArrayList<Sku> results = new ArrayList<>();
                                            if (productDetailsList != null && !productDetailsList.isEmpty()) {
                                                for (ProductDetails details : productDetailsList) {
                                                    //处理数据results.add()
                                                }
                                            }
                                            
                                        } else {
                                            LogUtil.w(TAG, "onSkuDetailsResponse: Response Code is not OK");
                                            LogUtil.w(TAG, "onSkuDetailsResponse ResponseCode: " + billingResult.getResponseCode());
                                        }
                                    }
                                });
                            }
                        }
                );
            }

            @Override
            public void disConnection(String msg) {
                
            }
        });

5.查询最近的购买交易
如果存在掉单的,可以获取到对应商品的信息,然后进行补单

	connection(context, new Connection() {
            @Override
            public void connected() {

                QueryPurchaseHistoryParams historyParams = QueryPurchaseHistoryParams.newBuilder()
                        .setProductType(BillingClient.ProductType.INAPP)
                        .build();

                mBillingClient.queryPurchaseHistoryAsync(historyParams, new PurchaseHistoryResponseListener() {
                    @Override
                    public void onPurchaseHistoryResponse(final BillingResult billingResult, List<PurchaseHistoryRecord> list) {
                        LogUtil.i(TAG, "onPurchaseHistoryResponse: " + billingResult.getDebugMessage());
                        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {

                            QueryPurchasesParams purchasesParams = QueryPurchasesParams.newBuilder()
                                    .setProductType(BillingClient.ProductType.INAPP)
                                    .build();

                            mBillingClient.queryPurchasesAsync(purchasesParams, new PurchasesResponseListener() {
                                @Override
                                public void onQueryPurchasesResponse(final BillingResult billingResult, final List<Purchase> purchaseList) {
                                    ((Activity) context).runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                                                ArrayList<Pur> results = new ArrayList<>();
                                                if (purchaseList != null && !purchaseList.isEmpty()) {
                                                    for (Purchase purchase : purchaseList) {
                                                       //掉单数据,回调出去处理
                                                       results.add(pur);
                                                    }
                                                }
                                                historyListener.result(results);
                                            } else {
                                                LogUtil.w(TAG, "purchasesResult: Response Code is not OK");
                                                
                                            }
                                        }
                                    });
                                }
                            });

                        } else {
                            
                        }
                    }
                });
            }

            @Override
            public void disConnection(String msg) {
                
            }
        });

6.拉起储值

	connection(activity, new Connection() {
            @Override
            public void connected() {

                ArrayList<QueryProductDetailsParams.Product> inAppProductInfo = new ArrayList<>();
                inAppProductInfo.add(QueryProductDetailsParams.Product.newBuilder()
                        .setProductId(productId)
                        .setProductType(BillingClient.ProductType.INAPP)
                        .build());

                QueryProductDetailsParams productDetailsParams = QueryProductDetailsParams.newBuilder()
                        .setProductList(inAppProductInfo)
                        .build();

                mBillingClient.queryProductDetailsAsync(productDetailsParams, new ProductDetailsResponseListener() {
                    @Override
                    public void onProductDetailsResponse(final BillingResult billingResult, final List<ProductDetails> list) {
                        LogUtil.i(TAG, "onSkuDetailsResponse: " + billingResult.getDebugMessage());
                        activity.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                                    if (list != null && !list.isEmpty()) {

                                        for (final ProductDetails details : list) {
                                            LogUtil.i(TAG, "onSkuDetailsResponse: " + details.toString());
                                            if (productId.equals(details.getProductId())) {

                                                

                                                /**
                                                 * 优惠购买流程参数:OfferToken
                                                 * 正常购买流程这个参数没有返回的,但是可能在某些优惠购买中需要使用这个参数
                                                 */
                                                BillingFlowParams.ProductDetailsParams.Builder newBuilder = BillingFlowParams.ProductDetailsParams.newBuilder();
                                                newBuilder.setProductDetails(details);

                                                List<ProductDetails.SubscriptionOfferDetails> offerDetailsList = details.getSubscriptionOfferDetails();
                                                if (offerDetailsList != null && offerDetailsList.size() > 0) {
                                                    for (ProductDetails.SubscriptionOfferDetails offerDetails : offerDetailsList) {
                                                        newBuilder.setOfferToken(offerDetails.getOfferToken());
                                                    }
                                                }

                                                ArrayList<BillingFlowParams.ProductDetailsParams> paramsArrayList = new ArrayList<>();
                                                paramsArrayList.add(newBuilder.build());

                                                BillingFlowParams.Builder builder = BillingFlowParams.newBuilder();
                                                builder.setProductDetailsParamsList(paramsArrayList);
                                                builder.setObfuscatedAccountId(obfuscatedAccountId);
                                                builder.setObfuscatedProfileId(obfuscatedProfileId);

                                                mBillingClient.launchBillingFlow(activity, builder.build());
                                                break;
                                            }
                                        }
                                    } else {
                                        LogUtil.w(TAG, "onSkuDetailsResponse: sku is not support");
                                        
                                    }
                                } else {
                                    LogUtil.w(TAG, "onSkuDetailsResponse: Response code is not ok");
                                    
                                }
                            }
                        });
                    }
                });
            }

            @Override
            public void disConnection(String msg) {
                
            }
        });

7.确认购买

	connection(context, new Connection() {
            @Override
            public void connected() {
                ConsumeParams params = ConsumeParams.newBuilder().setPurchaseToken(pur.getPurchaseToken()).build();
                mBillingClient.consumeAsync(params, new ConsumeResponseListener() {
                    @Override
                    public void onConsumeResponse(final BillingResult billingResult, final String outToken) {
                        
                    }
                });
            }

            @Override
            public void disConnection(String msg) {
                LogUtil.w(TAG, "consume disConnection: " + msg);
            }
        });

8.断开连接

		if (mBillingClient != null) {
            mBillingClient.endConnection();
        }

9.注意事项
1.建议在储值同一品项时触发掉单补单流程,补单完成后再拉起新的订单,具体的补单流程,创单流程自行完善!
2.billing5后,查询接口返回的数据格式和billing3、4是不一样的,这个一定要注意!!

billing3:
{
     "productId": "test.1usd",
     "type": "inapp",
     "title": "一钻 (test)",
     "name": "一钻",
     "description": "一钻",
     "price": "HK$9.90",
     "price_amount_micros": 9900000,
     "price_currency_code": "HKD",
     "skuDetailsToken": "*********************"
}


billing5:
{
    "productId":"test.1usd",
    "type":"inapp",
    "title":"一钻 (test)",
    "name":"一钻",
    "description":"一钻",
    "localizedIn":["zh-CN"],
    "skuDetailsToken":"****************",
    "oneTimePurchaseOfferDetails":{
        "priceAmountMicros":9900000,
        "priceCurrencyCode":"HKD",
        "formattedPrice":"HK$9.90"}
    }', 
    parsedJson={"productId":"test.1usd","type":"inapp","title":"一钻 (test)","name":"一钻","description":"一钻","localizedIn":["zh-CN"],
   "skuDetailsToken":"**************",
   "oneTimePurchaseOfferDetails":{"priceAmountMicros":9900000,"priceCurrencyCode":"HKD","formattedPrice":"HK$9.90"}}, 
   "productId='test.1usd', productType='inapp', title='一钻 (test)', "
    "productDetailsToken='*********', subscriptionOfferDetails=null}"

3.billing5后,会出现一个兼容的问题,就是在低版本的play商店中储值时会报错,导致无法储值,升级一下play商店的版本就行。
在这里插入图片描述

  1. 2023 年 8 月 2 日起,所有新应用都必须使用结算库版本 5 或更高版本。自 2023 年 11 月 1 日起,现有应用的所有新版本都必须使用结算库版本 5 或更高版本;billing6估计也在24年8月开始全面使用!!
    在这里插入图片描述

二、预注册和积分兑换

预注册官方文档:https://developer.android.com/distribute/best-practices/launch/pre-registration?hl=zh-cn

积分兑换官方文档:https://developer.android.com/guide/playpoints/deliver-items?hl=zh-cn

预注册和积分兑换,以及测试时使用的兑换码兑换都是使用以下接口进行查询,具体怎么区分是哪个类型,请自行设计接口实现,通过枚举类传入区分,因为这部分涉及比较多的服务端逻辑,此处就不详细说明了

			QueryPurchaseHistoryParams historyParams = QueryPurchaseHistoryParams.newBuilder()
                        .setProductType(BillingClient.ProductType.INAPP)
                        .build();

                mBillingClient.queryPurchaseHistoryAsync(historyParams, new PurchaseHistoryResponseListener() {
                    @Override
                    public void onPurchaseHistoryResponse(final BillingResult billingResult, List<PurchaseHistoryRecord> list) {
                        LogUtil.i(TAG, "onPurchaseHistoryResponse: " + billingResult.getDebugMessage());
                        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {

                            QueryPurchasesParams purchasesParams = QueryPurchasesParams.newBuilder()
                                    .setProductType(BillingClient.ProductType.INAPP)
                                    .build();

                            mBillingClient.queryPurchasesAsync(purchasesParams, new PurchasesResponseListener() {
                                @Override
                                public void onQueryPurchasesResponse(final BillingResult billingResult, final List<Purchase> purchaseList) {
                                    ((Activity) context).runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                                                ArrayList<Pur> results = new ArrayList<>();
                                                if (purchaseList != null && !purchaseList.isEmpty()) {
                                                    for (Purchase purchase : purchaseList) {
                                                    	//获取数据处理,回调出去
                                                       
                                                    }
                                                }
                                               
                                            } else {
                                               
                                            }
                                        }
                                    });
                                }
                            });

                        } else {
                           
                        }
                    }
                });

2.处理

//回调中处理逻辑,示例
						/**
                         * 注意:
                         * 兑换码兑换时(【预注册】和【积分兑换】)pru.getOrderId()是没有返回/返回是空的
                         * 正式时【积分兑换】pru.getOrderId()是有值返回的
                         * 【预注册】正式时还没验证,暂保留原来逻辑不变
                         */
                        //预注册,兑换码
                        if (PRE_REGISTRATION.equals(type) || PROMO_CODE.equals(type)) {
                            if (TextUtils.isEmpty(pru.getOrderId())) {
                                
                            }
                        } else if (REDEEM.equals(type)) {
                            //积分兑换
                            
                        }

//请求贵方服务端进行确认
逻辑省略.....

//服务端确认完成,可发奖后,通知移动端,移动端再请求服务端发奖
//发奖完成后,由客户端通知Google,确定商品
			ConsumeParams params = ConsumeParams.newBuilder().setPurchaseToken(pur.getPurchaseToken()).build();
                mBillingClient.consumeAsync(params, new ConsumeResponseListener() {
                    @Override
                    public void onConsumeResponse(final BillingResult billingResult, final String outToken) {
                        
                    }
                });

预注册:
在这里插入图片描述

积分兑换:
在这里插入图片描述

至此,预注册和积分兑换就完成了!!

其它常见的问题还没有整理好,后续再完善更新!!感谢~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值