Skip to content

Commit 5b01d48

Browse files
committed
chore(fc/iap): add retry and error reporting to billing client
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 792b447 commit 5b01d48

1 file changed

Lines changed: 105 additions & 1 deletion

File tree

services/flipchat/chat/src/main/kotlin/xyz/flipchat/services/billing/GooglePlayBillingClient.kt

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package xyz.flipchat.services.billing
22

33
import android.app.Activity
44
import android.content.Context
5+
import android.os.Handler
6+
import android.os.Looper
57
import com.android.billingclient.api.AcknowledgePurchaseParams
68
import com.android.billingclient.api.BillingClient.BillingResponseCode
79
import com.android.billingclient.api.BillingClientStateListener
@@ -19,6 +21,8 @@ import com.android.billingclient.api.QueryPurchasesParams
1921
import com.android.billingclient.api.acknowledgePurchase
2022
import com.android.billingclient.api.consumePurchase
2123
import com.getcode.model.uuid
24+
import com.getcode.utils.TraceType
25+
import com.getcode.utils.trace
2226
import com.google.common.collect.ImmutableList
2327
import dagger.hilt.android.qualifiers.ApplicationContext
2428
import kotlinx.coroutines.CoroutineScope
@@ -34,6 +38,7 @@ import kotlinx.coroutines.launch
3438
import kotlinx.coroutines.withContext
3539
import xyz.flipchat.services.internal.network.repository.iap.InAppPurchaseRepository
3640
import xyz.flipchat.services.user.UserManager
41+
import kotlin.math.pow
3742
import com.android.billingclient.api.BillingClient as GooglePlayBillingClient
3843

3944

@@ -43,6 +48,14 @@ class GooglePlayBillingClient(
4348
private val purchaseRepository: InAppPurchaseRepository
4449
) : BillingClient, PurchasesUpdatedListener {
4550

51+
companion object {
52+
private const val TAG = "IAP"
53+
private val MAX_RETRY_ATTEMPTS = 5
54+
private var retryAttempt = 0
55+
private val baseDelayMillis = 1000L // Initial delay: 1 second
56+
57+
}
58+
4659
private val scope = CoroutineScope(Dispatchers.IO)
4760

4861
private val _eventFlow: MutableSharedFlow<IapPaymentEvent> = MutableSharedFlow()
@@ -286,19 +299,110 @@ class GooglePlayBillingClient(
286299
billingResult: BillingResult
287300
) {
288301
if (billingResult.responseCode == BillingResponseCode.OK) {
302+
// Billing client connected successfully
289303
printLog("connected!")
304+
retryAttempt = 0 // Reset retry count
305+
290306
_stateFlow.update { BillingClientState.Connected }
291307
queryProducts()
292308
restorePurchases()
293309
} else {
294-
printLog("connection failed")
295310
_stateFlow.update { BillingClientState.Failed }
311+
handleConnectionFailure(billingResult)
296312
}
297313
}
298314

299315
override fun onBillingServiceDisconnected() {
300316
printLog("connection lost")
301317
_stateFlow.update { BillingClientState.ConnectionLost }
318+
retryBillingConnection()
319+
}
320+
}
321+
322+
private fun handleConnectionFailure(billingResult: BillingResult) {
323+
when (billingResult.responseCode) {
324+
BillingResponseCode.SERVICE_UNAVAILABLE -> {
325+
trace(
326+
tag = TAG,
327+
message = "Billing Service is unavailable. Please check your network connection.",
328+
type = TraceType.Silent
329+
)
330+
retryBillingConnection()
331+
}
332+
333+
BillingResponseCode.SERVICE_DISCONNECTED -> {
334+
trace(
335+
tag = TAG,
336+
message = "Billing Service disconnected. Retrying...",
337+
type = TraceType.Silent
338+
)
339+
retryBillingConnection()
340+
}
341+
342+
BillingResponseCode.BILLING_UNAVAILABLE -> {
343+
trace(
344+
tag = TAG,
345+
message = "Billing is not available on this device. Ensure Play Store is installed.",
346+
type = TraceType.Error
347+
)
348+
}
349+
350+
BillingResponseCode.ITEM_UNAVAILABLE -> {
351+
trace(
352+
tag = TAG,
353+
message = "Requested item is not available.",
354+
type = TraceType.Error
355+
)
356+
}
357+
358+
BillingResponseCode.ERROR -> {
359+
trace(
360+
tag = TAG,
361+
message = "An unknown error occurred with billing: ${billingResult.debugMessage}",
362+
type = TraceType.Error
363+
)
364+
retryBillingConnection()
365+
}
366+
367+
BillingResponseCode.USER_CANCELED -> {
368+
trace(
369+
tag = TAG,
370+
message = "User canceled the purchase flow.",
371+
type = TraceType.Silent
372+
)
373+
}
374+
375+
else -> {
376+
trace(
377+
tag = TAG,
378+
message = "Unhandled billing response: ${billingResult.responseCode}, ${billingResult.debugMessage}",
379+
type = TraceType.Error
380+
)
381+
retryBillingConnection()
382+
}
383+
}
384+
}
385+
386+
private fun retryBillingConnection() {
387+
if (retryAttempt < MAX_RETRY_ATTEMPTS) {
388+
val delayMillis = baseDelayMillis * (2.0.pow(retryAttempt)).toLong()
389+
390+
Handler(Looper.getMainLooper()).postDelayed({
391+
retryAttempt++
392+
connect()
393+
}, delayMillis)
394+
395+
trace(
396+
tag = TAG,
397+
message = "Retrying connection: Attempt $retryAttempt after ${delayMillis}ms",
398+
type = TraceType.Silent
399+
)
400+
} else {
401+
trace(
402+
tag = TAG,
403+
message = "Max retry attempts reached. Could not connect to billing service.",
404+
type = TraceType.Error
405+
)
302406
}
303407
}
304408

0 commit comments

Comments
 (0)