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:
BillingClient.BillingResponseCode.OK
: działanie wywołane przez wywołanie zostało wykonane.BillingClient.BillingResponseCode.USER_CANCELED
: w przypadku działań, które wyświetlają użytkownikowi przepływy interfejsu Sklepu Play, ta odpowiedź wskazuje, że użytkownik opuścił te przepływy bez ukończenia procesu.
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_OWNED
wywoł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.