diff --git a/apps/flipcash/core/src/main/res/values/strings.xml b/apps/flipcash/core/src/main/res/values/strings.xml
index 1a6e8bbd5..0767617d3 100644
--- a/apps/flipcash/core/src/main/res/values/strings.xml
+++ b/apps/flipcash/core/src/main/res/values/strings.xml
@@ -572,6 +572,9 @@
Payment Timed Out
The payment session took too long to respond. Please try again.
+ Purchase Timed Out
+ Purchases must be authorized within 60 seconds
+
Something Went Wrong
We are working with the Coinbase team to resolve the issue. Your card will be refunded in the meantime. Please try again later
diff --git a/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampHandler.kt b/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampHandler.kt
index 8f33d9884..1cdb10fd5 100644
--- a/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampHandler.kt
+++ b/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampHandler.kt
@@ -140,5 +140,12 @@ private fun showOnRampFailure(resources: Resources, error: CoinbaseOnRampWebErro
message = resources.getString(R.string.error_description_onrampInternal),
)
}
+
+ is CoinbaseOnRampWebError.PaymentSheetTimeout -> {
+ BottomBarManager.showInfo(
+ title = resources.getString(R.string.error_title_onrampPaymentSheetTimeout),
+ message = resources.getString(R.string.error_description_onrampPaymentSheetTimeout),
+ )
+ }
}
}
diff --git a/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampWebview.kt b/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampWebview.kt
index 8b112fee0..673f31509 100644
--- a/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampWebview.kt
+++ b/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/CoinbaseOnRampWebview.kt
@@ -1,6 +1,8 @@
package com.flipcash.app.onramp
import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Intent
import android.webkit.CookieManager
import android.webkit.JavascriptInterface
import android.webkit.WebChromeClient
@@ -77,10 +79,12 @@ private fun WebView.configureForCoinbaseOnRamp(
var initialTimeoutRunnable: Runnable? = null
var interEventTimeoutRunnable: Runnable? = null
+ var paymentSheetTimeoutRunnable: Runnable? = null
fun cancelAllTimeouts() {
initialTimeoutRunnable?.let { removeCallbacks(it) }
interEventTimeoutRunnable?.let { removeCallbacks(it) }
+ paymentSheetTimeoutRunnable?.let { removeCallbacks(it) }
}
val timeoutAction = Runnable {
@@ -125,8 +129,30 @@ private fun WebView.configureForCoinbaseOnRamp(
post { cancelAllTimeouts(); scheduleInterEventTimeout() }
}
+ val paymentSheetTimeoutAction = Runnable {
+ if (terminalEventReceived.compareAndSet(false, true)) {
+ trace(tag = "CoinbaseOnRamp", message = "Payment sheet timeout fired (60s)")
+ cancelAllTimeouts()
+ // The Google Pay sheet is a GMS Activity sitting on top of ours
+ // in the task stack. Relaunching our Activity with CLEAR_TOP
+ // finishes everything above it, dismissing the sheet.
+ (context as? Activity)?.let { activity ->
+ val intent = Intent(activity, activity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ }
+ activity.startActivity(intent)
+ }
+ onPaymentFailure(CoinbaseOnRampWebError.PaymentSheetTimeout())
+ }
+ }
+
val pauseWatchdog: () -> Unit = {
- post { cancelAllTimeouts() }
+ post {
+ cancelAllTimeouts()
+ val runnable = Runnable { paymentSheetTimeoutAction.run() }
+ paymentSheetTimeoutRunnable = runnable
+ postDelayed(runnable, PAYMENT_SHEET_TIMEOUT_MS)
+ }
}
settings.javaScriptEnabled = true
@@ -213,8 +239,13 @@ private fun WebView.configureForCoinbaseOnRamp(
CookieManager.getInstance().setAcceptThirdPartyCookies(this, true)
}
- return { cancelAllTimeouts() }
+ return {
+ cancelAllTimeouts()
+ (parent as? android.view.ViewGroup)?.removeView(this@configureForCoinbaseOnRamp)
+ destroy()
+ }
}
private const val INITIAL_TIMEOUT_MS = 30_000L
-private const val INTER_EVENT_TIMEOUT_MS = 22_000L
\ No newline at end of file
+private const val INTER_EVENT_TIMEOUT_MS = 22_000L
+private const val PAYMENT_SHEET_TIMEOUT_MS = 60_000L
\ No newline at end of file
diff --git a/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/internal/CoinbaseOnRampEventHandler.kt b/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/internal/CoinbaseOnRampEventHandler.kt
index 9b103d811..e88518172 100644
--- a/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/internal/CoinbaseOnRampEventHandler.kt
+++ b/apps/flipcash/shared/onramp/coinbase/src/main/kotlin/com/flipcash/app/onramp/internal/CoinbaseOnRampEventHandler.kt
@@ -278,6 +278,7 @@ sealed class CoinbaseOnRampWebError(val data: String? = null): Throwable() {
class Internal(data: String? = null) : CoinbaseOnRampWebError(data), NotifiableError
class GooglePayButtonNotFound(data: String? = null) : CoinbaseOnRampWebError(data), NotifiableError
class WebViewTimeout(data: String? = null) : CoinbaseOnRampWebError(data), NotifiableError
+ class PaymentSheetTimeout(data: String? = null) : CoinbaseOnRampWebError(data)
companion object {
fun fromErrorCode(errorCode: String, data: String? = null): CoinbaseOnRampWebError {
diff --git a/apps/flipcash/shared/onramp/coinbase/src/test/kotlin/com/flipcash/app/onramp/internal/CoinbaseOnRampEventHandlerTest.kt b/apps/flipcash/shared/onramp/coinbase/src/test/kotlin/com/flipcash/app/onramp/internal/CoinbaseOnRampEventHandlerTest.kt
index b51492183..b93269f63 100644
--- a/apps/flipcash/shared/onramp/coinbase/src/test/kotlin/com/flipcash/app/onramp/internal/CoinbaseOnRampEventHandlerTest.kt
+++ b/apps/flipcash/shared/onramp/coinbase/src/test/kotlin/com/flipcash/app/onramp/internal/CoinbaseOnRampEventHandlerTest.kt
@@ -241,4 +241,10 @@ class CoinbaseOnRampWebErrorTest {
assertIs(error)
assertIs(error)
}
+
+ @Test
+ fun paymentSheetTimeoutIsNotNotifiable() {
+ val error = CoinbaseOnRampWebError.PaymentSheetTimeout()
+ assertFalse(error is NotifiableError)
+ }
}