Android Google Play 支付SDK接入指南

本文详细介绍如何在Android应用中接入Google应用内支付服务,包括客户端SDK集成步骤、后端订单验证流程及常见问题解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前端

这里要接入的是google应用内支付
1.在app模块下的build.gradle模块下引入最新版本google billing 7.1.1版本,顺便引入google服务,如下

    implementation 'com.google.android.gms:play-services-wallet:19.0.0'
   implementation 'com.android.billingclient:billing:7.1.1'

2.在app模块下的AndroidManifest.xml加入谷歌商店应用内购买结算需要的权限

 <uses-permission android:name="com.android.vending.BILLING" />
 <uses-permission android:name="com.farsitel.bazaar.permission.PAY_THROUGH_BAZAAR" />

com.android.vending.BILLING权限在我们上传包到google后台的时候需要用到

流程

下面是一次性购买或订阅的典型购买流程。

1.向用户展示他们可以购买什么。
2.启动购买流程,以便用户接受购买交易。
3.在您的服务器上验证购买交易。
4.向用户提供内容。
5.确认内容已传送给用户。对于消耗型商品,用户要先消耗掉已购商品,才能再次购买。
6.订阅会自动续订,直到被取消。订阅可处于下面这几种状态:

有效:用户信誉良好,可享用订阅内容。
已取消:用户已取消订阅,但在到期前仍可享用订阅内容。
处于宽限期:用户遇到了付款问题,但仍可享用订阅内容,同时 Google 会重新尝试通过相应的付款方式扣款。
暂时保留:用户遇到了付款问题,不能再享用订阅内容,同时 Google 会重新尝试通过相应的付款方式扣款。
已暂停:用户暂停了其订阅,在恢复之前不能享用订阅内容。
已到期:用户已取消且不能再享用订阅内容。用户在订阅到期时会被视为流失。

前端核心代码

1.初始化BillingClient
注意:如果购买的是一次性商品 ,则需要调用enableOneTimeProducts启用一次性购买

 public BillingClient mBillingclient;
 
  PendingPurchasesParams var10001 = PendingPurchasesParams.newBuilder().enableOneTimeProducts().build();  
 mBillingclient=BillingClient.newBuilder(context).enablePendingPurchases(var10001).setListener(mPurchasesUpdatedListener).build();  

2.添加监听

 private PurchasesUpdatedListener mPurchasesUpdatedListener=new PurchasesUpdatedListener() {
        @Override
        public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> list) {
            String debugMessage = billingResult.getDebugMessage();
            if (list != null && list.size() > 0) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    for (Purchase purchase : list) {
                        if(purchase == null || purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) {

                            continue;
                        }

                        //通知服务器支付成功,服务端验证后,消费商品
                        VerifyProduct(purchase);
                        //TODO客户端同步回调支付成功
                       // acknowledged(purchase);  //非消耗性商品 确认交易
                        consumePurchase(purchase); //消费商品  消费商品后才能进行下一次购买
                    }
                }
            } else {
                switch (billingResult.getResponseCode()) {
                    case BillingClient.BillingResponseCode.NETWORK_ERROR:
                        break;
                    case BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED:
                        break;

                    case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED: //服务未连接
                        break;

                    case BillingClient.BillingResponseCode.USER_CANCELED: //取消
                        break;

                    case BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE: //服务不可用
                        break;

                    case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE: //购买不可用
                        break;

                    case BillingClient.BillingResponseCode.ITEM_UNAVAILABLE: //商品不存在
                        break;

                    case BillingClient.BillingResponseCode.DEVELOPER_ERROR: //提供给 API 的无效参数
                        break;

                    case BillingClient.BillingResponseCode.ERROR: //错误
                        break;

                    case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED: //未消耗掉
                        queryPurchases();
                        break;

                    case BillingClient.BillingResponseCode.ITEM_NOT_OWNED: //不可购买
                        break;

                }
            }
        }
    };

3.连接google服务 连接失败可以进行重新连接
productId:这是在google后台填的商品id

  public void pay() {
        if(mBillingclient == null || !mBillingclient.isReady()){
            //TODO客户端同步回调支付失败,原因是为链接到google或者google的支付服务不能使用
            mBillingclient.startConnection(new BillingClientStateListener() {  //重新连接
                @Override
                public void onBillingServiceDisconnected() {
                    //尝试重新启动连接的下一个请求
                    //谷歌通过调用startConnection()方法进行播放。
                    retryBillingServiceConnection(productId);
                }

                @Override
                public void onBillingSetupFinished(@NonNull BillingResult billingResult) {

                    if (billingResult.getResponseCode() ==  BillingClient.BillingResponseCode.OK) {
                        //BillingClient已经准备好。 你可以在这里查询购买情况。
                        querySkuDetailsAsync(productId);

                    }

                }
            });
            return;
        }
        //查询商品详情
        querySkuDetailsAsync(productId);
    }

    /**
     * 重试启动连接
     */
    void retryBillingServiceConnection(final String productId){
        if(retryCount>=3)return;
        try {
                retryCount++;
                mBillingclient.startConnection(new BillingClientStateListener() {
                    @Override
                    public void onBillingServiceDisconnected() {
                        retryBillingServiceConnection(productId);
                    }

                    @Override
                    public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
                           retryCount=0;
                        if (billingResult.getResponseCode() ==  BillingClient.BillingResponseCode.OK) {
                            //BillingClient已经准备好。 你可以在这里查询购买情况。
                            querySkuDetailsAsync(productId);

                        }

                    }
                });
        }catch (Exception e){

        }
    }

4.查询商品详情 商品列表里有购买的商品后在拉取支付页

  /**
     * 查询商品详情
     * @param productId
     */
    void querySkuDetailsAsync(final String productId){
        QueryProductDetailsParams queryProductDetailsParams =
                QueryProductDetailsParams.newBuilder()
                        .setProductList(
                                ImmutableList.of(
                                        QueryProductDetailsParams.Product.newBuilder()
                                                .setProductId(productId)
                                                .setProductType(BillingClient.ProductType.INAPP)
                                                .build()))
                        .build();
                        
        mBillingclient.queryProductDetailsAsync(queryProductDetailsParams, new ProductDetailsResponseListener() {
                    @Override
                    public void onProductDetailsResponse(@NonNull BillingResult billingResult, @NonNull List<ProductDetails> skuDetailsList) {
                                               
                        if (skuDetailsList != null && billingResult.getResponseCode() ==  BillingClient.BillingResponseCode.OK){
                            for(ProductDetails skuDetails : skuDetailsList){
                                if(productId.equals(skuDetails.getProductId())){

                                    //发起支付
                                    launchBillingFlow(skuDetails);
                                }
                            }
                        }
                    }
                });
    }

5.拉取支付页面
mch_order_no:透传参数,这里用的是我们服务端生成的订单号,可不传,查询google订单的时候,会从google返回回来

 /**
     * 拉取google支付页面
     * @param cpOrder 你自己的订单号或者用户id,用于关联到对应的用户,发放道具时使用
     * @param skuDetails
     */
    void launchBillingFlow(ProductDetails productDetails){
       ImmutableList<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
                ImmutableList.of(
                        BillingFlowParams.ProductDetailsParams.newBuilder()
                                // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
                                .setProductDetails(productDetails)
                                // For one-time products, "setOfferToken" method shouldn't be called.
                                // For subscriptions, to get an offer token, call
                                // ProductDetails.subscriptionOfferDetails() for a list of offers
                                // that are available to the user.
                                .build()
                );

        BillingResult billingResult= mBillingclient.launchBillingFlow(
                (Activity) context,
                BillingFlowParams
                        .newBuilder()
                        .setProductDetailsParamsList(productDetailsParamsList)
                        .setObfuscatedAccountId(mch_order_no)
                        .build()
        );
    }

6.消耗商品

 public void consumePurchase(final Purchase purchase){
        if(mBillingclient == null || purchase == null || purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED)              return;

        ConsumeParams consumeParams = ConsumeParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build();
        ConsumeResponseListener listener = new ConsumeResponseListener() {
            @Override
            public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ERROR) {
                    //消费失败将商品重新放入消费队列

                    return;
                }
                Log.i("TAG", "消费成功: ");
            }
        };
        mBillingclient.consumeAsync(consumeParams, listener);
    }

7.非消耗品 确认交易

  /**
     * 非消耗品 确认购买
     */
    public void acknowledged(Purchase purchase){

        if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
            if (!purchase.isAcknowledged()) {
                AcknowledgePurchaseParams acknowledgePurchaseParams =
                        AcknowledgePurchaseParams.newBuilder()
                                .setPurchaseToken(purchase.getPurchaseToken())
                                .build();
                mBillingclient.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener() {
                    @Override
                    public void onAcknowledgePurchaseResponse(@NonNull BillingResult billingResult) {
                        
                    }
                });
            }
        }
    }

以上大致就是客户端核心流程,接下来就是后端查询订单验证的过程了

服务端

1.Gooale Play Console 后台创建应用 传送门:https://play.google.com/console
2.Google Cloud Platform创建api项目 传送门:https://console.cloud.google.com/
3.Google Play Console后台的API权限那里进入并关联到这个api项目
4.Google Play Android Developer API启用
5.设置oauth同意屏幕
6.创建web应用的oauth2.0客户端ID
7.拉起授权页面,使用google开发者账号给项目授权,得到code
8.通过code,拿到refreshToken,这个token只有第一次才会返回需要永久储存(这个refreshtoken很重要,需要保存下来),如果弄丢,只有重新创建一个oauth客户端ID,然后重复步骤6,7,拿到新的refreshtoken
9.刷新refreshToken, 得到accessToken,通过accesstoken就可以去查询订单状态了,这里的accessToken一般只有5分钟左右,5分钟后需要重新用refreshToken换取新的accessToken

具体参考这篇博客:https://www.cnblogs.com/xiaogou/p/15568393.html

查询

1.获取code
地址:https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri={填写的重定向地址}&client_id={创建的clientId}
将上面的{XX}替换成创建api项目时填写的重定向地址,和clientId,然后将连接放到浏览器中打开,就会吊起授权界面,使用你的开发者账号授权登录
请求方式:浏览器中打开
请求完重定向地址上有两个参数code和scope,我们只需要code就行了
2.获取refreshToken
使用code换取refreshToken
地址:https://accounts.google.com/o/oauth2/token
请求方式:post
参数:grant_type=authorization_code
code=获取到的code(需要看看code中是否有%号,如果有需要urldecode)
client_id=创建api项目是的clientId(客户端ID)
client_secret=创建api项目时的clientSecret(客户端密钥)
redirect_uri=创建api项目时的重定向地址
3.查询订单状态
https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{token}?access_token={access_token}
packageName:app包名,必须是创建登录api项目时,创建android客户端Id使用包名
productId:对应购买商品的商品ID
token:购买成功后Purchase对象的getPurchaseToken()
access_token:上面咋们获取到的accessToken
请求方式:get

注意

1.接好sdk后打个app bundle上传到Gooale Play Console后台的内部测试,添加测试人员,发送邀请和测试链接,即可使用沙盒测试。
2.支付无法拉起请检查下面:

  • 连接的网络不能使用国内的,可以使用港澳台漫游网络,vpn有时候也不顶用
  • 使用的海外手机,并且安装有google服务套件(google play ,google sever)
  • 登录的google账号得是海外账号(港澳台的也行),必须海外注册的账号,不然不可用
  • 可以登录google支付商店查看付费项能否正常显示

常见报错(FAQ)

1.In-app Billing API version is less than 3
检查google play 是否最新,使用的账号是否是海外账号,连接网络不能使用国内网络,可以使用港澳台漫游
2.查询订单报错:The current user has insufficient permissions to perform the requested operation
这是因为上面获得refreshToken的账号没有被授予Sevvice Account权限,进入Google Cloud Platform创建服务账号,并授予权限,用获得refreshToken的账号添加角色,并授予Sevvice Account权限即可查询订单
3.An internal error occurred
可能是网络问题,检查网络是否能上网,然后看其他付费应用能否拉取google支付

官方文档:https://developer.android.google.cn/google/play/billing/integrate

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值