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:
BillingClient.BillingResponseCode.OK
: działanie wywołane przez połączenie zostało zakończone pomyślnie.BillingClient.BillingResponseCode.USER_CANCELED
: w przypadku działań, które wyświetlają interfejs użytkownika Sklepu Play, ta odpowiedź wskazuje, że użytkownik opuścił te przepływy interfejsu użytkownika, nie kończąc proces tworzenia konta.
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.