Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-17 11:20:30 +03:00
parent 422e406d15
commit 7c91472d99
7 changed files with 26 additions and 12 deletions

View file

@ -83,11 +83,14 @@ internal class VisaCardScanHandler @Inject constructor(
Timber.i("Requesting challenge for wallet authorization") Timber.i("Requesting challenge for wallet authorization")
val challengeResponse = visaAuthRepository.getCardWalletAuthChallenge(cardWalletAddress = walletAddress.value) val challengeResponse = visaAuthRepository.getCardWalletAuthChallenge(
.getOrElse { cardId = card.cardId,
Timber.i("Failed to get Access token for Wallet public key authorization") // This is the wallet public key, not the address and it's alright, as the API expects it in this format
return CompletionResult.Failure(it.tangemError) cardWalletAddress = wallet.publicKey.toHexString(),
} ).getOrElse {
Timber.i("Failed to get Access token for Wallet public key authorization")
return CompletionResult.Failure(it.tangemError)
}
val signChallengeResult = signChallengeWithWallet( val signChallengeResult = signChallengeWithWallet(
publicKey = wallet.publicKey, publicKey = wallet.publicKey,
@ -96,15 +99,16 @@ internal class VisaCardScanHandler @Inject constructor(
return when (signChallengeResult) { return when (signChallengeResult) {
is CompletionResult.Success -> { is CompletionResult.Success -> {
val signature = signChallengeResult.data.cardSignature ?: run { val signature = signChallengeResult.data.walletSignature
Timber.i("Failed to sign challenge with Wallet public key") val salt = signChallengeResult.data.salt
return CompletionResult.Failure(VisaCardScanError.FailedToSignChallenge.tangemError)
}
Timber.i("Challenge signed with Wallet public key") Timber.i("Challenge signed with Wallet public key")
handleWalletAuthorizationTokens( handleWalletAuthorizationTokens(
cardWalletAddress = walletAddress.value, cardWalletAddress = walletAddress.value,
signedChallenge = challengeResponse.toSignedChallenge(signature.toHexString()), signedChallenge = challengeResponse.toSignedChallenge(
signedChallenge = signature.toHexString(),
salt = salt.toHexString(),
),
) )
} }
is CompletionResult.Failure -> { is CompletionResult.Failure -> {

View file

@ -7,4 +7,5 @@ import com.squareup.moshi.JsonClass
data class GenerateNoneByCardWalletRequest( data class GenerateNoneByCardWalletRequest(
@Json(name = "auth_type") val authType: String = "card_wallet", @Json(name = "auth_type") val authType: String = "card_wallet",
@Json(name = "card_wallet_address") val cardWalletAddress: String, @Json(name = "card_wallet_address") val cardWalletAddress: String,
@Json(name = "card_id") val cardId: String,
) )

View file

@ -8,4 +8,5 @@ data class GetAccessTokenByCardWalletRequest(
@Json(name = "auth_type") val authType: String = "card_wallet", @Json(name = "auth_type") val authType: String = "card_wallet",
@Json(name = "session_id") val sessionId: String, @Json(name = "session_id") val sessionId: String,
@Json(name = "signature") val signature: String, @Json(name = "signature") val signature: String,
@Json(name = "salt") val salt: String,
) )

View file

@ -47,12 +47,14 @@ internal class DefaultVisaAuthRepository @Inject constructor(
} }
override suspend fun getCardWalletAuthChallenge( override suspend fun getCardWalletAuthChallenge(
cardId: String,
cardWalletAddress: String, cardWalletAddress: String,
): Either<VisaApiError, VisaAuthChallenge.Wallet> = withContext(dispatchers.io) { ): Either<VisaApiError, VisaAuthChallenge.Wallet> = withContext(dispatchers.io) {
request { request {
visaAuthApi.generateNonceByCardWallet( visaAuthApi.generateNonceByCardWallet(
GenerateNoneByCardWalletRequest( GenerateNoneByCardWalletRequest(
cardWalletAddress = cardWalletAddress, cardWalletAddress = cardWalletAddress,
cardId = cardId,
), ),
).getOrThrow() ).getOrThrow()
}.map { response -> }.map { response ->
@ -82,6 +84,7 @@ internal class DefaultVisaAuthRepository @Inject constructor(
GetAccessTokenByCardWalletRequest( GetAccessTokenByCardWalletRequest(
sessionId = signedChallenge.challenge.session.sessionId, sessionId = signedChallenge.challenge.session.sessionId,
signature = signedChallenge.signature, signature = signedChallenge.signature,
salt = signedChallenge.salt,
), ),
).getOrThrow() ).getOrThrow()
} }

View file

@ -23,9 +23,10 @@ fun VisaAuthChallenge.Card.toSignedChallenge(signedChallenge: String, salt: Stri
) )
} }
fun VisaAuthChallenge.Wallet.toSignedChallenge(signedChallenge: String): VisaAuthSignedChallenge { fun VisaAuthChallenge.Wallet.toSignedChallenge(signedChallenge: String, salt: String): VisaAuthSignedChallenge {
return VisaAuthSignedChallenge.ByWallet( return VisaAuthSignedChallenge.ByWallet(
challenge = this, challenge = this,
signature = signedChallenge, signature = signedChallenge,
salt = salt,
) )
} }

View file

@ -11,5 +11,6 @@ sealed class VisaAuthSignedChallenge {
data class ByWallet( data class ByWallet(
val challenge: VisaAuthChallenge.Wallet, val challenge: VisaAuthChallenge.Wallet,
val signature: String, val signature: String,
val salt: String,
) : VisaAuthSignedChallenge() ) : VisaAuthSignedChallenge()
} }

View file

@ -13,7 +13,10 @@ interface VisaAuthRepository {
cardPublicKey: String, cardPublicKey: String,
): Either<VisaApiError, VisaAuthChallenge.Card> ): Either<VisaApiError, VisaAuthChallenge.Card>
suspend fun getCardWalletAuthChallenge(cardWalletAddress: String): Either<VisaApiError, VisaAuthChallenge.Wallet> suspend fun getCardWalletAuthChallenge(
cardId: String,
cardWalletAddress: String,
): Either<VisaApiError, VisaAuthChallenge.Wallet>
suspend fun getAccessTokens(signedChallenge: VisaAuthSignedChallenge): Either<VisaApiError, VisaAuthTokens> suspend fun getAccessTokens(signedChallenge: VisaAuthSignedChallenge): Either<VisaApiError, VisaAuthTokens>