Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-19 10:20:09 +03:00
commit f0e4aec9a4
858 changed files with 13724 additions and 5705 deletions

View file

@ -0,0 +1,94 @@
package com.tangem.data.pay
import com.tangem.common.card.FirmwareVersion
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import javax.inject.Inject
internal class DefaultTangemPayEligibilityManager @Inject constructor(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val onboardingRepository: OnboardingRepository,
) : TangemPayEligibilityManager {
private var cachedEligibleWallets: List<UserWallet>? = null
private var eligibleWalletsDeferred: Deferred<List<UserWallet>>? = null
private val loadMutex = Mutex()
override suspend fun getEligibleWallets(): List<UserWallet> {
cachedEligibleWallets?.let { return it }
return loadMutex.withLock {
cachedEligibleWallets?.let { return it }
eligibleWalletsDeferred?.let { return it.await() }
coroutineScope {
val deferred = async {
getPossibleWalletsForTangemPay()
.excludePaeraCustomers()
.also { cachedEligibleWallets = it }
}
eligibleWalletsDeferred = deferred
try {
deferred.await()
} finally {
eligibleWalletsDeferred = null
}
}
}
}
private suspend fun getPossibleWalletsForTangemPay(): List<UserWallet> {
if (!onboardingRepository.checkCustomerEligibility()) {
return emptyList()
}
val wallets = if (hotWalletFeatureToggles.isHotWalletEnabled) {
userWalletsListRepository.userWallets.value
} else {
userWalletsListManager.userWalletsSync
} ?: return emptyList()
return wallets.filter { wallet ->
wallet.isMultiCurrency && !wallet.isLocked && wallet.isCompatible()
}
}
private fun UserWallet.isCompatible(): Boolean = when (this) {
is UserWallet.Cold ->
scanResponse.card.firmwareVersion >= FirmwareVersion.HDWalletAvailable
is UserWallet.Hot -> true
}
private suspend fun List<UserWallet>.excludePaeraCustomers(): List<UserWallet> {
if (isEmpty()) return this
return coroutineScope {
map { wallet ->
async {
val isCustomer = onboardingRepository
.checkCustomerWallet(wallet.walletId)
.getOrNull() == true
wallet to isCustomer
}
}
.awaitAll()
.mapNotNull { (wallet, isCustomer) ->
wallet.takeUnless { isCustomer }
}
}
}
}

View file

@ -1,16 +1,19 @@
package com.tangem.data.pay.di
import com.tangem.data.pay.DefaultTangemPayCryptoCurrencyFactory
import com.tangem.data.pay.DefaultTangemPayEligibilityManager
import com.tangem.data.pay.repository.*
import com.tangem.data.pay.usecase.DefaultGetTangemPayCurrencyStatusUseCase
import com.tangem.data.pay.usecase.DefaultTangemPayWithdrawUseCase
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.pay.repository.*
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.tangempay.GetTangemPayCurrencyStatusUseCase
import com.tangem.domain.tangempay.TangemPayWithdrawUseCase
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
import com.tangem.security.DeviceSecurityInfoProvider
import dagger.Binds
import dagger.Module
import dagger.Provides
@ -62,6 +65,10 @@ internal interface TangemPayDataModule {
@Singleton
fun bindTangemPayWithdrawUseCase(impl: DefaultTangemPayWithdrawUseCase): TangemPayWithdrawUseCase
@Binds
@Singleton
fun bindTangemPayEligibilityManager(impl: DefaultTangemPayEligibilityManager): TangemPayEligibilityManager
companion object {
@Provides
@Singleton
@ -69,11 +76,15 @@ internal interface TangemPayDataModule {
repository: OnboardingRepository,
customerOrderRepository: CustomerOrderRepository,
tangemPayOnboardingRepository: OnboardingRepository,
eligibilityManager: TangemPayEligibilityManager,
deviceSecurity: DeviceSecurityInfoProvider,
): TangemPayMainScreenCustomerInfoUseCase {
return TangemPayMainScreenCustomerInfoUseCase(
repository = repository,
customerOrderRepository = customerOrderRepository,
tangemPayOnboardingRepository = tangemPayOnboardingRepository,
eligibilityManager = eligibilityManager,
deviceSecurity = deviceSecurity,
)
}

View file

@ -49,7 +49,7 @@ internal class DefaultOnboardingRepository @Inject constructor(
return requestHelper.performWithStaticToken {
tangemPayApi.validateDeeplink(body = DeeplinkValidityRequest(link = link))
}.map { response ->
response.result?.status == VALID_STATUS
response.result?.status.equals(VALID_STATUS, ignoreCase = true)
}
}
@ -84,7 +84,6 @@ internal class DefaultOnboardingRepository @Inject constructor(
}
override suspend fun getCustomerInfo(userWalletId: UserWalletId): Either<VisaApiError, CustomerInfo> {
// TODO implement selector
return requestHelper.performRequest(userWalletId) { authHeader -> tangemPayApi.getCustomerMe(authHeader) }
.map { response -> getCustomerInfo(userWalletId = userWalletId, response = response.result) }
}
@ -182,7 +181,7 @@ internal class DefaultOnboardingRepository @Inject constructor(
customerWalletId = userWalletId.stringValue,
)
}.map { response ->
val id = response.id
val id = response.result?.id
val isPaeraCustomer = !id.isNullOrEmpty()
tangemPayStorage.storeCheckCustomerWalletResult(userWalletId = userWalletId, isPaeraCustomer)
isPaeraCustomer
@ -193,4 +192,19 @@ internal class DefaultOnboardingRepository @Inject constructor(
error
}
}
override suspend fun checkCustomerEligibility(): Boolean {
val response = requestHelper.performWithoutToken {
tangemPayApi.checkCustomerEligibility()
}.getOrNull()
return response?.result?.isTangemPayAvailable == true
}
override suspend fun getHideMainOnboardingBanner(userWalletId: UserWalletId): Boolean {
return tangemPayStorage.getHideMainOnboardingBanner(userWalletId)
}
override suspend fun setHideMainOnboardingBanner(userWalletId: UserWalletId) {
tangemPayStorage.storeHideOnboardingBanner(userWalletId, hide = true)
}
}

View file

@ -288,6 +288,7 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
ApiEnvironment.DEV_2,
ApiEnvironment.DEV_3,
ApiEnvironment.STAGE,
ApiEnvironment.STAGE_2,
ApiEnvironment.MOCK,
-> visaLibLoader.getOrCreateConfig().rainRSAPublicKey.dev
ApiEnvironment.PROD -> visaLibLoader.getOrCreateConfig().rainRSAPublicKey.prod

View file

@ -89,6 +89,19 @@ internal class TangemPayRequestPerformer @Inject constructor(
)
}
suspend fun <T : Any> performWithoutToken(requestBlock: suspend () -> ApiResponse<T>): Either<VisaApiError, T> =
withContext(dispatchers.io) {
catch(
block = {
when (val apiResponse = requestBlock()) {
is ApiResponse.Error -> errorConverter.convert(apiResponse.cause).left()
is ApiResponse.Success<T> -> apiResponse.data.right()
}
},
catch = { errorConverter.convert(it).left() },
)
}
suspend fun <T : Any> performRequest(
userWalletId: UserWalletId,
requestBlock: suspend (header: String) -> ApiResponse<T>,

View file

@ -53,7 +53,7 @@ internal class DefaultGetTangemPayCurrencyStatusUseCase @Inject constructor(
),
sources = CryptoCurrencyStatus.Sources(),
pendingTransactions = emptySet(),
yieldBalance = null,
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
),

View file

@ -2,7 +2,7 @@ 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.api.pay.models.response.TangemPayErrorResponse
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.utils.converter.Converter
@ -14,7 +14,7 @@ internal class TangemPayErrorConverter @Inject constructor(
@NetworkMoshi moshi: Moshi,
) : Converter<Throwable, VisaApiError> {
private val visaErrorAdapter by lazy { moshi.adapter(VisaErrorResponse::class.java) }
private val tangemPayErrorAdapter by lazy { moshi.adapter(TangemPayErrorResponse::class.java) }
override fun convert(value: Throwable): VisaApiError {
return if (value is ApiResponseError.HttpException) {
@ -23,7 +23,7 @@ internal class TangemPayErrorConverter @Inject constructor(
val errorBody = value.errorBody ?: return VisaApiError.UnknownWithoutCode
return runCatching {
visaErrorAdapter.fromJson(errorBody)?.error?.code ?: value.code.numericCode
tangemPayErrorAdapter.fromJson(errorBody)?.error?.code ?: value.code.numericCode
}.map {
VisaApiError.fromBackendError(it)
}.getOrElse {

View file

@ -1,39 +0,0 @@
package com.tangem.data.pay.util
import com.tangem.common.card.FirmwareVersion
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import javax.inject.Inject
// TODO remove after implement wallet selector in pay
class TangemPayWalletsManager @Inject constructor(
private val manager: UserWalletsListManager,
private val repository: UserWalletsListRepository,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) {
@Deprecated("Don't use and put userWallet in features that need it")
suspend fun getDefaultWalletForTangemPay(): UserWallet.Cold {
val userWalletsFlow = if (useNewRepository()) repository.userWallets else manager.userWallets
val userWallets = userWalletsFlow.filter { !it.isNullOrEmpty() }.first()
return findColdWallet(userWallets)
}
@Deprecated("Don't use and put userWallet in features that need it")
fun getDefaultWalletForTangemPayBlocking(): UserWallet.Cold {
val userWallets = if (useNewRepository()) repository.userWallets.value else manager.userWalletsSync
return findColdWallet(userWallets)
}
private fun useNewRepository(): Boolean = hotWalletFeatureToggles.isHotWalletEnabled
private fun findColdWallet(userWallets: List<UserWallet>?): UserWallet.Cold {
return userWallets?.find {
it is UserWallet.Cold && it.scanResponse.card.firmwareVersion >= FirmwareVersion.HDWalletAvailable
} as? UserWallet.Cold ?: error("Cannot find cold user wallet")
}
}

View file

@ -0,0 +1,87 @@
package com.tangem.data.visa
import arrow.core.Either
import com.squareup.moshi.Moshi
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.pay.TangemPayAuthApi
import com.tangem.datasource.api.pay.models.request.GenerateNonceByCustomerWalletRequest
import com.tangem.datasource.api.pay.models.request.GetTokenByCustomerWalletRequest
import com.tangem.datasource.api.pay.models.response.TangemPayErrorResponse
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.domain.visa.datasource.TangemPayRemoteDataSource
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.domain.visa.model.TangemPayAuthTokens
import com.tangem.domain.visa.model.VisaAuthChallenge
import com.tangem.domain.visa.model.VisaAuthSession
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import javax.inject.Inject
internal class DefaultTangemPayRemoteDataSource @Inject constructor(
@NetworkMoshi private val moshi: Moshi,
private val tangemPayAuthApi: TangemPayAuthApi,
private val dispatchers: CoroutineDispatcherProvider,
) : TangemPayRemoteDataSource {
private val errorAdapter by lazy { moshi.adapter(TangemPayErrorResponse::class.java) }
override suspend fun getCustomerWalletAuthChallenge(
customerWalletAddress: String,
customerWalletId: String,
): Either<VisaApiError, VisaAuthChallenge.Wallet> = withContext(dispatchers.io) {
request {
tangemPayAuthApi.generateNonceByCustomerWallet(
request = GenerateNonceByCustomerWalletRequest(
customerWalletAddress = customerWalletAddress,
customerWalletId = customerWalletId,
),
).getOrThrow()
}.map { response ->
VisaAuthChallenge.Wallet(
challenge = response.nonce,
session = VisaAuthSession(response.sessionId),
)
}
}
override suspend fun getTokenWithCustomerWallet(
sessionId: String,
signature: String,
nonce: String,
): Either<VisaApiError, TangemPayAuthTokens> = withContext(dispatchers.io) {
request {
tangemPayAuthApi.getTokenByCustomerWallet(
request = GetTokenByCustomerWalletRequest(
authType = "customer_wallet",
sessionId = sessionId,
signature = signature,
messageFormat = "Tangem Pay wants to sign in with your account. Nonce: $nonce",
),
).getOrThrow()
}.map { response ->
TangemPayAuthTokens(
accessToken = response.accessToken,
expiresAt = response.expiresAt,
refreshToken = response.refreshToken,
refreshExpiresAt = response.refreshExpiresAt,
)
}
}
private suspend fun <T : Any> request(requestBlock: suspend () -> T): Either<VisaApiError, T> {
return runCatching {
Either.Right(requestBlock())
}.getOrElse { responseError ->
if (responseError is ApiResponseError.HttpException &&
responseError.errorBody != null
) {
val errorCode =
errorAdapter.fromJson(responseError.errorBody)?.error?.code ?: responseError.code.numericCode
return Either.Left(VisaApiError.fromBackendError(errorCode))
}
return Either.Left(VisaApiError.UnknownWithoutCode)
}
}
}

View file

@ -10,15 +10,16 @@ import com.tangem.datasource.api.common.config.ApiEnvironment
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.models.request.*
import com.tangem.datasource.api.pay.models.response.VisaErrorResponseJsonAdapter
import com.tangem.datasource.api.pay.models.request.SetPinCodeRequest
import com.tangem.datasource.api.pay.models.response.TangemPayErrorResponse
import com.tangem.datasource.api.visa.VisaApi
import com.tangem.datasource.api.visa.models.request.*
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.domain.visa.model.*
import com.tangem.domain.visa.repository.VisaActivationRepository
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -29,7 +30,7 @@ import kotlinx.coroutines.withContext
internal class DefaultVisaActivationRepository @AssistedInject constructor(
@Assisted private val visaCardId: VisaCardId,
@NetworkMoshi private val moshi: Moshi,
private val visaApi: TangemPayApi,
private val visaApi: VisaApi,
private val dispatcherProvider: CoroutineDispatcherProvider,
private val visaAuthTokenStorage: VisaAuthTokenStorage,
private val visaAuthRemoteDataSource: VisaAuthRemoteDataSource,
@ -37,7 +38,7 @@ internal class DefaultVisaActivationRepository @AssistedInject constructor(
private val apiConfigsManager: ApiConfigsManager,
) : VisaActivationRepository {
private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi)
private val errorAdapter by lazy { moshi.adapter(TangemPayErrorResponse::class.java) }
override suspend fun getActivationRemoteState(): Either<VisaApiError, VisaActivationRemoteState> =
withContext(dispatcherProvider.io) {
@ -171,6 +172,7 @@ internal class DefaultVisaActivationRepository @AssistedInject constructor(
ApiEnvironment.DEV_2,
ApiEnvironment.DEV_3,
ApiEnvironment.STAGE,
ApiEnvironment.STAGE_2,
ApiEnvironment.MOCK,
-> rsaPublicKey.dev
ApiEnvironment.PROD -> rsaPublicKey.prod
@ -186,7 +188,7 @@ internal class DefaultVisaActivationRepository @AssistedInject constructor(
responseError.errorBody != null
) {
val errorCode =
visaErrorAdapter.fromJson(responseError.errorBody!!)?.error?.code ?: responseError.code.numericCode
errorAdapter.fromJson(responseError.errorBody!!)?.error?.code ?: responseError.code.numericCode
return Either.Left(VisaApiError.fromBackendError(errorCode))
}
@ -206,7 +208,7 @@ internal class DefaultVisaActivationRepository @AssistedInject constructor(
}.getOrElse { responseError ->
if (responseError is ApiResponseError.HttpException && responseError.errorBody != null) {
val errorCode =
visaErrorAdapter.fromJson(responseError.errorBody!!)?.error?.code
errorAdapter.fromJson(responseError.errorBody!!)?.error?.code
?: responseError.code.numericCode
Either.Left(VisaApiError.fromBackendError(errorCode))
} else {

View file

@ -4,33 +4,35 @@ import arrow.core.Either
import com.squareup.moshi.Moshi
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.TangemPayAuthApi
import com.tangem.datasource.api.pay.models.request.*
import com.tangem.datasource.api.pay.models.response.VisaErrorResponseJsonAdapter
import com.tangem.datasource.api.pay.models.request.RefreshTokenByCardIdRequest
import com.tangem.datasource.api.pay.models.response.TangemPayErrorResponse
import com.tangem.datasource.api.visa.VisaApi
import com.tangem.datasource.api.visa.models.request.*
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.domain.visa.model.*
import com.tangem.domain.visa.model.VisaAuthChallenge
import com.tangem.domain.visa.model.VisaAuthSession
import com.tangem.domain.visa.model.VisaAuthSignedChallenge
import com.tangem.domain.visa.model.VisaAuthTokens
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import javax.inject.Inject
internal class DefaultVisaAuthRemoteDataSource @Inject constructor(
@NetworkMoshi private val moshi: Moshi,
private val visaAuthApi: TangemPayApi,
private val tangemPayAuthApi: TangemPayAuthApi,
private val visaApi: VisaApi,
private val dispatchers: CoroutineDispatcherProvider,
) : VisaAuthRemoteDataSource {
private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi)
private val errorAdapter by lazy { moshi.adapter(TangemPayErrorResponse::class.java) }
override suspend fun getCardAuthChallenge(
cardId: String,
cardPublicKey: String,
): Either<VisaApiError, VisaAuthChallenge.Card> = withContext(dispatchers.io) {
request {
visaAuthApi.generateNonceByCardId(
visaApi.generateNonceByCardId(
GenerateNoneByCardIdRequest(
cardId = cardId,
cardPublicKey = cardPublicKey,
@ -49,7 +51,7 @@ internal class DefaultVisaAuthRemoteDataSource @Inject constructor(
cardWalletAddress: String,
): Either<VisaApiError, VisaAuthChallenge.Wallet> = withContext(dispatchers.io) {
request {
visaAuthApi.generateNonceByCardWallet(
visaApi.generateNonceByCardWallet(
GenerateNoneByCardWalletRequest(
cardWalletAddress = cardWalletAddress,
cardId = cardId,
@ -63,56 +65,13 @@ internal class DefaultVisaAuthRemoteDataSource @Inject constructor(
}
}
override suspend fun getCustomerWalletAuthChallenge(
customerWalletAddress: String,
customerWalletId: String,
): Either<VisaApiError, VisaAuthChallenge.Wallet> = withContext(dispatchers.io) {
request {
tangemPayAuthApi.generateNonceByCustomerWallet(
request = GenerateNonceByCustomerWalletRequest(
customerWalletAddress = customerWalletAddress,
customerWalletId = customerWalletId,
),
).getOrThrow()
}.map { response ->
VisaAuthChallenge.Wallet(
challenge = response.nonce,
session = VisaAuthSession(response.sessionId),
)
}
}
override suspend fun getTokenWithCustomerWallet(
sessionId: String,
signature: String,
nonce: String,
): Either<VisaApiError, TangemPayAuthTokens> = withContext(dispatchers.io) {
request {
tangemPayAuthApi.getTokenByCustomerWallet(
request = GetTokenByCustomerWalletRequest(
authType = "customer_wallet",
sessionId = sessionId,
signature = signature,
messageFormat = "Tangem Pay wants to sign in with your account. Nonce: $nonce",
),
).getOrThrow()
}.map { response ->
TangemPayAuthTokens(
accessToken = response.accessToken,
expiresAt = response.expiresAt,
refreshToken = response.refreshToken,
refreshExpiresAt = response.refreshExpiresAt,
)
}
}
override suspend fun getAccessTokens(
signedChallenge: VisaAuthSignedChallenge,
): Either<VisaApiError, VisaAuthTokens> = withContext(dispatchers.io) {
request {
when (signedChallenge) {
is VisaAuthSignedChallenge.ByCardPublicKey -> {
visaAuthApi.getAccessTokenByCardId(
visaApi.getAccessTokenByCardId(
GetAccessTokenByCardIdRequest(
sessionId = signedChallenge.challenge.session.sessionId,
signature = signedChallenge.signature,
@ -121,7 +80,7 @@ internal class DefaultVisaAuthRemoteDataSource @Inject constructor(
).getOrThrow()
}
is VisaAuthSignedChallenge.ByWallet -> {
visaAuthApi.getAccessTokenByCardWallet(
visaApi.getAccessTokenByCardWallet(
GetAccessTokenByCardWalletRequest(
sessionId = signedChallenge.challenge.session.sessionId,
signature = signedChallenge.signature,
@ -150,11 +109,11 @@ internal class DefaultVisaAuthRemoteDataSource @Inject constructor(
request {
when (refreshToken.authType) {
VisaAuthTokens.RefreshToken.Type.CardId ->
visaAuthApi.refreshCardIdAccessToken(
visaApi.refreshCardIdAccessToken(
RefreshTokenByCardIdRequest(refreshToken = refreshToken.value),
)
VisaAuthTokens.RefreshToken.Type.CardWallet ->
visaAuthApi.refreshCardIdAccessToken(
visaApi.refreshCardIdAccessToken(
RefreshTokenByCardIdRequest(refreshToken = refreshToken.value),
)
}.getOrThrow()
@ -169,7 +128,7 @@ internal class DefaultVisaAuthRemoteDataSource @Inject constructor(
override suspend fun exchangeAccessToken(tokens: VisaAuthTokens): Either<VisaApiError, VisaAuthTokens> =
withContext(dispatchers.io) {
request {
visaAuthApi.exchangeAccessToken(
visaApi.exchangeAccessToken(
ExchangeAccessTokenRequest(
accessToken = tokens.accessToken,
refreshToken = tokens.refreshToken.value,
@ -194,7 +153,7 @@ internal class DefaultVisaAuthRemoteDataSource @Inject constructor(
responseError.errorBody != null
) {
val errorCode =
visaErrorAdapter.fromJson(responseError.errorBody!!)?.error?.code ?: responseError.code.numericCode
errorAdapter.fromJson(responseError.errorBody!!)?.error?.code ?: responseError.code.numericCode
return Either.Left(VisaApiError.fromBackendError(errorCode))
}

View file

@ -14,8 +14,8 @@ import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.common.quote.QuotesFetcher
import com.tangem.data.visa.config.VisaLibLoader
import com.tangem.data.visa.utils.*
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.models.response.VisaTxHistoryResponse
import com.tangem.datasource.api.visa.VisaApi
import com.tangem.datasource.api.visa.models.response.VisaTxHistoryResponse
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.wallet.UserWallet
@ -43,7 +43,7 @@ internal class DefaultVisaRepository @Inject constructor(
private val userWalletsStore: UserWalletsStore,
private val dispatchers: CoroutineDispatcherProvider,
private val visaApiRequestMaker: VisaApiRequestMaker,
private val visaApi: TangemPayApi,
private val visaApi: VisaApi,
private val visaCurrencyFactory: VisaCurrencyFactory,
) : VisaRepository {

View file

@ -1,6 +1,6 @@
package com.tangem.data.visa.converter
import com.tangem.datasource.api.pay.models.response.CardActivationRemoteStateResponse
import com.tangem.datasource.api.visa.models.response.CardActivationRemoteStateResponse
import com.tangem.domain.visa.model.VisaActivationOrderInfo
import com.tangem.domain.visa.model.VisaActivationRemoteState
import com.tangem.utils.converter.Converter

View file

@ -1,10 +1,12 @@
package com.tangem.data.visa.di
import com.tangem.data.pay.datasource.DefaultTangemPayAuthDataSource
import com.tangem.data.visa.DefaultTangemPayRemoteDataSource
import com.tangem.data.visa.DefaultVisaActivationRepository
import com.tangem.data.visa.DefaultVisaAuthRemoteDataSource
import com.tangem.data.visa.MockVisaRepository
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
import com.tangem.domain.visa.datasource.TangemPayRemoteDataSource
import com.tangem.domain.visa.repository.VisaActivationRepository
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
import com.tangem.domain.visa.repository.VisaRepository
@ -22,22 +24,16 @@ internal interface VisaDataModule {
@Singleton
fun bindVisaAuthRemoteDataSource(repository: DefaultVisaAuthRemoteDataSource): VisaAuthRemoteDataSource
@Binds
@Singleton
fun bindTangemPayRemoteDataSource(impl: DefaultTangemPayRemoteDataSource): TangemPayRemoteDataSource
@Binds
@Singleton
fun bindVisaActivationRepositoryFactory(
repository: DefaultVisaActivationRepository.Factory,
): VisaActivationRepository.Factory
// Mocked
// @Binds
// @Singleton
// fun bindVisaActivationRepositoryFactory(
// repository: MockVisaActivationRepository.Factory,
// ): VisaActivationRepository.Factory
// @Binds
// fun bindVisaRepository(repository: DefaultVisaRepository): VisaRepository
// Mocked
@Binds
fun bindVisaRepository(repository: MockVisaRepository): VisaRepository

View file

@ -5,8 +5,8 @@ import com.tangem.data.visa.model.AccessCodeData
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.models.request.RefreshTokenByCardWalletRequest
import com.tangem.datasource.api.visa.VisaApi
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.wallet.UserWallet
@ -24,7 +24,7 @@ typealias VisaAuthorizationHeader = String
internal class VisaApiRequestMaker @Inject constructor(
private val userWalletsStore: UserWalletsStore,
private val visaAuthApi: TangemPayApi,
private val visaAuthApi: VisaApi,
private val accessCodeDataConverter: AccessCodeDataConverter,
private val dispatcherProvider: CoroutineDispatcherProvider,
) {

View file

@ -2,7 +2,7 @@ package com.tangem.data.visa.utils
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.externallinkprovider.TxExploreState
import com.tangem.datasource.api.pay.models.response.VisaTxHistoryResponse
import com.tangem.datasource.api.visa.models.response.VisaTxHistoryResponse
import com.tangem.domain.visa.model.VisaTxDetails
internal class VisaTxDetailsFactory {

View file

@ -1,6 +1,6 @@
package com.tangem.data.visa.utils
import com.tangem.datasource.api.pay.models.response.VisaTxHistoryResponse
import com.tangem.datasource.api.visa.models.response.VisaTxHistoryResponse
import com.tangem.domain.visa.model.VisaTxHistoryItem
import com.tangem.utils.converter.Converter

View file

@ -3,7 +3,7 @@ package com.tangem.data.visa.utils
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.datasource.api.pay.models.response.VisaTxHistoryResponse
import com.tangem.datasource.api.visa.models.response.VisaTxHistoryResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.visa.model.VisaTxHistoryItem
import com.tangem.utils.coroutines.CoroutineDispatcherProvider