Obsługa kodów odpowiedzi BillingResult

Gdy wywołanie Biblioteki płatności w Play wywoła działanie, biblioteka zwróci BillingResult aby poinformować deweloperów o wynikach. 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 błąd, jego przyczyną jest czasami przejściowe, więc możliwe jest przywrócenie zdrowia. Podczas połączenia z aplikacją Google Play Metoda Biblioteki płatności zwraca BillingResponseCode wskazujący warunek, który można przywrócić, należy ponowić próbę wywołania. W w innych przypadkach warunki nie są uznawane za przejściowe i dlatego ponawianie niezalecane.

Błędy przejściowe wymagają różnych strategii ponawiania w zależności od takich czynników jak: czy błąd występuje, gdy użytkownicy są w sesji – na przykład gdy użytkownik podczas procesu zakupu – lub błąd występuje w tle – Na przykład gdy wysyłasz zapytanie o zakupy użytkownika w 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

Proste ponowienie

W sytuacjach, gdy użytkownik jest w trakcie sesji, lepiej zastosować proste ponawianie próby tak, by błąd zakłócał działanie usługi jak to tylko możliwe. W takim przypadku zalecamy prostą strategię ponawiania z maksymalną jako warunku wyjścia.

Poniższy przykład pokazuje prostą strategię ponawiania próby obsługi błędu podczas tworzenia BillingClient połączenie:

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

W przypadku operacji na bibliotece Płatności w Play zalecamy stosowanie wykładniczego czasu do ponowienia są rejestrowane w tle i nie wpływają na wrażenia użytkownika, w sesji.

Wdrożenie tej funkcji było na przykład słuszne, jeśli chcesz poinformować o nowych ponieważ ta operacja może odbywać się w tle, w przypadku wystąpienia błędu potwierdzenie nie musi następować 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 z możliwością pobrania wyniku płatności

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ć dane, użyj prostych ponownych prób lub wykładniczego czasu do ponowienia, w zależności od tego, działanie spowodowało błąd.

SERVICE_TIMEOUT (kod błędu -3)

Problem

Ten błąd oznacza, że żądanie osiągnęło maksymalny czas oczekiwania, zanim Google Play może 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 oznacza, że połączenie aplikacji klienckiej z Google Play Sklep dostępny przez BillingClient została zerwana.

Możliwe rozwiązanie

Aby uniknąć tego błędu, zawsze sprawdzaj połączenie z Google Usługi Google Play przed nawiązaniem połączenia przy użyciu Biblioteki płatności w Play przez połączenie 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 Biblioteki płatności w Google Play w wersji 6.0.0 usługa SERVICE_UNAVAILABLE nie jest zwracany w przypadku problemów z siecią. Jest on zwracany, gdy usługa rozliczeniowa jest niedostępne i wycofane scenariusze przypadku SERVICE_TIMEOUT.

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. 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 , połączenie z usługą Płatności w Google Play nie zostanie przerwane i musisz ponowienie próby.

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 parametr karta kredytowa użytkownika mogła wygasnąć.

Możliwe rozwiązanie

W tym przypadku automatyczne ponawianie próby prawdopodobnie nie pomogą. Ręczne ponowienie próby pomoc, jeśli użytkownik rozwiązuje problem, który spowodował problem. Na przykład, jeśli użytkownik aktualizuje wersję Sklepu Play do obsługiwanej wersji, a następnie ponowienie pierwszej operacji.

Jeśli ten błąd występuje, gdy użytkownik nie jest w sesji, ponawianie próby może nie spowodować z całego świata. Gdy otrzymasz BILLING_UNAVAILABLE błąd w trakcie procesu zakupu, jest duże prawdopodobieństwo, że użytkownik otrzymał opinii z Google Play w trakcie procesu zakupu i być może wiesz, co poszło nie tak. W takim przypadku może wyświetlić się komunikat o błędzie, w którym wskażesz poszło nie tak i zaoferował przycisk „Spróbuj ponownie”, aby użytkownik mógł ponownych prób po rozwiązaniu problemu.

ERROR (kod błędu 6)

Problem

To błąd krytyczny, który wskazuje na wewnętrzny problem z Google Play

Możliwe rozwiązanie

Czasami wewnętrzne problemy z Google Play prowadzące do ERROR są przejściowe i można wdrożyć ponowienie ze wzrastającym czasem do ponowienia dla środków zaradczych. Gdy użytkownicy są w trakcie sesji, preferowane jest proste ponowienie próby.

IDENTYFIKATOR_PRODUKTU

Problem

Ta odpowiedź wskazuje, że użytkownik Google Play ma już subskrypcji ani jednorazowego zakupu produktu. 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 przyczyna nie jest problemem z pamięcią podręczną, nie dodawaj produktu do kupienia, gdy użytkownik jest już jego właścicielem. Sprawdź uprawnień użytkowników, gdy prezentujesz produkty dostępne do zakupu. filtrować według tego, co 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. Zadzwoń pod numer BillingClient.queryPurchasesAsync() po uzyskaniu ITEM_ALREADY_OWNED aby sprawdzić, czy użytkownik nabył produkt – jeśli nie wdrożyć prostą zasadę ponawiania prób, aby dokonać zakupu.

PRODUKT_NIE_DOSTĘPNY

Problem

Ta odpowiedź zakupowa wskazuje, że użytkownik Google Play nie jest właścicielem subskrypcję lub produkt kupowany raz, który użytkownik próbuje zastąpić; akceptowalne i wykorzystywane. Nie jest to błąd przejściowy w większości przypadków, chyba że jest spowodowany przez Google. Pamięć podręczna Google Play staje się nieaktualna.

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. Próbuję ponownie ze prostą strategią ponawiania próby, gdy błąd powinien rozwiązać problem. instancji przejściowej. Zadzwoń pod numer BillingClient.queryPurchasesAsync() po otrzymaniu ITEM_NOT_OWNED, aby sprawdzić, czy użytkownik nabył(a) produkt. Jeśli tak nie jest, użyj prostej funkcji logicznej ponawiania prób, aby ponownie zakup.

Odpowiedzi, których nie można pobrać w wyniku płatności

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

FUNKCJA_NIE_OBSŁUGIWOWANA

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.

Na przykład niektórzy z Twoich użytkowników Urządzenia nie obsługują funkcji wiadomości w aplikacji.

Możliwe środki zaradcze

Zanim zadzwonisz do Płatności w Play, sprawdź listę obsługiwanych funkcji w usłudze BillingClient.isFeatureSupported(). Biblioteka.

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

ANULOWANO_UŻYTKOWNIKA

Problem

Użytkownik wyłączył interfejs procesu płatności.

Możliwe rozwiązanie

Ta funkcja ma charakter wyłącznie informacyjny, a jej niepowodzenie może się zakończyć niepowodzeniem.

PRODUKT_NIEDOSTĘPNY

Problem

Subskrypcja Płatności w Google Play lub produkt do jednorazowego zakupu nie są dostępnych do zakupu dla tego użytkownika.

Możliwe środki zaradcze

Upewnij się, że zgodnie z zaleceniami szczegóły produktu w aplikacji queryProductDetailsAsync są odświeżane. Weź pod uwagę częstotliwość przesyłania danych należy wprowadzić zmiany w katalogu produktów w konfiguracji Konsoli Play, w razie potrzeby – 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óźnienia na propagację szczegółów produktu na serwerach Google. Spróbuj jeszcze raz później.

BŁĄD_PROGRAMISTY

Problem

Jest to błąd krytyczny, który wskazuje, że nieprawidłowo używasz interfejsu API. 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ń. Przeczytaj też wiadomość na temat debugowania, aby uzyskać więcej informacji o błędzie.