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

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