Saat panggilan Library Layanan Penagihan Play memicu tindakan, library akan menampilkan
respons
BillingResult
untuk memberi tahu developer tentang hasilnya. Misalnya, jika Anda menggunakan
queryProductDetailsAsync
untuk mendapatkan penawaran yang tersedia bagi pengguna, kode respons akan berisi
kode OK dan memberikan objek ProductDetails
yang tepat, atau berisi respons berbeda yang menunjukkan alasan objek
ProductDetails
tidak dapat diberikan.
Tidak semua kode respons merupakan error. Halaman referensi BillingResponseCode
memberikan deskripsi mendetail tentang setiap respons
yang dibahas dalam panduan ini.
Beberapa contoh kode respons yang tidak menunjukkan error adalah:
BillingClient.BillingResponseCode.OK
: tindakan yang dipicu oleh panggilan berhasil diselesaikan.BillingClient.BillingResponseCode.USER_CANCELED
: untuk tindakan yang menampilkan alur UI Play Store kepada pengguna, respons ini menunjukkan bahwa pengguna telah keluar dari alur UI tersebut tanpa menyelesaikan proses.
Ketika kode respons menunjukkan error, terkadang hal tersebut disebabkan oleh
kondisi sementara sehingga dapat dipulihkan. Saat panggilan ke metode Library Layanan Penagihan
Play menampilkan nilai BillingResponseCode
yang menunjukkan kondisi yang dapat dipulihkan, Anda harus mencoba lagi panggilan tersebut. Dalam
kasus lain, kondisi tidak dianggap sementara sehingga disarankan untuk tidak mencoba
lagi.
Error sementara memerlukan strategi percobaan ulang yang berbeda bergantung pada faktor-faktor
seperti apakah error terjadi saat pengguna sedang berada dalam sesi—misalnya, saat pengguna berada
dalam alur pembelian—atau error terjadi di latar belakang—misalnya,
saat Anda membuat kueri pembelian yang sudah ada milik pengguna selama onResume
.
Bagian strategi percobaan ulang di bawah memberikan contoh strategi yang berbeda
ini dan bagian
Respons BillingResult
yang Dapat Dicoba Ulang merekomendasikan strategi yang paling sesuai untuk setiap kode respons.
Selain kode respons, beberapa respons error menyertakan pesan untuk tujuan proses debug dan logging.
Strategi percobaan ulang
Percobaan ulang sederhana
Dalam situasi ketika pengguna sedang berada dalam sesi, sebaiknya implementasikan strategi percobaan ulang yang sederhana sehingga error tersebut tidak akan mengganggu pengalaman pengguna. Dalam hal ini, kami merekomendasikan strategi percobaan ulang sederhana dengan jumlah maksimum upaya sebagai kondisi keluar.
Contoh berikut menunjukkan strategi percobaan ulang sederhana untuk menangani error
saat membuat koneksi
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)
}
...
}
Percobaan ulang backoff eksponensial
Sebaiknya gunakan backoff eksponensial untuk operasi Library Layanan Penagihan Play yang terjadi di latar belakang dan tidak memengaruhi pengalaman pengguna saat pengguna berada dalam sesi.
Misalnya, akan lebih tepat untuk menerapkan ini saat mengonfirmasi pembelian baru karena operasi ini dapat terjadi di latar belakang, dan konfirmasi tidak perlu terjadi secara real time jika terjadi error.
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
}
Respons BillingResult yang dapat dicoba ulang
NETWORK_ERROR (Kode Error 12)
Masalah
Error ini menunjukkan adanya masalah dengan koneksi jaringan antara perangkat dan sistem Play.
Kemungkinan resolusi
Untuk memulihkan, gunakan percobaan ulang sederhana atau backoff eksponensial, bergantung pada tindakan mana yang memicu error.
SERVICE_TIMEOUT (Kode Error -3)
Masalah
Error ini menunjukkan bahwa permintaan telah mencapai waktu tunggu maksimum sebelum Google Play dapat merespons. Hal ini dapat disebabkan, misalnya, karena keterlambatan eksekusi tindakan yang diminta oleh panggilan Library Layanan Penagihan Play.
Kemungkinan resolusi
Masalah ini biasanya bersifat sementara. Coba lagi permintaan tersebut menggunakan strategi backoff eksponensial atau percobaan ulang sederhana, bergantung pada tindakan yang menampilkan error.
Tidak seperti SERVICE_DISCONNECTED
di bawah, koneksi ke Layanan Penagihan Google Play tidak terputus, dan Anda
hanya perlu mencoba kembali operasi Library Layanan Penagihan Play.
SERVICE_DISCONNECTED (Kode Error -1)
Masalah
Error fatal ini menunjukkan bahwa koneksi aplikasi klien ke layanan Google Play Store
melalui BillingClient
telah terputus.
Kemungkinan resolusi
Untuk menghindari error ini sebanyak mungkin, selalu periksa koneksi ke layanan
Google Play sebelum melakukan panggilan dengan Library Layanan Penagihan Play dengan memanggil
BillingClient.isReady()
.
Untuk mencoba pemulihan dari SERVICE_DISCONNECTED
,
aplikasi klien Anda harus mencoba membuat kembali koneksi menggunakan
BillingClient.startConnection
.
Sama seperti SERVICE_TIMEOUT
,
gunakan percobaan ulang sederhana atau backoff eksponensial, bergantung pada tindakan yang memicu
error.
SERVICE_UNAVAILABLE (Kode Error 2)
Catatan Penting:
Mulai Library Layanan Penagihan Google Play 6.0.0, SERVICE_UNAVAILABLE
tidak
lagi ditampilkan untuk masalah jaringan. Layanan ini ditampilkan saat layanan penagihan
tidak tersedia dan skenario kasus SERVICE_TIMEOUT
tidak digunakan lagi.
Masalah
Error sementara ini menunjukkan bahwa Layanan Penagihan Google Play saat ini tidak tersedia. Dalam kebanyakan kasus, hal ini berarti ada masalah koneksi jaringan antara perangkat klien dan layanan Layanan Penagihan Google Play.
Kemungkinan resolusi
Masalah ini biasanya bersifat sementara. Coba lagi permintaan tersebut menggunakan strategi backoff eksponensial atau percobaan ulang sederhana, bergantung pada tindakan yang menampilkan error.
Tidak seperti SERVICE_DISCONNECTED
,
koneksi ke layanan Layanan Penagihan Google Play tidak terputus, dan Anda perlu
mencoba kembali operasi apa pun yang sedang dicoba.
BILLING_UNAVAILABLE (Kode Error 3)
Masalah
Error ini menunjukkan bahwa error penagihan pengguna terjadi selama proses pembelian. Contoh kapan hal ini dapat terjadi adalah:
- Aplikasi Play Store di perangkat pengguna belum diupdate.
- Pengguna berada di negara yang tidak didukung.
- Pengguna tersebut adalah pengguna versi bisnis, dan admin perusahaan mereka telah menonaktifkan pengguna agar tidak melakukan pembelian.
- Google Play tidak dapat menagih metode pembayaran pengguna. Misalnya, masa berlaku kartu kredit pengguna mungkin telah berakhir.
Kemungkinan resolusi
Percobaan ulang otomatis kemungkinan tidak akan membantu dalam kasus ini. Namun, percobaan ulang manual dapat membantu jika pengguna mengatasi kondisi yang menyebabkan masalah tersebut. Misalnya, jika pengguna mengupdate versi Play Store ke versi yang didukung, percobaan ulang manual operasi awal dapat berfungsi.
Jika error ini terjadi saat pengguna tidak berada dalam sesi, percobaan ulang mungkin bukan hal
yang umum dilakukan.
Saat Anda menerima error BILLING_UNAVAILABLE
sebagai hasil dari alur pembelian, kemungkinan besar pengguna menerima
masukan dari Google Play selama proses pembelian dan mungkin mengetahui error
yang terjadi. Dalam hal ini, Anda dapat menampilkan pesan error yang menjelaskan
error yang terjadi dan menawarkan tombol “Coba lagi” untuk memberi pengguna opsi
percobaan ulang manual setelah mereka mengatasi masalah.
ERROR (Kode Error 6)
Masalah
Ini adalah error fatal yang menunjukkan masalah internal pada Google Play itu sendiri.
Kemungkinan resolusi
Terkadang masalah Google Play internal yang menyebabkan ERROR
bersifat sementara, dan percobaan ulang dengan backoff eksponensial dapat diterapkan untuk
mitigasi. Saat pengguna berada dalam sesi, sebaiknya lakukan percobaan ulang sederhana.
ITEM_ALREADY_OWNED
Masalah
Respons ini menunjukkan bahwa pengguna Google Play sudah memiliki langganan atau produk pembelian satu kali yang mereka coba beli. Umumnya, ini bukan error sementara, kecuali jika disebabkan oleh cache Google Play yang sudah tidak berlaku.
Kemungkinan resolusi
Jika penyebabnya bukan masalah cache, hindari error ini dengan tidak menawarkan
produk untuk dibeli saat pengguna sudah memilikinya. Pastikan Anda memeriksa
hak pengguna saat menampilkan produk yang tersedia untuk dibeli, dan
memfilter apa yang dapat dibeli pengguna sebagaimana mestinya.
Saat aplikasi klien menerima error ini karena masalah cache, error tersebut memicu
cache Google Play untuk diperbarui dengan data terbaru dari backend Play.
Mencoba ulang setelah error akan menyelesaikan masalah sementara ini dalam
kasus ini. Panggil BillingClient.queryPurchasesAsync()
setelah mendapatkan ITEM_ALREADY_OWNED
untuk memeriksa apakah pengguna telah memperoleh produk, dan jika bukan,
implementasikan logika percobaan ulang sederhana untuk mencoba kembali pembelian.
ITEM_NOT_OWNED
Masalah
Respons pembelian ini menunjukkan bahwa pengguna Google Play tidak memiliki langganan atau produk pembelian satu kali yang coba diganti, dikonfirmasi, atau digunakan oleh pengguna. Ini bukanlah error sementara dalam sebagian besar kasus, kecuali jika disebabkan oleh cache Google Play yang mengalami status tidak berlaku.
Kemungkinan resolusi
Saat error diterima karena masalah cache, error tersebut memicu cache Google Play untuk diperbarui dengan data terbaru dari backend Play. Mencoba ulang
dengan strategi percobaan ulang sederhana setelah error akan menyelesaikan masalah sementara
ini. Panggil BillingClient.queryPurchasesAsync()
setelah mendapatkan ITEM_NOT_OWNED
untuk memeriksa apakah pengguna telah
mendapatkan produk. Jika belum, gunakan logika percobaan ulang sederhana untuk mencoba kembali
pembelian.
Respons BillingResult yang Tidak Dapat Dicoba Ulang
Anda tidak dapat memulihkan dari error ini menggunakan logika percobaan ulang.
FEATURE_NOT_SUPPORTED
Masalah
Error yang tidak dapat dicoba ulang ini menunjukkan bahwa fitur Layanan Penagihan Google Play tidak didukung di perangkat pengguna, kemungkinan karena versi Play Store yang lama.
Misalnya, mungkin beberapa perangkat pengguna Anda tidak mendukung in-app messaging.
Kemungkinan mitigasi
Gunakan BillingClient.isFeatureSupported()
untuk memeriksa dukungan fitur sebelum melakukan panggilan ke Library
Layanan Penagihan Play.
when {
billingClient.isReady -> {
if (billingClient.isFeatureSupported(BillingClient.FeatureType.IN_APP_MESSAGING)) {
// use feature
}
}
}
USER_CANCELED
Masalah
Pengguna telah mengklik untuk keluar dari UI alur penagihan.
Kemungkinan resolusi
Hal ini hanya bersifat informatif dan bisa gagal.
ITEM_UNAVAILABLE
Masalah
Langganan Layanan Penagihan Google Play atau produk pembelian satu kali tidak tersedia untuk dibeli pengguna ini.
Kemungkinan mitigasi
Pastikan aplikasi Anda memperbarui detail produk melalui queryProductDetailsAsync
seperti yang direkomendasikan. Pertimbangkan seberapa sering
katalog produk Anda berubah pada konfigurasi Konsol Play untuk mengimplementasikan
pembaruan tambahan jika diperlukan.
Hanya jual produk di Layanan Penagihan Google Play yang menampilkan informasi yang tepat
melalui queryProductDetailsAsync
.
Periksa pengaturan kelayakan produk untuk menemukan inkonsistensi.
Misalnya, Anda mungkin membuat kueri untuk produk yang hanya tersedia untuk
wilayah selain produk yang ingin dibeli pengguna.
Agar tersedia untuk dibeli, produk harus aktif, aplikasinya harus dipublikasikan,
dan aplikasinya harus tersedia di negara pengguna.
Terkadang, khususnya selama pengujian, semua yang ada di konfigurasi produk sudah benar, tetapi pengguna masih melihat error ini. Ini mungkin karena penundaan propagasi detail produk di seluruh server Google. Coba lagi nanti.
DEVELOPER_ERROR
Masalah
Ini adalah error fatal yang menunjukkan bahwa Anda tidak menggunakan API dengan benar.
Misalnya, memberikan parameter yang salah ke BillingClient.launchBillingFlow
dapat
menyebabkan error ini.
Kemungkinan resolusi
Pastikan Anda menggunakan panggilan Library Layanan Penagihan Play yang berbeda dengan benar. Selain itu, periksa pesan debug untuk info selengkapnya tentang error tersebut.