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