Updated on 2026-08-14
This commit is contained in:
commit
0470e1f010
1144 changed files with 48463 additions and 11166 deletions
|
|
@ -7,8 +7,8 @@ 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.entity.TangemPayCurrencyFactory
|
||||
import com.tangem.data.pay.util.TangemPayErrorConverter
|
||||
import com.tangem.domain.card.common.visa.VisaUtilities
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
|
||||
|
|
@ -16,14 +16,8 @@ import com.tangem.utils.logging.TangemLogger
|
|||
import javax.inject.Inject
|
||||
|
||||
private const val TAG = "TangemPay: DefaultTangemPayCryptoCurrencyFactory"
|
||||
/**
|
||||
* Custom token parameters. Will be used only for F&F.
|
||||
*/
|
||||
private const val TOKEN_ID = "usd-coin"
|
||||
private const val TOKEN_NAME = "USDC"
|
||||
private const val TOKEN_CONTRACT_ADDRESS = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
|
||||
private const val TOKEN_DECIMALS = 6
|
||||
|
||||
@Deprecated("Use TangemPayCurrencyFactory instead")
|
||||
internal class DefaultTangemPayCryptoCurrencyFactory @Inject constructor(
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
private val errorConverter: TangemPayErrorConverter,
|
||||
|
|
@ -47,32 +41,11 @@ internal class DefaultTangemPayCryptoCurrencyFactory @Inject constructor(
|
|||
)
|
||||
cryptoCurrencyFactory.createToken(
|
||||
network = requireNotNull(network),
|
||||
rawId = CryptoCurrency.RawID(TOKEN_ID),
|
||||
name = TOKEN_NAME,
|
||||
symbol = TOKEN_NAME,
|
||||
contractAddress = TOKEN_CONTRACT_ADDRESS,
|
||||
decimals = TOKEN_DECIMALS,
|
||||
)
|
||||
}.mapLeft { exception ->
|
||||
TangemLogger.withTag(TAG).e("Error", exception)
|
||||
errorConverter.convert(exception)
|
||||
}
|
||||
}
|
||||
|
||||
override fun create(userWallet: UserWallet): Either<UniversalError, CryptoCurrency.Token> {
|
||||
return catch {
|
||||
val network = networkFactory.create(
|
||||
blockchain = VisaUtilities.visaBlockchain,
|
||||
extraDerivationPath = null,
|
||||
userWallet = userWallet,
|
||||
)
|
||||
cryptoCurrencyFactory.createToken(
|
||||
network = requireNotNull(network),
|
||||
rawId = CryptoCurrency.RawID(TOKEN_ID),
|
||||
name = TOKEN_NAME,
|
||||
symbol = TOKEN_NAME,
|
||||
contractAddress = TOKEN_CONTRACT_ADDRESS,
|
||||
decimals = TOKEN_DECIMALS,
|
||||
rawId = CryptoCurrency.RawID(TangemPayCurrencyFactory.TOKEN_ID),
|
||||
name = TangemPayCurrencyFactory.TOKEN_NAME,
|
||||
symbol = TangemPayCurrencyFactory.TOKEN_NAME,
|
||||
contractAddress = TangemPayCurrencyFactory.TOKEN_CONTRACT_ADDRESS,
|
||||
decimals = TangemPayCurrencyFactory.TOKEN_DECIMALS,
|
||||
)
|
||||
}.mapLeft { exception ->
|
||||
TangemLogger.withTag(TAG).e("Error", exception)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.data.pay
|
|||
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.TangemPayEligibilityType
|
||||
import com.tangem.domain.models.pay.TangemPayEligibilityType
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
package com.tangem.data.pay.converter
|
||||
|
||||
import com.tangem.data.pay.converter.PaymentAccountStatusValueDMConverter.convert
|
||||
import com.tangem.data.pay.converter.PaymentAccountStatusValueDMConverter.convertBack
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.data.pay.entity.TangemPayCurrencyFactory
|
||||
import com.tangem.datasource.local.visa.entity.PaymentAccountStatusValueDM
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.account.CardDisplayName
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
import com.tangem.domain.models.pay.TangemPayCard
|
||||
import com.tangem.domain.models.pay.TangemPayCardLimit
|
||||
import com.tangem.domain.models.pay.TangemPayCardLimitData
|
||||
import com.tangem.domain.models.pay.TangemPayCardLimitPeriod
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Two-way converter between [PaymentAccountStatusValue] and [PaymentAccountStatusValueDM].
|
||||
|
|
@ -15,10 +22,12 @@ import com.tangem.utils.converter.TwoWayConverter
|
|||
*
|
||||
* [convertBack] maps data model → domain. All restored statuses have [StatusSource.CACHE] as source.
|
||||
*/
|
||||
internal object PaymentAccountStatusValueDMConverter :
|
||||
TwoWayConverter<PaymentAccountStatusValue, PaymentAccountStatusValueDM?> {
|
||||
@Singleton
|
||||
internal class PaymentAccountStatusValueDMConverter @Inject constructor(
|
||||
private val tangemPayCurrencyFactory: TangemPayCurrencyFactory,
|
||||
) {
|
||||
|
||||
override fun convert(value: PaymentAccountStatusValue): PaymentAccountStatusValueDM? {
|
||||
fun convert(value: PaymentAccountStatusValue): PaymentAccountStatusValueDM? {
|
||||
return when (value) {
|
||||
is PaymentAccountStatusValue.NotCreated -> PaymentAccountStatusValueDM.NotCreated()
|
||||
is PaymentAccountStatusValue.UnderReview -> PaymentAccountStatusValueDM.UnderReview(
|
||||
|
|
@ -26,27 +35,23 @@ internal object PaymentAccountStatusValueDMConverter :
|
|||
customerId = value.customerId,
|
||||
)
|
||||
is PaymentAccountStatusValue.IssuingCard -> PaymentAccountStatusValueDM.IssuingCard()
|
||||
is PaymentAccountStatusValue.Locked -> PaymentAccountStatusValueDM.ActiveCard(
|
||||
isLocked = true,
|
||||
is PaymentAccountStatusValue.Loaded -> PaymentAccountStatusValueDM.ActiveAccount(
|
||||
customerId = value.customerId,
|
||||
cardId = value.cardId,
|
||||
lastFourDigits = value.lastFourDigits,
|
||||
currencyCode = value.currencyCode,
|
||||
depositAddress = value.depositAddress,
|
||||
isPinSet = value.isPinSet,
|
||||
fiatBalance = value.fiatBalance.toDM(),
|
||||
cryptoBalance = value.cryptoBalance.toDM(),
|
||||
)
|
||||
is PaymentAccountStatusValue.Loaded -> PaymentAccountStatusValueDM.ActiveCard(
|
||||
isLocked = false,
|
||||
customerId = value.customerId,
|
||||
cardId = value.cardId,
|
||||
lastFourDigits = value.lastFourDigits,
|
||||
currencyCode = value.currencyCode,
|
||||
depositAddress = value.depositAddress,
|
||||
isPinSet = value.isPinSet,
|
||||
fiatBalance = value.fiatBalance.toDM(),
|
||||
cryptoBalance = value.cryptoBalance.toDM(),
|
||||
cards = value.cards.map { card ->
|
||||
PaymentAccountStatusValueDM.TangemPayCard(
|
||||
id = card.id,
|
||||
hasPinCode = card.hasPinCode,
|
||||
displayName = card.displayName?.value,
|
||||
actualDailyLimit = card.limit?.actualCardLimit?.amount,
|
||||
adminDailyLimit = card.limit?.adminCardLimit?.amount,
|
||||
isFrozen = card.isFrozen,
|
||||
lastDigits = card.lastDigits,
|
||||
)
|
||||
},
|
||||
)
|
||||
is PaymentAccountStatusValue.Error.CardIssueFailed -> PaymentAccountStatusValueDM.CardIssueFailed(
|
||||
customerId = value.customerId,
|
||||
|
|
@ -61,7 +66,7 @@ internal object PaymentAccountStatusValueDMConverter :
|
|||
}
|
||||
}
|
||||
|
||||
override fun convertBack(value: PaymentAccountStatusValueDM?): PaymentAccountStatusValue {
|
||||
fun convertBack(userWalletId: UserWalletId, value: PaymentAccountStatusValueDM?): PaymentAccountStatusValue {
|
||||
return when (value) {
|
||||
is PaymentAccountStatusValueDM.Empty -> PaymentAccountStatusValue.Empty
|
||||
is PaymentAccountStatusValueDM.NotCreated -> PaymentAccountStatusValue.NotCreated
|
||||
|
|
@ -71,31 +76,32 @@ internal object PaymentAccountStatusValueDMConverter :
|
|||
is PaymentAccountStatusValueDM.IssuingCard -> PaymentAccountStatusValue.IssuingCard(
|
||||
source = StatusSource.CACHE,
|
||||
)
|
||||
is PaymentAccountStatusValueDM.ActiveCard -> if (value.isLocked) {
|
||||
PaymentAccountStatusValue.Locked(
|
||||
source = StatusSource.CACHE,
|
||||
customerId = value.customerId,
|
||||
cardId = value.cardId,
|
||||
lastFourDigits = value.lastFourDigits,
|
||||
currencyCode = value.currencyCode,
|
||||
depositAddress = value.depositAddress,
|
||||
isPinSet = value.isPinSet,
|
||||
fiatBalance = value.fiatBalance.toDomain(),
|
||||
cryptoBalance = value.cryptoBalance.toDomain(),
|
||||
)
|
||||
} else {
|
||||
PaymentAccountStatusValue.Loaded(
|
||||
source = StatusSource.CACHE,
|
||||
customerId = value.customerId,
|
||||
cardId = value.cardId,
|
||||
lastFourDigits = value.lastFourDigits,
|
||||
currencyCode = value.currencyCode,
|
||||
depositAddress = value.depositAddress,
|
||||
isPinSet = value.isPinSet,
|
||||
fiatBalance = value.fiatBalance.toDomain(),
|
||||
cryptoBalance = value.cryptoBalance.toDomain(),
|
||||
)
|
||||
}
|
||||
is PaymentAccountStatusValueDM.ActiveAccount -> PaymentAccountStatusValue.Loaded(
|
||||
source = StatusSource.CACHE,
|
||||
customerId = value.customerId,
|
||||
currencyCode = value.currencyCode,
|
||||
depositAddress = value.depositAddress,
|
||||
fiatBalance = value.fiatBalance.toDomain(),
|
||||
cryptoBalance = value.cryptoBalance.toDomain(),
|
||||
cryptoCurrency = tangemPayCurrencyFactory.create(userWalletId),
|
||||
cards = value.cards.map { card ->
|
||||
TangemPayCard(
|
||||
id = card.id,
|
||||
hasPinCode = card.hasPinCode,
|
||||
displayName = card.displayName?.let { CardDisplayName(it).getOrElse { null } },
|
||||
limit = TangemPayCardLimitData(
|
||||
actualCardLimit = card.actualDailyLimit?.let { limit ->
|
||||
TangemPayCardLimit(limit, TangemPayCardLimitPeriod.DAY)
|
||||
},
|
||||
adminCardLimit = card.adminDailyLimit?.let { limit ->
|
||||
TangemPayCardLimit(limit, TangemPayCardLimitPeriod.DAY)
|
||||
},
|
||||
),
|
||||
isFrozen = card.isFrozen,
|
||||
lastDigits = card.lastDigits,
|
||||
)
|
||||
},
|
||||
)
|
||||
is PaymentAccountStatusValueDM.UnderReview -> PaymentAccountStatusValue.UnderReview(
|
||||
source = StatusSource.CACHE,
|
||||
kycStatus = value.kycStatus,
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@ package com.tangem.data.pay.di
|
|||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStoreFactory
|
||||
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
|
||||
import androidx.datastore.dataStoreFile
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.data.pay.DefaultTangemPayCryptoCurrencyFactory
|
||||
import com.tangem.data.pay.DefaultTangemPayEligibilityManager
|
||||
import com.tangem.data.pay.converter.PaymentAccountStatusValueDMConverter
|
||||
import com.tangem.data.pay.flow.DefaultPaymentAccountStatusFetcher
|
||||
import com.tangem.data.pay.flow.DefaultPaymentAccountStatusProducer
|
||||
import com.tangem.data.pay.repository.*
|
||||
import com.tangem.domain.pay.repository.TangemPayReissueCardRepository
|
||||
import com.tangem.data.pay.store.PaymentAccountStatusesStore
|
||||
import com.tangem.data.pay.usecase.DefaultGetTangemPayCurrencyStatusUseCase
|
||||
import com.tangem.data.pay.usecase.DefaultGetTangemPayCustomerIdUseCase
|
||||
|
|
@ -24,7 +27,9 @@ import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
|||
import com.tangem.domain.pay.flow.PaymentAccountStatusProducer
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
|
||||
import com.tangem.domain.pay.repository.*
|
||||
import com.tangem.domain.pay.usecase.GetPaymentAccountCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
|
||||
import com.tangem.domain.pay.usecase.SetTangemPayCardLimitUseCase
|
||||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
import com.tangem.domain.tangempay.GetTangemPayCurrencyStatusUseCase
|
||||
import com.tangem.domain.tangempay.GetTangemPayCustomerIdUseCase
|
||||
|
|
@ -69,6 +74,10 @@ internal interface TangemPayDataModule {
|
|||
@Singleton
|
||||
fun bindCustomerOrderRepository(repository: DefaultCustomerOrderRepository): CustomerOrderRepository
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindReissueCardRepository(repository: DefaultReissueCardRepository): TangemPayReissueCardRepository
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindTangemPayCryptoCurrencyFactory(
|
||||
|
|
@ -112,6 +121,7 @@ internal interface TangemPayDataModule {
|
|||
@ApplicationContext context: Context,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
scope: AppCoroutineScope,
|
||||
converter: PaymentAccountStatusValueDMConverter,
|
||||
): PaymentAccountStatusesStore {
|
||||
return PaymentAccountStatusesStore(
|
||||
runtimeStore = RuntimeSharedStore(),
|
||||
|
|
@ -121,9 +131,11 @@ internal interface TangemPayDataModule {
|
|||
types = mapWithStringKeyTypes<PaymentAccountStatusValueDM>(),
|
||||
defaultValue = emptyMap(),
|
||||
),
|
||||
corruptionHandler = ReplaceFileCorruptionHandler { emptyMap() },
|
||||
produceFile = { context.dataStoreFile(fileName = "payment_account_statuses") },
|
||||
scope = scope,
|
||||
),
|
||||
converter = converter,
|
||||
scope = scope,
|
||||
)
|
||||
}
|
||||
|
|
@ -139,6 +151,22 @@ internal interface TangemPayDataModule {
|
|||
) {}
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideSetTangemPayCardLimitUseCase(
|
||||
cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||
paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
|
||||
): SetTangemPayCardLimitUseCase {
|
||||
return SetTangemPayCardLimitUseCase(cardDetailsRepository, paymentAccountStatusFetcher)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetTangemPayCryptoCurrencyStatusUseCase(
|
||||
paymentAccountStatusSupplier: PaymentAccountStatusSupplier,
|
||||
): GetPaymentAccountCryptoCurrencyStatusUseCase {
|
||||
return GetPaymentAccountCryptoCurrencyStatusUseCase(paymentAccountStatusSupplier)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTangemPayMainScreenCustomerInfoUseCase(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package com.tangem.data.pay.entity
|
||||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.domain.card.common.visa.VisaUtilities
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.requireUserWalletsSync
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
internal class TangemPayCurrencyFactory @Inject constructor(
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val networkFactory: NetworkFactory,
|
||||
) {
|
||||
private val cryptoCurrencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
CryptoCurrencyFactory(excludedBlockchains)
|
||||
}
|
||||
|
||||
fun create(userWalletId: UserWalletId): CryptoCurrency.Token {
|
||||
val userWallet = userWalletsListRepository.requireUserWalletsSync()
|
||||
.firstOrNull { it.walletId == userWalletId }
|
||||
?: error("User wallet with id $userWalletId not found")
|
||||
val network = networkFactory.create(
|
||||
blockchain = VisaUtilities.visaBlockchain,
|
||||
userWallet = userWallet,
|
||||
extraDerivationPath = null,
|
||||
)
|
||||
return cryptoCurrencyFactory.createToken(
|
||||
network = requireNotNull(network),
|
||||
rawId = CryptoCurrency.RawID(TOKEN_ID),
|
||||
name = TOKEN_NAME,
|
||||
symbol = TOKEN_NAME,
|
||||
contractAddress = TOKEN_CONTRACT_ADDRESS,
|
||||
decimals = TOKEN_DECIMALS,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal const val TOKEN_ID = "usd-coin"
|
||||
internal const val TOKEN_NAME = "USDC"
|
||||
internal const val TOKEN_CONTRACT_ADDRESS = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
|
||||
internal const val TOKEN_DECIMALS = 6
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.data.pay.flow
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.data.pay.entity.TangemPayCurrencyFactory
|
||||
import com.tangem.data.pay.store.PaymentAccountStatusesStore
|
||||
import com.tangem.domain.core.utils.catchOn
|
||||
import com.tangem.domain.models.StatusSource
|
||||
|
|
@ -8,8 +9,10 @@ import com.tangem.domain.models.account.Account
|
|||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
import com.tangem.domain.models.kyc.KycStatus
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.pay.TangemPayCard
|
||||
import com.tangem.domain.models.pay.TangemPayCardLimitData
|
||||
import com.tangem.domain.pay.TangemPayEligibilityManager
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||
import com.tangem.domain.pay.model.CustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderData
|
||||
|
|
@ -38,6 +41,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
private val customerOrderRepository: CustomerOrderRepository,
|
||||
private val deviceSecurity: DeviceSecurityInfoProvider,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val tangemPayCurrencyFactory: TangemPayCurrencyFactory,
|
||||
private val eligibilityManager: TangemPayEligibilityManager,
|
||||
) : PaymentAccountStatusFetcher {
|
||||
|
||||
|
|
@ -142,7 +146,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
},
|
||||
ifRight = { customerInfo ->
|
||||
logger.i("proceedWithoutOrder data customerInfo ${account.userWalletId}")
|
||||
val status = customerInfo.mapToPaymentAccountStatus()
|
||||
val status = customerInfo.mapToPaymentAccountStatus(account.userWalletId)
|
||||
if (status is PaymentAccountStatusValue.IssuingCard && customerInfo.kycStatus == KycStatus.APPROVED) {
|
||||
// If order id wasn't saved -> start order creation and get customer info
|
||||
onboardingRepository.createOrder(account.userWalletId)
|
||||
|
|
@ -169,7 +173,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
KycStatus.PENDING,
|
||||
KycStatus.INIT,
|
||||
KycStatus.REJECTED,
|
||||
-> return customerInfo.mapToPaymentAccountStatus()
|
||||
-> return customerInfo.mapToPaymentAccountStatus(account.userWalletId)
|
||||
KycStatus.APPROVED -> Unit // proceed to order check
|
||||
}
|
||||
|
||||
|
|
@ -247,11 +251,11 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
return onboardingRepository.getCustomerInfo(userWalletId = account.userWalletId)
|
||||
.fold(
|
||||
ifLeft = { it.mapToPaymentAccountStatus(account.userWalletId) },
|
||||
ifRight = { customerInfo -> customerInfo.mapToPaymentAccountStatus() },
|
||||
ifRight = { customerInfo -> customerInfo.mapToPaymentAccountStatus(account.userWalletId) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun CustomerInfo.mapToPaymentAccountStatus(): PaymentAccountStatusValue {
|
||||
private fun CustomerInfo.mapToPaymentAccountStatus(userWalletId: UserWalletId): PaymentAccountStatusValue {
|
||||
val cardInfo = this.cardInfo
|
||||
val productInstance = this.productInstance
|
||||
return if (kycStatus != KycStatus.APPROVED && !customerId.isNullOrEmpty()) {
|
||||
|
|
@ -262,6 +266,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
)
|
||||
} else if (cardInfo != null && productInstance != null && !customerId.isNullOrEmpty()) {
|
||||
convertToContentState(
|
||||
userWalletId = userWalletId,
|
||||
productInstance = productInstance,
|
||||
cardInfo = cardInfo,
|
||||
customerId = requireNotNull(customerId) { "CustomerId must not be null" },
|
||||
|
|
@ -272,34 +277,34 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
}
|
||||
|
||||
private fun convertToContentState(
|
||||
userWalletId: UserWalletId,
|
||||
productInstance: CustomerInfo.ProductInstance,
|
||||
cardInfo: CustomerInfo.CardInfo,
|
||||
customerId: String,
|
||||
): PaymentAccountStatusValue {
|
||||
return when (productInstance.frozenState) {
|
||||
TangemPayCardFrozenState.Frozen -> PaymentAccountStatusValue.Locked(
|
||||
source = StatusSource.ACTUAL,
|
||||
customerId = customerId,
|
||||
cardId = productInstance.cardId,
|
||||
lastFourDigits = cardInfo.lastFourDigits,
|
||||
currencyCode = cardInfo.currencyCode,
|
||||
depositAddress = cardInfo.depositAddress,
|
||||
isPinSet = cardInfo.isPinSet,
|
||||
fiatBalance = cardInfo.fiatBalance,
|
||||
cryptoBalance = cardInfo.cryptoBalance,
|
||||
)
|
||||
else -> PaymentAccountStatusValue.Loaded(
|
||||
source = StatusSource.ACTUAL,
|
||||
customerId = customerId,
|
||||
cardId = productInstance.cardId,
|
||||
lastFourDigits = cardInfo.lastFourDigits,
|
||||
currencyCode = cardInfo.currencyCode,
|
||||
depositAddress = cardInfo.depositAddress,
|
||||
isPinSet = cardInfo.isPinSet,
|
||||
fiatBalance = cardInfo.fiatBalance,
|
||||
cryptoBalance = cardInfo.cryptoBalance,
|
||||
)
|
||||
}
|
||||
val cryptoCurrency = tangemPayCurrencyFactory.create(userWalletId)
|
||||
return PaymentAccountStatusValue.Loaded(
|
||||
source = StatusSource.ACTUAL,
|
||||
customerId = customerId,
|
||||
currencyCode = cardInfo.currencyCode,
|
||||
depositAddress = cardInfo.depositAddress,
|
||||
fiatBalance = cardInfo.fiatBalance,
|
||||
cryptoBalance = cardInfo.cryptoBalance,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
cards = listOf(
|
||||
TangemPayCard(
|
||||
id = productInstance.cardId,
|
||||
hasPinCode = cardInfo.isPinSet,
|
||||
displayName = productInstance.displayName,
|
||||
limit = TangemPayCardLimitData(
|
||||
actualCardLimit = productInstance.actualCardLimit,
|
||||
adminCardLimit = productInstance.adminCardLimit,
|
||||
),
|
||||
isFrozen = productInstance.frozenState is TangemPayCardFrozenState.Frozen,
|
||||
lastDigits = cardInfo.lastFourDigits,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun VisaApiError.mapToPaymentAccountStatus(userWalletId: UserWalletId): PaymentAccountStatusValue {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.data.pay.repository
|
|||
|
||||
import arrow.core.Either
|
||||
import arrow.core.flatMap
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
|
|
@ -15,7 +16,8 @@ import com.tangem.datasource.api.pay.models.response.OrderResponse
|
|||
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
|
||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.TangemPayEligibilityType
|
||||
import com.tangem.domain.models.pay.TangemPayEligibilityType
|
||||
import com.tangem.domain.models.account.CardDisplayName
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
|
|
@ -26,6 +28,8 @@ import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
|
|||
import com.tangem.domain.pay.model.CustomerInfo
|
||||
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
|
||||
import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
|
||||
import com.tangem.domain.models.pay.TangemPayCardLimit
|
||||
import com.tangem.domain.models.pay.TangemPayCardLimitPeriod
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
|
|
@ -202,7 +206,16 @@ internal class DefaultOnboardingRepository @Inject constructor(
|
|||
}
|
||||
cardFrozenStateStore.store(key = instance.cardId, value = cardFrozenState)
|
||||
|
||||
ProductInstance(id = instance.id, cardId = instance.cardId, frozenState = cardFrozenState)
|
||||
val displayName = instance.displayName?.ifEmpty { null }
|
||||
|
||||
ProductInstance(
|
||||
id = instance.id,
|
||||
cardId = instance.cardId,
|
||||
frozenState = cardFrozenState,
|
||||
displayName = if (displayName != null) CardDisplayName(displayName).getOrElse { null } else null,
|
||||
actualCardLimit = instance.actualCardLimit?.parseCardLimit(),
|
||||
adminCardLimit = instance.adminCardLimit?.parseCardLimit(),
|
||||
)
|
||||
}
|
||||
return CustomerInfo(
|
||||
customerId = response?.id,
|
||||
|
|
@ -214,6 +227,13 @@ internal class DefaultOnboardingRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun CustomerMeResponse.CardLimit.parseCardLimit(): TangemPayCardLimit {
|
||||
return TangemPayCardLimit(
|
||||
amount = amount,
|
||||
period = TangemPayCardLimitPeriod.fromString(periodType),
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendKycAnalytics(kycStatus: KycStatus) {
|
||||
val event = when (kycStatus) {
|
||||
KycStatus.APPROVED -> TangemPayAnalyticsEvents.KycPassedAndOrderCreated()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
package com.tangem.data.pay.repository
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.right
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.datasource.api.pay.TangemPayApi
|
||||
import com.tangem.datasource.api.pay.models.request.ReissueCardRequest
|
||||
import com.tangem.datasource.api.pay.models.response.OrderResponse
|
||||
import com.tangem.datasource.local.visa.TangemPayReissueCardStore
|
||||
import com.tangem.domain.models.pay.TangemPayReissueCardFee
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.model.OrderStatus
|
||||
import com.tangem.domain.pay.model.TangemPayReissueOrderInfo
|
||||
import com.tangem.domain.pay.repository.TangemPayReissueCardRepository
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import com.tangem.utils.coroutines.runSuspendCatching
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultReissueCardRepository @Inject constructor(
|
||||
private val tangemPayApi: TangemPayApi,
|
||||
private val requestHelper: TangemPayRequestPerformer,
|
||||
private val tangemPayReissueCardStore: TangemPayReissueCardStore,
|
||||
) : TangemPayReissueCardRepository {
|
||||
|
||||
override suspend fun getReissueCardFee(userWalletId: UserWalletId): Either<VisaApiError, TangemPayReissueCardFee> =
|
||||
either {
|
||||
runSuspendCatching {
|
||||
tangemPayReissueCardStore.getReissueFee(userWalletId)?.let { return Either.Right(it) }
|
||||
}
|
||||
|
||||
val response = requestHelper.performRequest(userWalletId) { authHeader ->
|
||||
tangemPayApi.getFee(
|
||||
authHeader = authHeader,
|
||||
type = CARD_REPLACEMENT_FEE_TYPE,
|
||||
)
|
||||
}.bind()
|
||||
|
||||
val result = response.result
|
||||
val fee = TangemPayReissueCardFee(
|
||||
amount = result.amount.toBigDecimal(),
|
||||
currencyCode = result.currency,
|
||||
)
|
||||
|
||||
runSuspendCatching {
|
||||
tangemPayReissueCardStore.storeReissueFee(userWalletId, fee)
|
||||
}
|
||||
|
||||
fee
|
||||
}
|
||||
|
||||
override suspend fun reissueCard(
|
||||
userWalletId: UserWalletId,
|
||||
cardId: String,
|
||||
): Either<VisaApiError, TangemPayReissueOrderInfo> = either {
|
||||
val response = requestHelper.performRequest(userWalletId) { authHeader ->
|
||||
tangemPayApi.reissueCard(
|
||||
authHeader = authHeader,
|
||||
body = ReissueCardRequest(cardId = cardId),
|
||||
)
|
||||
}.bind()
|
||||
|
||||
TangemPayReissueOrderInfo(response.result.orderId, OrderStatus.fromString(response.result.status))
|
||||
}
|
||||
|
||||
override suspend fun storeReissueOrderId(cardId: String, orderId: String): Either<UniversalError, Unit> =
|
||||
runSuspendCatching {
|
||||
tangemPayReissueCardStore.storeReissueOrderId(cardId, orderId)
|
||||
}.fold(
|
||||
onSuccess = { Unit.right() },
|
||||
onFailure = { Either.Left(VisaApiError.Unspecified) },
|
||||
)
|
||||
|
||||
override suspend fun getReissueOrderInfo(
|
||||
userWalletId: UserWalletId,
|
||||
cardId: String,
|
||||
): Either<UniversalError, TangemPayReissueOrderInfo?> = either {
|
||||
val orderId = runSuspendCatching { tangemPayReissueCardStore.getOrderId(cardId) }.getOrNull()
|
||||
|
||||
if (orderId == null) {
|
||||
return null.right()
|
||||
}
|
||||
|
||||
val order = requestHelper.performRequest(userWalletId) { authHeader ->
|
||||
tangemPayApi.getOrder(authHeader, orderId)
|
||||
}.bind()
|
||||
|
||||
val result = order.result ?: raise(VisaApiError.Unspecified)
|
||||
|
||||
TangemPayReissueOrderInfo(
|
||||
orderId = result.id,
|
||||
orderStatus = when (result.status) {
|
||||
OrderResponse.Result.Status.NEW -> OrderStatus.NEW
|
||||
OrderResponse.Result.Status.PROCESSING -> OrderStatus.PROCESSING
|
||||
OrderResponse.Result.Status.COMPLETED -> OrderStatus.COMPLETED
|
||||
OrderResponse.Result.Status.CANCELED -> OrderStatus.CANCELED
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val CARD_REPLACEMENT_FEE_TYPE = "CARD_REPLACEMENT"
|
||||
}
|
||||
}
|
||||
|
|
@ -14,11 +14,13 @@ import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
|||
import com.tangem.datasource.api.pay.TangemPayApi
|
||||
import com.tangem.datasource.api.pay.models.request.CardDetailsRequest
|
||||
import com.tangem.datasource.api.pay.models.request.FreezeUnfreezeCardRequest
|
||||
import com.tangem.datasource.api.pay.models.request.UpdateCardRequest
|
||||
import com.tangem.datasource.api.pay.models.request.SetPinRequest
|
||||
import com.tangem.datasource.api.pay.models.response.FreezeUnfreezeCardResponse
|
||||
import com.tangem.datasource.api.pay.models.response.OrderResponse.Result.Status
|
||||
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
|
||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
||||
import com.tangem.domain.models.account.CardDisplayName
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.model.SetPinResult
|
||||
import com.tangem.domain.pay.model.TangemPayCardBalance
|
||||
|
|
@ -39,7 +41,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||
|
||||
private const val TAG = "TangemPay: CardDetailsRepository"
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
|
||||
private val tangemPayApi: TangemPayApi,
|
||||
private val requestHelper: TangemPayRequestPerformer,
|
||||
|
|
@ -318,6 +320,54 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun updateCardDisplayName(
|
||||
cardId: String,
|
||||
userWalletId: UserWalletId,
|
||||
displayName: CardDisplayName,
|
||||
): Either<UniversalError, Unit> {
|
||||
return catch(
|
||||
block = {
|
||||
requestHelper.performRequest(userWalletId) { authHeader ->
|
||||
tangemPayApi.updateCard(
|
||||
authHeader = authHeader,
|
||||
body = UpdateCardRequest(
|
||||
displayName = displayName.value,
|
||||
),
|
||||
cardId = cardId,
|
||||
)
|
||||
}.fold(
|
||||
ifLeft = { error -> error.left() },
|
||||
ifRight = { Unit.right() },
|
||||
)
|
||||
},
|
||||
catch = ::catchException,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun updateCardLimit(
|
||||
cardId: String,
|
||||
userWalletId: UserWalletId,
|
||||
limit: String,
|
||||
): Either<UniversalError, Unit> {
|
||||
return catch(
|
||||
block = {
|
||||
requestHelper.performRequest(userWalletId) { authHeader ->
|
||||
tangemPayApi.updateCard(
|
||||
authHeader = authHeader,
|
||||
body = UpdateCardRequest(
|
||||
cardLimit = UpdateCardRequest.CardLimit(limit),
|
||||
),
|
||||
cardId = cardId,
|
||||
)
|
||||
}.fold(
|
||||
ifLeft = { error -> error.left() },
|
||||
ifRight = { Unit.right() },
|
||||
)
|
||||
},
|
||||
catch = ::catchException,
|
||||
)
|
||||
}
|
||||
|
||||
override fun cardFrozenState(cardId: String): Flow<TangemPayCardFrozenState> {
|
||||
return cardFrozenStateStore.get(cardId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.models.account.AccountStatus
|
|||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.coroutines.runSuspendCatching
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -29,6 +30,7 @@ internal typealias WalletIdWithPaymentStatusDM = Map<String, PaymentAccountStatu
|
|||
internal class PaymentAccountStatusesStore(
|
||||
private val runtimeStore: RuntimeSharedStore<WalletIdWithPaymentStatus>,
|
||||
private val persistenceDataStore: DataStore<WalletIdWithPaymentStatusDM>,
|
||||
private val converter: PaymentAccountStatusValueDMConverter,
|
||||
scope: AppCoroutineScope,
|
||||
) {
|
||||
|
||||
|
|
@ -39,11 +41,12 @@ internal class PaymentAccountStatusesStore(
|
|||
runtimeStore.store(
|
||||
value = cachedStatuses.mapValues { (rawUserWalletId, statusDM) ->
|
||||
val account = Account.Payment(userWalletId = UserWalletId(rawUserWalletId))
|
||||
val statusValue = PaymentAccountStatusValueDMConverter.convertBack(value = statusDM)
|
||||
val statusValue = converter.convertBack(userWalletId = account.userWalletId, value = statusDM)
|
||||
AccountStatus.Payment(account = account, value = statusValue)
|
||||
},
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
runSuspendCatching { persistenceDataStore.updateData { emptyMap() } }
|
||||
TangemLogger.e("Error while loading cached payment account statuses", e)
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +90,7 @@ internal class PaymentAccountStatusesStore(
|
|||
}
|
||||
|
||||
private suspend fun storeInPersistence(userWalletId: UserWalletId, status: PaymentAccountStatusValue) {
|
||||
val statusDM = PaymentAccountStatusValueDMConverter.convert(value = status) ?: return
|
||||
val statusDM = converter.convert(value = status) ?: return
|
||||
persistenceDataStore.updateData { storedStatuses ->
|
||||
storedStatuses.toMutableMap().apply {
|
||||
put(key = userWalletId.stringValue, value = statusDM)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue