Google支付V3.0集成,使用Google play结算库结算,弃用developerPayload字段

Android 集成Google支付,目前有两种方式,一种是使用Google Play结算库,另一种是使用AIDL进行应用内购买结算。今天我们来说一下如何使用Google play结算库结算,另一种请见:Android集成Google支付,以及遇到的坑、坑

使用Google Play结算库比使用AIDL相对简单很多,但是Google废弃了一个关键字段developerPayload,下面会说到。

Google支付官方给的demo GitHub地址,可以去拉下来瞅瞅。https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive_v2

本文只介绍Google结算库的接入,至于Google后台的配置及BASE_64_ENCODED_PUBLIC_KEY获取,可以去Google后台看看。

一:官方API接入方式:Google支付官方API

我们先按Google API接一遍,先了解一下原理,后面有使用demo中的方法接入的Google支付,使用很简单,如果不想看官方API可以直接看第二步,代码是一样的,只不过封装的好一点,demo中把重连什么的都封装好了。

1.首先在app的build.gradle中添加依赖:

  //Google 支付
    compile 'com.android.billingclient:billing:1.2'

2.Connect to Google Play

在每次支付之前,都需要链接到Google Play:

1):调用 newBuilder() 方法创建BillingClient类的实例,也就是Play Billing Library client。还必须调用setListener()方法,并将引用传递给PurchasesUpdatedListener的实例,以便接收应用程序启动的购买以及谷歌Play Store启动的购买的更新。

2):建立与Google Play的连接。设置过程是异步的,你必须实现一个BillingClientStateListener,以便在客户端发出进一步请求后接收回调。

3):重写onBillingServiceDisconnected()回调方法,并实现自己的重试策略,以便在客户端丢失连接时处理到谷歌Play的连接丢失。如果要是与Google链接失败,或者在支付过程中断开了,可以在这个方法中重连。其实Demo中已经封装好了,使用很方便。

下面是代码:

// create new Person
private BillingClient billingClient;
...
billingClient = BillingClient.newBuilder(activity).setListener(this).build();
billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(@BillingResponse int billingResponseCode) {
        if (billingResponseCode == BillingResponse.OK) {
            // The billing client is ready. You can query purchases here.
        }
    }
    @Override
    public void onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
});

3.查询应用程序内的所有产品信息

去查询应用程序内的所有产品信息,也就是查一下你要购买的产品是否在Google后台配置了。我的产品ID是在应用内写死的,Google后台也配置过了,所以没管这个查询操作。

skuList中添加的是你的产品ID。SkuType是产品类型。SkuType.INAPP(用于一次性产品或奖励产品)或SkuType.SUBS(订阅)。

List skuList = new ArrayList<> ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
    new SkuDetailsResponseListener() {
        @Override
        public void onSkuDetailsResponse(int responseCode, List skuDetailsList) {
            // Process the result.
        }
    });

 下面是对查询结果的处理:如果请求成功,则响应代码为BillingResponse.OK。

if (responseCode == BillingResponse.OK
                    && skuDetailsList != null) {
   for (SkuDetails skuDetails : skuDetailsList) {
       String sku = skuDetails.getSku();
       String price = skuDetails.getPrice();
       if ("premium_upgrade".equals(sku)) {
           premiumUpgradePrice = price;
       } else if ("gas".equals(sku)) {
           gasPrice = price;
       }
   }
}

查询到的结果:

Google支付V3.0集成,使用Google play结算库结算,弃用developerPayload字段_第1张图片

4:开始购买应用内产品

(注:有些老手机可能不支持你的产品,比如订阅,你调用isFeatureSupported()方法检查设备是否支持要销售的产品。)

要从应用程序启动购买请求,请从UI线程调用launchBillingFlow()方法:

// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
        .setSkuDetails(skuDetails)
        .build();
int responseCode = billingClient.launchBillingFlow(flowParams);

当您调用launchBillingFlow()方法时,系统将显示Google 购买一次性产品截图:

Google支付V3.0集成,使用Google play结算库结算,弃用developerPayload字段_第2张图片

下面是购买订阅的截图:

Google支付V3.0集成,使用Google play结算库结算,弃用developerPayload字段_第3张图片

5:接受购买成功的回调:

购买完成之后可以拿到Google返回的订单信息,自己做一些处理。还要做消耗操作,如果不消耗,下次不能购买好像。

@Override
void onPurchasesUpdated(@BillingResponse int responseCode, List purchases) {
    if (responseCode == BillingResponse.OK
            && purchases != null) {
        for (Purchase purchase : purchases) {
            handlePurchase(purchase);
        }
    } else if (responseCode == BillingResponse.USER_CANCELED) {
        // Handle an error caused by a user cancelling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

 purchase中就是订单信息。可以从中获取到订单的ID和产品ID等,发给服务器验证。

二:使用官方的Demo

1:还是先导入依赖:

  //Google 支付
    compile 'com.android.billingclient:billing:1.2'

 2:把Demo中shared-module的billing文件夹中的代码拷到项目中:

         https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive_v2

Google支付V3.0集成,使用Google play结算库结算,弃用developerPayload字段_第4张图片

Google支付V3.0集成,使用Google play结算库结算,弃用developerPayload字段_第5张图片

3:在支付的Activity中:

首先Activity 要 implements BillingProvider

然后在Activity中添加一下方法:

 /**
     * Google支付内容**************************************
     */
 //Google支付
    private BillingManager mBillingManager;
    private BillingProvider mBillingProvider;

    @Override
    public BillingManager getBillingManager() {
        return mBillingManager;
    }

    @Override
    public boolean isPremiumPurchased() {
        return false;
    }

    /**
     * 购买事件
     * purchaseId:产品ID
     */
    public void googlePay(String purchaseId) {
        mBillingProvider.getBillingManager().initiatePurchaseFlow(purchaseId,"", BillingClient.SkuType.INAPP);
    }

    /**
     * Handler to billing updates
     */
    private class UpdateListener implements BillingManager.BillingUpdatesListener {
        @Override
        public void onBillingClientSetupFinished() {
            //通过商品ID,去查询Google后台是否有该ID的商品
            final List skuList = new ArrayList<>();
            skuList.add(purchaseId);
            mBillingProvider.getBillingManager().querySkuDetailsAsync(BillingClient.SkuType.INAPP, skuList, new SkuDetailsResponseListener() {
                @Override
                public void onSkuDetailsResponse(int responseCode, List skuDetailsList) {
                    if (responseCode != BillingClient.BillingResponse.OK) {
                        
                    } else if (skuDetailsList != null && skuDetailsList.size() > 0) {
                        for (SkuDetails details : skuDetailsList) {
                        
                            //获取到所查商品信息
                        }
                    } else {
                        //没有要消耗的产品
//                        Toast.makeText(PayActivity.this, "没有要查询的产品", Toast.LENGTH_LONG).show();
                    }
                }
            });


        }

        @Override
        public void onConsumeFinished(String token, @BillingClient.BillingResponse int result) {

//            Toast.makeText(PayActivity.this, "消耗完成", Toast.LENGTH_LONG).show();
            // Note: We know this is the SKU_GAS, because it's the only one we consume, so we don't
            // check if token corresponding to the expected sku was consumed.
            // If you have more than one sku, you probably need to validate that the token matches
            // the SKU you expect.
            // It could be done by maintaining a map (updating it every time you call consumeAsync)
            // of all tokens into SKUs which were scheduled to be consumed and then looking through
            // it here to check which SKU corresponds to a consumed token.
            if (result == BillingClient.BillingResponse.OK) {
                // Successfully consumed, so we apply the effects of the item in our
                // game world's logic, which in our case means filling the gas tank a bit
                //消耗成功
//                Toast.makeText(PayActivity.this, "消耗成功", Toast.LENGTH_LONG).show();
            } else {
//                Toast.makeText(PayActivity.this, "消耗失败", Toast.LENGTH_LONG).show();
            }
        }

        @Override
        public void onPurchasesUpdated(List purchaseList) {
            for (Purchase purchase : purchaseList) {
                //拿到订单信息,做自己的处理,发生到服务端验证订单信息,然后去消耗
                
                //购买成功,拿着令牌 去消耗
//                Toast.makeText(PayActivity.this, "购买成功:" + purchase.getPurchaseToken(), Toast.LENGTH_LONG).show();
                // We should consume the purchase and fill up the tank once it was consumed
                mBillingProvider.getBillingManager().consumeAsync(purchase.getPurchaseToken());
            }
        }
    }
    /**
     * Google支付内容**************************************
     */

要购买直接调用googlePay()方法,把产品ID传进去,就可以调起Google支付了。

注:Google的BASE_64_ENCODED_PUBLIC_KEY,是在BillingManager.java中添加的,BASE_64_ENCODED_PUBLIC_KEY的获取,是从Google后台获取的。

OK了,就这么多,代码不多。

三:下面我们来说说developerPayload吧

使用AIDL进行应用内购买结算,客户端可以传一个developerPayload作为额外参数传给Google,在用户支付成功之后的订单信息里,也会有这个字段,服务端可以根据这个额外参数来判断是哪个用户购买的,从而进行处理。但是使用Google play结算库结算,不能添加这个字段了,我们可以在Google依赖库中的com/android/billingclient/api/BillingClientImpl.java看到这个字段已经被弃用:第80行和第88行

 @Override
  public int launchBillingFlow(Activity activity, BillingFlowParams params) {
    if (!isReady()) {
      return broadcastFailureAndReturnBillingResponse(BillingResponse.SERVICE_DISCONNECTED);
    }

    @SkuType String skuType = params.getSkuType();
    String newSku = params.getSku();
    SkuDetails skuDetails = params.getSkuDetails();
    boolean rewardedSku = skuDetails != null && skuDetails.isRewarded();

    // Checking for mandatory params fields
    if (newSku == null) {
      BillingHelper.logWarn(TAG, "Please fix the input params. SKU can't be null.");
      return broadcastFailureAndReturnBillingResponse(BillingResponse.DEVELOPER_ERROR);
    }

    if (skuType == null) {
      BillingHelper.logWarn(TAG, "Please fix the input params. SkuType can't be null.");
      return broadcastFailureAndReturnBillingResponse(BillingResponse.DEVELOPER_ERROR);
    }

    // Checking for requested features support
    if (skuType.equals(SkuType.SUBS) && !mSubscriptionsSupported) {
      BillingHelper.logWarn(TAG, "Current client doesn't support subscriptions.");
      return broadcastFailureAndReturnBillingResponse(BillingResponse.FEATURE_NOT_SUPPORTED);
    }

    boolean isSubscriptionUpdate = (params.getOldSku() != null);

    if (isSubscriptionUpdate && !mSubscriptionUpdateSupported) {
      BillingHelper.logWarn(TAG, "Current client doesn't support subscriptions update.");
      return broadcastFailureAndReturnBillingResponse(BillingResponse.FEATURE_NOT_SUPPORTED);
    }

    if (params.hasExtraParams() && !mIABv6Supported) {
      BillingHelper.logWarn(TAG, "Current client doesn't support extra params for buy intent.");
      return broadcastFailureAndReturnBillingResponse(BillingResponse.FEATURE_NOT_SUPPORTED);
    }

    if (rewardedSku && !mIABv6Supported) {
      BillingHelper.logWarn(TAG, "Current client doesn't support extra params for buy intent.");
      return broadcastFailureAndReturnBillingResponse(BillingResponse.FEATURE_NOT_SUPPORTED);
    }

    try {
      BillingHelper.logVerbose(
          TAG, "Constructing buy intent for " + newSku + ", item type: " + skuType);

      Bundle buyIntentBundle;
      // If IAB v6 is supported, we always try to use buyIntentExtraParams and report the version
      if (mIABv6Supported) {
        Bundle extraParams = constructExtraParams(params);
        extraParams.putString(BillingHelper.LIBRARY_VERSION_KEY, LIBRARY_VERSION);
        if (rewardedSku) {
          extraParams.putString(BillingFlowParams.EXTRA_PARAM_KEY_RSKU, skuDetails.rewardToken());
          if (mChildDirected == ChildDirected.CHILD_DIRECTED
              || mChildDirected == ChildDirected.NOT_CHILD_DIRECTED) {
            extraParams.putInt(BillingFlowParams.EXTRA_PARAM_CHILD_DIRECTED, mChildDirected);
          }
        }
        int apiVersion = (params.getVrPurchaseFlow()) ? 7 : 6;
        buyIntentBundle =
            mService.getBuyIntentExtraParams(
                apiVersion,
                mApplicationContext.getPackageName(),
                newSku,
                skuType,
                null,
                extraParams);
      } else if (isSubscriptionUpdate) {
        // For subscriptions update we are calling corresponding service method
        buyIntentBundle =
            mService.getBuyIntentToReplaceSkus(
                /* apiVersion */ 5,
                mApplicationContext.getPackageName(),
                Arrays.asList(params.getOldSku()),
                newSku,
                SkuType.SUBS,
                /* developerPayload */ null);
      } else {
        buyIntentBundle =
            mService.getBuyIntent(
                /* apiVersion */ 3,
                mApplicationContext.getPackageName(),
                newSku,
                skuType,
                /* developerPayload */ null);
      }

      int responseCode = BillingHelper.getResponseCodeFromBundle(buyIntentBundle, TAG);
      if (responseCode != BillingResponse.OK) {
        BillingHelper.logWarn(TAG, "Unable to buy item, Error response code: " + responseCode);
        return broadcastFailureAndReturnBillingResponse(responseCode);
      }
      // Launching an invisible activity that will handle the purchase result
      Intent intent = new Intent(activity, ProxyBillingActivity.class);
      intent.putExtra(ProxyBillingActivity.KEY_RESULT_RECEIVER, onPurchaseFinishedReceiver);
      PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT_KEY);
      intent.putExtra(RESPONSE_BUY_INTENT_KEY, pendingIntent);
      // We need an activity reference here to avoid using FLAG_ACTIVITY_NEW_TASK.
      // But we don't want to keep a reference to it inside the field to avoid memory leaks.
      // Plus all the other methods need just a Context reference, so could be used from the
      // Service or Application.
      activity.startActivity(intent);
    } catch (RemoteException e) {
      String msg =
          "RemoteException while launching launching replace subscriptions flow: "
              + "; for sku: "
              + newSku
              + "; try to reconnect";
      BillingHelper.logWarn(TAG, msg);
      return broadcastFailureAndReturnBillingResponse(BillingResponse.SERVICE_DISCONNECTED);
    }

    return BillingResponse.OK;
  }

developerPayload已经写死为null了,很气人,不能用了。并且Google开发人员也明确说出以后也不会开通这个字段。请看https://issuetracker.google.com/issues/63381481

所以,在集成的时候,要特别注意这一点。 

Google支付V3.0集成,使用Google play结算库结算,弃用developerPayload字段_第6张图片

当然也有人自己开发了对Payload支持的Demo:https://github.com/DimaDake/billing

他就是把Google Play结算库中的代码全部拉出来,然后把developerPayload的重新添加上。这个demo的其他代码没细看,我是没使用这个demo,毕竟支付是和钱有关的,还是用Google Play的官方SDK比较好。

ok,集成好,就可以购买测试了。测试过程这里不细说了,可以看另一篇文章Android集成Google支付,以及遇到的坑、坑

你可能感兴趣的:(Android)