相关文章推荐
坏坏的眼镜  ·  【Unity】2D ...·  4 周前    · 
道上混的椅子  ·  android ...·  1 年前    · 
  • 发布应用(正式版或者内测版)并创建好应用内商品。
  • 发布应用和创建商品步骤较多, 这里详细说下。 我们最终的目的是能够将应用发布到测试版或者是正式版。只有当要发布的应用的所有信息填写完毕,所有对勾都变绿以后。才能发布版本。填写没有顺序,比如填写 定价和分发范围 的时候,要先上传一个apk才能继续填写, 那就先在 应用版本 传一个apk再去继续填写,耐心点按照提示来就可以了。 创建商品在 应用内商品 里面添加。 内购型商品在 受管理商品 创建,订阅型产品在 订阅 里面创建。 所以信息填写好,就可以到 应用版本 ->管理->查看->发布到XX版来发布。

    3.2 集成谷歌结算库

    主要参考 官方文档 ,如果可以科学上网尽量多看官方文档,耐心看下去会少踩很多坑。 集成很简单, 直接依赖就好了:

    implementation 'com.android.billingclient:billing:2.0.3'
    复制代码

    3.3 使用

    整个支付过程在安卓客户端这里分为5步:

  • 支付结果回调
  • 整个过程走下来就是, 先与Google Play 建立连接, 通过商品id来查询本应用是否存在该商品,存在则去支付,如果支付成功了,就消费此次商品。 消费成功后客户端需要去和服务端校验此次支付。

    支付工具类封装如下:

    public class GooglePayUtils implements PurchasesUpdatedListener {
        String TAG = ".GooglePayUtils";
        private BillingClient mBillingClient = null;
        //是否已经建立连接
        private boolean isClientInit = false;
        //支付结果回调接口
        private MyListener mListener = null;
        private Context mContext = null;
        private String orderId = "";
        private String purchaseId = "";
        //购买类型:内购、 订阅,   默认为内购
        private String purchaseType = BillingClient.SkuType.INAPP;
        public GooglePayUtils(Context context, MyListener mListener, String orderId, String purchaseId, String purchaseType) {
            this.mContext = context;
            this.mListener = mListener;
            this.orderId = orderId;
            this.purchaseId = purchaseId;
            this.purchaseType = purchaseType;
        // 1. 建立连接
        public GooglePayUtils pay() {
            mBillingClient = BillingClient.newBuilder(mContext).enablePendingPurchases().setListener(this).build();
            mBillingClient.startConnection(new BillingClientStateListener() {
                @Override
                public void onBillingSetupFinished(BillingResult billingResult) {
                    Log.e(TAG, "onBillingSetupFinished code = " + billingResult.getResponseCode() + " ,  msg = " + billingResult.getDebugMessage());
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        isClientInit = true;
                        queryAndPayPurchases(purchaseId);
                @Override
                public void onBillingServiceDisconnected() {
                    isClientInit = false;
            return this;
        // 2. 查询商品信息
        private void queryAndPayPurchases(@NonNull final String purchaseId) {
            if (!isClientInit) {
                if (mListener != null) {
                    mListener.onError(mContext.getResources().getString(R.string.not_connect));
                return;
            List<String> skuList = new ArrayList<>();
            skuList.add(purchaseId);
    //        skuList.add("xxx");  // 这个参数不能为空,值随便传
            SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
            params.setSkusList(skuList).setType(purchaseType);
            mBillingClient.querySkuDetailsAsync(params.build(),
                    new SkuDetailsResponseListener() {
                        @Override
                        public void onSkuDetailsResponse(BillingResult billingResult,
                                                         List<SkuDetails> skuDetailsList) {
                            Log.e(TAG, "onSkuDetailsResponse code = " + billingResult.getResponseCode() + " ,  msg = " + billingResult.getDebugMessage() + " , skuDetailsList = " + skuDetailsList);
                            // Process the result.
                            if (billingResult.getResponseCode()!= BillingClient.BillingResponseCode.OK) {
                                onFail(mContext.getResources().getString(R.string.no_goods));
                                return;
                            if (skuDetailsList == null || skuDetailsList.isEmpty()) {
                                if (mListener != null) {
                                    mListener.onError(mContext.getResources().getString(R.string.no_goods));
                                return;
                            SkuDetails skuDetails = null;
                            for (SkuDetails details : skuDetailsList) {
                                Log.e(TAG, "onSkuDetailsResponse skuDetails = " + details.toString());
                                if (purchaseId.equals(details.getSku())) {
                                    skuDetails = details;
                            if (skuDetails != null) {
                                pay(skuDetails);
                            } else {
                                if (mListener != null) {
                                    mListener.onError(mContext.getResources().getString(R.string.no_goods));
        //3. 调起支付
        private void pay(SkuDetails skuDetails) {
            BillingFlowParams flowParams = BillingFlowParams.newBuilder()
                    .setSkuDetails(skuDetails)
                    .build();
            int code = mBillingClient.launchBillingFlow((Activity) mContext, flowParams).getResponseCode();
            if (BillingClient.BillingResponseCode.OK!=code) {
                onFail(mContext.getResources().getString(R.string.dump_pay_faile));
        //4. 支付回调
        @Override
        public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
            Log.e(TAG, "onPurchasesUpdated code = " + billingResult.getResponseCode() + " ,  msg = " + billingResult.getDebugMessage());
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
                for (Purchase purchase : purchases) {
                    handlePurchase(purchase);
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
                if (mListener != null) {
                    mListener.onError(mContext.getResources().getString(R.string.pay_cancle));
            } else {
                // Handle any other error codes.
                if (mListener != null) {
                    mListener.onError(mContext.getResources().getString(R.string.pay_faile));
        //5. 支付成功, 去消费此次支付, 支付成功后 不消费会自动退款。
        private void handlePurchase(Purchase purchase) {
    //        if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
    //        }
            if (!purchase.isAcknowledged()) {
                if(BillingClient.SkuType.INAPP.equals(purchaseType)){
                    ConsumeParams consumeParams =
                            ConsumeParams.newBuilder()
                                    .setPurchaseToken(purchase.getPurchaseToken())
                                    .setDeveloperPayload(orderId)
                                    .build();
                    mBillingClient.consumeAsync(consumeParams, new ConsumeResponseListener() {
                        @Override
                        public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
                            Log.e(TAG, "onConsumeResponse code = " + billingResult.getResponseCode() + " ,  msg = " + billingResult.getDebugMessage() + " , purchaseToken = " + purchase.getPurchaseToken());              // 消费成功  处理自己的流程,我选择先存入数据库
                            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                                if (mListener != null) {
                                    mListener.onSuccess(purchase.getPurchaseToken());
                            } else {
                                // 消费失败,后面查询消费记录后再次消费,否则,就只能等待退款
                                if (mListener != null) {
                                    mListener.onError(mContext.getResources().getString(R.string.pay_success_comfirm_faile));
                }else {
                AcknowledgePurchaseParams acknowledgePurchaseParams =
                        AcknowledgePurchaseParams.newBuilder()
                                .setPurchaseToken(purchase.getPurchaseToken()).setDeveloperPayload(orderId)
                                .build();
                mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener() {
                    @Override
                    public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
                        Log.e(TAG, "onConsumeResponse code = " + billingResult.getResponseCode() + " ,  msg = " + billingResult.getDebugMessage() + " , purchaseToken = " + purchase.getPurchaseToken());              // 消费成功  处理自己的流程,我选择先存入数据库
                        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                            if (mListener != null) {
                                mListener.onSuccess(purchase.getPurchaseToken());
                        } else {
                            // 消费失败,后面查询消费记录后再次消费,否则,就只能等待退款
                            if (mListener != null) {
                                mListener.onError(mContext.getResources().getString(R.string.pay_success_comfirm_faile));
            } else {
                if (mListener != null) {
                    mListener.onError(mContext.getResources().getString(R.string.pay_success_comfirm_faile));
        public interface MyListener {
            void onSuccess(String purchaseToken);
            void onError(String msg);
        private void onFail(String str){
            if (mListener != null) {
                mListener.onError(str);
    复制代码

    3.4 测试

    代码编写完了,要做的就是测试。 测试购买分两种, 一种是消耗性的也就是购买类型是 内购 。一种是非消耗性型,购买类型是 订阅 。 要测试内购,直接传结算库里面几个预设好拿来测试的商品id就行了,在 官方文档 中查看。如果是订阅, 则需要 应用发布到正式版 才能够测试。第一次发布后,审核的时间会比较久,3-4天左右。 页面上显示正在处理更新就是还在审核中。

  • 添加测试账号
  • 只有在控制台添加谷歌测试账号才能够用这个账号来测试支付。在这里添加测试账号: 控制台->开发者账号->账号详情->可用于测试的 Gmail帐号

    这些都准备好了, 就可以传商品id和商品类型到上面的支付工具类中测试支付了。

    测试过程中有哪些坑:

    没有科学上网, 导致查询不到商品。

    手机登录的不是刚刚添加的测试账号,导致调起支付框,但提示无法支付之类的信息。在手机->设置->账号/同步 -> Google账号 里查看。

    版本号verionCode 、应用标识applicationId、签名等要保持和控制台上传的一致。

    订阅后是自动续订的, 要通过谷歌发送的续订邮件中找到退订入口, 退订后才能继续测试订阅支付。 不然提示,系统正在处理你的订单,稍后再试。

  • 在Google Play 控制台创建应用、应用内商品。
  • 发布应用到内测版或者正式版(测试订阅需要)。
  • 参照官方文档集成结算库、封装好支付工具类。
  • 在Google Play 控制台添加测试账号,用这个账号来测试支付。
  • 5. 历史文章目录

  • juejin.cn/post/684490…
  • 分类:
    Android
    标签: