Updated on 2026-08-14
This commit is contained in:
parent
f3c2d98214
commit
f192c85914
59 changed files with 1679 additions and 432 deletions
|
|
@ -33,8 +33,11 @@ dependencies {
|
|||
api(projects.domain.models)
|
||||
api(projects.domain.tokens)
|
||||
api(projects.domain.wallets)
|
||||
api(projects.domain.visa)
|
||||
// endregion
|
||||
|
||||
implementation(projects.features.tangempay.details.api) // Remove after TANGEM_PAY_ACCOUNTS_REFACTOR_ENABLED
|
||||
|
||||
// region Project - Data
|
||||
implementation(projects.data.common)
|
||||
// endregion
|
||||
|
|
@ -47,6 +50,7 @@ dependencies {
|
|||
// region Tangem dependencies
|
||||
implementation(tangemDeps.card.core)
|
||||
implementation(tangemDeps.blockchain)
|
||||
implementation(tangemDeps.hot.core)
|
||||
// endregion
|
||||
|
||||
// region DI
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
package com.tangem.data.account.producer
|
||||
|
||||
import arrow.core.Option
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.none
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.producer.SingleAccountListProducer
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.getSyncStrict
|
||||
import com.tangem.domain.core.flow.FlowProducerTools
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -12,6 +20,7 @@ import dagger.assisted.AssistedInject
|
|||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/**
|
||||
* Default implementation of [SingleAccountListProducer].
|
||||
|
|
@ -27,6 +36,8 @@ internal class DefaultSingleAccountListProducer @AssistedInject constructor(
|
|||
@Assisted val params: SingleAccountListProducer.Params,
|
||||
override val flowProducerTools: FlowProducerTools,
|
||||
private val walletAccountListFlowFactory: WalletAccountListFlowFactory,
|
||||
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : SingleAccountListProducer {
|
||||
|
||||
|
|
@ -34,8 +45,32 @@ internal class DefaultSingleAccountListProducer @AssistedInject constructor(
|
|||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun produce(): Flow<AccountList> {
|
||||
return walletAccountListFlowFactory.create(userWalletId = params.userWalletId)
|
||||
.flowOn(dispatchers.default)
|
||||
val accountListFlow: Flow<AccountList> = if (tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled) {
|
||||
combineWithPaymentAccount()
|
||||
} else {
|
||||
walletAccountListFlowFactory.create(userWalletId = params.userWalletId)
|
||||
}
|
||||
|
||||
return accountListFlow.flowOn(dispatchers.default)
|
||||
}
|
||||
|
||||
private fun combineWithPaymentAccount(): Flow<AccountList> {
|
||||
return walletAccountListFlowFactory.create(params.userWalletId)
|
||||
.map { accountList ->
|
||||
val userWallet = userWalletsListRepository.getSyncStrict(id = params.userWalletId)
|
||||
if (userWallet.isPaymentAccountSupported()) {
|
||||
accountList.plus(Account.Payment(params.userWalletId)).getOrElse { throwable ->
|
||||
error("Can not combine account list and payment account status: $throwable")
|
||||
}
|
||||
} else {
|
||||
accountList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun UserWallet.isPaymentAccountSupported(): Boolean = when (this) {
|
||||
is UserWallet.Cold -> scanResponse.card.firmwareVersion >= FirmwareVersion.HDWalletAvailable
|
||||
is UserWallet.Hot -> hotWalletId.authType != HotWalletId.AuthType.NoPassword
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ package com.tangem.data.account.producer
|
|||
import com.google.common.truth.Truth
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.producer.SingleAccountListProducer
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.core.flow.FlowProducerTools
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import com.tangem.test.core.getEmittedValues
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.*
|
||||
|
|
@ -29,6 +31,10 @@ class DefaultSingleAccountListProducerTest {
|
|||
|
||||
private val userWalletId = UserWalletId("011")
|
||||
private val flowProducerTools: FlowProducerTools = mockk()
|
||||
private val tangemPayFeatureToggles = mockk<TangemPayFeatureToggles> {
|
||||
every { this@mockk.isTangemPayAccountsRefactorEnabled } returns false
|
||||
}
|
||||
private val userWalletsListRepository = mockk<UserWalletsListRepository>()
|
||||
private val userWallet = mockk<UserWallet> {
|
||||
every { this@mockk.walletId } returns userWalletId
|
||||
}
|
||||
|
|
@ -38,6 +44,8 @@ class DefaultSingleAccountListProducerTest {
|
|||
walletAccountListFlowFactory = walletAccountListFlowFactory,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
flowProducerTools = flowProducerTools,
|
||||
tangemPayFeatureToggles = tangemPayFeatureToggles,
|
||||
userWalletsListRepository = userWalletsListRepository,
|
||||
)
|
||||
|
||||
@AfterEach
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
package com.tangem.data.pay.converter
|
||||
|
||||
import com.tangem.data.pay.converter.PaymentAccountStatusDMConverter.convert
|
||||
import com.tangem.data.pay.converter.PaymentAccountStatusDMConverter.convertBack
|
||||
import com.tangem.datasource.local.visa.entity.PaymentAccountStatusDM
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.pay.PaymentAccountStatus
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
|
||||
/**
|
||||
* Two-way converter between [PaymentAccountStatus] and [PaymentAccountStatusDM].
|
||||
*
|
||||
* [convert] maps domain → data model. Returns null for transient statuses that should not be persisted
|
||||
* (Loading, ExposedDevice, Unavailable, NotSynced).
|
||||
*
|
||||
* [convertBack] maps data model → domain. All restored statuses have [StatusSource.CACHE] as source.
|
||||
*/
|
||||
internal object PaymentAccountStatusDMConverter :
|
||||
TwoWayConverter<PaymentAccountStatus, PaymentAccountStatusDM?> {
|
||||
|
||||
override fun convert(value: PaymentAccountStatus): PaymentAccountStatusDM? {
|
||||
return when (value) {
|
||||
is PaymentAccountStatus.NotCreated -> PaymentAccountStatusDM.NotCreated()
|
||||
is PaymentAccountStatus.UnderReview -> PaymentAccountStatusDM.UnderReview(kycStatus = value.kycStatus)
|
||||
is PaymentAccountStatus.IssuingCard -> PaymentAccountStatusDM.IssuingCard()
|
||||
is PaymentAccountStatus.Locked -> PaymentAccountStatusDM.Locked()
|
||||
is PaymentAccountStatus.Loaded -> PaymentAccountStatusDM.Loaded(
|
||||
cardId = value.cardId,
|
||||
lastFourDigits = value.lastFourDigits,
|
||||
balance = value.balance,
|
||||
currencyCode = value.currencyCode,
|
||||
depositAddress = value.depositAddress,
|
||||
isPinSet = value.isPinSet,
|
||||
)
|
||||
is PaymentAccountStatus.Error.CardIssueFailed -> PaymentAccountStatusDM.CardIssueFailed()
|
||||
// Transient statuses are not persisted
|
||||
is PaymentAccountStatus.Loading,
|
||||
is PaymentAccountStatus.Error.ExposedDevice,
|
||||
is PaymentAccountStatus.Error.Unavailable,
|
||||
is PaymentAccountStatus.Error.NotSynced,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertBack(value: PaymentAccountStatusDM?): PaymentAccountStatus {
|
||||
return when (value) {
|
||||
is PaymentAccountStatusDM.CardIssueFailed -> PaymentAccountStatus.Error.CardIssueFailed
|
||||
is PaymentAccountStatusDM.NotCreated -> PaymentAccountStatus.NotCreated
|
||||
is PaymentAccountStatusDM.IssuingCard -> PaymentAccountStatus.IssuingCard(source = StatusSource.CACHE)
|
||||
is PaymentAccountStatusDM.Locked -> PaymentAccountStatus.Locked(source = StatusSource.CACHE)
|
||||
is PaymentAccountStatusDM.UnderReview -> PaymentAccountStatus.UnderReview(
|
||||
source = StatusSource.CACHE,
|
||||
kycStatus = value.kycStatus,
|
||||
)
|
||||
is PaymentAccountStatusDM.Loaded -> PaymentAccountStatus.Loaded(
|
||||
source = StatusSource.CACHE,
|
||||
cardId = value.cardId,
|
||||
lastFourDigits = value.lastFourDigits,
|
||||
balance = value.balance,
|
||||
currencyCode = value.currencyCode,
|
||||
depositAddress = value.depositAddress,
|
||||
isPinSet = value.isPinSet,
|
||||
)
|
||||
null -> PaymentAccountStatus.Error.Unavailable(source = StatusSource.CACHE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package com.tangem.data.pay.converter
|
||||
|
||||
import com.tangem.data.pay.converter.PaymentAccountStatusValueDMConverter.convert
|
||||
import com.tangem.data.pay.converter.PaymentAccountStatusValueDMConverter.convertBack
|
||||
import com.tangem.datasource.local.visa.entity.PaymentAccountStatusValueDM
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
|
||||
/**
|
||||
* Two-way converter between [PaymentAccountStatusValue] and [PaymentAccountStatusValueDM].
|
||||
*
|
||||
* [convert] maps domain → data model. Returns null for transient statuses that should not be persisted
|
||||
* (Loading, ExposedDevice, Unavailable, NotSynced).
|
||||
*
|
||||
* [convertBack] maps data model → domain. All restored statuses have [StatusSource.CACHE] as source.
|
||||
*/
|
||||
internal object PaymentAccountStatusValueDMConverter :
|
||||
TwoWayConverter<PaymentAccountStatusValue, PaymentAccountStatusValueDM?> {
|
||||
|
||||
override fun convert(value: PaymentAccountStatusValue): PaymentAccountStatusValueDM? {
|
||||
return when (value) {
|
||||
is PaymentAccountStatusValue.NotCreated -> PaymentAccountStatusValueDM.NotCreated()
|
||||
is PaymentAccountStatusValue.UnderReview -> PaymentAccountStatusValueDM.UnderReview(
|
||||
kycStatus = value.kycStatus,
|
||||
customerId = value.customerId,
|
||||
)
|
||||
is PaymentAccountStatusValue.IssuingCard -> PaymentAccountStatusValueDM.IssuingCard()
|
||||
is PaymentAccountStatusValue.Locked -> PaymentAccountStatusValueDM.ActiveCard(
|
||||
isLocked = true,
|
||||
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(),
|
||||
)
|
||||
is PaymentAccountStatusValue.Error.CardIssueFailed -> PaymentAccountStatusValueDM.CardIssueFailed(
|
||||
customerId = value.customerId,
|
||||
)
|
||||
// Transient statuses are not persisted
|
||||
is PaymentAccountStatusValue.Loading,
|
||||
is PaymentAccountStatusValue.Error.ExposedDevice,
|
||||
is PaymentAccountStatusValue.Error.Unavailable,
|
||||
is PaymentAccountStatusValue.Error.NotSynced,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertBack(value: PaymentAccountStatusValueDM?): PaymentAccountStatusValue {
|
||||
return when (value) {
|
||||
is PaymentAccountStatusValueDM.NotCreated -> PaymentAccountStatusValue.NotCreated
|
||||
is PaymentAccountStatusValueDM.CardIssueFailed -> PaymentAccountStatusValue.Error.CardIssueFailed(
|
||||
customerId = value.customerId,
|
||||
)
|
||||
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.UnderReview -> PaymentAccountStatusValue.UnderReview(
|
||||
source = StatusSource.CACHE,
|
||||
kycStatus = value.kycStatus,
|
||||
customerId = value.customerId,
|
||||
)
|
||||
null -> PaymentAccountStatusValue.Error.Unavailable
|
||||
}
|
||||
}
|
||||
|
||||
private fun PaymentAccountStatusValue.FiatBalance.toDM(): PaymentAccountStatusValueDM.FiatBalanceDM {
|
||||
return PaymentAccountStatusValueDM.FiatBalanceDM(
|
||||
availableBalance = availableBalance,
|
||||
currency = currency,
|
||||
)
|
||||
}
|
||||
|
||||
private fun PaymentAccountStatusValue.CryptoBalance.toDM(): PaymentAccountStatusValueDM.CryptoBalanceDM {
|
||||
return PaymentAccountStatusValueDM.CryptoBalanceDM(
|
||||
id = id,
|
||||
chainId = chainId,
|
||||
depositAddress = depositAddress,
|
||||
tokenContractAddress = tokenContractAddress,
|
||||
balance = balance,
|
||||
)
|
||||
}
|
||||
|
||||
private fun PaymentAccountStatusValueDM.FiatBalanceDM.toDomain(): PaymentAccountStatusValue.FiatBalance {
|
||||
return PaymentAccountStatusValue.FiatBalance(
|
||||
availableBalance = availableBalance,
|
||||
currency = currency,
|
||||
)
|
||||
}
|
||||
|
||||
private fun PaymentAccountStatusValueDM.CryptoBalanceDM.toDomain(): PaymentAccountStatusValue.CryptoBalance {
|
||||
return PaymentAccountStatusValue.CryptoBalance(
|
||||
id = id,
|
||||
chainId = chainId,
|
||||
depositAddress = depositAddress,
|
||||
tokenContractAddress = tokenContractAddress,
|
||||
balance = balance,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -15,10 +15,9 @@ import com.tangem.data.pay.usecase.DefaultGetTangemPayCustomerIdUseCase
|
|||
import com.tangem.data.pay.usecase.DefaultTangemPayWithdrawUseCase
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||
import com.tangem.datasource.local.visa.entity.PaymentAccountStatusDM
|
||||
import com.tangem.datasource.local.visa.entity.PaymentAccountStatusValueDM
|
||||
import com.tangem.datasource.utils.MoshiDataStoreSerializer
|
||||
import com.tangem.datasource.utils.mapWithStringKeyTypes
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
|
||||
import com.tangem.domain.pay.TangemPayEligibilityManager
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||
|
|
@ -32,6 +31,7 @@ import com.tangem.domain.tangempay.GetTangemPayCustomerIdUseCase
|
|||
import com.tangem.domain.tangempay.TangemPayWithdrawUseCase
|
||||
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
|
||||
import com.tangem.security.DeviceSecurityInfoProvider
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -118,7 +118,7 @@ internal interface TangemPayDataModule {
|
|||
persistenceDataStore = DataStoreFactory.create(
|
||||
serializer = MoshiDataStoreSerializer(
|
||||
moshi = moshi,
|
||||
types = mapWithStringKeyTypes<PaymentAccountStatusDM>(),
|
||||
types = mapWithStringKeyTypes<PaymentAccountStatusValueDM>(),
|
||||
defaultValue = emptyMap(),
|
||||
),
|
||||
produceFile = { context.dataStoreFile(fileName = "payment_account_statuses") },
|
||||
|
|
|
|||
|
|
@ -2,17 +2,19 @@ package com.tangem.data.pay.flow
|
|||
|
||||
import arrow.core.Either
|
||||
import com.tangem.data.pay.store.PaymentAccountStatusesStore
|
||||
import com.tangem.domain.core.utils.eitherOn
|
||||
import com.tangem.domain.core.utils.catchOn
|
||||
import com.tangem.domain.models.StatusSource
|
||||
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.pay.PaymentAccountStatus
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||
import com.tangem.domain.pay.model.CustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus
|
||||
import com.tangem.domain.pay.repository.CustomerOrderRepository
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import com.tangem.security.DeviceSecurityInfoProvider
|
||||
import com.tangem.security.isSecurityExposed
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -29,87 +31,101 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : PaymentAccountStatusFetcher {
|
||||
|
||||
private val logger = TangemLogger.withTag(TAG)
|
||||
|
||||
override suspend fun invoke(params: PaymentAccountStatusFetcher.Params): Either<Throwable, Unit> =
|
||||
eitherOn(dispatchers.default) {
|
||||
TangemLogger.withTag(TAG).i("fetch: ${params.userWalletId.stringValue}")
|
||||
Either.catchOn(dispatchers.default) {
|
||||
val account = Account.Payment(userWalletId = params.userWalletId)
|
||||
logger.i("fetch: ${params.userWalletId.stringValue}")
|
||||
|
||||
if (deviceSecurity.isSecurityExposed()) {
|
||||
TangemLogger.withTag(TAG).i("fetch security info: rooted: ${deviceSecurity.isRooted}")
|
||||
TangemLogger.withTag(TAG).i("fetch security info: xposed: ${deviceSecurity.isXposed}")
|
||||
TangemLogger.withTag(
|
||||
TAG,
|
||||
).i("fetch security info: bootloader unlocked: ${deviceSecurity.isBootloaderUnlocked}")
|
||||
logger.i("fetch security info: rooted: ${deviceSecurity.isRooted}")
|
||||
logger.i("fetch security info: xposed: ${deviceSecurity.isXposed}")
|
||||
logger.i("fetch security info: bootloader unlocked: ${deviceSecurity.isBootloaderUnlocked}")
|
||||
|
||||
return@eitherOn paymentAccountStatusesStore.store(
|
||||
return@catchOn paymentAccountStatusesStore.store(
|
||||
userWalletId = params.userWalletId,
|
||||
status = PaymentAccountStatus.Error.ExposedDevice,
|
||||
status = AccountStatus.Payment(
|
||||
account = account,
|
||||
value = PaymentAccountStatusValue.Error.ExposedDevice,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val status = onboardingRepository.hasTangemPayInWallet(userWalletId = params.userWalletId)
|
||||
.fold(
|
||||
ifLeft = { error ->
|
||||
TangemLogger.withTag(
|
||||
TAG,
|
||||
).e("Failed check wallet ${params.userWalletId}: ${error.javaClass.simpleName}")
|
||||
logger.e("Failed check wallet ${params.userWalletId}: ${error.javaClass.simpleName}")
|
||||
when (error) {
|
||||
is VisaApiError.NotPaeraCustomer -> PaymentAccountStatus.NotCreated
|
||||
else -> PaymentAccountStatus.Error.Unavailable(source = StatusSource.ACTUAL)
|
||||
is VisaApiError.NotPaeraCustomer -> PaymentAccountStatusValue.NotCreated
|
||||
else -> PaymentAccountStatusValue.Error.Unavailable
|
||||
}
|
||||
},
|
||||
ifRight = { hasTangemPay ->
|
||||
proceedHasTangemPayResult(userWalletId = params.userWalletId, hasTangemPay = hasTangemPay)
|
||||
proceedHasTangemPayResult(account = account, hasTangemPay = hasTangemPay)
|
||||
},
|
||||
)
|
||||
TangemLogger.withTag(TAG).i("invoke status ${params.userWalletId}: $status")
|
||||
paymentAccountStatusesStore.store(userWalletId = params.userWalletId, status = status)
|
||||
logger.i("invoke status ${params.userWalletId}: $status")
|
||||
paymentAccountStatusesStore.store(
|
||||
userWalletId = params.userWalletId,
|
||||
status = AccountStatus.Payment(account = account, value = status),
|
||||
)
|
||||
}.onLeft {
|
||||
paymentAccountStatusesStore.updateStatusSource(
|
||||
userWalletId = params.userWalletId,
|
||||
source = StatusSource.ONLY_CACHE,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun proceedHasTangemPayResult(
|
||||
userWalletId: UserWalletId,
|
||||
account: Account.Payment,
|
||||
hasTangemPay: Boolean,
|
||||
): PaymentAccountStatus {
|
||||
TangemLogger.withTag(TAG).i("proceedHasTangemPayResult for $userWalletId hasTangemPay: $hasTangemPay")
|
||||
): PaymentAccountStatusValue {
|
||||
logger.i("proceedHasTangemPayResult for ${account.userWalletId} hasTangemPay: $hasTangemPay")
|
||||
return if (hasTangemPay) {
|
||||
fetchTangemPayAccountStatus(userWalletId = userWalletId)
|
||||
fetchTangemPayAccountStatus(account)
|
||||
} else {
|
||||
PaymentAccountStatus.NotCreated
|
||||
PaymentAccountStatusValue.NotCreated
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchTangemPayAccountStatus(userWalletId: UserWalletId): PaymentAccountStatus {
|
||||
val prevResult = paymentAccountStatusesStore.getSyncOrNull(userWalletId)
|
||||
if (prevResult == null || prevResult is PaymentAccountStatus.Error) {
|
||||
paymentAccountStatusesStore.store(userWalletId = userWalletId, status = PaymentAccountStatus.Loading)
|
||||
private suspend fun fetchTangemPayAccountStatus(account: Account.Payment): PaymentAccountStatusValue {
|
||||
val prevResult = paymentAccountStatusesStore.getSyncOrNull(account.userWalletId)
|
||||
if (prevResult == null || prevResult.value is PaymentAccountStatusValue.Error) {
|
||||
paymentAccountStatusesStore.store(
|
||||
userWalletId = account.userWalletId,
|
||||
status = AccountStatus.Payment(account = account, value = PaymentAccountStatusValue.Loading),
|
||||
)
|
||||
}
|
||||
|
||||
return proceedWithOrderId(userWalletId = userWalletId)
|
||||
return proceedWithOrderId(account = account)
|
||||
}
|
||||
|
||||
private suspend fun proceedWithOrderId(userWalletId: UserWalletId): PaymentAccountStatus {
|
||||
return if (!onboardingRepository.isTangemPayInitialDataProduced(userWalletId)) {
|
||||
PaymentAccountStatus.Error.NotSynced
|
||||
private suspend fun proceedWithOrderId(account: Account.Payment): PaymentAccountStatusValue {
|
||||
return if (!onboardingRepository.isTangemPayInitialDataProduced(account.userWalletId)) {
|
||||
PaymentAccountStatusValue.Error.NotSynced
|
||||
} else {
|
||||
val orderId = onboardingRepository.getOrderId(userWalletId)
|
||||
val orderId = onboardingRepository.getOrderId(account.userWalletId)
|
||||
if (orderId != null) {
|
||||
proceedWithOrderId(userWalletId = userWalletId, orderId = orderId)
|
||||
proceedWithOrderId(account = account, orderId = orderId)
|
||||
} else {
|
||||
proceedWithoutOrder(userWalletId = userWalletId)
|
||||
proceedWithoutOrder(account = account)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun proceedWithoutOrder(userWalletId: UserWalletId): PaymentAccountStatus {
|
||||
return onboardingRepository.getCustomerInfo(userWalletId).fold(
|
||||
private suspend fun proceedWithoutOrder(account: Account.Payment): PaymentAccountStatusValue {
|
||||
return onboardingRepository.getCustomerInfo(account.userWalletId).fold(
|
||||
ifLeft = { error ->
|
||||
TangemLogger.withTag(TAG).e("proceedWithoutOrder $userWalletId error: $error")
|
||||
logger.e("proceedWithoutOrder ${account.userWalletId} error: $error")
|
||||
error.mapToPaymentAccountStatus()
|
||||
},
|
||||
ifRight = { customerInfo ->
|
||||
TangemLogger.withTag(TAG).i("proceedWithoutOrder data customerInfo $userWalletId")
|
||||
logger.i("proceedWithoutOrder data customerInfo ${account.userWalletId}")
|
||||
val status = customerInfo.mapToPaymentAccountStatus()
|
||||
if (customerInfo.productInstance == null) {
|
||||
onboardingRepository.createOrder(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)
|
||||
.onLeft { TangemLogger.withTag(TAG).e("createOrder failed: $it") }
|
||||
}
|
||||
status
|
||||
|
|
@ -117,63 +133,94 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun proceedWithOrderId(userWalletId: UserWalletId, orderId: String): PaymentAccountStatus {
|
||||
return customerOrderRepository.getOrderData(userWalletId, orderId = orderId).fold(
|
||||
private suspend fun proceedWithOrderId(account: Account.Payment, orderId: String): PaymentAccountStatusValue {
|
||||
return customerOrderRepository.getOrderData(userWalletId = account.userWalletId, orderId = orderId).fold(
|
||||
ifLeft = { error ->
|
||||
TangemLogger.withTag(TAG).e("proceedWithOrderId $userWalletId orderId: $orderId error: $error")
|
||||
logger.e("proceedWithOrderId ${account.userWalletId} orderId: $orderId error: $error")
|
||||
error.mapToPaymentAccountStatus()
|
||||
},
|
||||
ifRight = { orderData ->
|
||||
TangemLogger.withTag(TAG).i("proceedWithOrderId $userWalletId: $orderId status: ${orderData.status}")
|
||||
logger.i("proceedWithOrderId $account.userWalletId: $orderId status: ${orderData.status}")
|
||||
when (orderData.status) {
|
||||
// Kyc is passed and user waits for order creation -> no need to get customer info
|
||||
OrderStatus.NEW,
|
||||
OrderStatus.PROCESSING,
|
||||
-> PaymentAccountStatus.IssuingCard(source = StatusSource.ACTUAL)
|
||||
-> PaymentAccountStatusValue.IssuingCard(source = StatusSource.ACTUAL)
|
||||
|
||||
OrderStatus.CANCELED -> {
|
||||
PaymentAccountStatus.Error.CardIssueFailed
|
||||
PaymentAccountStatusValue.Error.CardIssueFailed(customerId = orderData.customerId)
|
||||
}
|
||||
OrderStatus.COMPLETED -> {
|
||||
// Order was completed -> clear order id and get customer info
|
||||
onboardingRepository.clearOrderId(userWalletId)
|
||||
onboardingRepository.getCustomerInfo(userWalletId = userWalletId)
|
||||
onboardingRepository.clearOrderId(account.userWalletId)
|
||||
onboardingRepository.getCustomerInfo(userWalletId = account.userWalletId)
|
||||
.fold(
|
||||
ifLeft = { it.mapToPaymentAccountStatus() },
|
||||
ifRight = { customerInfo -> customerInfo.mapToPaymentAccountStatus() },
|
||||
)
|
||||
}
|
||||
OrderStatus.UNKNOWN -> PaymentAccountStatus.Error.Unavailable(source = StatusSource.ACTUAL)
|
||||
OrderStatus.UNKNOWN -> PaymentAccountStatusValue.Error.Unavailable
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun CustomerInfo.mapToPaymentAccountStatus(): PaymentAccountStatus {
|
||||
private fun CustomerInfo.mapToPaymentAccountStatus(): PaymentAccountStatusValue {
|
||||
val cardInfo = this.cardInfo
|
||||
val productInstance = this.productInstance
|
||||
return if (kycStatus != KycStatus.APPROVED && !customerId.isNullOrEmpty()) {
|
||||
PaymentAccountStatus.UnderReview(source = StatusSource.ACTUAL, kycStatus = kycStatus)
|
||||
} else if (cardInfo != null && productInstance != null) {
|
||||
PaymentAccountStatus.Loaded(
|
||||
PaymentAccountStatusValue.UnderReview(
|
||||
source = StatusSource.ACTUAL,
|
||||
cardId = productInstance.cardId,
|
||||
lastFourDigits = cardInfo.lastFourDigits,
|
||||
balance = cardInfo.balance,
|
||||
currencyCode = cardInfo.currencyCode,
|
||||
depositAddress = cardInfo.depositAddress,
|
||||
isPinSet = cardInfo.isPinSet,
|
||||
kycStatus = kycStatus,
|
||||
customerId = requireNotNull(customerId) { "CustomerId must not be null" },
|
||||
)
|
||||
} else if (cardInfo != null && productInstance != null && !customerId.isNullOrEmpty()) {
|
||||
convertToContentState(
|
||||
productInstance = productInstance,
|
||||
cardInfo = cardInfo,
|
||||
customerId = requireNotNull(customerId) { "CustomerId must not be null" },
|
||||
)
|
||||
} else {
|
||||
PaymentAccountStatus.IssuingCard(source = StatusSource.ACTUAL)
|
||||
PaymentAccountStatusValue.IssuingCard(source = StatusSource.ACTUAL)
|
||||
}
|
||||
}
|
||||
|
||||
private fun VisaApiError.mapToPaymentAccountStatus(): PaymentAccountStatus {
|
||||
private fun convertToContentState(
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun VisaApiError.mapToPaymentAccountStatus(): PaymentAccountStatusValue {
|
||||
return when (this) {
|
||||
is VisaApiError.RefreshTokenExpired -> PaymentAccountStatus.Error.NotSynced
|
||||
is VisaApiError.NotPaeraCustomer -> PaymentAccountStatus.NotCreated
|
||||
else -> PaymentAccountStatus.Error.Unavailable(source = StatusSource.ACTUAL)
|
||||
is VisaApiError.RefreshTokenExpired -> PaymentAccountStatusValue.Error.NotSynced
|
||||
is VisaApiError.NotPaeraCustomer -> PaymentAccountStatusValue.NotCreated
|
||||
else -> PaymentAccountStatusValue.Error.Unavailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,9 @@ import arrow.core.Option
|
|||
import arrow.core.some
|
||||
import com.tangem.data.pay.store.PaymentAccountStatusesStore
|
||||
import com.tangem.domain.core.flow.FlowProducerTools
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.pay.PaymentAccountStatus
|
||||
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.pay.flow.PaymentAccountStatusProducer
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -21,12 +22,15 @@ internal class DefaultPaymentAccountStatusProducer @AssistedInject constructor(
|
|||
private val paymentAccountStatusesStore: PaymentAccountStatusesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : PaymentAccountStatusProducer {
|
||||
override val fallback: Option<PaymentAccountStatus>
|
||||
get() = PaymentAccountStatus.Error.Unavailable(source = StatusSource.ACTUAL).some()
|
||||
|
||||
override fun produce(): Flow<PaymentAccountStatus> {
|
||||
private val account = Account.Payment(userWalletId = params.userWalletId)
|
||||
|
||||
override val fallback: Option<AccountStatus.Payment>
|
||||
get() = AccountStatus.Payment(account = account, value = PaymentAccountStatusValue.Error.Unavailable).some()
|
||||
|
||||
override fun produce(): Flow<AccountStatus.Payment> {
|
||||
return paymentAccountStatusesStore.get(userWalletId = params.userWalletId)
|
||||
.onEmpty { emit(value = PaymentAccountStatus.NotCreated) }
|
||||
.onEmpty { emit(value = AccountStatus.Payment(account, PaymentAccountStatusValue.NotCreated)) }
|
||||
.flowOn(dispatchers.default)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ 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.account.PaymentAccountStatusValue
|
||||
import com.tangem.domain.models.kyc.KycStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -139,6 +140,7 @@ internal class DefaultOnboardingRepository @Inject constructor(
|
|||
?: error("no userWallet found")
|
||||
}
|
||||
|
||||
@Suppress("ComplexCondition")
|
||||
private suspend fun getCustomerInfo(
|
||||
userWalletId: UserWalletId,
|
||||
response: CustomerMeResponse.Result?,
|
||||
|
|
@ -148,14 +150,26 @@ internal class DefaultOnboardingRepository @Inject constructor(
|
|||
|
||||
val card = response?.card
|
||||
val fiatBalance = response?.balance?.fiat
|
||||
val cryptoBalance = response?.balance?.crypto
|
||||
val paymentAccount = response?.paymentAccount
|
||||
val cardInfo = if (paymentAccount != null && card != null && fiatBalance != null) {
|
||||
val cardInfo = if (paymentAccount != null && card != null && fiatBalance != null && cryptoBalance != null) {
|
||||
CardInfo(
|
||||
lastFourDigits = card.cardNumberEnd,
|
||||
balance = fiatBalance.availableBalance,
|
||||
currencyCode = fiatBalance.currency,
|
||||
depositAddress = response.depositAddress,
|
||||
isPinSet = response.card?.isPinSet == true,
|
||||
fiatBalance = PaymentAccountStatusValue.FiatBalance(
|
||||
availableBalance = fiatBalance.availableBalance,
|
||||
currency = fiatBalance.currency,
|
||||
),
|
||||
cryptoBalance = PaymentAccountStatusValue.CryptoBalance(
|
||||
id = cryptoBalance.id,
|
||||
chainId = cryptoBalance.chainId.toLong(),
|
||||
depositAddress = cryptoBalance.depositAddress.orEmpty(),
|
||||
tokenContractAddress = cryptoBalance.tokenContractAddress,
|
||||
balance = cryptoBalance.balance,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
@ -167,7 +181,7 @@ internal class DefaultOnboardingRepository @Inject constructor(
|
|||
}
|
||||
cardFrozenStateStore.store(key = instance.cardId, value = cardFrozenState)
|
||||
|
||||
ProductInstance(id = instance.id, cardId = instance.cardId)
|
||||
ProductInstance(id = instance.id, cardId = instance.cardId, frozenState = cardFrozenState)
|
||||
}
|
||||
return CustomerInfo(
|
||||
customerId = response?.id,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
package com.tangem.data.pay.store
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import com.tangem.data.pay.converter.PaymentAccountStatusDMConverter
|
||||
import com.tangem.data.pay.converter.PaymentAccountStatusValueDMConverter
|
||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||
import com.tangem.datasource.local.visa.entity.PaymentAccountStatusDM
|
||||
import com.tangem.datasource.local.visa.entity.PaymentAccountStatusValueDM
|
||||
import com.tangem.domain.models.StatusSource
|
||||
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.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.PaymentAccountStatus
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
|
@ -14,8 +17,8 @@ import kotlinx.coroutines.flow.firstOrNull
|
|||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal typealias WalletIdWithPaymentStatus = Map<String, PaymentAccountStatus>
|
||||
internal typealias WalletIdWithPaymentStatusDM = Map<String, PaymentAccountStatusDM>
|
||||
internal typealias WalletIdWithPaymentStatus = Map<String, AccountStatus.Payment>
|
||||
internal typealias WalletIdWithPaymentStatusDM = Map<String, PaymentAccountStatusValueDM>
|
||||
|
||||
/**
|
||||
* Store for payment account statuses with dual storage (runtime + persistence).
|
||||
|
|
@ -26,7 +29,7 @@ internal typealias WalletIdWithPaymentStatusDM = Map<String, PaymentAccountStatu
|
|||
internal class PaymentAccountStatusesStore(
|
||||
private val runtimeStore: RuntimeSharedStore<WalletIdWithPaymentStatus>,
|
||||
private val persistenceDataStore: DataStore<WalletIdWithPaymentStatusDM>,
|
||||
private val scope: AppCoroutineScope,
|
||||
scope: AppCoroutineScope,
|
||||
) {
|
||||
|
||||
init {
|
||||
|
|
@ -34,8 +37,10 @@ internal class PaymentAccountStatusesStore(
|
|||
try {
|
||||
val cachedStatuses = persistenceDataStore.data.firstOrNull() ?: return@launch
|
||||
runtimeStore.store(
|
||||
value = cachedStatuses.mapValues { (_, statusDM) ->
|
||||
PaymentAccountStatusDMConverter.convertBack(statusDM)
|
||||
value = cachedStatuses.mapValues { (rawUserWalletId, statusDM) ->
|
||||
val account = Account.Payment(userWalletId = UserWalletId(rawUserWalletId))
|
||||
val statusValue = PaymentAccountStatusValueDMConverter.convertBack(value = statusDM)
|
||||
AccountStatus.Payment(account = account, value = statusValue)
|
||||
},
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
|
|
@ -44,18 +49,28 @@ internal class PaymentAccountStatusesStore(
|
|||
}
|
||||
}
|
||||
|
||||
fun get(userWalletId: UserWalletId): Flow<PaymentAccountStatus> {
|
||||
fun get(userWalletId: UserWalletId): Flow<AccountStatus.Payment> {
|
||||
return runtimeStore.get().mapNotNull { it[userWalletId.stringValue] }
|
||||
}
|
||||
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId): PaymentAccountStatus? {
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId): AccountStatus.Payment? {
|
||||
return runtimeStore.getSyncOrNull()?.get(userWalletId.stringValue)
|
||||
}
|
||||
|
||||
suspend fun store(userWalletId: UserWalletId, status: PaymentAccountStatus) {
|
||||
suspend fun updateStatusSource(userWalletId: UserWalletId, source: StatusSource) {
|
||||
runtimeStore.update(emptyMap()) { stored ->
|
||||
stored.toMutableMap().apply {
|
||||
val paymentAccountStatus = this[userWalletId.stringValue] ?: return@update stored
|
||||
val newValue = paymentAccountStatus.copy(value = paymentAccountStatus.value.copySealed(source = source))
|
||||
put(key = userWalletId.stringValue, value = newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun store(userWalletId: UserWalletId, status: AccountStatus.Payment) {
|
||||
coroutineScope {
|
||||
launch { storeInRuntime(userWalletId = userWalletId, status = status) }
|
||||
launch { storeInPersistence(userWalletId = userWalletId, status = status) }
|
||||
launch { storeInPersistence(userWalletId = userWalletId, status = status.value) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +78,7 @@ internal class PaymentAccountStatusesStore(
|
|||
return runtimeStore.getSyncOrDefault(emptyMap()).containsKey(userWalletId.stringValue)
|
||||
}
|
||||
|
||||
private suspend fun storeInRuntime(userWalletId: UserWalletId, status: PaymentAccountStatus) {
|
||||
private suspend fun storeInRuntime(userWalletId: UserWalletId, status: AccountStatus.Payment) {
|
||||
runtimeStore.update(default = emptyMap()) { stored ->
|
||||
stored.toMutableMap().apply {
|
||||
put(key = userWalletId.stringValue, value = status)
|
||||
|
|
@ -71,8 +86,8 @@ internal class PaymentAccountStatusesStore(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun storeInPersistence(userWalletId: UserWalletId, status: PaymentAccountStatus) {
|
||||
val statusDM = PaymentAccountStatusDMConverter.convert(value = status) ?: return
|
||||
private suspend fun storeInPersistence(userWalletId: UserWalletId, status: PaymentAccountStatusValue) {
|
||||
val statusDM = PaymentAccountStatusValueDMConverter.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