Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-22 19:30:22 +05:00
parent 7b46bfda11
commit 6d686a0820
11 changed files with 103 additions and 26 deletions

View file

@ -51,6 +51,7 @@ internal object PaymentAccountStatusValueDMConverter :
is PaymentAccountStatusValue.Error.CardIssueFailed -> PaymentAccountStatusValueDM.CardIssueFailed(
customerId = value.customerId,
)
is PaymentAccountStatusValue.Empty -> PaymentAccountStatusValueDM.Empty()
// Transient statuses are not persisted
is PaymentAccountStatusValue.Loading,
is PaymentAccountStatusValue.Error.ExposedDevice,
@ -62,6 +63,7 @@ internal object PaymentAccountStatusValueDMConverter :
override fun convertBack(value: PaymentAccountStatusValueDM?): PaymentAccountStatusValue {
return when (value) {
is PaymentAccountStatusValueDM.Empty -> PaymentAccountStatusValue.Empty
is PaymentAccountStatusValueDM.NotCreated -> PaymentAccountStatusValue.NotCreated
is PaymentAccountStatusValueDM.CardIssueFailed -> PaymentAccountStatusValue.Error.CardIssueFailed(
customerId = value.customerId,

View file

@ -8,10 +8,13 @@ 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.TangemPayEligibilityManager
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
import com.tangem.domain.pay.model.CustomerInfo
import com.tangem.domain.pay.model.OrderData
import com.tangem.domain.pay.model.OrderStatus
import com.tangem.domain.pay.model.TangemPayEntryPoint
import com.tangem.domain.pay.repository.CustomerOrderRepository
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.visa.error.VisaApiError
@ -28,12 +31,14 @@ import kotlin.time.Duration.Companion.minutes
private const val TAG = "PaymentAccountStatusFetcher"
@Suppress("LongParameterList")
internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
private val paymentAccountStatusesStore: PaymentAccountStatusesStore,
private val onboardingRepository: OnboardingRepository,
private val customerOrderRepository: CustomerOrderRepository,
private val deviceSecurity: DeviceSecurityInfoProvider,
private val dispatchers: CoroutineDispatcherProvider,
private val eligibilityManager: TangemPayEligibilityManager,
) : PaymentAccountStatusFetcher {
private val logger = TangemLogger.withTag(TAG)
@ -43,6 +48,16 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
val account = Account.Payment(userWalletId = params.userWalletId)
logger.i("fetch: ${params.userWalletId.stringValue}")
if (onboardingRepository.isTangemPayDeactivated(params.userWalletId)) {
return@catchOn paymentAccountStatusesStore.store(
userWalletId = params.userWalletId,
status = AccountStatus.Payment(
account = account,
value = PaymentAccountStatusValue.Empty,
),
)
}
if (deviceSecurity.isSecurityExposed()) {
logger.i("fetch security info: rooted: ${deviceSecurity.isRooted}")
logger.i("fetch security info: xposed: ${deviceSecurity.isXposed}")
@ -57,22 +72,12 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
)
}
if (onboardingRepository.isTangemPayDeactivated(params.userWalletId)) {
return@catchOn paymentAccountStatusesStore.store(
userWalletId = params.userWalletId,
status = AccountStatus.Payment(
account = account,
value = PaymentAccountStatusValue.NotCreated,
),
)
}
val status = onboardingRepository.hasTangemPayInWallet(userWalletId = params.userWalletId)
.fold(
ifLeft = { error ->
logger.e("Failed check wallet ${params.userWalletId}: ${error.javaClass.simpleName}")
when (error) {
is VisaApiError.NotPaeraCustomer -> PaymentAccountStatusValue.NotCreated
is VisaApiError.NotPaeraCustomer -> constructNotCreatedOrEmptyStatus(params.userWalletId)
else -> PaymentAccountStatusValue.Error.Unavailable
}
},
@ -100,7 +105,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
return if (hasTangemPay) {
fetchTangemPayAccountStatus(account)
} else {
PaymentAccountStatusValue.NotCreated
constructNotCreatedOrEmptyStatus(account.userWalletId)
}
}
@ -133,7 +138,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
return onboardingRepository.getCustomerInfo(account.userWalletId).fold(
ifLeft = { error ->
logger.e("proceedWithoutOrder ${account.userWalletId} error: $error")
error.mapToPaymentAccountStatus()
error.mapToPaymentAccountStatus(account.userWalletId)
},
ifRight = { customerInfo ->
logger.i("proceedWithoutOrder data customerInfo ${account.userWalletId}")
@ -153,7 +158,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
val customerInfo = onboardingRepository.getCustomerInfo(account.userWalletId).fold(
ifLeft = { error ->
logger.e("proceedWithOrderId KYC check ${account.userWalletId} error: $error")
return error.mapToPaymentAccountStatus()
return error.mapToPaymentAccountStatus(account.userWalletId)
},
ifRight = { it },
)
@ -172,7 +177,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
return customerOrderRepository.getOrderData(userWalletId = account.userWalletId, orderId = orderId).fold(
ifLeft = { error ->
logger.e("proceedWithOrderId ${account.userWalletId} orderId: $orderId error: $error")
error.mapToPaymentAccountStatus()
error.mapToPaymentAccountStatus(account.userWalletId)
},
ifRight = { orderData ->
logger.i("proceedWithOrderId ${account.userWalletId}: $orderId status: ${orderData.status}")
@ -241,7 +246,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
onboardingRepository.clearOrderId(account.userWalletId)
return onboardingRepository.getCustomerInfo(userWalletId = account.userWalletId)
.fold(
ifLeft = { it.mapToPaymentAccountStatus() },
ifLeft = { it.mapToPaymentAccountStatus(account.userWalletId) },
ifRight = { customerInfo -> customerInfo.mapToPaymentAccountStatus() },
)
}
@ -297,12 +302,22 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
}
}
private fun VisaApiError.mapToPaymentAccountStatus(): PaymentAccountStatusValue {
private suspend fun VisaApiError.mapToPaymentAccountStatus(userWalletId: UserWalletId): PaymentAccountStatusValue {
return when (this) {
is VisaApiError.RefreshTokenExpired -> PaymentAccountStatusValue.Error.NotSynced
is VisaApiError.NotPaeraCustomer -> PaymentAccountStatusValue.NotCreated
is VisaApiError.Deactivated -> PaymentAccountStatusValue.NotCreated
is VisaApiError.NotPaeraCustomer -> constructNotCreatedOrEmptyStatus(userWalletId)
is VisaApiError.Deactivated -> constructNotCreatedOrEmptyStatus(userWalletId)
else -> PaymentAccountStatusValue.Error.Unavailable
}
}
private suspend fun constructNotCreatedOrEmptyStatus(userWalletId: UserWalletId): PaymentAccountStatusValue {
val entryPoint = TangemPayEntryPoint.BANNER
val shouldShowBanner = !eligibilityManager.isPaeraCustomerForAnyWallet(entryPoint) &&
eligibilityManager.getEligibleWallets(shouldExcludePaeraCustomers = false, entryPoint = entryPoint)
.any { it.walletId == userWalletId } &&
!onboardingRepository.getHideMainOnboardingBanner(userWalletId)
return if (shouldShowBanner) PaymentAccountStatusValue.NotCreated else PaymentAccountStatusValue.Empty
}
}

View file

@ -5,6 +5,7 @@ import arrow.core.flatMap
import arrow.core.left
import arrow.core.right
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.data.pay.store.PaymentAccountStatusesStore
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.models.request.DeeplinkValidityRequest
import com.tangem.datasource.api.pay.models.request.OrderRequest
@ -15,6 +16,8 @@ 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.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.UserWallet
@ -44,6 +47,7 @@ internal class DefaultOnboardingRepository @Inject constructor(
private val authDataSource: TangemPayAuthDataSource,
private val cardFrozenStateStore: TangemPayCardFrozenStateStore,
private val userWalletsListRepository: UserWalletsListRepository,
private val paymentAccountStatusStore: PaymentAccountStatusesStore,
) : OnboardingRepository {
// Save data for a session
@ -265,6 +269,13 @@ internal class DefaultOnboardingRepository @Inject constructor(
override suspend fun setHideMainOnboardingBanner(userWalletId: UserWalletId) {
tangemPayStorage.storeHideOnboardingBanner(userWalletId, hide = true)
paymentAccountStatusStore.store(
userWalletId = userWalletId,
status = AccountStatus.Payment(
account = Account.Payment(userWalletId),
value = PaymentAccountStatusValue.Empty,
),
)
}
override suspend fun disableTangemPay(userWalletId: UserWalletId): Either<VisaApiError, Unit> {