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) + } }