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 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支付,以及遇到的坑、坑
更多相关文章
- 轻松彻底清晰Android(安卓)Activity生命周期
- Android实现字幕滚动的方法
- android精确绘制文字位置的方法
- Android中TextView控件的singleLine废弃解决
- 【Android】WebView加载https页面不能正常显示资源问题
- Android(安卓)通用的下拉刷新,重温事件传递
- android基础知识12:android自动化测试06―Instrumentation 03 技
- Jetpack系列之 Paging 详解
- ubuntu下eclipse Android(安卓)ADT中SDK Manager中安装SDK失败的