Согласно моим журналам, многие пользователи не могут приобрести этот предмет, так как Поток покупок не запускается.
Журналы показывают, что большинство пользователей, запускающих поток покупок, "застревают" в методе queryProductDetailsAsync, что приводит к ошибкам:
int SERVICE_UNAVAILABLE = 2;
int ERROR = 6;
int NETWORK_ERROR = 12;
Дело в следующем что я использую queryProductDetailsAsync после успешного вызова startConnection — я не понимаю, почему это может быть ошибкой сети/соединения.
Согласно моим тестам, это всегда удается.Я также провел тест на телефоне своего друга, и он тоже прошел успешно.
Более того, я вижу некоторые успешные покупки некоторых пользователей.
Поэтому я предполагаю, что это не проблема конфигурации.< /p>
Все ошибки происходят из журналов, поступающих с устройств пользователей.
Вот код моего класса 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? = 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()
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
}
Что мне не хватает?
Я использую:
реализация 'com.android.billingclient:billing:7.0.0'
Подробнее здесь: https://stackoverflow.com/questions/788 ... -6-12-many
Мобильная версия