公司项目是在谷歌应用商店上线发布的,最近产品经理说要给项目加个订阅的功能,按月订阅免广告的形式,对于我来说也是第一次接入谷歌应用商店的订阅,支付功能,是照着谷歌的官方文档集成边测试才做出的,下面分享下自己集成的过程和经验。
直接写关键代码在这里:

第一步:在 AndroidManifest.xml 文件中添加以下权限:
<!--谷歌商店应用内购买结算需要的权限--> <uses-permission
android:name="com.android.vending.BILLING" />
第二步:在项目src/main下New Directory命名为aidl,然后在这个目录下 New Package
命名为com.android.vending.billing,然后复制 IInAppBillingService.aidl 文件,到我们刚创建的
com.android.vending.billing 软件包中。
这里不得不吐槽下谷歌Play支付的官方中文文档是个渣
https://developer.android.com/google/play/billing/billing_integrate.html?hl=zh-cn,如果照着它的中文文档来做的话你会遇到这个问题:集成Google

<https://developer.android.com/google/play/billing/billing_integrate.html?hl=zh-cn%EF%BC%8C%E5%A6%82%E6%9E%9C%E7%85%A7%E7%9D%80%E5%AE%83%E7%9A%84%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3%E6%9D%A5%E5%81%9A%E7%9A%84%E8%AF%9D%E4%BD%A0%E4%BC%9A%E9%81%87%E5%88%B0%E8%BF%99%E4%B8%AA%E9%97%AE%E9%A2%98%EF%BC%9A%E9%9B%86%E6%88%90Google>
Play 支付,缺失 IInAppBillingService.aidl 或 Google Play Billing Library
SDK Manager中根本没有 Google Play Billing Library,正确解决这个棘手问题的方法:找到英文版说明 的示例项目:
https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive
<https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive>

然后在分支:
https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive/app/src/main/aidl/com/android/vending/billing下

<https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive/app/src/main/aidl/com/android/vending/billing%E4%B8%8B>

下载下来复制到我们创建的com.android.vending.billing
包中就行,最后rebuild下,就可以在下面图片中的目录下看到生成的java文件,就说明我们导入aidl文件成功了!

第三步:开始集成代码:
var mService: IInAppBillingService? = null var mServiceConn:
ServiceConnection? = null var sub_monthly_price:String? = null var
querySkus:Bundle? = null //与应用内购买结算服务建立连接 mServiceConn = object :
ServiceConnection { override fun onServiceDisconnected(name: ComponentName) {
mService = null } override fun onServiceConnected(name: ComponentName, service:
IBinder) { mService = IInAppBillingService.Stub.asInterface(service)
if(mService!=null){ initRn() }else{ Log.e(TAG,"没走initRn()方法") } } } //onCreate
方法中,通过调用 bindService 方法执行绑定。 向方法传递引用应用内购买结算服务的 Intent 和您创建的一个 ServiceConnection
实例, // 并明确地将 Intent 的目标软件包名称设置为 com.android.vending — Google Play 应用的软件包名称。 val
serviceIntent = Intent("com.android.vending.billing.InAppBillingService.BIND")
serviceIntent.`package` = "com.android.vending" bindService(serviceIntent,
mServiceConn, Context.BIND_AUTO_CREATE)
initRn方法
private fun initRn() { Log.e(TAG,"initRn==initRn") // 警告:请不要在主线程上调用
getSkuDetails 方法。 调用此方法会触发网络请求,进而阻塞主线程。 请创建单独的线程并从该线程内部调用 getSkuDetails 方法。
Thread(Runnable { //在您的应用中,可以使用 In-app Billing Version 3 API 从 Google Play
查询商品详情。 要将请求传递至应用内购买结算服务, // 首先需要创建一个包含商品 ID 字符串 ArrayList 的
Bundle,该字符串带有键“ITEM_ID_LIST”,每个字符串是可购买商品的商品 ID。 val skuList =
ArrayList<String>() skuList.add("sub_monthly") querySkus = Bundle()
querySkus!!.putStringArrayList("ITEM_ID_LIST", skuList) //要从 Google Play
检索此信息,请在 In-app Billing Version 3 API 上调用 getSkuDetails 方法, // 然后将 In-app
Billing API 版本(“3”)、发起调用的应用的软件包名称、商品类型(“应用内”)以及您创建的 Bundle 传递给方法。 var
skuDetails = mService!!.getSkuDetails(3, packageName, "subs", querySkus)
//查询结果将保存在带有键 DETAILS_LIST 的字符串 ArrayList 中。购买信息存储在 JSON 格式的字符串中 //将从之前代码段返回的
skuDetails Bundle 中检索您的应用内商品的价格。 val response =
skuDetails.getInt("RESPONSE_CODE") Log.e(TAG,"response==$response") if
(response == 0) { val responseList =
skuDetails.getStringArrayList("DETAILS_LIST")
Log.e(TAG,"responseList=="+responseList.toString()) for (thisResponse in
responseList) { val `object` = JSONObject(thisResponse) val sku =
`object`.getString("productId") val price = `object`.getString("price")
Log.e(TAG,"price=="+price) if (sku == "sub_monthly") sub_monthly_price = price
// else if (sku == "gas") mGasPrice = price //要从您的应用发起订阅请求,请在应用内购买结算服务上调用
getBuyIntent 方法。 将 In-app Billing API 版本(“3”)、发起调用的应用的软件包名称、要购买商品的商品 ID、 //
商品类型(“应用内”或“订阅”)以及 developerPayload 字符串传递给方法。 developerPayload 字符串用于指定您想要
Google Play 随购买信息一同发送的任何其他参数。 val buyIntentBundle = mService!!.getBuyIntent(3,
packageName, sku, "subs", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ")
//如果请求成功,返回的 Bundle 将包含响应代码 BILLING_RESPONSE_RESULT_OK (0) 和您可以用于开始购买流程的
PendingIntent。 要从 Google Play 查看所 // 有可能的响应代码,请参阅应用内购买结算参考。 接下来,请使用键 BUY_INTENT
从响应 Bundle 中提取 PendingIntent。 val pendingIntent =
(buyIntentBundle.getParcelable("BUY_INTENT")) as PendingIntent //要完成购买交易,请调用
startIntentSenderForResult 方法并使用您创建的 PendingIntent。 这里用任意值 1001 用于请求代码。
startIntentSenderForResult(pendingIntent.intentSender, 1001, Intent(),
Integer.valueOf(0)!!, Integer.valueOf(0)!!, Integer.valueOf(0)!!) } }else{
Log.e(TAG,"response==========="+response) } }).start() } /** * Google Play
会将对您 PendingIntent 的响应发送至应用的 onActivityResult 方法。 onActivityResult 方法将获得结果代码
Activity.RESULT_OK (1) 或 Activity.RESULT_CANCELED (0)。要查看响应 Intent
中返回的订单类型信息,请参阅应用内购买结算参考。 *订单的购买数据是 JSON 格式的字符串,将映射到响应 Intent 中的
INAPP_PURCHASE_DATA 键,例如: '{ "orderId":"GPA.1234-5678-9012-34567",
"packageName":"com.example.app", "productId":"exampleSku",
"purchaseTime":1345678900000, "purchaseState":0,
"developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ",
"purchaseToken":"opaque-token-up-to-1000-characters" }' 注:Google Play
会为购买生成令牌。此令牌是不透明的字符序列,最长可为 1,000 字符。 将整个令牌传递至其他方法(例如在您消耗购买时,如消耗购买中所述)。
不要省略或者截断此令牌,您必须保存并返回整个令牌。 */ override fun onActivityResult(requestCode: Int,
resultCode: Int, data: Intent) { super.onActivityResult(requestCode,
resultCode, data) if (requestCode == 1001) { val responseCode =
data.getIntExtra("RESPONSE_CODE", 0) val purchaseData =
data.getStringExtra("INAPP_PURCHASE_DATA") val dataSignature =
data.getStringExtra("INAPP_DATA_SIGNATURE") if (resultCode == RESULT_OK) { try
{ val jo = JSONObject(purchaseData) val sku = jo.getString("productId")
Log.e(TAG,"You have bought the \" + sku + \". Excellent choice, adventurer!")
alert("You have bought the " + sku + ". Excellent choice, adventurer!") } catch
(e : JSONException) { Log.e(TAG,"Failed to parse purchase data") alert("Failed
to parse purchase data.") e.printStackTrace() } } }else{
faceBookLoginUtil!!.getCallbackManager().onActivityResult(requestCode,
resultCode, data) } } //安全性建议:在您发送购买请求时,请创建一个可以对此购买请求进行唯一标识的字符串令牌并在
developerPayload 中包含此令牌。您可以将随机生成的字符串作为令牌。 从 Google Play
接收到购买响应时,请确保检查返回的数据签名、orderId 和 developerPayload 字符串。 为了增强安全性,您应在自己安全的服务器上执行检查。
请确保验证 orderId 为您之前未处理的唯一值,且 developerPayload 字符串与您之前通过购买请求发送的令牌相匹配。 //重要说明:完成您的
Activity 后,请务必与应用内购买结算服务解除绑定。 如果不解除绑定,开启的服务连接会导致您的设备性能下降。 // 此示例说明了如何通过重写
Activity 的 onDestroy 方法对到应用内购买结算的服务连接 mServiceConn 执行解除绑定操作。 public override
fun onDestroy() { super.onDestroy() if (mService != null) {
unbindService(mServiceConn) } }
最后贡献一点我遇到的一点问题和解决的方案:
运行后出现异常提示:错误,此版本的应用程序未配置为通过Google Play结算。有关详情,请访问帮助中心

解决方案:我遇到的情况是在测试阶段,需要将我的谷歌测试账号添加到google play console
设置->账号详情->许可测试,然后运行就不出刚才那个提示了,可以订阅了就,运行起来是这个样子:


另外网上说的其他几方面原因我还没有遇到,但也是同类问题可以参考的解决方案,放到这里:

别的博友分享的解决方案,当你集成时出现这个问题用上面的方案也不行时,可以参考下面的:
造成这个错误的原因有两个,第一个是打包的时候,versionCode的值比提交到google play后台的版本要高。
第二个就是:打包的时候,和google play后台上的包的签名不一致。

还有一个异常:Google Play 遇到“无法购买您要买的商品?”问题,知乎上有一个贴对此总结的特别好,我这里贴下并附上原贴地址(
https://www.zhihu.com/question/35295183)。
<https://www.zhihu.com/question/35295183%EF%BC%89%E3%80%82>
解决方案:
1 保证VersionCode, 上传的包和你测试的包一致。
2 保证Payment List, google后台的单据名和你游戏中配置的一致。
3 保证Payment Key, google后台Key和你的Key一致。
4 保证Test User, 你在测试阶段可以正常支付你的游戏。
5 保证GooglePlay商店, 这个一般来说只要弹出支付是没问题的, 但是如果商店无法使用,请检查Vpn。
6 保证Package Name, 包名一致。
7 上传到后台和测试的包签名要一致 (不一致传不上去)。
8 想要用其他谷歌账号测试游戏, 需要让应用主账号发送给其他账号测试邀请连接, 并让其他账号手动确认加入,才可以测试.

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信