diff --git a/apps/flipcash/core/src/main/res/values/strings.xml b/apps/flipcash/core/src/main/res/values/strings.xml
index 46da99ed8..e93d3261b 100644
--- a/apps/flipcash/core/src/main/res/values/strings.xml
+++ b/apps/flipcash/core/src/main/res/values/strings.xml
@@ -570,6 +570,9 @@
Billing Address Invalid
Please check that your billing address is correct and try again
+ Billing Name Invalid
+ Please check that the name on your card matches your billing name and try again
+
Something Went Wrong
The Coinbase team has been notified and is investigating the issue. Your funds will arrive once resolved. We appreciate your patience
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 a45ed0806..e6537cd52 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
@@ -62,14 +62,45 @@ fun CoinbaseOnRampHandler(
private fun showOnRampFailure(resources: Resources, error: CoinbaseOnRampWebError) {
when (error) {
- is CoinbaseOnRampWebError.Unknown,
- is CoinbaseOnRampWebError.MissingTransactionUuid -> {
+ // --- Grouped errors ---
+
+ is CoinbaseOnRampWebError.UnknownFailure -> {
BottomBarManager.showError(
title = resources.getString(R.string.error_title_onrampUnknownFailure),
message = resources.getString(R.string.error_description_onrampUnknownFailure),
)
}
+ is CoinbaseOnRampWebError.CardDeclined -> {
+ BottomBarManager.showAlert(
+ title = resources.getString(R.string.error_title_onrampCardSoftDeclined),
+ message = resources.getString(R.string.error_description_onrampCardSoftDeclined),
+ )
+ }
+
+ is CoinbaseOnRampWebError.BillingAddressInvalid -> {
+ BottomBarManager.showAlert(
+ title = resources.getString(R.string.error_title_onrampTransactionAvsValidationFailed),
+ message = resources.getString(R.string.error_description_onrampTransactionAvsValidationFailed),
+ )
+ }
+
+ is CoinbaseOnRampWebError.InternalFailure -> {
+ BottomBarManager.showError(
+ title = resources.getString(R.string.error_title_onrampInternal),
+ message = resources.getString(R.string.error_description_onrampInternal),
+ )
+ }
+
+ is CoinbaseOnRampWebError.TransactionFailed -> {
+ BottomBarManager.showError(
+ title = resources.getString(R.string.error_title_onrampTransactionFailed),
+ message = resources.getString(R.string.error_description_onrampTransactionFailed),
+ )
+ }
+
+ // --- Single-variant errors ---
+
is CoinbaseOnRampWebError.GuestCardNotDebit -> {
BottomBarManager.showAlert(
title = resources.getString(R.string.error_title_onrampInvalidCard),
@@ -77,68 +108,73 @@ private fun showOnRampFailure(resources: Resources, error: CoinbaseOnRampWebErro
)
}
- is CoinbaseOnRampWebError.GuestRegionMismatch -> {
+ is CoinbaseOnRampWebError.GuestCardRiskDeclined -> {
BottomBarManager.showAlert(
- title = resources.getString(R.string.error_title_onrampRegionMismatch),
- message = resources.getString(R.string.error_description_onrampRegionMismatch),
+ title = resources.getString(R.string.error_title_onrampCardRiskDeclined),
+ message = resources.getString(R.string.error_description_onrampCardRiskDeclined),
+ )
+ }
+
+ is CoinbaseOnRampWebError.GuestPermissionDenied -> {
+ BottomBarManager.showAlert(
+ title = resources.getString(R.string.error_title_onrampCardPermissionDenied),
+ message = resources.getString(R.string.error_description_onrampCardPermissionDenied),
)
}
- is CoinbaseOnRampWebError.AssetNotTradableInRegion -> {
+ is CoinbaseOnRampWebError.RegionNotSupported -> {
BottomBarManager.showAlert(
title = resources.getString(R.string.error_title_onrampRegionMismatch),
message = resources.getString(R.string.error_description_onrampRegionMismatch),
)
}
- is CoinbaseOnRampWebError.GuestGooglePayError -> {
- BottomBarManager.showError(
- title = resources.getString(R.string.error_title_onrampTransactionFailed),
- message = resources.getString(R.string.error_description_onrampTransactionFailed),
+ is CoinbaseOnRampWebError.GuestWeeklyTransactionLimitReached -> {
+ BottomBarManager.showAlert(
+ title = resources.getString(R.string.error_title_onrampTransactionLimit),
+ message = resources.getString(R.string.error_description_onrampTransactionLimit),
)
}
- is CoinbaseOnRampWebError.GuestGooglePayNotReady -> {
+ is CoinbaseOnRampWebError.GuestTransactionMaxLimitReached -> {
BottomBarManager.showAlert(
- title = resources.getString(R.string.error_title_onrampGooglePayNotReady),
- message = resources.getString(R.string.error_description_onrampGooglePayNotReady),
+ title = resources.getString(R.string.error_title_onrampTransactionCount),
+ message = resources.getString(R.string.error_description_onrampTransactionCount),
)
}
- is CoinbaseOnRampWebError.GuestTransactionBuyFailed -> {
- BottomBarManager.showError(
- title = resources.getString(R.string.error_title_onrampTransactionBuyFailed),
- message = resources.getString(R.string.error_description_onrampTransactionBuyFailed),
+ is CoinbaseOnRampWebError.GuestGooglePayNotReady -> {
+ BottomBarManager.showAlert(
+ title = resources.getString(R.string.error_title_onrampGooglePayNotReady),
+ message = resources.getString(R.string.error_description_onrampGooglePayNotReady),
)
}
- is CoinbaseOnRampWebError.GuestTransactionSendFailed -> {
- BottomBarManager.showError(
- title = resources.getString(R.string.error_title_onrampTransactionSendFailed),
- message = resources.getString(R.string.error_description_onrampTransactionSendFailed),
+ is CoinbaseOnRampWebError.GuestGooglePayNotSupported -> {
+ BottomBarManager.showAlert(
+ title = resources.getString(R.string.error_title_onrampGooglePayNotSupported),
+ message = resources.getString(R.string.error_description_onrampGooglePayNotSupported),
)
}
- is CoinbaseOnRampWebError.GuestTransactionAvsValidationFailed -> {
- BottomBarManager.showError(
- title = resources.getString(R.string.error_title_onrampTransactionAvsValidationFailed),
- message = resources.getString(R.string.error_description_onrampTransactionAvsValidationFailed),
+ is CoinbaseOnRampWebError.GuestCardInsufficientBalance -> {
+ BottomBarManager.showAlert(
+ title = resources.getString(R.string.error_title_onrampCardInsufficientBalance),
+ message = resources.getString(R.string.error_description_onrampCardInsufficientBalance),
)
}
- is CoinbaseOnRampWebError.GuestTransactionTransactionFailed -> {
- BottomBarManager.showError(
- title = resources.getString(R.string.error_title_onrampTransactionFailed),
- message = resources.getString(R.string.error_description_onrampTransactionFailed),
+ is CoinbaseOnRampWebError.GuestCardPrepaidDeclined -> {
+ BottomBarManager.showAlert(
+ title = resources.getString(R.string.error_title_onrampCardPrepaidDeclined),
+ message = resources.getString(R.string.error_description_onrampCardPrepaidDeclined),
)
}
- is CoinbaseOnRampWebError.Internal,
- is CoinbaseOnRampWebError.GooglePayButtonNotFound,
- is CoinbaseOnRampWebError.WebViewTimeout -> {
- BottomBarManager.showError(
- title = resources.getString(R.string.error_title_onrampInternal),
- message = resources.getString(R.string.error_description_onrampInternal),
+ is CoinbaseOnRampWebError.InvalidBillingName -> {
+ BottomBarManager.showAlert(
+ title = resources.getString(R.string.error_title_onrampInvalidBillingName),
+ message = resources.getString(R.string.error_description_onrampInvalidBillingName),
)
}
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 673f31509..8275186ab 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
@@ -97,7 +97,7 @@ private fun WebView.configureForCoinbaseOnRamp(
"gmsVersion" to gmsVersion
},
)
- onPaymentFailure(CoinbaseOnRampWebError.WebViewTimeout())
+ onPaymentFailure(CoinbaseOnRampWebError.InternalFailure.WebViewTimeout())
}
}
@@ -221,7 +221,7 @@ private fun WebView.configureForCoinbaseOnRamp(
error: WebResourceError?
) {
if (request?.isForMainFrame == true) {
- wrappedOnPaymentFailure(CoinbaseOnRampWebError.GuestGooglePayError())
+ wrappedOnPaymentFailure(CoinbaseOnRampWebError.TransactionFailed.GooglePayError())
}
}
}
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 5a96cc7da..f0c42f1d2 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
@@ -187,7 +187,7 @@ internal class CoinbaseOnRampEventHandler(
metadata = {
"webViewVersion" to webViewVersion
"gmsVersion" to gmsVersion
- if (errorCode == "ERROR_CODE_GOOGLE_PAY_BUTTON_NOT_FOUND" && data != null) {
+ if (errorCode == CoinbaseOnRampWebError.CODE_GOOGLE_PAY_BUTTON_NOT_FOUND && data != null) {
"buttons" to data.optInt("buttons", -1)
"gpayElements" to data.optInt("gpayElements", -1)
"iframes" to data.optInt("iframes", -1)
@@ -265,40 +265,124 @@ internal class CoinbaseOnRampEventHandler(
}
}
-sealed class CoinbaseOnRampWebError(val data: String? = null): Throwable(data) {
- class Unknown(data: String? = null) : CoinbaseOnRampWebError(data), NotifiableError
- class MissingTransactionUuid(data: String? = null) : CoinbaseOnRampWebError(data), NotifiableError
+/** @see [Docs](https://docs.cdp.coinbase.com/onramp/headless-onramp/overview#events-names) */
+sealed class CoinbaseOnRampWebError(val data: String? = null) : Throwable(data) {
+
+ // --- Grouped errors (shared UI) ---
+
+ /** "Something Went Wrong" — unknown / unmapped error codes */
+ sealed class UnknownFailure(data: String?) : CoinbaseOnRampWebError(data), NotifiableError {
+ class Unknown(data: String? = null) : UnknownFailure(data)
+ class MissingTransactionUuid(data: String? = null) : UnknownFailure(data)
+ }
+
+ /** "Card Declined" — declined by issuing bank */
+ sealed class CardDeclined(data: String?) : CoinbaseOnRampWebError(data) {
+ class Soft(data: String? = null) : CardDeclined(data)
+ class Hard(data: String? = null) : CardDeclined(data)
+ class BuyFailed(data: String? = null) : CardDeclined(data)
+ }
+
+ /** "Billing Address Invalid" — AVS / zip / address mismatch */
+ sealed class BillingAddressInvalid(data: String?) : CoinbaseOnRampWebError(data) {
+ class AvsValidationFailed(data: String? = null) : BillingAddressInvalid(data)
+ class InvalidZip(data: String? = null) : BillingAddressInvalid(data)
+ class InvalidAddress(data: String? = null) : BillingAddressInvalid(data)
+ }
+
+ /** "Something Went Wrong" — internal / infra failures */
+ sealed class InternalFailure(data: String?) : CoinbaseOnRampWebError(data), NotifiableError {
+ class Internal(data: String? = null) : InternalFailure(data)
+ class GooglePayButtonNotFound(data: String? = null) : InternalFailure(data)
+ class WebViewTimeout(data: String? = null) : InternalFailure(data)
+ class InitError(data: String? = null) : InternalFailure(data)
+ }
+
+ /** "Something Went Wrong" — transaction processing failure */
+ sealed class TransactionFailed(data: String?) : CoinbaseOnRampWebError(data) {
+ class GooglePayError(data: String? = null) : TransactionFailed(data)
+ class SendFailed(data: String? = null) : TransactionFailed(data), NotifiableError
+ class ProcessingFailed(data: String? = null) : TransactionFailed(data), NotifiableError
+ }
+
+ /** "Your Region Isn't Supported" — region / asset availability */
+ sealed class RegionNotSupported(data: String?) : CoinbaseOnRampWebError(data) {
+ class RegionMismatch(data: String? = null) : RegionNotSupported(data)
+ class AssetNotTradable(data: String? = null) : RegionNotSupported(data)
+ }
+
+ // --- Single-variant errors ---
+
class GuestCardNotDebit(data: String? = null) : CoinbaseOnRampWebError(data)
- class GuestGooglePayError(data: String? = null) : CoinbaseOnRampWebError(data)
- class GuestTransactionBuyFailed(data: String? = null) : CoinbaseOnRampWebError(data)
- class GuestTransactionSendFailed(data: String? = null) : CoinbaseOnRampWebError(data), NotifiableError
- class GuestTransactionAvsValidationFailed(data: String? = null) : CoinbaseOnRampWebError(data)
- class GuestTransactionTransactionFailed(data: String? = null) : CoinbaseOnRampWebError(data), NotifiableError
- class GuestRegionMismatch(data: String? = null) : CoinbaseOnRampWebError(data)
- class AssetNotTradableInRegion(data: String? = null): CoinbaseOnRampWebError(data)
+ class GuestCardRiskDeclined(data: String? = null) : CoinbaseOnRampWebError(data)
+ class GuestPermissionDenied(data: String? = null) : CoinbaseOnRampWebError(data)
+ class GuestWeeklyTransactionLimitReached(data: String? = null) : CoinbaseOnRampWebError(data)
+ class GuestTransactionMaxLimitReached(data: String? = null) : CoinbaseOnRampWebError(data)
class GuestGooglePayNotReady(data: String? = null) : CoinbaseOnRampWebError(data)
- 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 GuestGooglePayNotSupported(data: String? = null) : CoinbaseOnRampWebError(data)
+ class GuestCardInsufficientBalance(data: String? = null) : CoinbaseOnRampWebError(data)
+ class GuestCardPrepaidDeclined(data: String? = null) : CoinbaseOnRampWebError(data)
+ class InvalidBillingName(data: String? = null) : CoinbaseOnRampWebError(data)
class PaymentSheetTimeout(data: String? = null) : CoinbaseOnRampWebError(data)
companion object {
- fun fromErrorCode(errorCode: String, data: String? = null): CoinbaseOnRampWebError {
- return when (errorCode) {
- "ERROR_CODE_MISSING_TRANSACTION_UUID" -> MissingTransactionUuid(data)
- "ERROR_CODE_ASSET_NOT_TRADABLE" -> AssetNotTradableInRegion(data)
- "ERROR_CODE_GUEST_CARD_NOT_DEBIT" -> GuestCardNotDebit(data)
- "ERROR_CODE_GUEST_GOOGLE_PAY_ERROR" -> GuestGooglePayError(data)
- "ERROR_CODE_GUEST_TRANSACTION_BUY_FAILED" -> GuestTransactionBuyFailed(data)
- "ERROR_CODE_GUEST_TRANSACTION_SEND_FAILED" -> GuestTransactionSendFailed(data)
- "ERROR_CODE_GUEST_TRANSACTION_AVS_VALIDATION_FAILED" -> GuestTransactionAvsValidationFailed(data)
- "ERROR_CODE_GUEST_TRANSACTION_TRANSACTION_FAILED" -> GuestTransactionTransactionFailed(data)
- "ERROR_CODE_GUEST_REGION_MISMATCH" -> GuestRegionMismatch(data)
- "ERROR_CODE_GUEST_GOOGLE_PAY_NOT_READY" -> GuestGooglePayNotReady(data)
- "ERROR_CODE_INTERNAL" -> Internal(data)
- "ERROR_CODE_GOOGLE_PAY_BUTTON_NOT_FOUND" -> GooglePayButtonNotFound(data)
- else -> Unknown(data)
- }
- }
+ const val CODE_INIT = "ERROR_CODE_INIT"
+ const val CODE_INTERNAL = "ERROR_CODE_INTERNAL"
+ const val CODE_MISSING_TRANSACTION_UUID = "ERROR_CODE_MISSING_TRANSACTION_UUID"
+ const val CODE_GOOGLE_PAY_BUTTON_NOT_FOUND = "ERROR_CODE_GOOGLE_PAY_BUTTON_NOT_FOUND"
+ const val CODE_GUEST_INVALID_CARD = "ERROR_CODE_GUEST_INVALID_CARD"
+ const val CODE_GUEST_CARD_NOT_DEBIT = "ERROR_CODE_GUEST_CARD_NOT_DEBIT"
+ const val CODE_GUEST_CARD_SOFT_DECLINED = "ERROR_CODE_GUEST_CARD_SOFT_DECLINED"
+ const val CODE_GUEST_CARD_HARD_DECLINED = "ERROR_CODE_GUEST_CARD_HARD_DECLINED"
+ const val CODE_GUEST_CARD_RISK_DECLINED = "ERROR_CODE_GUEST_CARD_RISK_DECLINED"
+ const val CODE_GUEST_CARD_INSUFFICIENT_BALANCE = "ERROR_CODE_GUEST_CARD_INSUFFICIENT_BALANCE"
+ const val CODE_GUEST_CARD_PREPAID_DECLINED = "ERROR_CODE_GUEST_CARD_PREPAID_DECLINED"
+ const val CODE_GUEST_PERMISSION_DENIED = "ERROR_CODE_GUEST_PERMISSION_DENIED"
+ const val CODE_GUEST_REGION_MISMATCH = "ERROR_CODE_GUEST_REGION_MISMATCH"
+ const val CODE_GUEST_GOOGLE_PAY_ERROR = "ERROR_CODE_GUEST_GOOGLE_PAY_ERROR"
+ const val CODE_GUEST_GOOGLE_PAY_NOT_READY = "ERROR_CODE_GUEST_GOOGLE_PAY_NOT_READY"
+ const val CODE_GUEST_GOOGLE_PAY_NOT_SUPPORTED = "ERROR_CODE_GUEST_GOOGLE_PAY_NOT_SUPPORTED"
+ const val CODE_GUEST_TRANSACTION_LIMIT = "ERROR_CODE_GUEST_TRANSACTION_LIMIT"
+ const val CODE_GUEST_TRANSACTION_COUNT = "ERROR_CODE_GUEST_TRANSACTION_COUNT"
+ const val CODE_GUEST_TRANSACTION_BUY_FAILED = "ERROR_CODE_GUEST_TRANSACTION_BUY_FAILED"
+ const val CODE_GUEST_TRANSACTION_SEND_FAILED = "ERROR_CODE_GUEST_TRANSACTION_SEND_FAILED"
+ const val CODE_GUEST_TRANSACTION_AVS_VALIDATION_FAILED = "ERROR_CODE_GUEST_TRANSACTION_AVS_VALIDATION_FAILED"
+ const val CODE_GUEST_TRANSACTION_TRANSACTION_FAILED = "ERROR_CODE_GUEST_TRANSACTION_TRANSACTION_FAILED"
+ const val CODE_ASSET_NOT_TRADABLE = "ERROR_CODE_ASSET_NOT_TRADABLE"
+ const val CODE_INVALID_BILLING_ZIP = "ERROR_CODE_INVALID_BILLING_ZIP"
+ const val CODE_INVALID_BILLING_ADDRESS = "ERROR_CODE_INVALID_BILLING_ADDRESS"
+ const val CODE_INVALID_BILLING_NAME = "ERROR_CODE_INVALID_BILLING_NAME"
+
+ private val codeMap: Map CoinbaseOnRampWebError> = mapOf(
+ CODE_MISSING_TRANSACTION_UUID to { UnknownFailure.MissingTransactionUuid(it) },
+ CODE_GUEST_INVALID_CARD to ::GuestCardNotDebit,
+ CODE_GUEST_CARD_NOT_DEBIT to ::GuestCardNotDebit,
+ CODE_GUEST_TRANSACTION_LIMIT to ::GuestWeeklyTransactionLimitReached,
+ CODE_GUEST_TRANSACTION_COUNT to ::GuestTransactionMaxLimitReached,
+ CODE_GUEST_CARD_RISK_DECLINED to ::GuestCardRiskDeclined,
+ CODE_ASSET_NOT_TRADABLE to { RegionNotSupported.AssetNotTradable(it) },
+ CODE_GUEST_REGION_MISMATCH to { RegionNotSupported.RegionMismatch(it) },
+ CODE_GUEST_GOOGLE_PAY_ERROR to { TransactionFailed.GooglePayError(it) },
+ CODE_GUEST_TRANSACTION_BUY_FAILED to { CardDeclined.BuyFailed(it) },
+ CODE_GUEST_TRANSACTION_SEND_FAILED to { TransactionFailed.SendFailed(it) },
+ CODE_GUEST_TRANSACTION_AVS_VALIDATION_FAILED to { BillingAddressInvalid.AvsValidationFailed(it) },
+ CODE_GUEST_TRANSACTION_TRANSACTION_FAILED to { TransactionFailed.ProcessingFailed(it) },
+ CODE_GUEST_PERMISSION_DENIED to ::GuestPermissionDenied,
+ CODE_GUEST_GOOGLE_PAY_NOT_READY to ::GuestGooglePayNotReady,
+ CODE_GUEST_GOOGLE_PAY_NOT_SUPPORTED to ::GuestGooglePayNotSupported,
+ CODE_GUEST_CARD_SOFT_DECLINED to { CardDeclined.Soft(it) },
+ CODE_GUEST_CARD_HARD_DECLINED to { CardDeclined.Hard(it) },
+ CODE_GUEST_CARD_INSUFFICIENT_BALANCE to ::GuestCardInsufficientBalance,
+ CODE_GUEST_CARD_PREPAID_DECLINED to ::GuestCardPrepaidDeclined,
+ CODE_INVALID_BILLING_ZIP to { BillingAddressInvalid.InvalidZip(it) },
+ CODE_INVALID_BILLING_ADDRESS to { BillingAddressInvalid.InvalidAddress(it) },
+ CODE_INVALID_BILLING_NAME to ::InvalidBillingName,
+ CODE_INIT to { InternalFailure.InitError(it) },
+ CODE_INTERNAL to { InternalFailure.Internal(it) },
+ CODE_GOOGLE_PAY_BUTTON_NOT_FOUND to { InternalFailure.GooglePayButtonNotFound(it) },
+ )
+
+ fun fromErrorCode(errorCode: String, data: String? = null): CoinbaseOnRampWebError =
+ codeMap[errorCode]?.invoke(data) ?: UnknownFailure.Unknown(data)
}
}
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 28fb3db6d..a4e55d528 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
@@ -6,6 +6,32 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.test.assertEquals
import kotlin.time.TimeSource
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_ASSET_NOT_TRADABLE
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GOOGLE_PAY_BUTTON_NOT_FOUND
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_CARD_HARD_DECLINED
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_CARD_INSUFFICIENT_BALANCE
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_CARD_NOT_DEBIT
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_CARD_PREPAID_DECLINED
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_CARD_RISK_DECLINED
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_CARD_SOFT_DECLINED
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_GOOGLE_PAY_ERROR
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_GOOGLE_PAY_NOT_READY
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_GOOGLE_PAY_NOT_SUPPORTED
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_INVALID_CARD
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_PERMISSION_DENIED
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_REGION_MISMATCH
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_TRANSACTION_AVS_VALIDATION_FAILED
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_TRANSACTION_BUY_FAILED
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_TRANSACTION_COUNT
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_TRANSACTION_LIMIT
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_TRANSACTION_SEND_FAILED
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_GUEST_TRANSACTION_TRANSACTION_FAILED
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_INIT
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_INTERNAL
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_INVALID_BILLING_ADDRESS
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_INVALID_BILLING_NAME
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_INVALID_BILLING_ZIP
+import com.flipcash.app.onramp.internal.CoinbaseOnRampWebError.Companion.CODE_MISSING_TRANSACTION_UUID
import com.getcode.utils.NotifiableError
import kotlin.test.assertFalse
import kotlin.test.assertIs
@@ -66,19 +92,19 @@ class CoinbaseOnRampEventHandlerTest {
@Test
fun commitErrorTriggersFailure() {
handler.handleEvent("""{"eventName":"onramp_api.commit_error","data":{"errorCode":"ERROR_CODE_INTERNAL"}}""")
- assertIs(lastError)
+ assertIs(lastError)
}
@Test
fun loadErrorTriggersFailure() {
handler.handleEvent("""{"eventName":"onramp_api.load_error","data":{"errorCode":"ERROR_CODE_GUEST_GOOGLE_PAY_ERROR"}}""")
- assertIs(lastError)
+ assertIs(lastError)
}
@Test
fun pollingErrorTriggersFailure() {
handler.handleEvent("""{"eventName":"onramp_api.polling_error","data":{"errorCode":"ERROR_CODE_GUEST_TRANSACTION_BUY_FAILED"}}""")
- assertIs(lastError)
+ assertIs(lastError)
}
@Test
@@ -90,19 +116,19 @@ class CoinbaseOnRampEventHandlerTest {
@Test
fun errorWithUnknownCodeFallsBackToUnknown() {
handler.handleEvent("""{"eventName":"onramp_api.commit_error","data":{"errorCode":"SOME_NEW_ERROR"}}""")
- assertIs(lastError)
+ assertIs(lastError)
}
@Test
fun errorWithMissingDataFallsBackToUnknown() {
handler.handleEvent("""{"eventName":"onramp_api.commit_error"}""")
- assertIs(lastError)
+ assertIs(lastError)
}
@Test
fun errorWithEmptyErrorCodeFallsBackToUnknown() {
handler.handleEvent("""{"eventName":"onramp_api.commit_error","data":{"errorCode":""}}""")
- assertIs(lastError)
+ assertIs(lastError)
}
// --- Data payload ---
@@ -111,7 +137,7 @@ class CoinbaseOnRampEventHandlerTest {
fun errorCarriesJsonData() {
handler.handleEvent("""{"eventName":"onramp_api.commit_error","data":{"errorCode":"ERROR_CODE_INTERNAL","transactionId":"abc-123"}}""")
val error = lastError
- assertIs(error)
+ assertIs(error)
assertNotNull(error.data)
assertTrue(error.data!!.contains("abc-123"))
}
@@ -203,16 +229,32 @@ class CoinbaseOnRampWebErrorTest {
@Test
fun fromErrorCodeAllKnownCodes() {
val expected = mapOf(
- "ERROR_CODE_MISSING_TRANSACTION_UUID" to CoinbaseOnRampWebError.MissingTransactionUuid::class,
- "ERROR_CODE_GUEST_CARD_NOT_DEBIT" to CoinbaseOnRampWebError.GuestCardNotDebit::class,
- "ERROR_CODE_GUEST_GOOGLE_PAY_ERROR" to CoinbaseOnRampWebError.GuestGooglePayError::class,
- "ERROR_CODE_GUEST_TRANSACTION_BUY_FAILED" to CoinbaseOnRampWebError.GuestTransactionBuyFailed::class,
- "ERROR_CODE_GUEST_TRANSACTION_SEND_FAILED" to CoinbaseOnRampWebError.GuestTransactionSendFailed::class,
- "ERROR_CODE_GUEST_TRANSACTION_AVS_VALIDATION_FAILED" to CoinbaseOnRampWebError.GuestTransactionAvsValidationFailed::class,
- "ERROR_CODE_GUEST_TRANSACTION_TRANSACTION_FAILED" to CoinbaseOnRampWebError.GuestTransactionTransactionFailed::class,
- "ERROR_CODE_GUEST_REGION_MISMATCH" to CoinbaseOnRampWebError.GuestRegionMismatch::class,
- "ERROR_CODE_INTERNAL" to CoinbaseOnRampWebError.Internal::class,
- "ERROR_CODE_GOOGLE_PAY_BUTTON_NOT_FOUND" to CoinbaseOnRampWebError.GooglePayButtonNotFound::class,
+ CODE_MISSING_TRANSACTION_UUID to CoinbaseOnRampWebError.UnknownFailure.MissingTransactionUuid::class,
+ CODE_GUEST_CARD_NOT_DEBIT to CoinbaseOnRampWebError.GuestCardNotDebit::class,
+ CODE_GUEST_INVALID_CARD to CoinbaseOnRampWebError.GuestCardNotDebit::class,
+ CODE_GUEST_GOOGLE_PAY_ERROR to CoinbaseOnRampWebError.TransactionFailed.GooglePayError::class,
+ CODE_GUEST_TRANSACTION_BUY_FAILED to CoinbaseOnRampWebError.CardDeclined.BuyFailed::class,
+ CODE_GUEST_TRANSACTION_SEND_FAILED to CoinbaseOnRampWebError.TransactionFailed.SendFailed::class,
+ CODE_GUEST_TRANSACTION_AVS_VALIDATION_FAILED to CoinbaseOnRampWebError.BillingAddressInvalid.AvsValidationFailed::class,
+ CODE_GUEST_TRANSACTION_TRANSACTION_FAILED to CoinbaseOnRampWebError.TransactionFailed.ProcessingFailed::class,
+ CODE_GUEST_REGION_MISMATCH to CoinbaseOnRampWebError.RegionNotSupported.RegionMismatch::class,
+ CODE_GUEST_TRANSACTION_LIMIT to CoinbaseOnRampWebError.GuestWeeklyTransactionLimitReached::class,
+ CODE_GUEST_TRANSACTION_COUNT to CoinbaseOnRampWebError.GuestTransactionMaxLimitReached::class,
+ CODE_GUEST_CARD_RISK_DECLINED to CoinbaseOnRampWebError.GuestCardRiskDeclined::class,
+ CODE_GUEST_PERMISSION_DENIED to CoinbaseOnRampWebError.GuestPermissionDenied::class,
+ CODE_ASSET_NOT_TRADABLE to CoinbaseOnRampWebError.RegionNotSupported.AssetNotTradable::class,
+ CODE_GUEST_GOOGLE_PAY_NOT_READY to CoinbaseOnRampWebError.GuestGooglePayNotReady::class,
+ CODE_GUEST_GOOGLE_PAY_NOT_SUPPORTED to CoinbaseOnRampWebError.GuestGooglePayNotSupported::class,
+ CODE_GUEST_CARD_SOFT_DECLINED to CoinbaseOnRampWebError.CardDeclined.Soft::class,
+ CODE_GUEST_CARD_HARD_DECLINED to CoinbaseOnRampWebError.CardDeclined.Hard::class,
+ CODE_GUEST_CARD_INSUFFICIENT_BALANCE to CoinbaseOnRampWebError.GuestCardInsufficientBalance::class,
+ CODE_GUEST_CARD_PREPAID_DECLINED to CoinbaseOnRampWebError.GuestCardPrepaidDeclined::class,
+ CODE_INVALID_BILLING_ZIP to CoinbaseOnRampWebError.BillingAddressInvalid.InvalidZip::class,
+ CODE_INVALID_BILLING_ADDRESS to CoinbaseOnRampWebError.BillingAddressInvalid.InvalidAddress::class,
+ CODE_INVALID_BILLING_NAME to CoinbaseOnRampWebError.InvalidBillingName::class,
+ CODE_INIT to CoinbaseOnRampWebError.InternalFailure.InitError::class,
+ CODE_INTERNAL to CoinbaseOnRampWebError.InternalFailure.Internal::class,
+ CODE_GOOGLE_PAY_BUTTON_NOT_FOUND to CoinbaseOnRampWebError.InternalFailure.GooglePayButtonNotFound::class,
)
for ((code, expectedType) in expected) {
@@ -223,29 +265,36 @@ class CoinbaseOnRampWebErrorTest {
@Test
fun fromErrorCodeUnknownCodeReturnsUnknown() {
- assertIs(CoinbaseOnRampWebError.fromErrorCode("SOMETHING_NEW"))
+ assertIs(CoinbaseOnRampWebError.fromErrorCode("SOMETHING_NEW"))
}
@Test
fun fromErrorCodeEmptyStringReturnsUnknown() {
- assertIs(CoinbaseOnRampWebError.fromErrorCode(""))
+ assertIs(CoinbaseOnRampWebError.fromErrorCode(""))
}
@Test
fun fromErrorCodeCaseSensitive() {
- assertIs(CoinbaseOnRampWebError.fromErrorCode("error_code_internal"))
+ assertIs(CoinbaseOnRampWebError.fromErrorCode("error_code_internal"))
}
@Test
- fun webViewTimeoutImplementsNotifiableError() {
- val error = CoinbaseOnRampWebError.WebViewTimeout()
- assertIs(error)
- assertIs(error)
+ fun internalFailureGroupImplementsNotifiableError() {
+ assertIs(CoinbaseOnRampWebError.InternalFailure.InitError())
+ assertIs(CoinbaseOnRampWebError.InternalFailure.Internal())
+ assertIs(CoinbaseOnRampWebError.InternalFailure.WebViewTimeout())
+ assertIs(CoinbaseOnRampWebError.InternalFailure.GooglePayButtonNotFound())
+ }
+
+ @Test
+ fun unknownFailureGroupImplementsNotifiableError() {
+ assertIs(CoinbaseOnRampWebError.UnknownFailure.Unknown())
+ assertIs(CoinbaseOnRampWebError.UnknownFailure.MissingTransactionUuid())
}
@Test
fun guestRegionMismatchIsNotNotifiable() {
- val error = CoinbaseOnRampWebError.GuestRegionMismatch()
+ val error = CoinbaseOnRampWebError.RegionNotSupported.RegionMismatch()
assertFalse(error is NotifiableError)
}