Android Google In-app-purchases v6无服务器环境下恢复购买失败求助
Google Play Billing v6+ 无服务器环境下恢复已购非消耗品解决方案
以下是针对重装/跨设备无法恢复非消耗品购买问题的具体客户端实现方案(无服务器依赖):
确保BillingClient连接稳定并正确初始化
查询返回空常因BillingClient未完全建立连接或断开后未重连导致。必须在onBillingSetupFinished确认连接成功后执行查询,同时监听断开事件自动重连:private lateinit var billingClient: BillingClient fun initBillingClient(context: Context) { billingClient = BillingClient.newBuilder(context) .enablePendingPurchases() // 必须开启,否则无法处理待购买状态 .setListener { billingResult, purchases -> if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) { handlePurchases(purchases) } } .build() connectBillingClient() } private fun connectBillingClient() { if (!billingClient.isReady) { billingClient.startConnection(object : BillingClientStateListener { override fun onBillingSetupFinished(billingResult: BillingResult) { if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { queryPurchases() } } override fun onBillingServiceDisconnected() { Handler(Looper.getMainLooper()).postDelayed({ connectBillingClient() }, 1000) } }) } }使用正确参数调用
queryPurchasesAsync
非消耗品属于INAPP类型,需确保查询参数指定正确类型,且完整处理回调结果:private fun queryPurchases() { val queryParams = QueryPurchasesParams.newBuilder() .setProductType(BillingClient.ProductType.INAPP) .build() billingClient.queryPurchasesAsync(queryParams) { billingResult, purchases -> if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { if (purchases.isNotEmpty()) { handlePurchases(purchases) } else { // 首次查询为空,触发后台同步 triggerPurchaseSync() } } } }触发Google Play后台同步(无需用户额外操作)
首次查询为空时,可发起目标非消耗品的静默购买请求(不会弹出付费弹窗,但会触发后台同步购买记录):private fun triggerPurchaseSync() { val productDetailsParams = QueryProductDetailsParams.newBuilder() .setProductList(listOf( QueryProductDetailsParams.Product.newBuilder() .setProductId("你的非消耗品ID") .setProductType(BillingClient.ProductType.INAPP) .build() )) .build() billingClient.queryProductDetailsAsync(productDetailsParams) { billingResult, productDetailsList -> if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && productDetailsList.isNotEmpty()) { val productDetails = productDetailsList[0] val billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(listOf( BillingFlowParams.ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .build() )) .build() val result = billingClient.launchBillingFlow(activity, billingFlowParams) if (result.responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) { queryPurchases() } } } }处理待处理购买状态
部分场景下购买可能处于待处理状态,需在回调中主动确认并处理:private fun handlePurchases(purchases: List<Purchase>) { for (purchase in purchases) { if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) { if (!purchase.isAcknowledged) { val acknowledgeParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.purchaseToken) .build() billingClient.acknowledgePurchase(acknowledgeParams) { billingResult -> if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { markItemAsOwned(purchase.products[0]) } } } else { markItemAsOwned(purchase.products[0]) } } else if (purchase.purchaseState == Purchase.PurchaseState.PENDING) { // 提示用户等待或稍后重试 } } }关键注意事项
- 应用包名、签名需与Google Play控制台注册信息完全一致,否则无法获取购买记录
- 测试需使用正式签名APK或Google Play内部测试渠道包,debug签名包可能无法正常跨设备查询
- 避免应用启动时立即查询,等待Google账号完全登录后再发起(可监听账号登录状态变化)
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

