From aea7f5f41586a10179b4fc91ffeeb17d2e90e0b3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Apr 2025 09:06:11 +0300 Subject: [PATCH] Updated on 2026-08-14 --- app/build.gradle.kts | 1 + .../tasks/visa/VisaCardActivationTask.kt | 18 +++--- .../visa/VisaCustomerWalletApproveTask.kt | 18 +++--- .../tap/domain/visa/VisaCardScanHandler.kt | 34 ++++------- .../domain/visa/VisaCardScanHandlerError.kt | 9 --- core/error/build.gradle.kts | 12 +--- core/error/ext/.gitignore | 1 + core/error/ext/build.gradle.kts | 16 +++++ .../com/tangem/core/error/ext/Extensions.kt | 27 +++++++++ .../kotlin/com/tangem/core/error/Utils.kt | 14 ----- domain/legacy/build.gradle.kts | 1 + .../common/visa/VisaWalletPublicKeyUtility.kt | 59 +++++++++---------- domain/visa/models/build.gradle.kts | 1 + .../com/tangem/domain/visa/error/VisaError.kt | 51 ++++++++++++++++ .../domain/visa/model/VisaActivationError.kt | 10 ---- features/onboarding-v2/impl/build.gradle.kts | 1 + .../model/OnboardingVisaAccessCodeModel.kt | 25 +++++--- .../model/OnboardingVisaApproveModel.kt | 24 +++++--- .../model/OnboardingVisaInProgressModel.kt | 9 ++- .../model/OnboardingVisaWelcomeModel.kt | 24 +++++--- settings.gradle.kts | 1 + 21 files changed, 220 insertions(+), 136 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandlerError.kt create mode 100644 core/error/ext/.gitignore create mode 100644 core/error/ext/build.gradle.kts create mode 100644 core/error/ext/src/main/kotlin/com/tangem/core/error/ext/Extensions.kt delete mode 100644 core/error/src/main/kotlin/com/tangem/core/error/Utils.kt create mode 100644 domain/visa/models/src/main/kotlin/com/tangem/domain/visa/error/VisaError.kt delete mode 100644 domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationError.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f95653d523..4102dcfb14 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -122,6 +122,7 @@ dependencies { implementation(projects.core.utils) implementation(projects.core.decompose) implementation(projects.core.deepLinks) + implementation(projects.core.error.ext) implementation(projects.libs.crypto) implementation(projects.libs.auth) implementation(projects.libs.blockchainSdk) diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt index 78ea74f626..93b23d3999 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt @@ -9,11 +9,13 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.CompletionCallback +import com.tangem.common.core.TangemError import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString import com.tangem.common.map import com.tangem.common.timemeasure.RealtimeMonotonicTimeSource +import com.tangem.core.error.ext.tangemError import com.tangem.crypto.CryptoUtils import com.tangem.datasource.local.visa.VisaAuthTokenStorage import com.tangem.datasource.local.visa.VisaOTPStorage @@ -22,6 +24,8 @@ import com.tangem.datasource.local.visa.hasSavedOTP import com.tangem.domain.common.visa.VisaUtilities import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.visa.error.VisaActivationError +import com.tangem.domain.visa.error.VisaAuthorizationAPIError import com.tangem.domain.visa.model.* import com.tangem.domain.visa.repository.VisaActivationRepository import com.tangem.domain.visa.repository.VisaAuthRepository @@ -68,7 +72,7 @@ class VisaCardActivationTask @AssistedInject constructor( val card = session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead()) if (card.cardId != activationInput.cardId) { - return CompletionResult.Failure(TangemSdkError.Underlying(VisaActivationError.WrongCard.message)) + return CompletionResult.Failure(VisaActivationError.WrongCard.tangemError) } val visaActivationRepository = visaActivationRepositoryFactory.create( @@ -149,7 +153,7 @@ class VisaCardActivationTask @AssistedInject constructor( private suspend fun SessionContext.getDataToSign( signedChallenge: VisaAuthSignedChallenge, - ): Either = either { + ): Either = either { catch( block = { val tokens = visaAuthRepository.getAccessTokens(signedChallenge) @@ -158,13 +162,13 @@ class VisaCardActivationTask @AssistedInject constructor( val remoteState = visaActivationRepository.getActivationRemoteState() if (remoteState !is VisaActivationRemoteState.CardWalletSignatureRequired) { - raise(TangemSdkError.Underlying(VisaActivationError.WrongRemoteState.message)) + raise(VisaActivationError.WrongRemoteState.tangemError) } visaActivationRepository.getCardWalletAcceptanceData(remoteState.request) }, catch = { - raise(TangemSdkError.Underlying("Underlying network error: ${it.message ?: ""}")) + raise(VisaAuthorizationAPIError.tangemError) }, ) } @@ -246,10 +250,10 @@ class VisaCardActivationTask @AssistedInject constructor( ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead()) val derivedPublicKey = wallet.derivedKeys[VisaUtilities.visaDefaultDerivationPath] - ?: return CompletionResult.Failure(TangemSdkError.Underlying(VisaActivationError.MissingWallet.message)) + ?: return CompletionResult.Failure(VisaActivationError.MissingWallet.tangemError) val walletAddress = VisaWalletPublicKeyUtility.generateAddressOnSecp256k1(derivedPublicKey.publicKey) - .getOrElse { return CompletionResult.Failure(TangemSdkError.Underlying(it.message)) } + .getOrElse { return CompletionResult.Failure(it.tangemError) } .value val task = SignHashCommand( @@ -291,7 +295,7 @@ class VisaCardActivationTask @AssistedInject constructor( ): CompletionResult { val otp = otpStorage.getOTP(cardId) ?: run { createOTP() - otpStorage.getOTP(cardId) ?: return CompletionResult.Failure(TangemSdkError.Underlying("OTP not found")) + otpStorage.getOTP(cardId) ?: return CompletionResult.Failure(VisaActivationError.MissingRootOTP.tangemError) } val signedActivationData = dataToSign.sign( diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt index 778c08b933..18ec47b7fb 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt @@ -11,6 +11,7 @@ import com.tangem.common.core.CompletionCallback import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString +import com.tangem.core.error.ext.tangemError import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.domain.common.util.derivationStyleProvider @@ -18,7 +19,7 @@ import com.tangem.domain.common.visa.VisaUtilities import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility.findKeyWithoutDerivation import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.visa.model.VisaActivationError +import com.tangem.domain.visa.error.VisaActivationError import com.tangem.domain.visa.model.VisaDataForApprove import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet import com.tangem.domain.visa.model.sign @@ -37,6 +38,7 @@ class VisaCustomerWalletApproveTask( } if (VisaUtilities.isVisaCard(card.firmwareVersion.doubleValue, card.batchId)) { + // TODO TVF-21 callback(CompletionResult.Failure(TangemSdkError.Underlying("Can't use Visa card for approve"))) return } @@ -44,7 +46,7 @@ class VisaCustomerWalletApproveTask( if (visaDataForApprove.customerWalletCardId != null && card.cardId != visaDataForApprove.customerWalletCardId) { callback( CompletionResult.Failure( - TangemSdkError.Underlying("Use tangem wallet specified during visa registration"), + TangemSdkError.Underlying("Use tangem wallet specified during visa registration"), // TODO TVF-21 ), ) return @@ -74,16 +76,12 @@ class VisaCustomerWalletApproveTask( } val derivationPath = VisaUtilities.visaDefaultDerivationPath(derivationStyle) ?: run { - callback( - CompletionResult.Failure( - TangemSdkError.Underlying("Failed to generate derivation path with provided derivation style"), - ), - ) + callback(CompletionResult.Failure(VisaActivationError.FailedToCreateAddress.tangemError)) return } val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: run { - callback(CompletionResult.Failure(TangemSdkError.Underlying(VisaActivationError.MissingWallet.message))) + callback(CompletionResult.Failure(VisaActivationError.MissingWallet.tangemError)) return } @@ -123,7 +121,7 @@ class VisaCustomerWalletApproveTask( ) validationResult.onLeft { - callback(CompletionResult.Failure(TangemSdkError.Underlying(it.message))) + callback(CompletionResult.Failure(it.tangemError)) return } @@ -144,7 +142,7 @@ class VisaCustomerWalletApproveTask( targetAddress = visaDataForApprove.targetAddress, card = CardDTO(card), ).getOrElse { - callback(CompletionResult.Failure(TangemSdkError.Underlying(it.message))) + callback(CompletionResult.Failure(it.tangemError)) return } diff --git a/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandler.kt b/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandler.kt index 4b6dd922b9..ad20167f85 100644 --- a/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandler.kt +++ b/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandler.kt @@ -8,11 +8,15 @@ import com.tangem.common.core.CardSession import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString +import com.tangem.core.error.ext.tangemError import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.datasource.local.visa.VisaAuthTokenStorage import com.tangem.domain.common.visa.VisaUtilities import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility +import com.tangem.domain.visa.error.VisaActivationError +import com.tangem.domain.visa.error.VisaAuthorizationAPIError +import com.tangem.domain.visa.error.VisaCardScanError import com.tangem.domain.visa.model.* import com.tangem.domain.visa.repository.VisaActivationRepository import com.tangem.domain.visa.repository.VisaAuthRepository @@ -71,9 +75,7 @@ internal class VisaCardScanHandler @Inject constructor( val derivationPath = VisaUtilities.visaDefaultDerivationPath ?: run { Timber.e("Failed to create derivation path while first scan") - return CompletionResult.Failure( - TangemSdkError.Underlying(VisaCardScanHandlerError.FailedToCreateDerivationPath.errorDescription), - ) + return CompletionResult.Failure(VisaCardScanError.FailedToCreateDerivationPath.tangemError) } val derivationTask = DeriveWalletPublicKeyTask(wallet.publicKey, derivationPath) @@ -104,32 +106,24 @@ internal class VisaCardScanHandler @Inject constructor( val derivationPath = VisaUtilities.visaDefaultDerivationPath ?: run { Timber.e("Failed to create derivation path while handling wallet authorization") - return CompletionResult.Failure( - TangemSdkError.Underlying(VisaCardScanHandlerError.FailedToCreateDerivationPath.errorDescription), - ) + return CompletionResult.Failure(VisaCardScanError.FailedToCreateDerivationPath.tangemError) } val card = session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead()) val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: run { Timber.e("Failed to find extended public key while handling wallet authorization") - return CompletionResult.Failure( - TangemSdkError.Underlying(VisaCardScanHandlerError.FailedToFindDerivedWalletKey.errorDescription), - ) + return CompletionResult.Failure(VisaCardScanError.FailedToFindWallet.tangemError) } val extendedPublicKey = wallet.derivedKeys[derivationPath] ?: run { Timber.e("Failed to find extended public key while handling wallet authorization") - return CompletionResult.Failure( - TangemSdkError.Underlying(VisaCardScanHandlerError.FailedToFindDerivedWalletKey.errorDescription), - ) + return CompletionResult.Failure(VisaCardScanError.FailedToFindDerivedWalletKey.tangemError) } val walletAddress = VisaWalletPublicKeyUtility.generateAddressOnSecp256k1(extendedPublicKey.publicKey) .getOrElse { - return CompletionResult.Failure( - TangemSdkError.Underlying("Cannot generate address on Visa curve"), - ) + return CompletionResult.Failure(it.tangemError) } Timber.i("Requesting challenge for wallet authorization") @@ -200,7 +194,7 @@ internal class VisaCardScanHandler @Inject constructor( ) }.getOrElse { Timber.e("Failed to get challenge for Card authorization. Plain error: ${it.message}") - return CompletionResult.Failure(TangemSdkError.Underlying(it.message ?: "Unknown error")) + return CompletionResult.Failure(VisaAuthorizationAPIError.tangemError) } Timber.i("Received challenge to sign: ${challengeResponse.challenge}") @@ -230,11 +224,7 @@ internal class VisaCardScanHandler @Inject constructor( ) }.getOrElse { Timber.e("Failed to sign challenge with Card public key. Plain error: ${it.message}") - return CompletionResult.Failure( - TangemSdkError.Underlying( - customMessage = it.message ?: "Unknown error", - ), - ) + return CompletionResult.Failure(VisaAuthorizationAPIError.tangemError) } visaAuthTokenStorage.store( @@ -251,7 +241,7 @@ internal class VisaCardScanHandler @Inject constructor( } if (error != null) { - return CompletionResult.Failure(TangemSdkError.Underlying(error.message)) + return CompletionResult.Failure(error.tangemError) } val activationInput = VisaActivationInput( diff --git a/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandlerError.kt b/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandlerError.kt deleted file mode 100644 index d507ac7b14..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandlerError.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.tap.domain.visa - -private const val COMMON_DESCRIPTION = "Error occurred. Please contact support." - -internal enum class VisaCardScanHandlerError(val errorDescription: String) { - FailedToCreateDerivationPath(COMMON_DESCRIPTION), - FailedToFindWallet(COMMON_DESCRIPTION), - FailedToFindDerivedWalletKey(COMMON_DESCRIPTION), -} \ No newline at end of file diff --git a/core/error/build.gradle.kts b/core/error/build.gradle.kts index 59a90763a4..7ff7fb7522 100644 --- a/core/error/build.gradle.kts +++ b/core/error/build.gradle.kts @@ -1,14 +1,4 @@ plugins { - alias(deps.plugins.android.library) - alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.jvm) id("configuration") -} - -android { - namespace = "com.tangem.core.error" -} - -dependencies { - implementation(tangemDeps.card.core) - implementation(tangemDeps.blockchain) } \ No newline at end of file diff --git a/core/error/ext/.gitignore b/core/error/ext/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/core/error/ext/.gitignore @@ -0,0 +1 @@ +/build diff --git a/core/error/ext/build.gradle.kts b/core/error/ext/build.gradle.kts new file mode 100644 index 0000000000..f6e50d5bd6 --- /dev/null +++ b/core/error/ext/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + id("configuration") +} + +android { + namespace = "com.tangem.core.error.ext" +} + +dependencies { + api(projects.core.error) + + implementation(tangemDeps.card.core) + implementation(tangemDeps.blockchain) +} \ No newline at end of file diff --git a/core/error/ext/src/main/kotlin/com/tangem/core/error/ext/Extensions.kt b/core/error/ext/src/main/kotlin/com/tangem/core/error/ext/Extensions.kt new file mode 100644 index 0000000000..587b53931c --- /dev/null +++ b/core/error/ext/src/main/kotlin/com/tangem/core/error/ext/Extensions.kt @@ -0,0 +1,27 @@ +package com.tangem.core.error.ext + +import com.tangem.common.core.TangemSdkError +import com.tangem.blockchain.common.BlockchainSdkError +import com.tangem.common.core.TangemError +import com.tangem.core.error.UniversalError + +val TangemError.universalError: UniversalError + get() = when (this) { + is TangemSdkError -> object : UniversalError { + override val errorCode: Int = 101000000 + code + } + else -> object : UniversalError { + override val errorCode: Int = code + } + } + +val BlockchainSdkError.universalError: UniversalError + get() = object : UniversalError { + override val errorCode: Int = 102000000 + code + } + +val UniversalError.tangemError: TangemError + get() = object : TangemError(errorCode) { + override var customMessage: String = errorCode.toString() + override val messageResId: Int? = null + } \ No newline at end of file diff --git a/core/error/src/main/kotlin/com/tangem/core/error/Utils.kt b/core/error/src/main/kotlin/com/tangem/core/error/Utils.kt deleted file mode 100644 index daf75b0558..0000000000 --- a/core/error/src/main/kotlin/com/tangem/core/error/Utils.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.tangem.core.error - -import com.tangem.common.core.TangemSdkError -import com.tangem.blockchain.common.BlockchainSdkError - -val TangemSdkError.universalError: UniversalError - get() = object : UniversalError { - override val errorCode: Int = 101000000 + code - } - -val BlockchainSdkError.universalError: UniversalError - get() = object : UniversalError { - override val errorCode: Int = 102000000 + code - } \ No newline at end of file diff --git a/domain/legacy/build.gradle.kts b/domain/legacy/build.gradle.kts index 11e080b895..fc7fa7889b 100644 --- a/domain/legacy/build.gradle.kts +++ b/domain/legacy/build.gradle.kts @@ -12,6 +12,7 @@ android { dependencies { implementation(projects.core.datasource) implementation(projects.core.utils) + implementation(projects.core.error) implementation(projects.common) implementation(projects.libs.auth) implementation(projects.libs.blockchainSdk) diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/visa/VisaWalletPublicKeyUtility.kt b/domain/legacy/src/main/java/com/tangem/domain/common/visa/VisaWalletPublicKeyUtility.kt index e7500baf4f..2bc3ecbc4a 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/visa/VisaWalletPublicKeyUtility.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/visa/VisaWalletPublicKeyUtility.kt @@ -8,29 +8,33 @@ import com.tangem.blockchain.common.address.AddressType import com.tangem.common.card.EllipticCurve import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.visa.error.VisaActivationError object VisaWalletPublicKeyUtility { - fun validateExtendedPublicKey(targetAddress: String, extendedPublicKey: ExtendedPublicKey): Either = - either { - validatePublicKey( - targetAddress = targetAddress, - publicKey = extendedPublicKey.publicKey, - ).bind() - } - - fun findKeyWithoutDerivation(targetAddress: String, card: CardDTO): Either = either { - val wallet = findWalletOnSecp256k1(card).bind() - + fun validateExtendedPublicKey( + targetAddress: String, + extendedPublicKey: ExtendedPublicKey, + ): Either = either { validatePublicKey( targetAddress = targetAddress, - publicKey = wallet.publicKey, + publicKey = extendedPublicKey.publicKey, ).bind() - - wallet.publicKey } - fun generateAddressOnSecp256k1(walletPublicKey: ByteArray): Either = either { + fun findKeyWithoutDerivation(targetAddress: String, card: CardDTO): Either = + either { + val wallet = findWalletOnSecp256k1(card).bind() + + validatePublicKey( + targetAddress = targetAddress, + publicKey = wallet.publicKey, + ).bind() + + wallet.publicKey + } + + fun generateAddressOnSecp256k1(walletPublicKey: ByteArray): Either = either { val addresses = catch( block = { VisaUtilities.visaBlockchain.makeAddresses( @@ -39,27 +43,22 @@ object VisaWalletPublicKeyUtility { curve = EllipticCurve.Secp256k1, ) }, - catch = { raise(Error.FailedToCreateAddress) }, + catch = { raise(VisaActivationError.FailedToCreateAddress) }, ) - addresses.firstOrNull { it.type == AddressType.Default } ?: raise(Error.FailedToCreateAddress) + addresses.firstOrNull { it.type == AddressType.Default } ?: raise(VisaActivationError.FailedToCreateAddress) } - private fun findWalletOnSecp256k1(card: CardDTO): Either = either { - card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: raise(Error.MissingWalletOnTargetCurve) + private fun findWalletOnSecp256k1(card: CardDTO): Either = either { + card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: raise(VisaActivationError.MissingWallet) } - private fun validatePublicKey(targetAddress: String, publicKey: ByteArray): Either = either { - val address = generateAddressOnSecp256k1(publicKey).bind() + private fun validatePublicKey(targetAddress: String, publicKey: ByteArray): Either = + either { + val address = generateAddressOnSecp256k1(publicKey).bind() - if (address.value != targetAddress) { - raise(Error.AddressNotMatched) + if (address.value != targetAddress) { + raise(VisaActivationError.AddressNotMatched) + } } - } - - enum class Error(val message: String) { - AddressNotMatched("ValidationError: Address not matched"), - FailedToCreateAddress("ValidationError: Failed to create address"), - MissingWalletOnTargetCurve("ValidationError: Missing wallet on target curve"), - } } \ No newline at end of file diff --git a/domain/visa/models/build.gradle.kts b/domain/visa/models/build.gradle.kts index b2df1fba1f..0aafe4291a 100644 --- a/domain/visa/models/build.gradle.kts +++ b/domain/visa/models/build.gradle.kts @@ -10,4 +10,5 @@ dependencies { ksp(deps.moshi.kotlin.codegen) implementation(deps.moshi.adapters) implementation(deps.kotlin.serialization) + implementation(projects.core.error) } \ No newline at end of file diff --git a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/error/VisaError.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/error/VisaError.kt new file mode 100644 index 0000000000..f73861c111 --- /dev/null +++ b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/error/VisaError.kt @@ -0,0 +1,51 @@ +@file:Suppress("MagicNumber") + +package com.tangem.domain.visa.error + +import com.tangem.core.error.UniversalError + +/** + * Each error code must follow this format: xxxyyyzzz where + * xxx - Feature code + * yyy - Subsystem code + * zzz - Specific error code + * If you need to add new subsystem add it to list below incrementing last code. + * `Subsystems`: + * `001` - Common API + * `002` - First card scan error + * `003` - Activation + * `004` - Authorization API + */ +object VisaError : UniversalError { + override val errorCode: Int = 104000000 +} + +object VisaAPIError : UniversalError { + override val errorCode: Int = 104001000 +} + +enum class VisaCardScanError( + override val errorCode: Int, +) : UniversalError { + FailedToCreateDerivationPath(104002001), + FailedToFindWallet(104002002), + FailedToFindDerivedWalletKey(104002003), +} + +enum class VisaActivationError( + override val errorCode: Int, +) : UniversalError { + BlockedForActivation(104003001), + InvalidActivationState(104003002), + WrongCard(104003003), + WrongRemoteState(104003004), + MissingWallet(104003005), + MissingRootOTP(104003006), + FailedToCreateAddress(104003007), + AddressNotMatched(104003008), + InconsistentRemoteState(104003009), +} + +object VisaAuthorizationAPIError : UniversalError { + override val errorCode: Int = 104004000 +} \ No newline at end of file diff --git a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationError.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationError.kt deleted file mode 100644 index ad826a2747..0000000000 --- a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationError.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tangem.domain.visa.model - -enum class VisaActivationError(val message: String) { - BlockedForActivation("Card is blocked for activation"), - InvalidActivationState("Invalid activation state"), - WrongCard("Wrong card tapped"), - WrongRemoteState("Wrong remote state"), - MissingWallet("Missing wallet"), - MissingRootOTP("Missing root OTP"), -} \ No newline at end of file diff --git a/features/onboarding-v2/impl/build.gradle.kts b/features/onboarding-v2/impl/build.gradle.kts index f0b9fcacbb..c557f4a7a8 100644 --- a/features/onboarding-v2/impl/build.gradle.kts +++ b/features/onboarding-v2/impl/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { implementation(projects.core.decompose) implementation(projects.core.navigation) implementation(projects.core.datasource) + implementation(projects.core.error.ext) /** Common */ implementation(projects.common.ui) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/model/OnboardingVisaAccessCodeModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/model/OnboardingVisaAccessCodeModel.kt index 086d02041d..71c5adec28 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/model/OnboardingVisaAccessCodeModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/model/OnboardingVisaAccessCodeModel.kt @@ -7,6 +7,11 @@ import com.tangem.common.extensions.toHexString import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.error.ext.universalError +import com.tangem.core.ui.utils.showErrorDialog +import com.tangem.domain.visa.error.VisaAPIError +import com.tangem.domain.visa.error.VisaAuthorizationAPIError import com.tangem.domain.visa.model.VisaCardActivationStatus import com.tangem.domain.visa.model.VisaCardId import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest @@ -34,6 +39,7 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor( @Suppress("UnusedPrivateMember") private val tangemSdkManager: TangemSdkManager, private val visaAuthRepository: VisaAuthRepository, + private val uiMessageSender: UiMessageSender, ) : Model() { private val params: OnboardingVisaAccessCodeComponent.Config = paramsContainer.require() @@ -138,7 +144,7 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor( ) }.getOrElse { loading(false) - // TODO show alert + uiMessageSender.showErrorDialog(VisaAuthorizationAPIError) return@launch } @@ -148,17 +154,22 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor( authorizationChallenge = challengeToSign, ), activationInput = activationStatus.activationInput, - ) as? CompletionResult.Success ?: run { - loading(false) - // TODO show alert - return@launch + ) + + val resultData = when (result) { + is CompletionResult.Failure -> { + loading(false) + uiMessageSender.showErrorDialog(result.error.universalError) + return@launch + } + is CompletionResult.Success -> result.data } runCatching { - visaActivationRepository.activateCard(result.data.signedActivationData) + visaActivationRepository.activateCard(resultData.signedActivationData) }.onFailure { loading(false) - // TODO show alert + uiMessageSender.showErrorDialog(VisaAPIError) return@launch } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/approve/model/OnboardingVisaApproveModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/approve/model/OnboardingVisaApproveModel.kt index 396a8039b2..a4803e73d0 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/approve/model/OnboardingVisaApproveModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/approve/model/OnboardingVisaApproveModel.kt @@ -6,6 +6,10 @@ import com.tangem.common.extensions.toHexString import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.error.ext.universalError +import com.tangem.core.ui.utils.showErrorDialog +import com.tangem.domain.visa.error.VisaAPIError import com.tangem.domain.visa.model.VisaCardId import com.tangem.domain.visa.model.VisaDataForApprove import com.tangem.domain.visa.repository.VisaActivationRepository @@ -27,6 +31,7 @@ internal class OnboardingVisaApproveModel @Inject constructor( visaActivationRepositoryFactory: VisaActivationRepository.Factory, override val dispatchers: CoroutineDispatcherProvider, private val tangemSdkManager: TangemSdkManager, + private val uiMessageSender: UiMessageSender, ) : Model() { private val params = paramsContainer.require() @@ -56,7 +61,7 @@ internal class OnboardingVisaApproveModel @Inject constructor( visaActivationRepository.getCustomerWalletAcceptanceData(params.preparationDataForApprove.request) }.getOrElse { loading(false) - // TODO show dialog + uiMessageSender.showErrorDialog(VisaAPIError) return@launch } @@ -66,17 +71,22 @@ internal class OnboardingVisaApproveModel @Inject constructor( targetAddress = params.preparationDataForApprove.customerWalletAddress, dataToSign = dataToSign, ), - ) as? CompletionResult.Success ?: run { - loading(false) - // TODO show dialog - return@launch + ) + + val resultData = when (result) { + is CompletionResult.Failure -> { + loading(false) + uiMessageSender.showErrorDialog(result.error.universalError) + return@launch + } + is CompletionResult.Success -> result.data } runCatching { - visaActivationRepository.approveByCustomerWallet(result.data) + visaActivationRepository.approveByCustomerWallet(resultData) }.onFailure { loading(false) - // TODO show dialog + uiMessageSender.showErrorDialog(VisaAPIError) return@launch } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt index 57f424606e..b8d608e1d3 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt @@ -5,9 +5,13 @@ import com.tangem.common.extensions.toHexString import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.ui.utils.showErrorDialog import com.tangem.datasource.local.visa.VisaAuthTokenStorage import com.tangem.datasource.local.visa.VisaOTPStorage import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.visa.error.VisaActivationError +import com.tangem.domain.visa.error.VisaAuthorizationAPIError import com.tangem.domain.visa.model.* import com.tangem.domain.visa.repository.VisaActivationRepository import com.tangem.domain.visa.repository.VisaAuthRepository @@ -36,6 +40,7 @@ internal class OnboardingVisaInProgressModel @Inject constructor( private val otpStorage: VisaOTPStorage, private val userWalletBuilderFactory: UserWalletBuilder.Factory, private val userWalletsListManager: UserWalletsListManager, + private val uiMessageSender: UiMessageSender, ) : Model() { private val params = paramsContainer.require() @@ -58,7 +63,7 @@ internal class OnboardingVisaInProgressModel @Inject constructor( is VisaActivationRemoteState.CardWalletSignatureRequired, VisaActivationRemoteState.BlockedForActivation, -> { - // TODO show alert inconsistent state + uiMessageSender.showErrorDialog(VisaActivationError.InconsistentRemoteState) return@launch } is VisaActivationRemoteState.CustomerWalletSignatureRequired, @@ -92,7 +97,7 @@ internal class OnboardingVisaInProgressModel @Inject constructor( val newTokens = runCatching { visaAuthRepository.refreshAccessTokens(authTokens.refreshToken) }.getOrElse { - // TODO show alert + uiMessageSender.showErrorDialog(VisaAuthorizationAPIError) return } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/welcome/model/OnboardingVisaWelcomeModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/welcome/model/OnboardingVisaWelcomeModel.kt index 82a7092b99..db3f28bd1a 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/welcome/model/OnboardingVisaWelcomeModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/welcome/model/OnboardingVisaWelcomeModel.kt @@ -5,6 +5,10 @@ import com.tangem.common.extensions.toHexString import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.error.ext.universalError +import com.tangem.core.ui.utils.showErrorDialog +import com.tangem.domain.visa.error.VisaAPIError import com.tangem.domain.visa.model.VisaCardId import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest import com.tangem.domain.visa.repository.VisaActivationRepository @@ -28,6 +32,7 @@ internal class OnboardingVisaWelcomeModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, private val tangemSdkManager: TangemSdkManager, private val visaActivationRepositoryFactory: VisaActivationRepository.Factory, + private val uiMessageSender: UiMessageSender, ) : Model() { private val params = paramsContainer.require() @@ -68,24 +73,29 @@ internal class OnboardingVisaWelcomeModel @Inject constructor( visaActivationRepository.getCardWalletAcceptanceData(params.dataToSignRequest) }.getOrElse { loading(false) - // TODO show alert + uiMessageSender.showErrorDialog(VisaAPIError) return@launch } val result = tangemSdkManager.activateVisaCard( mode = VisaCardActivationTaskMode.SignOnly(dataToSignByCardWallet = dataToSignByCardWallet), activationInput = params.activationInput, - ) as? CompletionResult.Success ?: run { - loading(false) - // TODO show alert - return@launch + ) + + val resultData = when (result) { + is CompletionResult.Failure -> { + loading(false) + uiMessageSender.showErrorDialog(result.error.universalError) + return@launch + } + is CompletionResult.Success -> result.data } runCatching { - visaActivationRepository.activateCard(result.data.signedActivationData) + visaActivationRepository.activateCard(resultData.signedActivationData) }.onFailure { loading(false) - // TODO show alert + uiMessageSender.showErrorDialog(VisaAPIError) return@launch } diff --git a/settings.gradle.kts b/settings.gradle.kts index b4efe097b4..cbcae1d8bb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -149,6 +149,7 @@ include(":core:deep-links:global") include(":core:decompose") include(":core:pagination") include(":core:error") +include(":core:error:ext") // endregion Core modules // region Common modules