前言
很多游戏研发同学可能都需要顺带接入谷歌支付,谷歌的文档大家都懂得,有时候感觉看下来好像抓住了什么却又什么都没有抓住,导致接入工作有时候会陷入瓶颈。我整理了下谷歌支付接口,并添加了一些解释性文本及扩展代码,希望能对你有所帮助。最后贴上谷歌支付的官网地址
准备工作
谷歌后台项目传入白包,需要麻烦运营同学后台配置好商品,白包可以什么功能都没有,但一定要谷歌支付SDK,因为谷歌需要你的包里有支付相关的权限。4.0开始不用单独在manifest申明,直接依赖谷歌支付包,依赖方法:
dependencies {
//版本可能有更新,建议使用最新版本
val billing_version = "4.0.0"
implementation("com.android.billingclient:billing-ktx:$billing_version")
}
传入白包后,才能够在接下来的流程中查询商品,拉起支付。
开始集成
注意:下文所指商品只针对消耗型商品!!!非消耗型商品除最后消耗步骤有区别,其余基本一致。
初始化并连接Google Play
谷歌支付需要先与Google play建立连接后才能查询商品等操作,所以最好在项目初始化成功后调用谷歌支付初始化,初始化及连接代码示例:
private lateinit var billingClient: BillingClient
billingClient = BillingClient.newBuilder(activity)
//谷歌支付回调,后面拉起支付后会用到,后面再详解
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
//执行初始化成功相关操作
}
}
override fun onBillingServiceDisconnected() {
//做好断开连接重连操作
}
})
查询商品信息
一般游戏都会配置一张内购商品表,定义好商品名称,价格,等参数,但如果发行多个国家,可以使用该接口查询玩家所在地的币种,对应价格,国家简码等信息。查询成功返回的结果也是支付所必需的参数。另外官方也建议在查询商品信息前有无需要消耗的商品,如果有就得先消耗商品再拉起支付,否则会导致支付失败。查询商品代码示例:
//协程请求商品信息
private var paySkuDetails: ArrayList<SkuDetails> = ArrayList()
private suspend fun queryProductInfo(productIds: List<String>) {
val params = SkuDetailsParams.newBuilder()
params.setSkusList(productIds).setType(BillingClient.SkuType.INAPP)
// leverage querySkuDetails Kotlin extension function
val skuDetailsResult = withContext(Dispatchers.IO) {
billingClient.querySkuDetails(params.build())
}
if (skuDetailsResult.billingResult.responseCode == BillingClient.BillingResponseCode.OK && !skuDetailsResult.skuDetailsList.isNullOrEmpty()){
//查询成功回调
paySkuDetails = ArrayList()
for (skuDetailList in skuDetailsResult.skuDetailsList){
}
} else{
//查询失败回调
}
}
启动购买流程
购买方法需要在主线程调用,支付所需参数是上个环节查询成功的商品详情,如果查询商品失败请不要调用支付接口。拉起支付代码示例:
val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(paySkuDetails[0])
.setObfuscatedAccountId("可选,谷歌支付检测机制,与游戏账号关联的唯一ID,例如账号的MD5值,注意不能为空")
.setObfuscatedProfileId("可选,和上述功能类似,但国内很多开发用来透传自己平台的订单号,慎用")
.build()
val responseCode = billingClient.launchBillingFlow(mActivity, flowParams).responseCode
一开始谷歌初始化的时候就提到了一个支付回调接口,用来监听支付状态,谷歌支付回调代码示例:
@DelicateCoroutinesApi
private val purchasesUpdatedListener = PurchasesUpdatedListener {
billingResult, purchases ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null){
//支付成功,处理消耗逻辑
for (purchase in purchases){
mPurchase = purchase
GlobalScope.launch {
consume(mPurchase)
}
}
}else if(billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED){
//用户取消支付回调
}else{
//其他支付错误
}
}
消耗已购买商品
最关键的来了,每次支付成功后请一定要消耗掉商品,如果不消耗测试账号5分钟,正常玩家三天就会收到谷歌的退款,如果游戏内发货了,那真的是赔了夫人又折兵,所以请一定要消耗!!!至于实在发货前消耗还是发货后消耗就看自己需求。消耗代码示例:
private suspend fun consume(purchase: Purchase) {
val consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
val consumeResult = withContext(Dispatchers.IO) {
billingClient.consumePurchase(consumeParams)
}
if (consumeResult.billingResult.responseCode == BillingClient.BillingResponseCode.OK){
//处理商品消耗成功的逻辑
}else{
//处理商品购买时消耗失败的逻辑
}
}
未消耗商品查询
在一些非正常购买流程下,可能会接收不到购买成功回调,例如:多台设备;内购过程中网络中断;用户在应用外进行交易等,所以谷歌推荐在onResume和onCreate中调用BillingClient.queryPurchasesAsync()来查询是否有需要消耗的商品,我感觉最好是每次拉起支付前都查询下,有就先消耗,没有就直接拉起支付。
GlobalScope.launch {
val purchasesResult = billingClient.queryPurchasesAsync(BillingClient.SkuType.INAPP)
if (purchasesResult.billingResult.responseCode == BillingClient.BillingResponseCode.OK){
if(purchasesResult.purchasesList.isNotEmpty()){
//处理商品消耗逻辑
for (purchase in purchasesResult.purchasesList){
//消耗商品
consume(mPurchase)
}
} else{
//没有需要补消的商品
}
}else{
//查询谷歌订单失败
}
}
测试支付
到此支付部分就差不多接入结束了,最后就差测试支付了,这部分也是搞脑子的大头,下面整理了几个我踩过的坑,希望能帮到你。
- 保证你测试APK的签名,包名,VersionCode需要与你上传的白包保持一致;
- 点击支付能够展现谷歌支付弹窗,但是出现了无法购买之类的错误一般都是配置问题,请仔细检查第一条,并且确认是否正确添加了测试账号;
- 一定要保证你Google Play登的账号是测试账号,比较省事的做法就是手机只保留一个测试账号;
- 如果加入了测试账号,并且谷歌登录的账号也是对的,但拉起支付还是提示无法购买,可以清除Google Play数据试试,谷歌缓存懂得都懂;
- 测试账号一定要加入测试计划,不点击加入计划就算后台添加了也无法生效。