Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-01 09:06:11 +03:00
parent ded577faff
commit aea7f5f415
21 changed files with 220 additions and 136 deletions

View file

@ -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)

View file

@ -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<TangemSdkError.Underlying, VisaDataToSignByCardWallet> = either {
): Either<TangemError, VisaDataToSignByCardWallet> = 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<VisaCardActivationResponse> {
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(

View file

@ -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
}

View file

@ -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(

View file

@ -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),
}

View file

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

1
core/error/ext/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

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

View file

@ -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
}

View file

@ -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
}

View file

@ -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)

View file

@ -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<Error, Unit> =
either {
validatePublicKey(
targetAddress = targetAddress,
publicKey = extendedPublicKey.publicKey,
).bind()
}
fun findKeyWithoutDerivation(targetAddress: String, card: CardDTO): Either<Error, ByteArray> = either {
val wallet = findWalletOnSecp256k1(card).bind()
fun validateExtendedPublicKey(
targetAddress: String,
extendedPublicKey: ExtendedPublicKey,
): Either<VisaActivationError, Unit> = either {
validatePublicKey(
targetAddress = targetAddress,
publicKey = wallet.publicKey,
publicKey = extendedPublicKey.publicKey,
).bind()
wallet.publicKey
}
fun generateAddressOnSecp256k1(walletPublicKey: ByteArray): Either<Error, Address> = either {
fun findKeyWithoutDerivation(targetAddress: String, card: CardDTO): Either<VisaActivationError, ByteArray> =
either {
val wallet = findWalletOnSecp256k1(card).bind()
validatePublicKey(
targetAddress = targetAddress,
publicKey = wallet.publicKey,
).bind()
wallet.publicKey
}
fun generateAddressOnSecp256k1(walletPublicKey: ByteArray): Either<VisaActivationError, Address> = 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<Error, CardDTO.Wallet> = either {
card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: raise(Error.MissingWalletOnTargetCurve)
private fun findWalletOnSecp256k1(card: CardDTO): Either<VisaActivationError, CardDTO.Wallet> = either {
card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: raise(VisaActivationError.MissingWallet)
}
private fun validatePublicKey(targetAddress: String, publicKey: ByteArray): Either<Error, Unit> = either {
val address = generateAddressOnSecp256k1(publicKey).bind()
private fun validatePublicKey(targetAddress: String, publicKey: ByteArray): Either<VisaActivationError, Unit> =
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"),
}
}

View file

@ -10,4 +10,5 @@ dependencies {
ksp(deps.moshi.kotlin.codegen)
implementation(deps.moshi.adapters)
implementation(deps.kotlin.serialization)
implementation(projects.core.error)
}

View file

@ -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
}

View file

@ -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"),
}

View file

@ -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)

View file

@ -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
}

View file

@ -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<OnboardingVisaApproveComponent.Config>()
@ -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
}

View file

@ -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<Config>()
@ -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
}

View file

@ -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<Config>()
@ -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
}

View file

@ -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