Updated on 2026-08-14
This commit is contained in:
parent
6d56af68f5
commit
3d9e937ee9
30 changed files with 258 additions and 344 deletions
|
|
@ -1,25 +0,0 @@
|
|||
package com.tangem.data.visa.di
|
||||
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.visa.DefaultVisaRepository
|
||||
import com.tangem.data.visa.config.VisaLibLoader
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.visa.repository.VisaRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ImplementedVisaDataModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
@ImplementedVisaRepository
|
||||
fun provideVisaRepository(impl: DefaultVisaRepository): VisaRepository
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.data.visa
|
||||
|
||||
import com.tangem.data.visa.config.VisaLibLoader
|
||||
import com.tangem.data.visa.converter.AccessCodeDataConverter
|
||||
import com.tangem.data.visa.converter.VisaActivationStatusConverter
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
|
|
@ -28,6 +29,7 @@ internal class DefaultVisaActivationRepository @AssistedInject constructor(
|
|||
private val visaAuthTokenStorage: VisaAuthTokenStorage,
|
||||
private val accessCodeDataConverter: AccessCodeDataConverter,
|
||||
private val visaAuthRepository: VisaAuthRepository,
|
||||
private val visaLibLoader: VisaLibLoader,
|
||||
) : VisaActivationRepository {
|
||||
|
||||
override suspend fun getActivationRemoteState(): VisaActivationRemoteState = withContext(dispatcherProvider.io) {
|
||||
|
|
@ -192,6 +194,10 @@ internal class DefaultVisaActivationRepository @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getPinCodeRsaEncryptionPublicKey(): String {
|
||||
return visaLibLoader.getOrCreateConfig().rsaPublicKey
|
||||
}
|
||||
|
||||
private suspend fun <T : Any> request(requestBlock: suspend () -> T): T {
|
||||
return runCatching {
|
||||
requestBlock()
|
||||
|
|
|
|||
|
|
@ -12,21 +12,18 @@ import com.tangem.common.extensions.toHexString
|
|||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.visa.config.VisaLibLoader
|
||||
import com.tangem.data.visa.utils.*
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.common.visa.TangemVisaAuthProvider
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.visa.TangemVisaApi
|
||||
import com.tangem.datasource.api.visa.models.response.VisaTxHistoryResponse
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.visa.VisaUtilities
|
||||
import com.tangem.domain.visa.exception.RefreshTokenExpiredException
|
||||
import com.tangem.domain.visa.model.*
|
||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
||||
import com.tangem.domain.visa.model.VisaCurrency
|
||||
import com.tangem.domain.visa.model.VisaTxDetails
|
||||
import com.tangem.domain.visa.model.VisaTxHistoryItem
|
||||
import com.tangem.domain.visa.repository.VisaRepository
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.lib.visa.api.VisaApi
|
||||
import com.tangem.lib.visa.model.VisaTxHistoryResponse
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -35,8 +32,8 @@ import kotlinx.coroutines.withContext
|
|||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Singleton
|
||||
internal class DefaultVisaRepository @Inject constructor(
|
||||
private val visaLibLoader: VisaLibLoader,
|
||||
|
|
@ -44,8 +41,8 @@ internal class DefaultVisaRepository @Inject constructor(
|
|||
private val cacheRegistry: CacheRegistry,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val visaAuthProvider: TangemVisaAuthProvider,
|
||||
private val visaAuthRepository: VisaAuthRepository,
|
||||
private val visaApiRequestMaker: VisaApiRequestMaker,
|
||||
private val visaApi: TangemVisaApi,
|
||||
) : VisaRepository {
|
||||
|
||||
private val currencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
|
|
@ -65,27 +62,32 @@ internal class DefaultVisaRepository @Inject constructor(
|
|||
override suspend fun getVisaCurrency(userWalletId: UserWalletId, isRefresh: Boolean): VisaCurrency {
|
||||
val address = makeAddress(userWalletId)
|
||||
|
||||
fetchVisaCurrencyIfExpired(address, isRefresh)
|
||||
fetchVisaCurrencyIfExpired(userWalletId, address, isRefresh)
|
||||
|
||||
return requireNotNull(fetchedCurrencies.value[address]) {
|
||||
"Unable to find VISA currency for $address"
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchVisaCurrencyIfExpired(address: String, isRefresh: Boolean) {
|
||||
private suspend fun fetchVisaCurrencyIfExpired(userWalletId: UserWalletId, address: String, isRefresh: Boolean) {
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getVisaCurrencyKey(address),
|
||||
skipCache = isRefresh,
|
||||
block = { fetchVisaCurrency(address) },
|
||||
block = { fetchVisaCurrency(userWalletId, address) },
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun fetchVisaCurrency(address: String) {
|
||||
private suspend fun fetchVisaCurrency(userWalletId: UserWalletId, address: String) {
|
||||
val contractInfoProvider = visaLibLoader.getOrCreateProvider()
|
||||
|
||||
parZip(
|
||||
dispatchers.io,
|
||||
{ contractInfoProvider.getContractInfo(address) },
|
||||
{
|
||||
contractInfoProvider.getContractInfo(
|
||||
walletAddress = address,
|
||||
paymentAccountAddress = getPaymentAccountAddress(userWalletId),
|
||||
)
|
||||
},
|
||||
{ getFiatRate() },
|
||||
{ contractInfo, fiatRate ->
|
||||
fetchedCurrencies.update { value ->
|
||||
|
|
@ -104,7 +106,7 @@ internal class DefaultVisaRepository @Inject constructor(
|
|||
): Flow<PagingData<VisaTxHistoryItem>> {
|
||||
val userWallet = findVisaUserWallet(userWalletId)
|
||||
val cardPubKey = getCardPubKey(userWallet)
|
||||
val api = visaLibLoader.getOrCreateApi()
|
||||
|
||||
val pager = Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = pageSize,
|
||||
|
|
@ -121,7 +123,7 @@ internal class DefaultVisaRepository @Inject constructor(
|
|||
cacheRegistry = cacheRegistry,
|
||||
fetchedItems = fetchedHistoryItems,
|
||||
dispatchers = dispatchers,
|
||||
requestTxHistory = { offset, pageSize -> getTxHistory(api, userWalletId, offset, pageSize) },
|
||||
requestTxHistory = { offset, pageSize -> getTxHistory(userWalletId, offset, pageSize) },
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
@ -145,22 +147,31 @@ internal class DefaultVisaRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getTxHistory(
|
||||
api: VisaApi,
|
||||
userWalletId: UserWalletId,
|
||||
offset: Int,
|
||||
pageSize: Int,
|
||||
): VisaTxHistoryResponse = withContext(dispatchers.io) {
|
||||
private suspend fun getPaymentAccountAddress(userWalletId: UserWalletId): String? = runCatching {
|
||||
val userWallet = findVisaUserWallet(userWalletId)
|
||||
val cardPubKey = getCardPubKey(userWallet)
|
||||
|
||||
request(userWalletId = userWalletId) {
|
||||
api.getTxHistory(
|
||||
authorizationHeader = visaAuthProvider.getAuthHeader(userWallet.cardId),
|
||||
cardPublicKey = cardPubKey,
|
||||
val customerInfo = visaApiRequestMaker.request(userWalletId) { authHeader, _ ->
|
||||
visaApi.getCustomerInfo(
|
||||
authHeader = authHeader,
|
||||
cardId = userWallet.scanResponse.card.cardId,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO select correct account when multiple accounts are available (will be implemented when backend is ready)
|
||||
customerInfo.paymentAccounts.firstOrNull()?.paymentAccountAddress
|
||||
}.getOrNull()
|
||||
|
||||
private suspend fun getTxHistory(userWalletId: UserWalletId, offset: Int, pageSize: Int): VisaTxHistoryResponse {
|
||||
return visaApiRequestMaker.request(
|
||||
userWalletId = userWalletId,
|
||||
) { authHeader, accessCodeData ->
|
||||
visaApi.getTxHistory(
|
||||
authHeader = authHeader,
|
||||
customerId = accessCodeData.customerId,
|
||||
productInstanceId = accessCodeData.productInstanceId,
|
||||
limit = pageSize,
|
||||
offset = offset,
|
||||
).getOrThrow()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,56 +228,4 @@ internal class DefaultVisaRepository @Inject constructor(
|
|||
private fun getVisaCurrencyKey(address: String): String {
|
||||
return "visa_currency_$address"
|
||||
}
|
||||
|
||||
private suspend fun <T : Any> request(
|
||||
userWalletId: UserWalletId,
|
||||
requestBlock: suspend () -> T,
|
||||
): T {
|
||||
return runCatching {
|
||||
requestBlock()
|
||||
}.getOrElse { responseError ->
|
||||
if (responseError !is ApiResponseError.HttpException ||
|
||||
responseError.code != ApiResponseError.HttpException.Code.UNAUTHORIZED
|
||||
) {
|
||||
throw responseError
|
||||
}
|
||||
|
||||
val authTokens = getAuthTokens(userWalletId)
|
||||
val newTokens = runCatching {
|
||||
visaAuthRepository.refreshAccessTokens(authTokens.refreshToken)
|
||||
}.getOrElse {
|
||||
if (it is ApiResponseError.HttpException &&
|
||||
it.code == ApiResponseError.HttpException.Code.UNAUTHORIZED
|
||||
) {
|
||||
userWalletsStore.update(userWalletId) { userWallet ->
|
||||
userWallet.copy(
|
||||
scanResponse = userWallet.scanResponse.copy(
|
||||
visaCardActivationStatus = VisaCardActivationStatus.RefreshTokenExpired
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
throw RefreshTokenExpiredException()
|
||||
}
|
||||
|
||||
userWalletsStore.update(userWalletId) { userWallet ->
|
||||
userWallet.copy(
|
||||
scanResponse = userWallet.scanResponse.copy(
|
||||
visaCardActivationStatus = VisaCardActivationStatus.Activated(
|
||||
visaAuthTokens = newTokens
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
requestBlock()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws
|
||||
private suspend fun getAuthTokens(userWalletId: UserWalletId): VisaAuthTokens {
|
||||
val userWallet = findVisaUserWallet(userWalletId)
|
||||
val status = userWallet.scanResponse.visaCardActivationStatus ?: error("Visa card activation status not found")
|
||||
return (status as? VisaCardActivationStatus.Activated)?.visaAuthTokens ?: error("Visa card is not activated")
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,10 @@ class MockVisaActivationRepository @AssistedInject constructor(
|
|||
|
||||
override suspend fun sendPinCode(pinCode: VisaEncryptedPinCode) {}
|
||||
|
||||
override suspend fun getPinCodeRsaEncryptionPublicKey(): String {
|
||||
return CryptoUtils.generateRandomBytes(length = 32).toHexString()
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : VisaActivationRepository.Factory {
|
||||
override fun create(cardId: VisaCardId): MockVisaActivationRepository
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ import com.tangem.domain.visa.model.VisaTxHistoryItem
|
|||
import com.tangem.domain.visa.repository.VisaRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DummyVisaRepository : VisaRepository {
|
||||
internal class MockVisaRepository @Inject constructor() : VisaRepository {
|
||||
|
||||
override suspend fun getVisaCurrency(userWalletId: UserWalletId, isRefresh: Boolean): VisaCurrency {
|
||||
TODO("Not implemented for this build type")
|
||||
|
|
@ -11,6 +11,8 @@ internal data class VisaConfig(
|
|||
val mainnet: Addresses,
|
||||
@Json(name = "txHistoryAPIAdditionalHeaders")
|
||||
val header: Header,
|
||||
@Json(name = "rsaPublicKey")
|
||||
val rsaPublicKey: String,
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
@ -1,13 +1,9 @@
|
|||
package com.tangem.data.visa.config
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.data.visa.BuildConfig
|
||||
import com.tangem.data.visa.utils.VisaConstants
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.lib.visa.VisaContractInfoProvider
|
||||
import com.tangem.lib.visa.api.VisaApi
|
||||
import com.tangem.lib.visa.api.VisaApiBuilder
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
|
@ -15,21 +11,19 @@ import javax.inject.Inject
|
|||
|
||||
internal class VisaLibLoader @Inject constructor(
|
||||
private val assetLoader: AssetLoader,
|
||||
@NetworkMoshi private val moshi: Moshi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
private val createMutex = Mutex()
|
||||
private val createMutex2 = Mutex()
|
||||
|
||||
private var config: VisaConfig? = null
|
||||
|
||||
private var provider: VisaContractInfoProvider? = null
|
||||
private var api: VisaApi? = null
|
||||
|
||||
suspend fun getOrCreateConfig(): VisaConfig = config ?: getOrLoadConfig()
|
||||
|
||||
suspend fun getOrCreateProvider(): VisaContractInfoProvider = provider ?: createProvider()
|
||||
|
||||
suspend fun getOrCreateApi(): VisaApi = api ?: createApi()
|
||||
|
||||
private suspend fun createProvider(): VisaContractInfoProvider = createMutex.withLock {
|
||||
val config = getOrLoadConfig()
|
||||
|
||||
|
|
@ -54,24 +48,7 @@ internal class VisaLibLoader @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun createApi(): VisaApi = createMutex.withLock {
|
||||
val config = getOrLoadConfig()
|
||||
|
||||
api = VisaApiBuilder(
|
||||
useDevApi = VisaConstants.USE_TEST_ENV,
|
||||
isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED,
|
||||
moshi = moshi,
|
||||
headers = mapOf(
|
||||
X_ASN_HEADER_NAME to config.header.xAsn,
|
||||
),
|
||||
).build()
|
||||
|
||||
return requireNotNull(api) {
|
||||
"Visa API is not created"
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getOrLoadConfig(): VisaConfig {
|
||||
private suspend fun getOrLoadConfig(): VisaConfig = createMutex2.withLock {
|
||||
config = assetLoader.load<VisaConfig>(VISA_CONFIG_FILE_NAME)
|
||||
|
||||
return requireNotNull(config) {
|
||||
|
|
@ -81,6 +58,5 @@ internal class VisaLibLoader @Inject constructor(
|
|||
|
||||
companion object {
|
||||
private const val VISA_CONFIG_FILE_NAME = "tangem-app-config/visa_config"
|
||||
private const val X_ASN_HEADER_NAME = "x-asn"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package com.tangem.data.visa.di
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
internal annotation class ImplementedVisaRepository
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package com.tangem.data.visa.di
|
||||
|
||||
import com.tangem.domain.visa.repository.VisaRepository
|
||||
import dagger.BindsOptionalOf
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ImplementedVisaRepositoryModule {
|
||||
|
||||
@BindsOptionalOf
|
||||
@ImplementedVisaRepository
|
||||
fun bindImplementedVisaRepository(): VisaRepository
|
||||
}
|
||||
|
|
@ -1,36 +1,20 @@
|
|||
package com.tangem.data.visa.di
|
||||
|
||||
import com.tangem.data.visa.DefaultVisaAuthRepository
|
||||
import com.tangem.data.visa.DummyVisaRepository
|
||||
import com.tangem.data.visa.MockVisaRepository
|
||||
import com.tangem.data.visa.MockVisaActivationRepository
|
||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
||||
import com.tangem.domain.visa.repository.VisaRepository
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import java.util.Optional
|
||||
import javax.inject.Singleton
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object VisaDataModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideVisaRepository(
|
||||
@ImplementedVisaRepository implementedVisaRepository: Optional<VisaRepository>,
|
||||
): VisaRepository {
|
||||
return implementedVisaRepository.getOrNull() ?: DummyVisaRepository()
|
||||
}
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface VisaDataBindsModule {
|
||||
internal interface VisaDataModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
|
|
@ -48,4 +32,11 @@ internal interface VisaDataBindsModule {
|
|||
fun bindVisaActivationRepositoryFactory(
|
||||
repository: MockVisaActivationRepository.Factory,
|
||||
): VisaActivationRepository.Factory
|
||||
|
||||
// @Binds
|
||||
// fun bindVisaRepository(repository: DefaultVisaRepository): VisaRepository
|
||||
|
||||
// Mocked
|
||||
@Binds
|
||||
fun bindVisaRepository(repository: MockVisaRepository): VisaRepository
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package com.tangem.data.visa.utils
|
||||
|
||||
import com.tangem.data.visa.converter.AccessCodeDataConverter
|
||||
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.visa.TangemVisaAuthApi
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.visa.exception.RefreshTokenExpiredException
|
||||
import com.tangem.domain.visa.model.VisaAuthTokens
|
||||
import com.tangem.domain.visa.model.VisaCardActivationStatus
|
||||
import com.tangem.domain.visa.model.getAuthHeader
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
typealias VisaAuthorizationHeader = String
|
||||
|
||||
internal class VisaApiRequestMaker @Inject constructor(
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val visaAuthApi: TangemVisaAuthApi,
|
||||
private val accessCodeDataConverter: AccessCodeDataConverter,
|
||||
private val dispatcherProvider: CoroutineDispatcherProvider,
|
||||
) {
|
||||
suspend fun <T : Any> request(
|
||||
userWalletId: UserWalletId,
|
||||
requestBlock: suspend (header: VisaAuthorizationHeader, accessCodeData: AccessCodeData) -> ApiResponse<T>,
|
||||
): T = withContext(dispatcherProvider.io) {
|
||||
val authTokens = getAuthTokens(userWalletId)
|
||||
val authHeader = authTokens.getAuthHeader()
|
||||
val accessCodeData = accessCodeDataConverter.convert(authTokens)
|
||||
|
||||
runCatching {
|
||||
requestBlock(authHeader, accessCodeData).getOrThrow()
|
||||
}.getOrElse { responseError ->
|
||||
if (responseError !is ApiResponseError.HttpException ||
|
||||
responseError.code != ApiResponseError.HttpException.Code.UNAUTHORIZED
|
||||
) {
|
||||
throw responseError
|
||||
}
|
||||
|
||||
val newTokens = runCatching {
|
||||
refreshAccessTokens(authTokens.refreshToken)
|
||||
}.getOrElse {
|
||||
if (it is ApiResponseError.HttpException &&
|
||||
it.code == ApiResponseError.HttpException.Code.UNAUTHORIZED
|
||||
) {
|
||||
userWalletsStore.update(userWalletId) { userWallet ->
|
||||
userWallet.copy(
|
||||
scanResponse = userWallet.scanResponse.copy(
|
||||
visaCardActivationStatus = VisaCardActivationStatus.RefreshTokenExpired,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
throw RefreshTokenExpiredException()
|
||||
}
|
||||
|
||||
userWalletsStore.update(userWalletId) { userWallet ->
|
||||
userWallet.copy(
|
||||
scanResponse = userWallet.scanResponse.copy(
|
||||
visaCardActivationStatus = VisaCardActivationStatus.Activated(
|
||||
visaAuthTokens = newTokens,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val newAuthHeader = newTokens.getAuthHeader()
|
||||
val newAccessCodeData = accessCodeDataConverter.convert(newTokens)
|
||||
|
||||
requestBlock(newAuthHeader, newAccessCodeData).getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshAccessTokens(refreshToken: VisaAuthTokens.RefreshToken): VisaAuthTokens {
|
||||
val result = visaAuthApi.refreshAccessToken(refreshToken.value).getOrThrow()
|
||||
|
||||
return VisaAuthTokens(
|
||||
accessToken = result.accessToken,
|
||||
refreshToken = VisaAuthTokens.RefreshToken(result.refreshToken),
|
||||
)
|
||||
}
|
||||
|
||||
@Throws
|
||||
private suspend fun getAuthTokens(userWalletId: UserWalletId): VisaAuthTokens {
|
||||
val userWallet = findVisaUserWallet(userWalletId)
|
||||
val status = userWallet.scanResponse.visaCardActivationStatus ?: error("Visa card activation status not found")
|
||||
return (status as? VisaCardActivationStatus.Activated)?.visaAuthTokens ?: error("Visa card is not activated")
|
||||
}
|
||||
|
||||
private suspend fun findVisaUserWallet(userWalletId: UserWalletId): UserWallet {
|
||||
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"No user wallet found: $userWalletId"
|
||||
}
|
||||
if (!userWallet.scanResponse.cardTypesResolver.isVisaWallet()) {
|
||||
error("VISA wallet required: $userWalletId")
|
||||
}
|
||||
|
||||
return userWallet
|
||||
}
|
||||
}
|
||||
|
|
@ -15,9 +15,9 @@ internal object VisaConstants {
|
|||
)
|
||||
|
||||
/*
|
||||
* Must be `false` in production
|
||||
* Don't forget to change CardTypesResolver.isVisaWallet
|
||||
* */
|
||||
* Must be `false` in production
|
||||
* Don't forget to change CardTypesResolver.isVisaWallet
|
||||
* */
|
||||
const val IS_DEMO_MODE_ENABLED = false
|
||||
|
||||
const val USE_TEST_ENV = true
|
||||
|
|
@ -40,5 +40,7 @@ internal fun getDemoAddress(): String {
|
|||
internal fun getDemoPublicKey(): String {
|
||||
return if (VisaConstants.USE_TEST_ENV) {
|
||||
VisaConstants.DEMO_TESTNET_PUBLIC_KEY
|
||||
} else VisaConstants.DEMO_MAINNET_PUBLIC_KEY
|
||||
} else {
|
||||
VisaConstants.DEMO_MAINNET_PUBLIC_KEY
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@ package com.tangem.data.visa.utils
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.externallinkprovider.TxExploreState
|
||||
import com.tangem.datasource.api.visa.models.response.VisaTxHistoryResponse
|
||||
import com.tangem.domain.visa.model.VisaTxDetails
|
||||
import com.tangem.lib.visa.model.VisaTxHistoryResponse
|
||||
|
||||
internal class VisaTxDetailsFactory {
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.data.visa.utils
|
||||
|
||||
import com.tangem.datasource.api.visa.models.response.VisaTxHistoryResponse
|
||||
import com.tangem.domain.visa.model.VisaTxHistoryItem
|
||||
import com.tangem.lib.visa.model.VisaTxHistoryResponse
|
||||
|
||||
internal class VisaTxHistoryItemFactory {
|
||||
|
||||
|
|
@ -3,13 +3,9 @@ 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.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.common.visa.TangemVisaAuthProvider
|
||||
import com.tangem.datasource.api.visa.models.response.VisaTxHistoryResponse
|
||||
import com.tangem.domain.visa.model.VisaTxHistoryItem
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.lib.visa.api.VisaApi
|
||||
import com.tangem.lib.visa.model.VisaTxHistoryResponse
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
Loading…
Add table
Add a link
Reference in a new issue