BillingResult yanıt kodlarını işleme

Bir Play Faturalandırma Kitaplığı çağrısı bir işlemi tetiklediğinde kitaplık, geliştiricileri sonuç hakkında bilgilendirmek için bir BillingResult yanıtı döndürür. Örneğin, kullanıcı için kullanılabilir teklifleri almak üzere queryProductDetailsAsync kullanırsanız yanıt kodu bir OK kodu ve doğru ProductDetails nesnesini sağlar ya da ProductDetails nesnesinin sağlanamama nedenini belirten farklı bir yanıt içerir.

Tüm yanıt kodları hata değildir. BillingResponseCode referans sayfasında bu kılavuzda açıklanan her bir yanıtın ayrıntılı açıklaması yer alır. Aşağıda, hata göstergesi olmayan bazı yanıt kodları örneği verilmiştir:

Yanıt kodu bir hata gösterdiğinde bunun nedeni bazen geçici koşullardır ve dolayısıyla kurtarma mümkündür. Bir Play Faturalandırma Kitaplığı yöntemine yapılan çağrı, kurtarılabilir bir koşulu belirten BillingResponseCode değeri döndürdüğünde çağrıyı yeniden denemeniz gerekir. Diğer durumlarda, koşullar geçici olarak kabul edilmez ve bu nedenle yeniden deneme önerilmez.

Geçici hatalar, hatanın kullanıcılar oturumdayken meydana gelip gelmediği (ör. kullanıcı bir satın alma akışını gerçekleştirirken veya hatanın arka planda meydana gelmesi) (örneğin, onResume sırasında kullanıcının mevcut satın alma işlemlerini sorgularken) gibi faktörlere bağlı olarak farklı yeniden deneme stratejileri gerektirir. Aşağıdaki yeniden deneme stratejileri bölümünde, bu farklı stratejilerle ilgili örnekler sağlanmaktadır. Geri alınabilir BillingResult Yanıtlar bölümünde ise her yanıt kodu için en uygun stratejinin nasıl bulunacağı açıklanmıştır.

Bazı hata yanıtları, yanıt koduna ek olarak hata ayıklama ve günlük kaydına yönelik mesajlar içerir.

Yeniden deneme stratejileri

Basit yeniden deneme

Kullanıcının oturumda olduğu durumlarda, hatanın kullanıcı deneyimini mümkün olduğunca az kesintiye uğratması için basit bir yeniden deneme stratejisi uygulamak daha iyidir. Bu durumda, çıkış koşulu olarak maksimum deneme sayısı içeren basit bir yeniden deneme stratejisi öneririz.

Aşağıdaki örnekte, BillingClient bağlantısı kurulurken hata oluşmasını gidermek için basit bir yeniden deneme stratejisi gösterilmektedir:

class BillingClientWrapper(context: Context) : PurchasesUpdatedListener {
  // Initialize the BillingClient.
  private val billingClient = BillingClient.newBuilder(context)
    .setListener(this)
    .enablePendingPurchases()
    .build()

  // Establish a connection to Google Play.
  fun startBillingConnection() {
    billingClient.startConnection(object : BillingClientStateListener {
      override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
          Log.d(TAG, "Billing response OK")
          // The BillingClient is ready. You can now query Products Purchases.
        } else {
          Log.e(TAG, billingResult.debugMessage)
          retryBillingServiceConnection()
        }
      }

      override fun onBillingServiceDisconnected() {
        Log.e(TAG, "GBPL Service disconnected")
        retryBillingServiceConnection()
      }
    })
  }

  // Billing connection retry logic. This is a simple max retry pattern
  private fun retryBillingServiceConnection() {
    val maxTries = 3
    var tries = 1
    var isConnectionEstablished = false
    do {
      try {
        billingClient.startConnection(object : BillingClientStateListener {
          override fun onBillingSetupFinished(billingResult: BillingResult) {
            if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
              isConnectionEstablished = true
              Log.d(TAG, "Billing connection retry succeeded.")
            } else {
              Log.e(
                TAG,
                "Billing connection retry failed: ${billingResult.debugMessage}"
              )
            }
          }
        })
      } catch (e: Exception) {
        e.message?.let { Log.e(TAG, it) }
        tries++
      }
    } while (tries <= maxTries && !isConnectionEstablished)
  }
  ...
}

Eksponansiyel geri yükleme denemesi

Arka planda gerçekleşen ve kullanıcı oturumdayken kullanıcı deneyimini etkilemeyen Play Faturalandırma Kitaplığı işlemleri için eksponansiyel geri yükleme kullanmanızı öneririz.

Örneğin, bu işlem arka planda gerçekleşebileceği ve bir hata oluşursa onayın gerçek zamanlı olarak gerçekleşmesi gerekmediği için yeni satın almaları onaylarken bunu uygulamak uygundur.

private fun acknowledge(purchaseToken: String): BillingResult {
  val params = AcknowledgePurchaseParams.newBuilder()
    .setPurchaseToken(purchaseToken)
    .build()
  var ackResult = BillingResult()
  billingClient.acknowledgePurchase(params) { billingResult ->
    ackResult = billingResult
  }
  return ackResult
}

suspend fun acknowledgePurchase(purchaseToken: String) {

  val retryDelayMs = 2000L
  val retryFactor = 2
  val maxTries = 3

  withContext(Dispatchers.IO) {
    acknowledge(purchaseToken)
  }

  AcknowledgePurchaseResponseListener { acknowledgePurchaseResult ->
    val playBillingResponseCode =
    PlayBillingResponseCode(acknowledgePurchaseResult.responseCode)
    when (playBillingResponseCode) {
      BillingClient.BillingResponseCode.OK -> {
        Log.i(TAG, "Acknowledgement was successful")
      }
      BillingClient.BillingResponseCode.ITEM_NOT_OWNED -> {
        // This is possibly related to a stale Play cache.
        // Querying purchases again.
        Log.d(TAG, "Acknowledgement failed with ITEM_NOT_OWNED")
        billingClient.queryPurchasesAsync(
          QueryPurchasesParams.newBuilder()
            .setProductType(BillingClient.ProductType.SUBS)
            .build()
        )
        { billingResult, purchaseList ->
          when (billingResult.responseCode) {
            BillingClient.BillingResponseCode.OK -> {
              purchaseList.forEach { purchase ->
                acknowledge(purchase.purchaseToken)
              }
            }
          }
        }
      }
      in setOf(
         BillingClient.BillingResponseCode.ERROR,
         BillingClient.BillingResponseCode.SERVICE_DISCONNECTED,
         BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE,
       ) -> {
        Log.d(
          TAG,
          "Acknowledgement failed, but can be retried --
          Response Code: ${acknowledgePurchaseResult.responseCode} --
          Debug Message: ${acknowledgePurchaseResult.debugMessage}"
        )
        runBlocking {
          exponentialRetry(
            maxTries = maxTries,
            initialDelay = retryDelayMs,
            retryFactor = retryFactor
          ) { acknowledge(purchaseToken) }
        }
      }
      in setOf(
         BillingClient.BillingResponseCode.BILLING_UNAVAILABLE,
         BillingClient.BillingResponseCode.DEVELOPER_ERROR,
         BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED,
       ) -> {
        Log.e(
          TAG,
          "Acknowledgement failed and cannot be retried --
          Response Code: ${acknowledgePurchaseResult.responseCode} --
          Debug Message: ${acknowledgePurchaseResult.debugMessage}"
        )
        throw Exception("Failed to acknowledge the purchase!")
      }
    }
  }
}

private suspend fun <T> exponentialRetry(
  maxTries: Int = Int.MAX_VALUE,
  initialDelay: Long = Long.MAX_VALUE,
  retryFactor: Int = Int.MAX_VALUE,
  block: suspend () -> T
): T? {
  var currentDelay = initialDelay
  var retryAttempt = 1
  do {
    runCatching {
      delay(currentDelay)
      block()
    }
      .onSuccess {
        Log.d(TAG, "Retry succeeded")
        return@onSuccess;
      }
      .onFailure { throwable ->
        Log.e(
          TAG,
          "Retry Failed -- Cause: ${throwable.cause} -- Message: ${throwable.message}"
        )
      }
    currentDelay *= retryFactor
    retryAttempt++
  } while (retryAttempt < maxTries)

  return block() // last attempt
}

Geri alınabilir BillingResult yanıtları

NETWORK_ERROR (Hata Kodu 12)

Sorun

Bu hata, cihaz ile Play sistemleri arasındaki ağ bağlantısında bir sorun olduğunu gösterir.

Olası çözüm

Hatayı kurtarmak için, hatayı tetikleyen işleme bağlı olarak basit yeniden deneme veya eksponansiyel geri yükleme yöntemini kullanın.

SERVICE_TIMEOUT (Hata Kodu -3)

Sorun

Bu hata, Google Play'in yanıt verebilmesi için isteğin maksimum zaman aşımına uğradığını gösterir. Bunun nedeni, örneğin Play Faturalandırma Kitaplığı çağrısı tarafından istenen işlemin yürütülmesindeki bir gecikme olabilir.

Olası çözüm

Bu genellikle geçici bir sorundur. Hatayı döndüren işleme bağlı olarak, ya basit ya da üstel geri yükleme stratejisi kullanarak isteği yeniden deneyin.

Aşağıdaki SERVICE_DISCONNECTED'in aksine, Google Play Faturalandırma hizmetiyle bağlantı kopmaz. Yalnızca Play Faturalandırma Kitaplığı'nın denendiği işlemi yeniden denemeniz yeterlidir.

SERVICE_DISCONNECTED (Hata Kodu -1)

Sorun

Bu önemli hata, istemci uygulamasının BillingClient üzerinden Google Play Store hizmetiyle olan bağlantısının koptuğunu gösterir.

Olası çözüm

Bu hatadan mümkün olduğunca kaçınmak için BillingClient.isReady() numaralı telefonu arayarak Play Faturalandırma Kitaplığı'ndan arama yapmadan önce her zaman Google Play Hizmetleri bağlantısını kontrol edin.

SERVICE_DISCONNECTED hesabından kurtarma işlemini denemek için istemci uygulamanız, BillingClient.startConnection kullanarak bağlantıyı yeniden kurmayı denemelidir.

SERVICE_TIMEOUT'te olduğu gibi, hatayı tetikleyen işleme bağlı olarak basit yeniden deneme veya eksponansiyel geri yükleme kullanın.

SERVICE_UNAVAILABLE (Hata Kodu 2)

Önemli Not:

SERVICE_UNAVAILABLE, Google Play Faturalandırma Kitaplığı 6.0.0 sürümünden itibaren ağ sorunları nedeniyle artık döndürülmemektedir. Faturalandırma hizmeti kullanılamadığında ve kullanımdan kaldırılan SERVICE_TIMEOUT örnek olay senaryolarında döndürülür.

Sorun

Bu geçici hata, Google Play Faturalandırma hizmetinin şu anda kullanılamadığını belirtir. Çoğu durumda bu, istemci cihaz ile Google Play Faturalandırma hizmetleri arasında herhangi bir yerde bir ağ bağlantısı sorunu olduğu anlamına gelir.

Olası çözüm

Bu genellikle geçici bir sorundur. Hatayı döndüren işleme bağlı olarak, ya basit ya da üstel geri yükleme stratejisi kullanarak isteği yeniden deneyin.

SERVICE_DISCONNECTED'in aksine, Google Play Faturalandırma hizmetiyle olan bağlantı ayrılmaz ve yapılmaya çalışılan işlemi yeniden denemeniz gerekir.

BILLING_UNAVAILABLE (Hata Kodu 3)

Sorun

Bu hata, satın alma işlemi sırasında bir kullanıcı faturalandırma hatasının oluştuğunu belirtir. Aşağıda, bu durumun yaşanabileceği durumlara örnekler verilmiştir:

  • Kullanıcının cihazındaki Play Store uygulaması güncel değil.
  • Kullanıcı desteklenmeyen bir ülkede.
  • Kullanıcı, kurumsal bir kullanıcı ve kurumsal yöneticisi kullanıcıların satın alma işlemi yapmasını devre dışı bırakmış.
  • Google Play, kullanıcının ödeme yönteminden ödeme alamıyor. Örneğin, kullanıcının kredi kartının son kullanma tarihi geçmiş olabilir.

Olası çözüm

Bu durumda otomatik yeniden denemelerin yardımcı olma olasılığı düşüktür. Bununla birlikte, kullanıcı soruna neden olan koşulu ele alırsa manuel yeniden deneme faydalı olabilir. Örneğin, kullanıcı Play Store sürümünü desteklenen bir sürüme güncellerse ilk işlem manuel olarak yeniden denenebilir.

Bu hata, kullanıcı oturumda değilken ortaya çıkarsa yeniden denemek mantıklı olmayabilir. Satın alma akışı nedeniyle bir BILLING_UNAVAILABLE hatası alırsanız kullanıcı, satın alma işlemi sırasında Google Play'den geri bildirim almış ve neyin yanlış gittiğinin farkında olabilir. Bu durumda, bir şeyin yanlış gittiğini belirten bir hata mesajı gösterebilir ve kullanıcıya sorunu ele aldıktan sonra manuel olarak yeniden deneme seçeneği sunmak için "Tekrar dene" düğmesi sunabilirsiniz.

ERROR (Hata Kodu 6)

Sorun

Bu, Google Play'in kendisiyle ilgili dahili bir sorun olduğunu gösteren önemli bir hatadır.

Olası çözüm

Bazen ERROR'e neden olan dahili Google Play sorunları geçicidir ve çözüm için üstel geri çekilme ile yeniden deneme uygulanabilir. Kullanıcılar oturumdayken basit bir yeniden deneme tercih edilir.

ÖĞE_ALREADY_OWNED

Sorun

Bu yanıt, Google Play kullanıcısının satın almaya çalıştığı aboneliğe veya tek seferlik satın alım ürününe zaten sahip olduğunu belirtir. Çoğu durumda bu, Google Play önbelleğinin eski olmasından kaynaklanmadığı sürece geçici bir hata değildir.

Olası çözüm

Bu hatanın nedeni bir önbellek sorunu olmadığında meydana gelmesini önlemek için kullanıcı zaten sahip olduğu halde satın alınabilecek bir ürün sunmayın. Satın alınabilecek ürünleri gösterirken kullanıcının haklarını kontrol ettiğinizden ve kullanıcının satın alabileceği öğeleri gerektiği şekilde filtrelediğinizden emin olun. İstemci uygulaması bir önbellek sorunu nedeniyle bu hatayı aldığında bu hata, Google Play'in önbelleğinin Play'in arka ucundan en son verilerle güncellenmesi için tetiklenir. Bu durumda, hatadan sonra yeniden denemek bu geçici örneği çözecektir. Kullanıcının ürünü edinip edinmediğini kontrol etmek için ITEM_ALREADY_OWNED aldıktan sonra BillingClient.queryPurchasesAsync() numaralı telefonu arayın. Bu durumda değilse satın almayı yeniden denemek için basit bir yeniden deneme mantığı uygulayın.

ÖĞE_DEĞİL_OWNED

Sorun

Bu satın alma yanıtı, kullanıcının değiştirmeye, onaylamaya veya kullanmaya çalıştığı aboneliğe ya da tek seferlik satın alınan ürüne Google Play kullanıcısının sahip olmadığını belirtir. Bu, Google Play önbelleğinin eski bir duruma gelmesinden kaynaklandığı durumlar haricinde, çoğu durumda geçici bir hata değildir.

Olası çözüm

Hata, bir önbellek sorunu nedeniyle alındığında, Google Play önbelleğinin Play'in arka ucundan en son verilerle güncellenmesi için tetiklenir. Hatadan sonra basit bir yeniden deneme stratejisiyle tekrar denemek, bu geçici örneği çözecektir. ITEM_NOT_OWNED satın aldıktan sonra, kullanıcının ürünü satın alıp almadığını kontrol etmek için BillingClient.queryPurchasesAsync() numaralı telefonu arayın. Aksi takdirde, basit yeniden deneme mantığını kullanarak satın alma işlemini tekrar deneyin.

Geri Alınamayan BillingResult yanıtları

Yeniden deneme mantığını kullanarak bu hataları düzeltemezsiniz.

FEATURE_NOT_SUPPORTED

Sorun

Geri alınamayan bu hata, Google Play Faturalandırma özelliğinin kullanıcının cihazında desteklenmediğini gösterir. Bu durum, büyük olasılıkla eski bir Play Store sürümünden kaynaklanıyordur.

Örneğin, kullanıcılarınızdan bazıları uygulama içi mesajlaşmayı desteklemiyor olabilir.

Olası çözüm

Play Faturalandırma Kitaplığı'nı aramadan önce özellik desteğini kontrol etmek için BillingClient.isFeatureSupported() adresini kullanın.

when {
  billingClient.isReady -> {
    if (billingClient.isFeatureSupported(BillingClient.FeatureType.IN_APP_MESSAGING)) {
       // use feature
    }
  }
}

KULLANICI_İPTAL EDİLDİ

Sorun

Kullanıcı, faturalandırma akışı kullanıcı arayüzünü tıkladı.

Olası çözüm

Bu, yalnızca bilgi verme amaçlıdır ve başarısızlıkla sonuçlanabilir.

ITEM_UNAVAILABLE

Sorun

Google Play Faturalandırma aboneliği veya tek seferlik satın alım ürünü bu kullanıcı tarafından satın alınamaz.

Olası çözüm

Uygulamanızın, ürün ayrıntılarını önerilen şekilde queryProductDetailsAsync üzerinden yenilediğinden emin olun. Gerekirse ilave yenilemeler uygulamak için ürün kataloğunuzun Play Console yapılandırmasında ne sıklıkta değiştiğini göz önünde bulundurun. Google Play Faturalandırma'da yalnızca doğru bilgileri queryProductDetailsAsync üzerinden veren ürünleri satmaya çalışın. Ürün uygunluğu yapılandırmasında tutarsızlık olup olmadığını kontrol edin. Örneğin, yalnızca kullanıcının satın almaya çalıştığı bölgeden farklı bir bölgede stokta olan bir ürünü sorguluyor olabilirsiniz. Bir ürünün satın alınabilmesi için etkin olması, uygulamasının yayınlanmış ve kullanıcının ülkesinde kullanılabilir olması gerekir.

Bazen, özellikle test sırasında, ürün yapılandırmasındaki her şey doğru olsa da kullanıcılar bu hatayı görmeye devam eder. Bunun nedeni, ürün ayrıntılarının Google sunucularındaki yayılmasında yaşanan gecikme olabilir. Daha sonra tekrar deneyin.

GELİŞTİRİCİ_HATASI

Sorun

Bu, bir API'yi uygunsuz kullandığınızı gösteren önemli bir hatadır. Örneğin, BillingClient.launchBillingFlow için yanlış parametrelerin sağlanması bu hataya neden olabilir.

Olası çözüm

Farklı Play Faturalandırma Kitaplığı çağrılarını doğru şekilde kullandığınızdan emin olun. Ayrıca, hata hakkında daha fazla bilgi için hata ayıklama mesajını inceleyin.