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

查询到的结果:

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 购买一次性产品截图:

下面是购买订阅的截图:

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

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

@Overridevoid 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

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

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

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

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

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

更多相关文章

  1. 轻松彻底清晰Android(安卓)Activity生命周期
  2. Android实现字幕滚动的方法
  3. android精确绘制文字位置的方法
  4. Android中TextView控件的singleLine废弃解决
  5. 【Android】WebView加载https页面不能正常显示资源问题
  6. Android(安卓)通用的下拉刷新,重温事件传递
  7. android基础知识12:android自动化测试06―Instrumentation 03 技
  8. Jetpack系列之 Paging 详解
  9. ubuntu下eclipse Android(安卓)ADT中SDK Manager中安装SDK失败的

随机推荐

  1. 当在调试模式下启动时,Android应用程序崩
  2. 如何将Maven添加到现有的IntelliJ Androi
  3. 超简单方式教你打造原生侧滑菜单
  4. Android NDK R9 安装配置 无需Cygwin
  5. Android 4.4源码下载与编译
  6. Android Studio错误“AppCompat不支持主
  7. 如何使用2个旋转圆圈获得与ICS相同的未定
  8. 有关WebView的一些使用方法
  9. 将不同的Android项目连接到现有的Google-
  10. Linux上杀死eclipse进程