Android Google Play 支付SDK接入指南

10 篇文章 0 订阅
6 篇文章 0 订阅

前端

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

    implementation 'com.google.android.gms:play-services-wallet:19.0.0'
    implementation 'com.android.support:appcompat-v7:24.1.1'
    implementation 'com.android.billingclient:billing:4.0.0'

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.连接到google支付服务,如果不能连接到,请检查以下:

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

2.查询上次未消费的商品,如果有未消费的商品通知服务器,然后消费掉。因为国外的支付环境和国内不一样,他们可以线上下单,然后到便利店去支付,所以有未消费的这种情况。
3.使用google play console后台配置的商品id发起支付
4.支付完成通知后端查询结果,验证完后消费商品

前端核心代码

1.初始化BillingClient,并注册商品变动监听 商品状态发生变化都会走这个监听

 public BillingClient mBillingclient;
 mBillingclient=BillingClient.newBuilder(context).setListener(mPurchasesUpdatedListener).enablePendingPurchases().build();
 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;

                        //通知服务器支付成功,服务端验证后,消费商品
                        //TODO客户端同步回调支付成功
                    }
                }
            } else {
                switch (billingResult.getResponseCode()) {
                    case BillingClient.BillingResponseCode.SERVICE_TIMEOUT: //服务连接超时
                        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;

                }
            }
        }
    };

2.支付操作,支付前查询商品详情
cpOrder:这里用的是我们服务端生成的订单号,可不传
productId:这是在google后台填的商品id

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

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

3.查询商品详情

 void querySkuDetailsAsync(final String cpOrder, final String productId){     
        List<String> skuList = new ArrayList<>();
        skuList.add(productId);
        SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
        params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
        mBillingclient.querySkuDetailsAsync(params.build(),
                new SkuDetailsResponseListener() {
                    @Override
                    public void onSkuDetailsResponse(BillingResult billingResult,
                                                     List<SkuDetails> skuDetailsList) {                
                        if (skuDetailsList != null && billingResult.getResponseCode() ==  BillingClient.BillingResponseCode.OK){
                            for(SkuDetails skuDetails : skuDetailsList){
                                if(productId.equals(skuDetails.getSku())){
                                    //发起支付
                                    launchBillingFlow(cpOrder, skuDetails);
                                }
                            }
                        }
                    }
                });
    }

4.发起支付

 /**
     * 拉取google支付页面
     * @param cpOrder 你自己的订单号或者用户id,用于关联到对应的用户,发放道具时使用
     * @param skuDetails
     */
    void launchBillingFlow(String cpOrder, SkuDetails skuDetails){
        mBillingclient.launchBillingFlow(
                (Activity) context,
                BillingFlowParams
                        .newBuilder()
                        .setSkuDetails(skuDetails)
                        .setObfuscatedAccountId(cpOrder)//这里本来的意思存放用户信息,类似于国内的透传参数,我这里传的我们的订单号。老版本使用DeveloperPayload字段,最新版本中这个字段已不可用了
                        .build()
        );
    }

5.消耗商品

 public void consumePurchase(final Purchase purchase){
        if(mBillingclient == null || purchase == null || purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) return;
        Log.i("Tag","消耗商品:商品id:" + purchase.getSkus() + "商品OrderId:" + purchase.getOrderId() + "token:" + purchase.getPurchaseToken());

        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);
    }

6.补单操作

  /**
     * 补单操作 查询已支付的商品,并通知服务器后消费(google的支付里面,没有消费的商品,不能再次购买)
     */
    private void queryPurchases(){
        PurchasesResponseListener mPurchasesResponseListener = new PurchasesResponseListener() {

            @Override
            public void onQueryPurchasesResponse(@NonNull BillingResult billingResult, @NonNull List<Purchase> purchasesResult) {
                if(billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK || purchasesResult == null) return;
                for (Purchase purchase : purchasesResult) {

                    if(purchase!=null){
                        consumePurchase(purchase);
                    }
                    if(purchase == null || purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) continue;

                    //这里处理已经支付过的订单,通知服务器去验证  并消耗商品
                }
            }
        };
        mBillingclient.queryPurchasesAsync(BillingClient.SkuType.INAPP, mPurchasesResponseListener);
    }

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

服务端

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

  • 34
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值