Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-11 11:30:58 +05:00
parent 33c331f529
commit b2b0b3cc72
14 changed files with 144 additions and 79 deletions

View file

@ -2,14 +2,12 @@ package com.tangem.data.pay
import arrow.core.Either
import arrow.core.Either.Companion.catch
import com.squareup.moshi.Moshi
import com.tangem.blockchain.blockchains.ethereum.Chain
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.core.error.UniversalError
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.data.common.network.NetworkFactory
import com.tangem.data.pay.util.TangemPayErrorConverter
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
@ -26,8 +24,8 @@ private const val TOKEN_CONTRACT_ADDRESS = "0x3c499c542cef5e3811e1192ce70d8cc03d
private const val TOKEN_DECIMALS = 6
internal class DefaultTangemPayCryptoCurrencyFactory @Inject constructor(
@NetworkMoshi moshi: Moshi,
excludedBlockchains: ExcludedBlockchains,
private val errorConverter: TangemPayErrorConverter,
) : TangemPayCryptoCurrencyFactory {
private val cryptoCurrencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
@ -37,8 +35,6 @@ internal class DefaultTangemPayCryptoCurrencyFactory @Inject constructor(
NetworkFactory(excludedBlockchains)
}
private val errorConverter by lazy(mode = LazyThreadSafetyMode.NONE) { TangemPayErrorConverter(moshi) }
override fun create(userWallet: UserWallet, chainId: Int): Either<UniversalError, CryptoCurrency> {
return catch {
val chain = requireNotNull(Chain.entries.find { it.id == chainId }) { "Can not find chain with $chainId" }

View file

@ -1,8 +1,12 @@
package com.tangem.data.pay.repository
import arrow.core.Either
import arrow.core.left
import arrow.core.raise.catch
import arrow.core.right
import com.tangem.core.error.UniversalError
import com.tangem.data.pay.util.RainCryptoUtil
import com.tangem.data.pay.util.TangemPayErrorConverter
import com.tangem.data.visa.config.VisaLibLoader
import com.tangem.datasource.api.common.config.ApiConfig
import com.tangem.datasource.api.common.config.ApiEnvironment
@ -40,6 +44,7 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
private val rainCryptoUtil: RainCryptoUtil,
private val storage: TangemPayStorage,
private val cardFrozenStateStore: TangemPayCardFrozenStateStore,
private val errorConverter: TangemPayErrorConverter,
) : TangemPayCardDetailsRepository {
private val pollingScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@ -64,37 +69,39 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
}
override suspend fun revealCardDetails(userWalletId: UserWalletId): Either<UniversalError, TangemPayCardDetails> {
return requestHelper.runWithErrorLogs(TAG) {
val publicKeyBase64 = getPublicKeyBase64()
val (secretKeyBytes, sessionId) = rainCryptoUtil.generateSecretKeyAndSessionId(publicKeyBase64)
return catch(
block = {
val publicKeyBase64 = getPublicKeyBase64()
val (secretKeyBytes, sessionId) = rainCryptoUtil.generateSecretKeyAndSessionId(publicKeyBase64)
val result = requestHelper.performRequest(userWalletId = userWalletId) { authHeader ->
tangemPayApi.revealCardDetails(
authHeader = authHeader,
body = CardDetailsRequest(sessionId = sessionId),
)
}.getOrNull()?.result ?: error("Cannot reveal card details")
val result = requestHelper.request(userWalletId) { authHeader ->
tangemPayApi.revealCardDetails(
authHeader = authHeader,
body = CardDetailsRequest(sessionId = sessionId),
val pan = rainCryptoUtil.decryptSecret(
base64Secret = result.pan.secret,
base64Iv = result.pan.iv,
secretKeyBytes = secretKeyBytes,
)
}.result ?: error("Cannot reveal card details")
val pan = rainCryptoUtil.decryptSecret(
base64Secret = result.pan.secret,
base64Iv = result.pan.iv,
secretKeyBytes = secretKeyBytes,
)
val cvv = rainCryptoUtil.decryptSecret(
base64Secret = result.cvv.secret,
base64Iv = result.cvv.iv,
secretKeyBytes = secretKeyBytes,
)
secretKeyBytes.fill(0)
val cvv = rainCryptoUtil.decryptSecret(
base64Secret = result.cvv.secret,
base64Iv = result.cvv.iv,
secretKeyBytes = secretKeyBytes,
)
secretKeyBytes.fill(0)
TangemPayCardDetails(
pan = pan,
cvv = cvv,
expirationYear = result.expirationYear,
expirationMonth = result.expirationMonth,
)
}
TangemPayCardDetails(
pan = pan,
cvv = cvv,
expirationYear = result.expirationYear,
expirationMonth = result.expirationMonth,
).right()
},
catch = { errorConverter.convert(it).left() },
)
}
override suspend fun setPin(userWalletId: UserWalletId, pin: String): Either<UniversalError, SetPinResult> {

View file

@ -5,14 +5,12 @@ import arrow.core.getOrElse
import arrow.core.left
import arrow.core.raise.catch
import arrow.core.right
import com.squareup.moshi.Moshi
import com.squareup.wire.Instant
import com.tangem.data.pay.util.TangemPayErrorConverter
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.pay.TangemPayAuthApi
import com.tangem.datasource.api.pay.models.request.RefreshCustomerWalletAccessTokenRequest
import com.tangem.datasource.api.pay.models.response.TangemPayGetTokensResponse
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.domain.models.wallet.UserWalletId
@ -33,7 +31,7 @@ private const val TAG = "TangemPayRequestPerformer"
@Singleton
internal class TangemPayRequestPerformer @Inject constructor(
@NetworkMoshi moshi: Moshi,
private val errorConverter: TangemPayErrorConverter,
private val environmentConfigStorage: EnvironmentConfigStorage,
private val dispatchers: CoroutineDispatcherProvider,
private val tangemPayAuthApi: TangemPayAuthApi,
@ -42,7 +40,6 @@ internal class TangemPayRequestPerformer @Inject constructor(
private val customerWalletAddresses = ConcurrentHashMap<UserWalletId, String>()
private val tokensMutex = Mutex()
private val errorConverter = TangemPayErrorConverter(moshi)
@Deprecated("Do not use this method")
suspend fun <T : Any> runWithErrorLogs(tag: String, requestBlock: suspend () -> T): Either<VisaApiError, T> {

View file

@ -3,10 +3,16 @@ package com.tangem.data.pay.util
import com.squareup.moshi.Moshi
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.pay.models.response.VisaErrorResponse
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.utils.converter.Converter
import javax.inject.Inject
import javax.inject.Singleton
class TangemPayErrorConverter(moshi: Moshi) : Converter<Throwable, VisaApiError> {
@Singleton
internal class TangemPayErrorConverter @Inject constructor(
@NetworkMoshi moshi: Moshi,
) : Converter<Throwable, VisaApiError> {
private val visaErrorAdapter by lazy { moshi.adapter(VisaErrorResponse::class.java) }