diff --git a/app/src/main/java/com/tangem/tap/data/DefaultTangemPayStorage.kt b/app/src/main/java/com/tangem/tap/data/DefaultTangemPayStorage.kt index 9e6fb969cc..46b35c3bce 100644 --- a/app/src/main/java/com/tangem/tap/data/DefaultTangemPayStorage.kt +++ b/app/src/main/java/com/tangem/tap/data/DefaultTangemPayStorage.kt @@ -249,6 +249,15 @@ internal class DefaultTangemPayStorage @Inject constructor( } } + override suspend fun storeIsTangemPayDeactivated(userWalletId: UserWalletId) { + appPreferencesStore.store(PreferencesKeys.getTangemPayDeactivatedKey(userWalletId), true) + } + + override suspend fun isTangemPayDeactivated(userWalletId: UserWalletId): Boolean { + val key = PreferencesKeys.getTangemPayDeactivatedKey(userWalletId) + return appPreferencesStore.getSyncOrNull(key) == true + } + override suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String) { withContext(dispatcherProvider.io) { secureStorage.delete(createAuthTokensKey(customerWalletAddress)) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index 738de04ec7..be52c28b41 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -209,6 +209,9 @@ object PreferencesKeys { fun getTangemPayHideOnboardingKey(userWalletId: UserWalletId) = booleanPreferencesKey("tangem_pay_hide_onboarding_key_$userWalletId") + fun getTangemPayDeactivatedKey(userWalletId: UserWalletId) = + booleanPreferencesKey("tangem_pay_deactivated_$userWalletId") + // endregion } diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/visa/TangemPayStorage.kt b/core/datasource/src/main/java/com/tangem/datasource/local/visa/TangemPayStorage.kt index c7540693bc..e6b2a37527 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/visa/TangemPayStorage.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/visa/TangemPayStorage.kt @@ -56,4 +56,7 @@ interface TangemPayStorage { suspend fun getTangemPayEligibility(): Set suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String) + + suspend fun storeIsTangemPayDeactivated(userWalletId: UserWalletId) + suspend fun isTangemPayDeactivated(userWalletId: UserWalletId): Boolean } \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayEligibilityManager.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayEligibilityManager.kt index a54b75b418..fb9bd81fda 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayEligibilityManager.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayEligibilityManager.kt @@ -84,15 +84,20 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor( } private suspend fun getPossibleWalletsForTangemPay(entryPoint: TangemPayEntryPoint?): List { + val wallets = userWalletsListRepository.userWallets.value ?: return emptyList() + + val candidates = wallets.filter { wallet -> + wallet.isMultiCurrency && !wallet.isLocked && wallet.isCompatible() && + !onboardingRepository.isTangemPayDeactivated(wallet.walletId) + } + + if (candidates.isEmpty()) return emptyList() + if (!checkTangemPayEligibility(entryPoint = entryPoint)) { return emptyList() } - val wallets = userWalletsListRepository.userWallets.value ?: return emptyList() - - return wallets.filter { wallet -> - wallet.isMultiCurrency && !wallet.isLocked && wallet.isCompatible() - } + return candidates } private fun UserWallet.isCompatible(): Boolean = when (this) { diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/flow/DefaultPaymentAccountStatusFetcher.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/flow/DefaultPaymentAccountStatusFetcher.kt index 566e56dfbd..cdd78706de 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/flow/DefaultPaymentAccountStatusFetcher.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/flow/DefaultPaymentAccountStatusFetcher.kt @@ -52,6 +52,16 @@ 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 -> @@ -236,6 +246,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor( return when (this) { is VisaApiError.RefreshTokenExpired -> PaymentAccountStatusValue.Error.NotSynced is VisaApiError.NotPaeraCustomer -> PaymentAccountStatusValue.NotCreated + is VisaApiError.Deactivated -> PaymentAccountStatusValue.NotCreated else -> PaymentAccountStatusValue.Error.Unavailable } } diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt index 6880483883..2419f48d7a 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt @@ -1,6 +1,8 @@ package com.tangem.data.pay.repository import arrow.core.Either +import arrow.core.flatMap +import arrow.core.left import arrow.core.right import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.datasource.api.pay.TangemPayApi @@ -88,7 +90,22 @@ internal class DefaultOnboardingRepository @Inject constructor( override suspend fun getCustomerInfo(userWalletId: UserWalletId): Either { return requestHelper.performRequest(userWalletId) { authHeader -> tangemPayApi.getCustomerMe(authHeader) } - .map { response -> getCustomerInfo(userWalletId = userWalletId, response = response.result) } + .flatMap { response -> + val result = response.result + val status = result?.productInstance?.status + val isDeactivated = status == CustomerMeResponse.ProductInstance.Status.DEACTIVATED + val isBlocked = result?.state?.let { CustomerInfo.State.fromString(it) } == CustomerInfo.State.BLOCKED + if (isDeactivated || isBlocked) { + tangemPayStorage.storeIsTangemPayDeactivated(userWalletId) + VisaApiError.Deactivated.left() + } else { + getCustomerInfo(userWalletId = userWalletId, response = result).right() + } + } + } + + override suspend fun isTangemPayDeactivated(userWalletId: UserWalletId): Boolean { + return tangemPayStorage.isTangemPayDeactivated(userWalletId) } override suspend fun clearOrderId(userWalletId: UserWalletId) { diff --git a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/error/VisaError.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/error/VisaError.kt index be321d6b33..1ebfcecd9a 100644 --- a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/error/VisaError.kt +++ b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/error/VisaError.kt @@ -57,6 +57,7 @@ sealed class VisaApiError( data object CustomerIsBlocked : VisaApiError(104110210) data object UnknownWithoutCode : VisaApiError(104110999) data class Unknown(override val errorCode: Int) : VisaApiError(errorCode) + data object Deactivated : VisaApiError(0) fun isUnknown() = this is UnknownWithoutCode || this is Unknown diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt index 11ef7e59c7..0dbd00d110 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt +++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt @@ -23,6 +23,24 @@ data class CustomerInfo( val kycStatus: KycStatus, val cardInfo: CardInfo?, ) { + enum class State { + NEW, + ACTIVE, + BLOCKED, + IN_PROGRESS, + UNDEFINED, + ; + + companion object { + fun fromString(value: String) = when (value.uppercase()) { + "NEW" -> NEW + "ACTIVE" -> ACTIVE + "BLOCKED" -> BLOCKED + "IN_PROGRESS" -> IN_PROGRESS + else -> UNDEFINED + } + } + } data class ProductInstance( val id: String, diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayCustomerInfoError.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayCustomerInfoError.kt index e2086cfed3..07e4dc7bb8 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayCustomerInfoError.kt +++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayCustomerInfoError.kt @@ -6,4 +6,5 @@ sealed interface TangemPayCustomerInfoError { data object RefreshNeededError : TangemPayCustomerInfoError data object UnknownError : TangemPayCustomerInfoError data object ExposedDeviceError : TangemPayCustomerInfoError + data object DeactivatedError : TangemPayCustomerInfoError } \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/repository/OnboardingRepository.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/repository/OnboardingRepository.kt index d826ae182d..8fcab526ee 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/repository/OnboardingRepository.kt +++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/repository/OnboardingRepository.kt @@ -35,4 +35,6 @@ interface OnboardingRepository { suspend fun setHideMainOnboardingBanner(userWalletId: UserWalletId) suspend fun disableTangemPay(userWalletId: UserWalletId): Either + + suspend fun isTangemPayDeactivated(userWalletId: UserWalletId): Boolean } \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/usecase/TangemPayMainScreenCustomerInfoUseCase.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/usecase/TangemPayMainScreenCustomerInfoUseCase.kt index 73a46626d2..04780f63c1 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/usecase/TangemPayMainScreenCustomerInfoUseCase.kt +++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/usecase/TangemPayMainScreenCustomerInfoUseCase.kt @@ -29,6 +29,11 @@ class TangemPayMainScreenCustomerInfoUseCase( suspend fun fetch(userWalletId: UserWalletId) { logger.i("fetch: ${userWalletId.stringValue}") + if (onboardingRepository.isTangemPayDeactivated(userWalletId)) { + updateState(userWalletId, MainCustomerInfoContentState.Empty.right()) + return + } + if (deviceSecurity.isSecurityExposed()) { logger.i("fetch security info: rooted: ${deviceSecurity.isRooted}") logger.i("fetch security info: xposed: ${deviceSecurity.isXposed}") @@ -57,8 +62,11 @@ class TangemPayMainScreenCustomerInfoUseCase( } val result = proceedWithPaeraCustomerResult(userWalletId) - .map(MainCustomerInfoContentState::Content) - updateState(userWalletId, result) + if (result.leftOrNull() is TangemPayCustomerInfoError.DeactivatedError) { + updateState(userWalletId, MainCustomerInfoContentState.Empty.right()) + return + } + updateState(userWalletId, result.map(MainCustomerInfoContentState::Content)) } else { // if there's no tangem pay, check eligibility and show onboarding banner showOnboardingBannerIfEligible(userWalletId) @@ -164,6 +172,7 @@ class TangemPayMainScreenCustomerInfoUseCase( return when (this) { is VisaApiError.RefreshTokenExpired -> TangemPayCustomerInfoError.RefreshNeededError is VisaApiError.NotPaeraCustomer -> TangemPayCustomerInfoError.UnknownError + is VisaApiError.Deactivated -> TangemPayCustomerInfoError.DeactivatedError else -> TangemPayCustomerInfoError.UnavailableError } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TangemPayMainSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TangemPayMainSubscriber.kt index cc818aa976..a800e1a2a7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TangemPayMainSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TangemPayMainSubscriber.kt @@ -65,6 +65,11 @@ internal class TangemPayMainSubscriber @AssistedInject constructor( TangemPayCustomerInfoError.ExposedDeviceError -> { stateController.update(TangemPayExposedDeviceTransformer(userWalletId)) } + TangemPayCustomerInfoError.DeactivatedError -> { + stateController.update( + transformer = TangemPayHiddenStateTransformer(userWalletId), + ) + } TangemPayCustomerInfoError.UnknownError -> { // hide TangemPay block TangemLogger.e("Failed when loading main screen TangemPay info: $tangemPayError")