谷歌内购接入指南

目前谷歌支付相关的文章并不多, 并且也大都写的很杂, 在接入过程中难免踩了一些坑, 这里做个总结, 把内购相关配置到支付接入的整个流程都写上来, 方便需要接入内购的开发者少踩一些坑, 闲话就不多说了, 开始.

在准备配置应用及内购信息之前, 先需要创建谷歌开发者账号, 并绑定海外银行卡(visa卡)等. 这里不再赘述. 梯子自备

1. 创建应用

点击创建应用,填写名称和描述来创建一个新的应用项目,如下:

应用创建

一般来说在完成开发者账号注册后,会存在一个未完善信息的应用. 可以直接跳过该步骤, 若担心信息填写错误的话,可以先创建一个测试应用来熟悉各项信息的填写(创建的应用在发布出去之前都是可以直接删掉的,不用担心无法删除,占用名称包名等等例如某宝)

2. 应用资料完善

应用基本资料在商品详情选项中填写(谷歌把应用也当做一种商品), 内购信息在另外的选项中,后面会说到. 带*号的项目都是必填项, 按照应用的基本信息一一填写就行.需要注意的是内容分级隐私权政策 这两项.

内容分级和隐私政策地址

内容分级需要先上传apk, 然后需要填写一个以上的针对所创建的应用的调查问卷, 谷歌会根据这个问卷来对你的应用初步分级. 实际定级由用户反馈及谷歌官方审核决定. 可以先保存, 最后再来完成这个问卷

隐私权政策这里虽然可以暂时不填写,但是后面创建内购,及发布版本的时候会无法通过. 这个地址填用户注册协议那样的网址页面就行, 可以参见这个 示例 ,是不是很眼熟很官方.这个地址有内容就行了.直接拷过去改改也是可以的.

3. 应用版本

在应用版本选项中管理和上传我们的应用. 开发阶段, 上传到Beta或者Alpha版本下就可以了.方便调试, 建议直接添加Beta版本.

应用版本

新建Beta版本 在新建页面中上传Apk文件并填写版本名称,及版本说明. 这里要注意上传的Apk需要加上内购权限<uses-permission android:name="com.android.vending.BILLING" /> 否则后面无法添加内购商品信息, 需要加上权限后更新版本信息.更新版本信息注意更改versionCode.

新建Beta版本

4. 添加测试人员

上一步添加完应用版本之后是无法发布的, 需要先添加测试人员, 测试人员的添加分为两步. 添加测试许可账号配置测试人员列表

添加测试许可账号

在 设置–>开发者账号–>账号详情项页面最下面,添加可用于测试的Gmail账号:

添加测试许可账号

账号添加成功后一般会收到一封测试邀请的邮件, 没收到也没关系, 后面可以主动加入进来.

配置测试人员列表

进入应用版本管理页面, 选择测试方法, 开发阶段选封闭式测试就好了, 创建测试用户列表并添加测试账号,保存

配置测试人员列表

在应用发布之后会生成加入测试的网址, 上面步骤中添加的测试用户可以通过访问这个地址来加入该应用的测试里面来. 网址一般如下(后面是你应用的包名): https://play.google.com/apps/testing/com.quanwe.googlepaydemo

5. 应用内容分级

进入内容分级选项, 填写参与内容分级调查问卷的邮箱, 选择应用类别开始进行问卷调查.根据所选分类不同, 问卷题目可能有所不同:

应用内容分级

保存问卷后, 判断分级, 确定分级无误后确定提交即可

6. 定价和分发范围

根据应用情况,选择是否付费, 这里说明下,这里的付费是指用户下载安装你的应用是否收费, 否则即使你有付费的内购产品也属于是免费应用 , 选择发布地区(一般全部选择就可以了) , 勾选必填项保存即可.

定价和分发范围

上述信息填写完成后, 会提示可以发布, 并且左边菜单选项不再有警告标示(若还是存在警告标示,进入对应的选项页面完善信息即可)

可发布状态

7. 发布Beta版本

上述步骤都完成后, 就可以进入版本管理里面发布版本了.

发布Beta版本

8. 添加应用内商品

在菜单项中选择应用内商品-> 创建受管理的商品

这里写图片描述

填写商品相关信息时, 注意商品id是唯一的,并且只能由数字字母和-组成. 同时别忘记把状态标签设置为有效, 商品定价上,谷歌是有限制的只能在0.99-400 美刀, 超过400美刀的商品是无法添加的.

这里写图片描述

上述步骤完成后, 内购所需的相关配置就已经完成了.(后台验证付款情况可能还需要配置API权限)

1. 加密串获取

从项目菜单项 开发者工具 -> 服务和API 找到许可密钥, 这个就是用于支付的加密串

这里写图片描述

2. 支付库配置

复制内购支付库文件到项目中, 这里有我提取好的 “谷歌内购库文件” 可以直接使用,解压覆盖到src目录即可

3. 编写支付逻辑 以下是具体步骤和逻辑流程, 不想看的话可以直接查看谷歌内购Demo示例 代码片段(界面和点击事件就自己加吧)

  • 定义内购商品, 订单信息,加密串等字段:
    IabHelper mHelper;
    /**
     * 谷歌内购- 加密串, 填写你自己谷歌上的RSA加密许可
     */
    private String base64EncodedPublicKey = "";
    /**
     * 内购产品唯一id, 填写你自己添加的内购商品id
     */
    private String SKU = "";//
    /**
     * 支付附加项- 这里放的是订单号
     */
    private String mDeveloperPayload = "201803131027009010";
     /**
     * 谷歌服务是否已正常安装
     */
    private boolean isGooglePlaySetup = false;
  • 初始化内购工具:
  //创建谷歌支付帮助类
        mHelper = new IabHelper(this, base64EncodedPublicKey);
        mHelper.enableDebugLogging(true);
        /**
         * 初始化和连接谷歌服务
         */
        mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
            @Override
            public void onIabSetupFinished(IabResult result) {
                if (!result.isSuccess()) {
                    Log.e("PayMethodActivity", "Problem setting up In-app Billing: " + result);
                    return;
                }
                /**
                 * 初始化成功,记录下
                 */
                isGooglePlaySetup = true;
                if (mHelper == null) {
                    return;
                }
            }
        });
  • 产品购买:
  /**
     * 产品购买
     */
    private void buyProduct() {

        try {
            mHelper.launchPurchaseFlow(this, SKU, REQUEST_CODE, new IabHelper.OnIabPurchaseFinishedListener() {
                @Override
                public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
                    if (result.isFailure()) {
                        Log.e("PayMethodActivity", "Error purchasing: " + result);
                        Toast.makeText(DemoPayMethodActivity.this, "支付失败", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    Log.d(TAG, "Purchase successful.");
                    if (purchase.getSku().equals(SKU)) {
                        // bought 1/4 tank of gas. So consume it.
                        Log.d(TAG, "Purchase is gas. Starting gas consumption.");
                        //購買成功,調用消耗產品
                        consumeProduct(purchase, false, "支付成功", "支付失败");
                    }
                }
            }, mDeveloperPayload);
        } catch (IabHelper.IabAsyncInProgressException e) {
            e.printStackTrace();
            Toast.makeText(DemoPayMethodActivity.this, "支付失败", Toast.LENGTH_SHORT).show();
        }
    }
  • 通知消费掉商品:
 /**
     * 消费掉已购买商品
     *
     * @param purchase
     * @param needNext
     * @param tipmsg1
     * @param tipmsg2
     */
    private void consumeProduct(Purchase purchase, final boolean needNext, final String tipmsg1, final String tipmsg2) {
        try {
            mHelper.consumeAsync(purchase, new IabHelper.OnConsumeFinishedListener() {
                @Override
                public void onConsumeFinished(Purchase purchase, IabResult result) {
                    if (mHelper == null) {
                        return;
                    }
                    if (result.isSuccess()) {
                        Log.e("PayMethodActivity", "Problem setting up In-app Billing: " + result);
                        if (!needNext) {
                            //处理内购中断的情况, 仅仅只是消费掉上一次未正常完成的商品
                            Toast.makeText(DemoPayMethodActivity.this, tipmsg1, Toast.LENGTH_SHORT).show();
                            return;
                        }
                        try {
                            //向服务器提交内购验证
                            //UIUtils.showLoadDialog(that, "验证支付结果");
//                            BaseQuestStart.IosneigouIndexGooglePayUrl_(that, purchase.getOriginalJson(), purchase.getSignature());
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }

                    } else {
                        Toast.makeText(DemoPayMethodActivity.this, tipmsg2, Toast.LENGTH_SHORT).show();
                    }
                }
            });
        } catch (IabHelper.IabAsyncInProgressException e) {
            e.printStackTrace();
            Toast.makeText(DemoPayMethodActivity.this, tipmsg2, Toast.LENGTH_SHORT).show();
        }
    }
  • 接收回调以及资源释放
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (mHelper == null) return;
        /**
         * 将回调交给帮助类来处理, 否则会出现支付正在进行的错误
         */
        mHelper.handleActivityResult(requestCode, resultCode, data);
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        /**
         * 释放掉资源
         */
        if (mHelper != null) {
            try {
                mHelper.dispose();
            } catch (IabHelper.IabAsyncInProgressException e) {
                e.printStackTrace();
            }
        }
        mHelper = null;
    }