我在游戏中使用计费客户端,以便让人们在应用程序项目中购买。
根据我的日志,许多用户无法购买该商品,因为购买流程未启动。
日志显示,大多数启动购买流程的用户都会“卡在”queryProductDetailsAsync 方法上,从而导致错误:
int SERVICE_UNAVAILABLE = 2;
整数错误 = 6;
int NETWORK_ERROR = 12;
问题是,我在成功调用 startConnection 后使用 queryProductDetailsAsync - 我不明白为什么会出现网络/连接错误。
根据我的测试,它总是成功的。 我也在朋友的手机上测试了一下,也成功了。 此外,我确实看到一些用户成功购买。 所以我猜这不是配置问题。
所有故障都来自用户设备的日志
这是我的 BillingHelper 类的代码:
import android.app.Activity
import android.content.Context
import android.os.Handler
import android.os.Looper
import com.android.billingclient.api.*
import com.android.billingclient.api.BillingFlowParams.ProductDetailsParams
import java.lang.ref.WeakReference
class BillingHelper
{
// region Enum
enum class PurchaseStatus
{
PURCHASED,
PENDING
}
// endregion
// region Companion
companion object
{
private const val TAG = "BillingHelper"
}
// endregion
// region Listener
interface BillingHelperListener
{
fun purchaseStatusUpdated(itemId: String, status: PurchaseStatus)
}
// endregion
// region Properties
private var billingClient: BillingClient? = null
private var context: WeakReference<Context>? = null
private var listener: BillingHelperListener? = null
private val handler = Handler(Looper.getMainLooper())
// endregion
// region Init
fun init(context: Context)
{
this.context = WeakReference(context)
}
fun setListener(listener: BillingHelperListener)
{
this.listener = listener
}
// endregion
// region Purchase flow
fun launchPurchase(activity: Activity, itemId: String, block: (purchaseStarted: Boolean) -> Unit)
{
KLog.i(TAG, "launchPurchase - $itemId")
connectToBillingClient { connected ->
KLog.i(TAG, "launchPurchase - $itemId. connected? - $connected")
if (connected)
{
prepareSkuForPurchase(activity, itemId, block)
} else
{
handler.post {
KLog.i(TAG, "launchPurchase - $itemId. invoking false")
block.invoke(false)
}
}
}
}
private fun connectToBillingClient(block: (connected: Boolean) -> Unit)
{
KLog.i(TAG, "connectToBillingClient")
getBillingClient()?.let {
if (it.isReady)
{
KLog.i(TAG, "connectToBillingClient - already connected")
block.invoke(true)
return@let
}
it.startConnection(object : BillingClientStateListener
{
override fun onBillingSetupFinished(billingResult: BillingResult)
{
KLog.i(TAG, "connectToBillingClient::onBillingSetupFinished")
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK)
{
KLog.i(TAG, "connectToBillingClient::onBillingSetupFinished - connected")
block.invoke(true)
return
}
KLog.i(TAG, "connectToBillingClient::onBillingSetupFinished - invoking false")
block.invoke(false)
}
override fun onBillingServiceDisconnected()
{
KLog.i(TAG, "connectToBillingClient::onBillingServiceDisconnected - invoking false")
block.invoke(false)
}
})
}
}
private fun prepareSkuForPurchase(activity: Activity, itemID: String, block: (purchaseStarted: Boolean) -> Unit)
{
KLog.i(TAG, "prepareSkuForPurchase - $itemID")
val skuList = ArrayList<String>()
skuList.add(itemID)
val productList =
listOf(
QueryProductDetailsParams.Product.newBuilder()
.setProductId(itemID)
.setProductType(BillingClient.ProductType.INAPP)
.build()
)
val params = QueryProductDetailsParams.newBuilder()
params.setProductList(productList)
getBillingClient()?.queryProductDetailsAsync(params.build()) { billingResult, skuDetailsList ->
KLog.i(TAG, "prepareSkuForPurchase::queryProductDetailsAsync - billing result - $billingResult")
/**
* PROBLEM IS HERE - ACCORDING TO THE LOG - IT SAYS:
* 1 - billing result - Response Code: ERROR, Debug Message: An internal error occurred.
* 2 - billing result - Response Code: NETWORK_ERROR, Debug Message: An internal error occurred.
* 3 - billing result - Response Code: SERVICE_UNAVAILABLE, Debug Message: Timeout communicating with service.
*/
var purchaseStarted = false
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && skuDetailsList.isNotEmpty())
{
launchSkuPurchase(activity, skuDetailsList[0])
purchaseStarted = true
}
handler.post {
KLog.i(TAG, "prepareSkuForPurchase::queryProductDetailsAsync - invoking purchase started? - $purchaseStarted")
block.invoke(purchaseStarted)
}
}
}
private fun launchSkuPurchase(activity: Activity, productDetails: ProductDetails)
{
KLog.i(TAG, "launchSkuPurchase - $productDetails")
val params = ProductDetailsParams.newBuilder()
params.setProductDetails(productDetails)
val billingFlowParams = BillingFlowParams.newBuilder().setProductDetailsParamsList(listOf(params.build())).build()
getBillingClient()?.launchBillingFlow(activity, billingFlowParams)
}
private fun handleSuccessPurchase(purchase: Purchase)
{
KLog.i(TAG, "handleSuccessPurchase - $purchase")
KLog.i(TAG, "handleSuccessPurchase. state = - ${purchase.purchaseState}")
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED)
{
if (!purchase.isAcknowledged)
{
acknowledgePurchase(purchase)
} else
{
handler.post {
listener?.purchaseStatusUpdated(purchase.products.firstOrNull() ?: "", PurchaseStatus.PURCHASED)
}
}
} else if (purchase.purchaseState == Purchase.PurchaseState.PENDING)
{
handler.post {
listener?.purchaseStatusUpdated(purchase.products.firstOrNull() ?: "", PurchaseStatus.PENDING)
}
}
}
private fun acknowledgePurchase(purchase: Purchase)
{
KLog.i(TAG, "acknowledgePurchase - $purchase")
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.purchaseToken).build()
getBillingClient()?.acknowledgePurchase(acknowledgePurchaseParams) { result ->
KLog.i(TAG, ":acknowledgePurchase::status - ${result.responseCode}")
if (result.responseCode == BillingClient.BillingResponseCode.OK)
{
handler.post {
listener?.purchaseStatusUpdated(purchase.products.firstOrNull() ?: "", PurchaseStatus.PURCHASED)
}
}
}
}
private val purchaseUpdateListener = PurchasesUpdatedListener { billingResult, purchases ->
KLog.i(TAG, "purchaseUpdateListener::onPurchasesUpdated : $billingResult")
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && !purchases.isNullOrEmpty())
{
val purchase = purchases.first()
handleSuccessPurchase(purchase)
}
}
// endregion
// region Billing client
private fun getBillingClient(): BillingClient?
{
val context = this.context?.get() ?: return let {
KLog.i(TAG, "getBillingClient - context is null")
null
}
var billingClient = this.billingClient
if (billingClient == null)
{
KLog.i(TAG, "getBillingClient - creating new billing client")
billingClient = BillingClient.newBuilder(context)
.setListener(purchaseUpdateListener)
.enablePendingPurchases()
.build()
this.billingClient = billingClient
} else
{
KLog.i(TAG, "getBillingClient - using existing billing client")
}
return billingClient
}
// endregion
}
如您所见 - 问题出在 queryProductDetailsAsync 方法中。
我错过了什么?
我正在使用: 实现 'com.android.billingclient:billing:7.0.0'
如果您是狂热的 Instagram 用户,您可能听说过 InstaUp APK。它是 Instagram 的修改和定制版本,提供一系列独特的功能。借助 InstaUp,您可以从 IGTV 和 Instagram 下载高清图像和视频、复制贴纸,甚至为您的帐户获得无限的真实关注者。 https://instaupak.com/