草庐IT

android--GooglePay 谷歌支付内购接入(1)

怀化纱厂球迷 2024-03-08 原文

android--GooglePay 谷歌支付内购接入(1)

由于谷歌应用市场的限制令,需要把目前的APP接入googlepay 谷歌支付,以免在3月31日底,没接入的APP将会有被下架掉的风险,

整个接入流程,以及注意事项,以及踩坑记录,我都会写出来,希望对大家有所帮助,文章大概拆成2篇,来全方位记录跟概况

在这我先提前说这么几个名词

1.消耗

消耗是什么意思:消耗就相当于是订单确认,如果没有确认google会在3天后自动退款,同时这一笔物品就重新买不了

2.重试

 重试是什么意思:就是重新去做处理,去查询谷歌这边,如果谷歌这边真的扣款成功,在去服务器这边查看是否成功)

3.依赖包版本选择

implementation 'com.android.billingclient:billing:3.0.0'

implementation 'com.android.billingclient:billing:4.0.0'

implementation 'com.google.android.gms:play-services-wallet:19.1.0'

这3个包都是谷歌支付有关的,3.0 跟4.0 最大区别是3.0不能重复购买,只能一个个的购买,4.0版本可以购买多件(根据产品的需求 不需要一次性购买多件,我这次选择3.0)

至于gms:play-services-wallet 没找到太多网上帖子,我就放弃了

一.接入前准备

##   1.申请一个google play开发者账号,这里我是有google play开发账号的,毕竟我们的APP是发谷歌市场的
##   2.提前准备好一个apk(不需要集成支付sdk,占位用),在google play控制台上传你的apk,这里你可以发封闭测试里面去,下面我会上图,不懂的看图
##   3.发布一个alpha或者beta的版本,发布之前需要点亮以下选项(提交商品详情内容)(确定内容分级)(选择发布范围)等,之后才能正常发布
##   4.添加测试人员,等应用审核通过之后,会得到一个地址,把地址发给对方,让对方点击同意加入测试即可
##   5.需要创建应用内商品(商品id,商品描述,定价),按提示填就可以了
##   6.在账户详细信息里面,添加许可测试的邮箱账号,许可测试响应改为 “RESPOND_NORMALLY/LICENSED”,点击保存,需要一两分钟生效,记得弄这一步,这个很坑,你不弄,你测试人员就一直不会出现测试卡测试的模式
##   7.检查你的包名和签名文件是否和Google Console 上面上传的apk包是否一致
##   8.检查版本号是否和Google console发布的apk版本是否一致
##   9.检查你是否可以购买,是否绑定了银行卡,手机支不支持Google支付,手机是否有Google服务
##   10.由于我是台湾上线APP,想测台币支付,我还得准备一个vpn,能选择线路台湾的

google play 后台配置:

1.设定定价,就是商品的定价:

按图所示建立价格, 我这里有4个价格。具体建立很简单

建立产品:一个产品对应一个定价,比如我这里580台币对应406点。创建完后,如果没问题,一定要启用,不然app那边取不到数据,另外产品ID就是唯一,后面用在代码里取数据用的。关于产品id的设置,谷歌API中有这么一说,建议是按他要求的来,比较好

 

2.在账户详细信息里面,添加许可测试的邮箱账号,许可测试响应改为 “RESPOND_NORMALLY/LICENSED”,点击保存,需要一两分钟生效,记得弄这一步,这个很坑,你不弄,你测试人员就一直不会出现测试卡测试的模式 

三.关于发一个占位包,跟设置测试人员

 成功发布后,应用市场会出现

二.整体流程

看完流程图后,我们可以简单的总结下步骤,这里业务部分我就不细说了,这东西你得根据自己业务来调整

1.进入商品选择列表界面,选择需要购买的物品
2.根据选择的物品id,创建订单
3.初始化google支付,如果google已经连接,查询这个商品ID得到商品详情;如果没连接googlepay,调用连接
4.购买操作
5.onPurchasesUpdated通过购买回调,判断是否购买成功
6。如果购买成功,拿到Google支付返回的相关信息,在服务器进行验证操作。
7.如果服务器拿到你上传的相关信息和Google支付进行交互验证,验证谷歌扣款成功,服务器收款成功,说明支付成功,成功后,要做一次消耗操作,(消耗是什么意思:消耗就相当于是订单确认,如果没有确认google会在3天后自动退款,同时这一笔物品就重新买不了

8.如果服务器拿到你上传的相关信息和Google支付进行交互验证,验证谷歌扣款成功,服务器未收款到账,说明有问题,需要做重试操作,(重试是什么意思:就是重新去做处理,去查询谷歌这边,如果谷歌这边真的扣款成功,在去服务器这边查看是否成功)

注:这里第8步,我在这2个环境下,都做了对应的操作,增加了程序的友好体验,
第1:如果用户支付完成,就没管了,每次打开APP首页,我都会去帮他们做重试操作。这种用户是无感的,体验比较友好
第2: 如果用户支付完成,退出当前页面,然后再进来商品选择页面,点击同样的商品,这个时候由于还没成功,也没做消耗处理,是会有提示框弹窗,已拥有该商品,这个时候,我是会做重试的,如果成功,他下次在点击,就可以重新购买了

三.代码接入

## 目前已经升级到V3、V4版本,AIDL的方法已经过时了,并且未来的版本会将之移除,推荐使用使用 Google Play 结算库

1.添加依赖 跟 权限

implementation 'com.android.billingclient:billing:3.0.0' 

 <!--谷歌商店应用内购买结算需要的权限-->
    <uses-permission android:name="com.android.vending.BILLING" />
    <uses-permission android:name="com.farsitel.bazaar.permission.PAY_THROUGH_BAZAAR" />

2.部分代码

2.1创建连接

//连接到GooglePay
    private void connectGooglePay(String skuId) {
        //请求连接到GooglePay
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
                int code = billingResult.getResponseCode();
                if (code != BillingClient.BillingResponseCode.OK) {
                    String msg = billingResult.getDebugMessage();
                    Log.e(TAG, "连接到GooglePay失败    code = " + code + "    msg = " + msg);
                    onFail();
                    return;
                }
                Log.e(TAG, "连接到GooglePay成功");
                checkSku(skuId);
            }

            //连接失败
            @Override
            public void onBillingServiceDisconnected() {
                Log.e(TAG, "连接到GooglePay失败,请重试");
            }
        });
    }

 

2.2查询

//查询商品
    private void checkSku(String id) {
        List<String> skuList = new ArrayList<>();
        skuList.add(id);
        SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder()
                .setSkusList(skuList)
                .setType(BillingClient.SkuType.INAPP);
        billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
            @Override
            public void onSkuDetailsResponse(@NonNull BillingResult billingResult, @Nullable List<SkuDetails> list) {
                int code = billingResult.getResponseCode();
                if (code != BillingClient.BillingResponseCode.OK || list == null || list.isEmpty()) {
                    String msg = billingResult.getDebugMessage();
                    Log.e(TAG, "查询商品失败    code = " + code + "    msg = " + msg);
                    onFail();
                    return;
                }
                Log.e(TAG, "查询商品成功");
                buyIt(list.get(0));
            }
        });
    }

 

2.3购买

 //购买
    private void buyIt(SkuDetails skuDetails) {
        BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
                .setSkuDetails(skuDetails)
                .build();
        BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
        int code = billingResult.getResponseCode();
        if (code != BillingClient.BillingResponseCode.OK) {
            String msg = billingResult.getDebugMessage();
            Log.e(TAG, "购买商品失败    code = " + code + "    msg = " + msg);
            onFail();
            return;
        }
        Log.e(TAG, "购买商品" + skuDetails.toString());
    }

 

2.4购买后的回调

@Override
    public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> list) {
        int code = billingResult.getResponseCode();
        String msg = billingResult.getDebugMessage();
        Log.e(TAG, "onPurchasesUpdated:code = " + code + "    msg = " + msg);
        if (list != null) {
            for (Purchase purchase : list) {
                Log.e(TAG, "onPurchasesUpdated:" + purchase.toString());
            }
        }
        if (code == BillingClient.BillingResponseCode.OK && list != null) {
            Log.e(TAG, "支付成功");
            onSuccess(list);
        } else if (code == BillingClient.BillingResponseCode.USER_CANCELED) {
            Log.e(TAG, "支付取消");
            onFail();
        } else {
            Log.e(TAG, "支付失败:code = " + code + "    msg = " + msg);
            onFail();
        }
    }

 

2.5消耗

 //消耗商品    
    private void consume(List<Purchase> list) {
        if (list == null || list.isEmpty() || billingClient == null) {
            return;
        }
        for (Purchase purchase : list) {
            billingClient.consumeAsync(ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build(), new ConsumeResponseListener() {
                @Override
                public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
                    Log.e(TAG, "onConsumeResponse    code = " + billingResult.getResponseCode() + " ,  msg = " + billingResult.getDebugMessage() + " , purchaseToken = " + purchaseToken);
                }
            });
        }
    }

 

2.6 成功跟失败的  

 private void onFail() {
        //自己写关于支付失败后的操作
    }

    private void onSuccess(List<Purchase> list) {
        //自己写关于支付成功后的操作
    }

 

重试操作这块

这里有2种场景,要注意

1.谷歌支付成功了,但是后端没成功

 2.谷歌支付成功,后端成功,但是没来得及消耗,可能是断网了,重新消耗

/**
 * 查询最近的购买交易
 *  
 */
    private void queryHistory() {
        Purchase.PurchasesResult mResult = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
        if (mResult != null) {
            List<Purchase> list = new ArrayList();
            if(mResult.getPurchasesList()!=null ) {
                if (mResult.getPurchasesList().size() > 0) {
                    for (int i = 0; i < mResult.getPurchasesList().size(); i++) {
                        if (mResult.getPurchasesList().size() > 0)
                            if (mResult.getPurchasesList().get(i).isAcknowledged()) {

                                list.add(mResult.getPurchasesList().get(i));
                                consume(list);
                            } else {

                                if (null != json) {
                                   
                                }

                            }


                    }
                }
            }
        }

    }

四.坑

1.GooglePay默认只能购买一次,如果你需要重复购买一个商品 请调用consume方法

有关android--GooglePay 谷歌支付内购接入(1)的更多相关文章

  1. unity---接入Admob - 2

    目录1.AdmobSDK下载地址2.将下载好的unityPackagesdk导入到unity里​编辑 3.解析依赖到项目中

  2. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  3. ruby-on-rails - 与 ActiveMerchant 一起使用的最佳支付网关是什么? - 2

    我需要使用ActiveMerchant库在我们的一个Rails应用程序中设置支付解决方案。尽管这个问题非常主观,但人们对主要网关(BrainTree、Authorize.net等)的体验如何?它必须:处理定期付款。有能力记入个人帐户。能够取消付款。有办法存储用户的付款详细信息(例如Authotize.netsCIM)。干杯 最佳答案 ActiveMerchant很棒,但在过去一年左右的时间里,我在使用它时发现了一些问题。首先,虽然某些网关可能会得到“支持”——但并非所有功能都包含在内。查看功能矩阵以确保完全支持您选择的网关-http

  4. ruby-on-rails - 使用 gmaps4rails 动态加载谷歌地图标记 - 2

    如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail

  5. ruby - 从谷歌开发者网站下载后,client_secret.json 为空 - 2

    我正在尝试从googleAPI下载client_secret.json。我正在执行https://developers.google.com/gmail/api/quickstart/ruby中列出的步骤.使用此向导在GoogleDevelopersConsole中创建或选择项目并自动启用API。在左侧边栏中,选择同意屏幕。选择电子邮件地址并输入产品名称(如果尚未设置),然后单击“保存”按钮。在左侧边栏中,选择凭据并点击创建新客户端ID。选择应用程序类型已安装应用程序,已安装应用程序类型为其他,然后单击“创建客户端ID”按钮。点击新客户端ID下的下载JSON按钮。将此文件移动到您的工作

  6. Spring Boot中的微信支付(小程序) - 2

    前言微信支付是企业级项目中经常使用到的功能,作为后端开发人员,完整地掌握该技术是十分有必要的。一、申请流程和步骤图1-1注册微信支付账号获取微信小程序APPID获取微信商家的商户ID获取微信商家的API私钥配置微信支付回调地址绑定微信小程序和微信支付的关系搭建SpringBoot工程编写后台支付接口发布部署接口服务项目使用微信小程序或者UniAPP调用微信支付功能支付接口的封装配置jwt或者openid的token派发原生微信小程序完成支付对接二、注册商家2.1商户平台商家或者企业想要通过微信支付来进行商品的销售,必须先通过微信平台(pay.weixin.qq.com)去将商家进行注册。注册成

  7. 用于下载私有(private)谷歌文档的 Ruby 脚本 - 2

    我想用Ruby编写脚本(使用gdatagem、rest-clientgem或直接使用Net::HTTP)使用gmail-userid/password对我的google文档进行身份验证,然后下载私有(private)列表文件和文件。GDatadocuments指南清楚地说明了如何获取公开可见的文档,但不清楚我如何在我的脚本中验证自己的身份以访问私有(private)文档。authenticationmethodstheyspecify所有这些似乎都需要人工干预,要么使用验证码,要么使用某种形式的OAuth/OpenID重定向。有什么方法可以只使用用户名/密码组合来访问我的私有(priv

  8. ruby - 获取谷歌搜索结果的正确方法是什么? - 2

    我想在google上获取特定关键字搜索的所有搜索结果。我已经看到了抓取的建议,但这似乎是个坏主意。我见过Gems(我计划使用ruby​​)进行抓取并使用API。我还看到了使用API的建议。有谁知道现在最好的方法吗?API不再受支持,我看到有人报告说他们取回了无法使用的数据。Gems是否有助于解决这个问题?提前致谢。 最佳答案 我也选择了抓取选项,它比向谷歌询问key和加号更快,而且您每天的搜索查询不限于100次。正如理查德指出的那样,谷歌的服务条款是一个问题。这是我做过的一个对我有用的例子——如果你想通过代理连接,它也很有用:req

  9. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  10. STM32+ESP8266接入云(3) - 2

    解析数据     进入阿里云的IOTStdio,点击新建项目。         新建项目后点击新建Web应用。名称     应用名称随便填写              创建完成后我们进入应用。    在左侧组件处拖入一个指示灯和一个开关。     点击指示灯组件,点击配置数据源     选择我们的产品、数据、和属性。     我们还可以配置开和关的显示颜色。    点击按钮,配置交互动作。     选择设备和属性,设置值位置点击数据来源,选择组件值     配置完成后进入预览,点击按钮,在esp8266就会收到来自平台的json格式的数据,MCU端需要做的就是解析来自平台的数据,进而达到控制下

随机推荐