Obsługa kodów odpowiedzi BillingResult

Gdy wywołanie Biblioteki płatności Google Play powoduje wykonanie działania, biblioteka zwraca odpowiedź BillingResult, aby poinformować deweloperów o wyniku. Na przykład, jeśli używasz queryProductDetailsAsync aby uzyskać oferty dostępne dla użytkownika, kod odpowiedzi zawiera albo OK i zapewnia prawidłowy kod ProductDetails obiektu lub zawiera inną odpowiedź, która wskazuje przyczynę, dla której ProductDetails. nie udało się podać obiektu.

Nie wszystkie kody odpowiedzi są błędami. BillingResponseCode strona odsyłająca zawiera szczegółowy opis każdej odpowiedzi omawiamy w tym przewodniku. Oto przykłady kodów odpowiedzi, które nie wskazują błędów:

Jeśli kod odpowiedzi wskazuje na błąd, jego przyczyną jest czasami przejściowe, więc możliwe jest przywrócenie zdrowia. Gdy wywołanie metody Biblioteki płatności Google Play zwraca wartość BillingResponseCode, która wskazuje na stan możliwy do odzyskania, należy ponownie wykonać wywołanie. W innych przypadkach warunki nie są uważane za przejściowe, dlatego nie zalecamy ponownego próbowania.

Przejściowe błędy wymagają różnych strategii ponownego próby w zależności od czynników takich jak to, czy błąd wystąpił podczas sesji (np. gdy użytkownik przechodzi przez proces zakupu) czy na drugim planie (np. gdy wysyłasz zapytanie o dotychczasowe zakupy użytkownika podczas onResume). W sekcji o strategiach ponawiania prób poniżej znajdziesz przykłady tych różnych strategii oraz BillingResult, które można wielokrotnie wykorzystać Sekcja Odpowiedzi zaleca, która strategia sprawdzi się najlepiej w przypadku każdego kodu odpowiedzi.

Oprócz kodu odpowiedzi niektóre odpowiedzi na błędy zawierają komunikaty dotyczące na potrzeby debugowania i logowania.

Strategie ponawiania

Prosta próba

W sytuacjach, gdy użytkownik jest w sesji, lepiej jest zastosować prostą strategię ponownego próbowania, aby błąd jak najmniej zakłócał wrażenia użytkownika. W takim przypadku zalecamy użycie prostej strategii ponownego próbowania z warunkiem wyjścia w postaci maksymalnej liczby prób.

Ten przykład pokazuje prostą strategię ponownych prób, która pozwala obsłużyć błąd podczas nawiązywania połączenia BillingClient:

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)
  }
  ...
}

Ponawianie wykładniczego ponowienia

Zalecamy stosowanie wykładniczego zmniejszania częstotliwości w przypadku operacji Biblioteki płatności Google Play, które odbywają się w tle i nie wpływają na wrażenia użytkownika podczas sesji.

Można go na przykład zastosować podczas potwierdzania nowych zakupów, ponieważ ta operacja może być wykonywana w tle, a jeśli wystąpi błąd, potwierdzenie nie musi nastąpić w czasie rzeczywistym.

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
}

Odpowiedzi BillingResult, które można pobrać ponownie

NETWORK_ERROR (kod błędu 12)

Problem

Ten błąd oznacza, że wystąpił problem z połączeniem sieciowym. między urządzeniem a systemami Google Play.

Możliwe rozwiązanie

Aby odzyskać kontrolę, użyj prostych ponownych prób lub wzrastającego czasu do ponowienia, w zależności od tego, które działanie spowodowało błąd.

SERVICE_TIMEOUT (kod błędu -3)

Problem

Ten błąd wskazuje, że żądanie osiągnęło maksymalny limit czasu, zanim Google Play zdołało odpowiedzieć. Może to być spowodowane np. opóźnieniem w wykonaniu działania żądanego przez wywołanie Biblioteki płatności w Play.

Możliwe rozwiązanie

Jest to zwykle problem przejściowy. Ponów żądanie, używając albo prostej lub wykładniczej strategii ponowienia, w zależności od tego, które działanie zwróciło .

Usuń polubienie: SERVICE_DISCONNECTED to połączenie z usługą Płatności w Google Play nie zostanie rozłączone, a Ty ponów próbę wykonania operacji w Bibliotece płatności w Play.

SERVICE_DISCONNECTED (kod błędu -1)

Problem

Ten błąd krytyczny wskazuje, że połączenie aplikacji klienta z usługą Google Play Store za pomocą interfejsu BillingClient zostało zerwane.

Możliwe rozwiązanie

Aby w jak największym stopniu uniknąć tego błędu, przed wywołaniem metody w bibliotece płatności Play zadbaj o połączenie z usługami Google Play, wykonując wywołanie BillingClient.isReady().

Aby spróbować odzyskać konto z SERVICE_DISCONNECTED , aplikacja kliencka powinna spróbować ponownie nawiązać połączenie przy użyciu BillingClient.startConnection

Tak samo jak w SERVICE_TIMEOUT , użyj prostych ponownych prób lub wykładniczego czasu do ponowienia w zależności od wywołanego działania błąd.

SERVICE_UNAVAILABLE (kod błędu 2)

Ważna uwaga:

Od wersji 6.0.0 Biblioteki płatności w Google Play wartość SERVICE_UNAVAILABLE nie jest już zwracana w przypadku problemów z siecią. Zwracany, gdy usługa płatności jest niedostępna, oraz w przypadkach SERVICE_TIMEOUT, które zostały wycofane.

Problem

Ten tymczasowy błąd wskazuje, że usługa Płatności w Google Play jest obecnie niedostępna. W większości przypadków oznacza to problem z połączeniem sieciowym. między urządzeniem klienta a usługami płatności w Google Play.

Możliwe rozwiązanie

Jest to zwykle problem przejściowy. Ponownie wysyła żądanie, stosując strategię prostego lub wykładniczego odsunięcia w czasie, w zależności od działania, które zwróciło błąd.

W przeciwieństwie do SERVICE_DISCONNECTED połączenie z usługą Płatności w Google Play nie zostało zerwane i musisz ponownie wykonać próbę wykonania danej operacji.

BILLING_UNAVAILABLE (kod błędu 3)

Problem

Ten błąd oznacza, że błąd płatności za użytkownika wystąpił podczas procesu zakupu. Oto kilka przykładów:

  • Aplikacja Sklep Play na urządzeniu użytkownika jest nieaktualna.
  • Użytkownik znajduje się w nieobsługiwanym kraju.
  • Użytkownik jest użytkownikiem firmowym, a jego administrator zablokował użytkowników dokonywania zakupów.
  • Google Play nie może obciążyć formy płatności użytkownika. Na przykład karta kredytowa użytkownika mogła stracić ważność.

Możliwe rozwiązanie

W tym przypadku automatyczne próby nie pomogą. Jednak ręczne ponowne uruchomienie może pomóc, jeśli użytkownik rozwiąże problem. Na przykład, jeśli użytkownik aktualizuje wersję Sklepu Play do obsługiwanej wersji, a następnie ponowna próba pierwotnej operacji może zadziałać.

Jeśli ten błąd wystąpi, gdy użytkownik nie jest w sesji, ponowne próbowanie może nie mieć sensu. Jeśli w ramach procesu zakupu otrzymasz błąd BILLING_UNAVAILABLE, najprawdopodobniej użytkownik otrzymał opinię od Google Play podczas procesu zakupu i może wiedzieć, co poszło nie tak. W takim przypadku może wyświetlić się komunikat o błędzie z informacją o czymś poszło nie tak i zaoferował przycisk „Spróbuj ponownie”, aby użytkownik mógł ponownych prób po rozwiązaniu problemu.

BŁĄD (kod błędu 6)

Problem

Jest to błąd krytyczny wskazujący na wewnętrzny problem z Google Play.

Możliwe rozwiązanie

Czasami wewnętrzne problemy Google Play, które powodują błąd ERROR, są przejściowe. Aby je rozwiązać, można zastosować ponowne próby z wykładniczym zmniejszaniem częstotliwości. Gdy użytkownicy są w trakcie sesji, preferowane jest proste ponowienie próby.

IDENTYFIKATOR_PRODUKTU

Problem

Ta odpowiedź wskazuje, że użytkownik Google Play jest już właścicielem subskrypcji lub produktu jednorazowego zakupu, który próbuje kupić. W większości przypadków nie jest to błąd przejściowy, chyba że jest spowodowany przez nieaktualnej pamięci podręcznej Google Play.

Możliwe rozwiązanie

Aby uniknąć tego błędu, gdy przyczyną nie jest problem z pamięcią podręczną, nie oferuj produktu do zakupu, jeśli użytkownik już go ma. Pamiętaj, aby sprawdzać uprawnienia użytkownika, gdy wyświetlasz produkty dostępne do kupienia, i odpowiednio filtrować produkty, które użytkownik może kupić. Gdy aplikacja kliencka otrzyma ten błąd z powodu problemu z pamięcią podręczną, zostaje on aktywowany Pamięć podręczna Google Play jest aktualizowana o najnowsze dane z backendu Google Play. Ponowna próba po wystąpieniu błędu powinna rozwiązać tę konkretną instancję przejściową w tych kwestii. Po otrzymaniu wywołania ITEM_ALREADY_OWNEDwywołaj funkcję BillingClient.queryPurchasesAsync(), aby sprawdzić, czy użytkownik kupił produkt. Jeśli nie, zastosuj prostą logikę ponownego próbowania, aby ponownie spróbować dokonać zakupu.

ITEM_NOT_OWNED

Problem

Ta odpowiedź na zakup wskazuje, że użytkownik Google Play nie jest właścicielem subskrypcji ani produktu zakupionego jednorazowo, który próbuje zastąpić, potwierdzić lub wykorzystać. W większości przypadków nie jest to błąd przejściowy, chyba że jest spowodowany przez nieaktualny stan pamięci podręcznej Google Play.

Możliwe rozwiązanie

Jeśli komunikat o błędzie pojawia się z powodu problemu z pamięcią podręczną, Google aktywuje ten błąd. Pamięć podręczna Google Play jest aktualizowana o najnowsze dane z backendu Google Play. Ponowna próba z prostą strategią ponownego próbowania po błędzie powinna rozwiązać ten konkretny przypadek przejściowy. Wywołaj BillingClient.queryPurchasesAsync() po otrzymaniu ITEM_NOT_OWNED, aby sprawdzić, czy użytkownik nabył produkt. Jeśli tak nie jest, użyj prostej funkcji logicznej ponawiania prób, aby ponownie zakup.

Odpowiedzi BillingResult, których nie można odzyskać

Nie możesz naprawić tych błędów przy użyciu mechanizmu ponownych prób.

FEATURE_NOT_SUPPORTED

Problem

Ten błąd, którego nie można pobrać, oznacza, że funkcja Płatności w Google Play jest obsługiwana na urządzeniu użytkownika, prawdopodobnie ze względu na starszą wersję Sklepu Play.

Może się na przykład zdarzyć, że niektóre urządzenia użytkowników nie obsługują wysyłania wiadomości w aplikacji.

Możliwe środki zaradcze

Zanim skontaktujesz się z biblioteką płatności Google Play, użyj BillingClient.isFeatureSupported(), aby sprawdzić, czy dana funkcja jest obsługiwana.

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

USER_CANCELED

Problem

Użytkownik kliknął poza interfejsem procesu płatności.

Możliwe rozwiązanie

Jest to tylko informacja i może się nie udać.

ITEM_UNAVAILABLE

Problem

Subskrypcja lub produkt kupowany raz w Płatnościach w Google Play nie jest dostępny do kupienia przez tego użytkownika.

Możliwe działania zaradcze

Upewnij się, że aplikacja odświeża szczegóły produktu za pomocą queryProductDetailsAsync zgodnie z zaleceniami. Zwróć uwagę, jak często zmienia się twój katalog produktów w Konsoli Play, aby w razie potrzeby wdrożyć dodatkowe odświeżenia. W ramach Płatności w Google Play próbuj sprzedawać tylko te produkty, które zwracają prawo informacje na stronie queryProductDetailsAsync. Sprawdź, czy w konfiguracji kwalifikacji produktów nie ma żadnych niespójności. Możesz na przykład poprosić o produkt, który jest dostępny tylko region inny niż ten, który użytkownik próbuje kupić. Aby można było kupić produkt, musi on być aktywny, a zawierająca go aplikacja musi być została opublikowana, a jej aplikacja musi być dostępna w kraju użytkownika.

Czasami, zwłaszcza podczas testów, wszystko w usłudze jest poprawne , a użytkownicy nadal widzą ten błąd. Przyczyną może być opóźnienie na propagację szczegółów produktu na serwerach Google. Spróbuj ponownie później.

BŁĄD_PROGRAMISTY

Problem

Jest to błąd krytyczny, który wskazuje, że interfejs API jest używany nieprawidłowo. Na przykład podanie nieprawidłowych parametrów w BillingClient.launchBillingFlow może powoduje ten błąd.

Możliwe rozwiązanie

Sprawdź, czy prawidłowo korzystasz z innej Biblioteki płatności w Play połączeń. Aby uzyskać więcej informacji o błędzie, sprawdź też komunikat debugowania.