Quando una chiamata della Libreria Fatturazione Play attiva un'azione, la libreria restituisce una
BillingResult
risposta per informare gli sviluppatori del risultato. Ad esempio, se utilizzi
queryProductDetailsAsync
per ottenere le offerte disponibili per l'utente, il codice di risposta contiene un
codice OK e fornisce l'oggetto ProductDetails
corretto oppure contiene una risposta diversa che indica il motivo per cui non è stato possibile fornire l'oggetto
ProductDetails.
Non tutti i codici di risposta indicano errori. La pagina di riferimento BillingResponseCode
fornisce una descrizione dettagliata di ciascuna delle risposte
trattate in questa guida.
Di seguito sono riportati alcuni esempi di codici di risposta che non indicano errori:
BillingClient.BillingResponseCode.OK: l'azione attivata dalla chiamata è stata completata correttamente.BillingClient.BillingResponseCode.USER_CANCELED: per le azioni che mostrano i flussi dell'interfaccia utente del Play Store all'utente, questa risposta indica che l'utente ha abbandonato questi flussi dell'interfaccia utente senza completare la procedura.
Quando il codice di risposta indica un errore, a volte la causa è dovuta a condizioni temporanee, quindi è possibile il ripristino. Quando una chiamata a un metodo della Libreria Fatturazione Play
restituisce un BillingResponseCode
valore che indica una condizione recuperabile, devi riprovare a effettuare la chiamata. In altri casi, le condizioni non sono considerate temporanee, pertanto non è consigliabile riprovare.
Gli errori temporanei richiedono strategie di ripetizione dei tentativi diverse a seconda di fattori quali
se l'errore si verifica quando gli utenti sono in sessione, ad esempio quando un utente sta
seguendo un flusso di acquisto, o se l'errore si verifica in background, ad
esempio quando esegui una query sugli acquisti esistenti dell'utente durante onResume.
La sezione relativa alle strategie di ripetizione dei tentativi di seguito fornisce esempi di
queste diverse strategie e la sezione Risposte BillingResult
ripetibili consiglia
la strategia più adatta per ogni codice di risposta.
Oltre al codice di risposta, alcune risposte di errore includono messaggi a scopo di debug e logging.
Strategie di ripetizione dei tentativi
Semplice ripetizione dei tentativi
Nelle situazioni in cui l'utente è in sessione, è meglio implementare una semplice strategia di ripetizione dei tentativi in modo che l'errore interrompa l'esperienza utente il meno possibile. In questo caso, ti consigliamo una semplice strategia di ripetizione dei tentativi con un numero massimo di tentativi come condizione di uscita.
L'esempio seguente mostra una semplice strategia di ripetizione dei tentativi per gestire un errore
durante la creazione di una BillingClient
connessione:
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) }
} finally {
tries++
}
} while (tries <= maxTries && !isConnectionEstablished)
}
...
}
Ripetizione dei tentativi con backoff esponenziale
Ti consigliamo di utilizzare il backoff esponenziale per le operazioni della Libreria Fatturazione Play che vengono eseguite in background e non influiscono sull'esperienza utente durante la sessione.
Ad esempio, è opportuno implementare questa operazione quando si riconoscono nuovi acquisti, perché può avvenire in background e il riconoscimento non deve avvenire in tempo reale se si verifica un errore.
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
}
Risposte BillingResult ripetibili
NETWORK_ERROR (codice di errore 12)
Problema
Questo errore indica che si è verificato un problema con la connessione di rete tra il dispositivo e i sistemi di Google Play.
Possibile risoluzione
Per eseguire il ripristino, utilizza semplici ripetizioni dei tentativi o il backoff esponenziale, a seconda dell'azione che ha attivato l'errore.
SERVICE_TIMEOUT (codice di errore -3)
Problema
Questo errore indica che la richiesta ha raggiunto il timeout massimo prima che Google Play possa rispondere. Ciò potrebbe essere causato, ad esempio, da un ritardo nell'esecuzione dell'azione richiesta dalla chiamata della Libreria Fatturazione Play.
Possibile risoluzione
In genere si tratta di un problema temporaneo. Riprova a effettuare la richiesta utilizzando una strategia di backoff semplice o backoff esponenziale, a seconda dell'azione che ha restituito l'errore.
A differenza di SERVICE_DISCONNECTED
di seguito, la connessione al servizio Fatturazione Google Play non viene interrotta e devi solo riprovare a eseguire l'operazione della Libreria Fatturazione Play tentata.
SERVICE_DISCONNECTED (codice di errore -1)
Problema
Questo errore irreversibile indica che la connessione dell'app client al servizio Google Play
Store tramite BillingClient
è stata interrotta.
Possibile risoluzione
Vivamente consigliato: attiva la riconnessione automatica del servizio
La versione 8.0.0 della Libreria Fatturazione Play ha introdotto la funzionalità enableAutoServiceReconnection().
Ti consigliamo vivamente di attivare questa funzionalità quando crei BillingClient. In questo modo, la libreria tenta automaticamente di ristabilire la connessione quando viene effettuata una chiamata all'API di fatturazione mentre il servizio è disconnesso, riducendo significativamente il verificarsi di questo errore.
Kotlin
val billingClient = BillingClient.newBuilder(context)
.setListener(listener)
.enablePendingPurchases()
.enableAutoServiceReconnection() // Enable automatic service reconnection
.build()
Java
BillingClient billingClient = BillingClient.newBuilder(context)
.setListener(listener)
.enablePendingPurchases()
.enableAutoServiceReconnection() // Enable automatic service reconnection
.build();
Se hai attivato la riconnessione automatica del servizio
La Libreria Fatturazione Play tenterà automaticamente di riconnettersi. Se continui a ricevere un codice di risposta SERVICE_DISCONNECTED quando effettui una chiamata API, significa che la libreria non è riuscita a riconnettersi dopo i tentativi automatici.
In questo scenario, devi implementare la logica di ripetizione dei tentativi nella tua app:
- Per le azioni avviate dall'utente (in sessione): utilizza semplici ripetizioni dei tentativi della chiamata API. Il problema sottostante potrebbe essere temporaneo.
- Per le richieste in background: implementa le ripetizioni dei tentativi con backoff esponenziale per evitare di sovraccaricare il sistema se la disconnessione è prolungata.
Se NON hai attivato la riconnessione automatica del servizio
Per evitare il più possibile questo errore, controlla sempre la connessione a Google
Play Services prima di effettuare chiamate con la Libreria Fatturazione Play chiamando
BillingClient.isReady().
Per tentare il ripristino da SERVICE_DISCONNECTED
, l'app client deve provare a ristabilire la connessione utilizzando
BillingClient.startConnection.
Come per SERVICE_TIMEOUT
, utilizza semplici ripetizioni dei tentativi o il backoff esponenziale, a seconda dell'azione che ha attivato
l'errore.
SERVICE_UNAVAILABLE (codice di errore 2)
Nota importante:
A partire dalla Libreria Fatturazione Google Play 6.0.0, SERVICE_UNAVAILABLE non viene più restituito per i problemi di rete. Viene restituito quando il servizio di fatturazione non è disponibile e negli scenari di SERVICE_TIMEOUT deprecati.
Problema
Questo errore temporaneo indica che il servizio di fatturazione Google Play non è attualmente disponibile. Nella maggior parte dei casi, ciò significa che si è verificato un problema di connessione di rete tra il dispositivo client e i servizi di fatturazione Google Play.
Possibile risoluzione
In genere si tratta di un problema temporaneo. Riprova a effettuare la richiesta utilizzando una strategia di backoff semplice o backoff esponenziale, a seconda dell'azione che ha restituito l'errore.
A differenza di SERVICE_DISCONNECTED
, la connessione al servizio Fatturazione Google Play non viene interrotta e devi
riprovare a eseguire l'operazione tentata.
BILLING_UNAVAILABLE (codice di errore 3)
Problema
Questo errore indica che si è verificato un errore di fatturazione dell'utente durante la procedura di acquisto. Di seguito sono riportati alcuni esempi di quando può verificarsi:
- L'app Play Store sul dispositivo dell'utente non è aggiornata.
- L'utente si trova in un paese non supportato.
- L'utente è un utente aziendale e l'amministratore aziendale ha disattivato la possibilità per gli utenti di effettuare acquisti.
- Google Play non è in grado di addebitare il metodo di pagamento dell'utente. Ad esempio, la carta di credito dell'utente potrebbe essere scaduta.
Possibile risoluzione
In questo caso, è improbabile che i tentativi automatici siano utili. Tuttavia, una ripetizione manuale dei tentativi può essere utile se l'utente risolve la condizione che ha causato il problema. Ad esempio, se l'utente aggiorna la versione del Play Store a una versione supportata, una ripetizione manuale dei tentativi dell'operazione iniziale potrebbe funzionare.
Se questo errore si verifica quando l'utente non è in sessione, potrebbe non avere senso riprovare.
Quando ricevi un BILLING_UNAVAILABLE
errore a seguito del flusso di acquisto, è molto probabile che l'utente abbia ricevuto
un feedback da Google Play durante la procedura di acquisto e potrebbe essere a conoscenza di cosa
è andato storto. In questo caso, potresti mostrare un messaggio di errore che specifica che si è verificato un problema e offrire un pulsante "Riprova" per dare all'utente la possibilità di una ripetizione manuale dei tentativi dopo aver risolto il problema.
ERRORE (codice di errore 6)
Problema
Si tratta di un errore irreversibile che indica un problema interno con Google Play stesso.
Possibile risoluzione
A volte i problemi interni di Google Play che causano ERROR
sono temporanei e per la
mitigazione è possibile implementare una ripetizione dei tentativi con backoff esponenziale. Quando gli utenti sono in sessione, è preferibile una semplice ripetizione dei tentativi.
ITEM_ALREADY_OWNED
Problema
Questa risposta indica che l'utente di Google Play possiede già l'abbonamento o il prodotto con acquisto una tantum che sta tentando di acquistare. Nella maggior parte dei casi, non si tratta di un errore temporaneo, tranne quando è causato da una cache di Google Play obsoleta.
Possibile risoluzione
Per evitare che questo errore si verifichi quando la causa non è un problema di cache, non offrire un prodotto in vendita quando l'utente lo possiede già. Assicurati di controllare i diritti dell'utente quando mostri i prodotti disponibili per l'acquisto e filtra di conseguenza ciò che l'utente può acquistare.
Quando l'app client riceve questo errore a causa di un problema di cache, l'errore attiva l'aggiornamento della cache di Google Play con i dati più recenti del backend di Play.
In questo caso, la ripetizione dei tentativi dopo l'errore dovrebbe risolvere questa istanza temporanea specifica. Chiama BillingClient.queryPurchasesAsync()
dopo aver ricevuto un ITEM_ALREADY_OWNED
per verificare se l'utente ha acquisito il prodotto e, in caso contrario,
implementa una semplice logica di ripetizione dei tentativi per riprovare ad acquistare.
ITEM_NOT_OWNED
Problema
Questa risposta all'acquisto indica che l'utente di Google Play non possiede l'abbonamento o il prodotto con acquisto una tantum che sta tentando di sostituire, riconoscere o utilizzare. Nella maggior parte dei casi, non si tratta di un errore temporaneo, tranne quando è causato dalla cache di Google Play che diventa obsoleta.
Possibile risoluzione
Quando l'errore viene ricevuto a causa di un problema di cache, l'errore attiva l'aggiornamento della cache di Google Play con i dati più recenti del backend di Play. La ripetizione dei tentativi con una semplice strategia di ripetizione dei tentativi dopo l'errore dovrebbe risolvere questa istanza temporanea specifica. Chiama BillingClient.queryPurchasesAsync() dopo aver ricevuto un ITEM_NOT_OWNED per verificare se l'utente ha
acquisito il prodotto. In caso contrario, utilizza una semplice logica di ripetizione dei tentativi per riprovare ad acquistare.
Risposte BillingResult non ripetibili
Non puoi eseguire il ripristino da questi errori utilizzando la logica di ripetizione dei tentativi.
FEATURE_NOT_SUPPORTED
Problema
Questo errore non ripetibile indica che la funzionalità di fatturazione Google Play non è supportata sul dispositivo dell'utente, probabilmente a causa di una versione precedente del Play Store.
Ad esempio, alcuni dispositivi degli utenti potrebbero non supportare la messaggistica in-app.
Possibile mitigazione
Utilizza BillingClient.isFeatureSupported() per verificare il supporto delle funzionalità prima di effettuare la chiamata alla Libreria Fatturazione Play.
when {
billingClient.isReady -> {
if (billingClient.isFeatureSupported(BillingClient.FeatureType.IN_APP_MESSAGING)) {
// use feature
}
}
}
USER_CANCELED
Problema
L'utente ha fatto clic al di fuori dell'interfaccia utente del flusso di fatturazione.
Possibile risoluzione
Si tratta di informazioni e può fallire senza problemi.
ITEM_UNAVAILABLE
Problema
L'abbonamento o il prodotto con acquisto una tantum di fatturazione Google Play non è disponibile per l'acquisto per questo utente.
Possibile mitigazione
Assicurati che l'app aggiorni i dettagli del prodotto tramite queryProductDetailsAsync come consigliato. Tieni conto della frequenza con cui il catalogo prodotti cambia nella configurazione di Play Console per implementare aggiornamenti aggiuntivi, se necessario.
Tenta di vendere solo i prodotti sulla fatturazione Google Play che restituiscono le informazioni corrette
tramite queryProductDetailsAsync.
Controlla la configurazione dell'idoneità del prodotto per eventuali incongruenze.
Ad esempio, potresti eseguire una query per un prodotto disponibile solo per una regione diversa da quella in cui l'utente sta tentando di acquistare.
Per essere disponibile per l'acquisto, un prodotto deve essere attivo, la sua app deve essere pubblicata e deve essere disponibile nel paese dell'utente.
A volte, in particolare durante i test, la configurazione del prodotto è corretta, ma gli utenti continuano a visualizzare questo errore. Ciò potrebbe essere dovuto a un ritardo di propagazione dei dettagli del prodotto sui server di Google. Riprova più tardi.
DEVELOPER_ERROR
Problema
Si tratta di un errore irreversibile che indica che stai utilizzando un'API in modo errato.
Ad esempio, la fornitura di parametri errati a BillingClient.launchBillingFlow può
causare questo errore.
Possibile risoluzione
Assicurati di utilizzare correttamente le diverse chiamate della Libreria Fatturazione Play. Inoltre, controlla il messaggio di debug per ulteriori informazioni sull'errore.