Updated on 2026-08-14
This commit is contained in:
parent
6a7391fd73
commit
bfeea010d0
12 changed files with 92 additions and 8 deletions
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,4 +56,7 @@ interface TangemPayStorage {
|
|||
suspend fun getTangemPayEligibility(): Set<String>
|
||||
|
||||
suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String)
|
||||
|
||||
suspend fun storeIsTangemPayDeactivated(userWalletId: UserWalletId)
|
||||
suspend fun isTangemPayDeactivated(userWalletId: UserWalletId): Boolean
|
||||
}
|
||||
|
|
@ -84,15 +84,20 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun getPossibleWalletsForTangemPay(entryPoint: TangemPayEntryPoint?): List<UserWallet> {
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<VisaApiError, CustomerInfo> {
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@ sealed interface TangemPayCustomerInfoError {
|
|||
data object RefreshNeededError : TangemPayCustomerInfoError
|
||||
data object UnknownError : TangemPayCustomerInfoError
|
||||
data object ExposedDeviceError : TangemPayCustomerInfoError
|
||||
data object DeactivatedError : TangemPayCustomerInfoError
|
||||
}
|
||||
|
|
@ -35,4 +35,6 @@ interface OnboardingRepository {
|
|||
suspend fun setHideMainOnboardingBanner(userWalletId: UserWalletId)
|
||||
|
||||
suspend fun disableTangemPay(userWalletId: UserWalletId): Either<VisaApiError, Unit>
|
||||
|
||||
suspend fun isTangemPayDeactivated(userWalletId: UserWalletId): Boolean
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue