From c5d0ba8f586b874f4bdc46fb408f001f35fc70f9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 29 Apr 2026 03:21:00 -0700 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/datasource/di/MoshiModule.kt | 1 + .../entity/PaymentAccountStatusValueDM.kt | 6 + core/res/src/main/res/values/strings.xml | 3 + data/visa/build.gradle.kts | 9 ++ .../PaymentAccountStatusValueDMConverter.kt | 7 + .../DefaultPaymentAccountStatusFetcher.kt | 42 ++--- .../repository/DefaultOnboardingRepository.kt | 40 +++-- ...aymentAccountStatusValueDMConverterTest.kt | 147 ++++++++++++++++++ .../usecase/IsAccountsModeEnabledUseCase.kt | 1 + .../IsAccountsModeEnabledUseCaseTest.kt | 30 ++++ .../account/PaymentAccountStatusValue.kt | 14 ++ .../domain/pay/TangemPayDetailsConfig.kt | 1 + .../com/tangem/domain/visa/error/VisaError.kt | 1 - .../tangem/domain/pay/model/CustomerInfo.kt | 20 ++- .../pay/model/TangemPayCustomerInfoError.kt | 1 - .../TangemPayMainScreenCustomerInfoUseCase.kt | 5 - .../entity/TangemPayDetailsStateFactory.kt | 116 ++++++++------ .../tangempay/entity/TangemPayDetailsUM.kt | 5 +- .../tangempay/model/TangemPayDetailsModel.kt | 15 +- .../tangempay/ui/TangemPayDetailsScreen.kt | 82 ++++++---- .../domain/GetMultiWalletWarningsFactory.kt | 1 + .../domain/GetWalletNotificationsFactory.kt | 1 + .../TangemPayUpdateInfoStateTransformer.kt | 1 + .../converter/TangemPayMainBlockConverter.kt | 27 ++++ .../subscribers/TangemPayMainSubscriber.kt | 5 - 25 files changed, 448 insertions(+), 133 deletions(-) create mode 100644 data/visa/src/test/kotlin/com/tangem/data/pay/converter/PaymentAccountStatusValueDMConverterTest.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt index 53e728d17c..3dd04dc106 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt @@ -53,6 +53,7 @@ class MoshiModule { .withSubtype(PaymentAccountStatusValueDM.UnderReview::class.java, "kyc_status") .withSubtype(PaymentAccountStatusValueDM.IssuingCard::class.java, "issuing_card") .withSubtype(PaymentAccountStatusValueDM.ActiveCard::class.java, "active_card") + .withSubtype(PaymentAccountStatusValueDM.DeactivatedAccount::class.java, "deactivated_account") .withSubtype(PaymentAccountStatusValueDM.CardIssueFailed::class.java, "card_issue_failed"), ) .add( diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/visa/entity/PaymentAccountStatusValueDM.kt b/core/datasource/src/main/java/com/tangem/datasource/local/visa/entity/PaymentAccountStatusValueDM.kt index fb91718338..37b8f3cee0 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/visa/entity/PaymentAccountStatusValueDM.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/visa/entity/PaymentAccountStatusValueDM.kt @@ -56,6 +56,12 @@ sealed interface PaymentAccountStatusValueDM { @Json(name = "customer_id") val customerId: String, ) : PaymentAccountStatusValueDM + @NameLabel("deactivated_account") + data class DeactivatedAccount( + @Json(name = "deactivated_account") val marker: Boolean = true, + @Json(name = "fiat_balance") val fiatBalance: FiatBalanceDM, + ) : PaymentAccountStatusValueDM + @JsonClass(generateAdapter = true) data class FiatBalanceDM( @Json(name = "available_balance") val availableBalance: BigDecimal, diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 5b19f4c6fd..1e8b04dd84 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1568,6 +1568,8 @@ Your card is unfrozen. Withdrawal Unable to use on rooted device + Your card was deactivated + This was done due to regulatory requirements. Anyway withdrawals are still available. Hide KYC from main screen Add funds Top-up options @@ -1632,6 +1634,7 @@ Unfortunately, we couldn\'t verify your identity KYC rejected KYC in progress + Card deactivated View Status KYC in progress for Tangem Pay Documents are usually verified automatically within 5 minutes. In rare cases, if manual review is required, it may take up to 48 hours. diff --git a/data/visa/build.gradle.kts b/data/visa/build.gradle.kts index 80f1139140..88d718323d 100644 --- a/data/visa/build.gradle.kts +++ b/data/visa/build.gradle.kts @@ -12,6 +12,10 @@ android { namespace = "com.tangem.data.visa" } +tasks.withType().configureEach { + useJUnitPlatform() +} + dependencies { /** Project - Data */ @@ -68,4 +72,9 @@ dependencies { /** DI */ implementation(deps.hilt.android) kapt(deps.hilt.kapt) + + /** Test */ + testRuntimeOnly(deps.test.junit5.engine) + testImplementation(projects.common.test) + testImplementation(projects.test.core) } \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/converter/PaymentAccountStatusValueDMConverter.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/converter/PaymentAccountStatusValueDMConverter.kt index 94006aaf9c..92c24a5b12 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/converter/PaymentAccountStatusValueDMConverter.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/converter/PaymentAccountStatusValueDMConverter.kt @@ -52,6 +52,9 @@ internal object PaymentAccountStatusValueDMConverter : customerId = value.customerId, ) is PaymentAccountStatusValue.Empty -> PaymentAccountStatusValueDM.Empty() + is PaymentAccountStatusValue.Deactivated -> PaymentAccountStatusValueDM.DeactivatedAccount( + fiatBalance = value.fiatBalance.toDM(), + ) // Transient statuses are not persisted is PaymentAccountStatusValue.Loading, is PaymentAccountStatusValue.Error.ExposedDevice, @@ -101,6 +104,10 @@ internal object PaymentAccountStatusValueDMConverter : kycStatus = value.kycStatus, customerId = value.customerId, ) + is PaymentAccountStatusValueDM.DeactivatedAccount -> PaymentAccountStatusValue.Deactivated( + source = StatusSource.CACHE, + fiatBalance = value.fiatBalance.toDomain(), + ) null -> PaymentAccountStatusValue.Error.Unavailable } } 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 30b61c32ef..c55ac9cb0c 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 @@ -48,16 +48,6 @@ 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}") @@ -254,20 +244,31 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor( private fun CustomerInfo.mapToPaymentAccountStatus(): PaymentAccountStatusValue { val cardInfo = this.cardInfo val productInstance = this.productInstance - return if (kycStatus != KycStatus.APPROVED && !customerId.isNullOrEmpty()) { - PaymentAccountStatusValue.UnderReview( - source = StatusSource.ACTUAL, - kycStatus = kycStatus, - customerId = requireNotNull(customerId) { "CustomerId must not be null" }, - ) - } else if (cardInfo != null && productInstance != null && !customerId.isNullOrEmpty()) { - convertToContentState( + + val isDeactivated = productInstance?.status == CustomerInfo.ProductInstance.Status.DEACTIVATED + val isFormer = state == CustomerInfo.State.FORMER + val fiatBalance = fiatBalance + + return when { + kycStatus != KycStatus.APPROVED && !customerId.isNullOrEmpty() -> { + PaymentAccountStatusValue.UnderReview( + source = StatusSource.ACTUAL, + kycStatus = kycStatus, + customerId = requireNotNull(customerId) { "CustomerId must not be null" }, + ) + } + fiatBalance != null && (isDeactivated || isFormer) -> { + PaymentAccountStatusValue.Deactivated( + source = StatusSource.ACTUAL, + fiatBalance = fiatBalance, + ) + } + cardInfo != null && productInstance != null && !customerId.isNullOrEmpty() -> convertToContentState( productInstance = productInstance, cardInfo = cardInfo, customerId = requireNotNull(customerId) { "CustomerId must not be null" }, ) - } else { - PaymentAccountStatusValue.IssuingCard(source = StatusSource.ACTUAL) + else -> PaymentAccountStatusValue.IssuingCard(source = StatusSource.ACTUAL) } } @@ -306,7 +307,6 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor( return when (this) { is VisaApiError.RefreshTokenExpired -> PaymentAccountStatusValue.Error.NotSynced is VisaApiError.NotPaeraCustomer -> constructNotCreatedOrEmptyStatus(userWalletId) - is VisaApiError.Deactivated -> constructNotCreatedOrEmptyStatus(userWalletId) 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 e1f741be61..8eff052a75 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 @@ -2,7 +2,6 @@ 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.data.pay.store.PaymentAccountStatusesStore @@ -11,6 +10,7 @@ import com.tangem.datasource.api.pay.models.request.DeeplinkValidityRequest import com.tangem.datasource.api.pay.models.request.OrderRequest import com.tangem.datasource.api.pay.models.request.SetTangemPayEnabledRequest import com.tangem.datasource.api.pay.models.response.CustomerMeResponse +import com.tangem.datasource.api.pay.models.response.FiatBalance import com.tangem.datasource.api.pay.models.response.OrderResponse import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore import com.tangem.datasource.local.visa.TangemPayStorage @@ -101,10 +101,8 @@ internal class DefaultOnboardingRepository @Inject constructor( val isFormer = result?.state?.let { CustomerInfo.State.fromString(it) } == CustomerInfo.State.FORMER if (isDeactivated || isFormer) { tangemPayStorage.storeIsTangemPayDeactivated(userWalletId) - VisaApiError.Deactivated.left() - } else { - getCustomerInfo(userWalletId = userWalletId, response = result).right() } + getCustomerInfo(userWalletId = userWalletId, response = result).right() } } @@ -180,10 +178,7 @@ internal class DefaultOnboardingRepository @Inject constructor( currencyCode = fiatBalance.currency, depositAddress = response.depositAddress, isPinSet = response.card?.isPinSet == true, - fiatBalance = PaymentAccountStatusValue.FiatBalance( - availableBalance = fiatBalance.availableBalance, - currency = fiatBalance.currency, - ), + fiatBalance = fiatBalance.toDomain(), cryptoBalance = PaymentAccountStatusValue.CryptoBalance( id = cryptoBalance.id, chainId = cryptoBalance.chainId.toLong(), @@ -202,13 +197,20 @@ internal class DefaultOnboardingRepository @Inject constructor( } cardFrozenStateStore.store(key = instance.cardId, value = cardFrozenState) - ProductInstance(id = instance.id, cardId = instance.cardId, frozenState = cardFrozenState) + ProductInstance( + id = instance.id, + cardId = instance.cardId, + frozenState = cardFrozenState, + status = instance.status.toDomain(), + ) } return CustomerInfo( customerId = response?.id, productInstance = productInstance, kycStatus = kycStatus, cardInfo = cardInfo, + state = response?.state?.let { CustomerInfo.State.fromString(it) } ?: CustomerInfo.State.UNDEFINED, + fiatBalance = fiatBalance?.toDomain(), ).also { lastFetchedCustomerInfoMap[userWalletId] = it } @@ -290,4 +292,24 @@ internal class DefaultOnboardingRepository @Inject constructor( setHideMainOnboardingBanner(userWalletId) } } +} + +private fun FiatBalance.toDomain() = PaymentAccountStatusValue.FiatBalance( + availableBalance = availableBalance, + currency = currency, +) + +private fun CustomerMeResponse.ProductInstance.Status.toDomain() = when (this) { + CustomerMeResponse.ProductInstance.Status.NEW -> ProductInstance.Status.NEW + CustomerMeResponse.ProductInstance.Status.READY_FOR_MANUFACTURING -> ProductInstance.Status.READY_FOR_MANUFACTURING + CustomerMeResponse.ProductInstance.Status.MANUFACTURING -> ProductInstance.Status.MANUFACTURING + CustomerMeResponse.ProductInstance.Status.SENT_TO_DELIVERY -> ProductInstance.Status.SENT_TO_DELIVERY + CustomerMeResponse.ProductInstance.Status.DELIVERED -> ProductInstance.Status.DELIVERED + CustomerMeResponse.ProductInstance.Status.ACTIVATING -> ProductInstance.Status.ACTIVATING + CustomerMeResponse.ProductInstance.Status.ACTIVE -> ProductInstance.Status.ACTIVE + CustomerMeResponse.ProductInstance.Status.BLOCKED -> ProductInstance.Status.BLOCKED + CustomerMeResponse.ProductInstance.Status.DEACTIVATING -> ProductInstance.Status.DEACTIVATING + CustomerMeResponse.ProductInstance.Status.DEACTIVATED -> ProductInstance.Status.DEACTIVATED + CustomerMeResponse.ProductInstance.Status.CANCELED -> ProductInstance.Status.CANCELED + CustomerMeResponse.ProductInstance.Status.UNKNOWN -> ProductInstance.Status.UNKNOWN } \ No newline at end of file diff --git a/data/visa/src/test/kotlin/com/tangem/data/pay/converter/PaymentAccountStatusValueDMConverterTest.kt b/data/visa/src/test/kotlin/com/tangem/data/pay/converter/PaymentAccountStatusValueDMConverterTest.kt new file mode 100644 index 0000000000..37ac01a0fc --- /dev/null +++ b/data/visa/src/test/kotlin/com/tangem/data/pay/converter/PaymentAccountStatusValueDMConverterTest.kt @@ -0,0 +1,147 @@ +package com.tangem.data.pay.converter + +import com.google.common.truth.Truth.assertThat +import com.tangem.datasource.local.visa.entity.PaymentAccountStatusValueDM +import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.account.PaymentAccountStatusValue +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import java.math.BigDecimal + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +internal class PaymentAccountStatusValueDMConverterTest { + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class Convert { + + @Test + fun `GIVEN domain Empty WHEN convert THEN returns DM Empty`() { + // GIVEN + val domain = PaymentAccountStatusValue.Empty + + // WHEN + val result = PaymentAccountStatusValueDMConverter.convert(domain) + + // THEN + assertThat(result).isInstanceOf(PaymentAccountStatusValueDM.Empty::class.java) + } + + @Test + fun `GIVEN domain Deactivated with balance WHEN convert THEN returns DM DeactivatedAccount with same balance`() { + // GIVEN + val domain = PaymentAccountStatusValue.Deactivated( + source = StatusSource.ACTUAL, + fiatBalance = PaymentAccountStatusValue.FiatBalance( + availableBalance = BigDecimal("100"), + currency = "USD", + ) + ) + + // WHEN + val result = PaymentAccountStatusValueDMConverter.convert(domain) + + // THEN + assertThat(result).isInstanceOf(PaymentAccountStatusValueDM.DeactivatedAccount::class.java) + val dm = result as PaymentAccountStatusValueDM.DeactivatedAccount + assertThat(dm.fiatBalance.availableBalance).isEqualTo(BigDecimal("100")) + assertThat(dm.fiatBalance.currency).isEqualTo("USD") + } + + @Test + fun `GIVEN domain Loading WHEN convert THEN returns null (transient, not persisted)`() { + // GIVEN + val domain = PaymentAccountStatusValue.Loading + + // WHEN + val result = PaymentAccountStatusValueDMConverter.convert(domain) + + // THEN + assertThat(result).isNull() + } + + @Test + fun `GIVEN domain Error Unavailable WHEN convert THEN returns null (transient, not persisted)`() { + // GIVEN + val domain = PaymentAccountStatusValue.Error.Unavailable + + // WHEN + val result = PaymentAccountStatusValueDMConverter.convert(domain) + + // THEN + assertThat(result).isNull() + } + + @Test + fun `GIVEN domain NotCreated WHEN convert THEN returns DM NotCreated`() { + // GIVEN + val domain = PaymentAccountStatusValue.NotCreated + + // WHEN + val result = PaymentAccountStatusValueDMConverter.convert(domain) + + // THEN + assertThat(result).isInstanceOf(PaymentAccountStatusValueDM.NotCreated::class.java) + } + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class ConvertBack { + + @Test + fun `GIVEN DM Empty WHEN convertBack THEN returns domain Empty`() { + // GIVEN + val dm = PaymentAccountStatusValueDM.Empty() + + // WHEN + val result = PaymentAccountStatusValueDMConverter.convertBack(dm) + + // THEN + assertThat(result).isEqualTo(PaymentAccountStatusValue.Empty) + } + + @Test + fun `GIVEN DM DeactivatedAccount WHEN convertBack THEN returns domain Deactivated with CACHE source`() { + // GIVEN + val dm = PaymentAccountStatusValueDM.DeactivatedAccount( + fiatBalance = PaymentAccountStatusValueDM.FiatBalanceDM( + availableBalance = BigDecimal("200"), + currency = "EUR", + ) + ) + + // WHEN + val result = PaymentAccountStatusValueDMConverter.convertBack(dm) + + // THEN + assertThat(result).isInstanceOf(PaymentAccountStatusValue.Deactivated::class.java) + val deactivated = result as PaymentAccountStatusValue.Deactivated + assertThat(deactivated.source).isEqualTo(StatusSource.CACHE) + assertThat(deactivated.fiatBalance.availableBalance).isEqualTo(BigDecimal("200")) + assertThat(deactivated.fiatBalance.currency).isEqualTo("EUR") + } + + @Test + fun `GIVEN null DM WHEN convertBack THEN returns Error Unavailable`() { + // GIVEN / WHEN + val result = PaymentAccountStatusValueDMConverter.convertBack(null) + + // THEN + assertThat(result).isEqualTo(PaymentAccountStatusValue.Error.Unavailable) + } + + @Test + fun `GIVEN DM NotCreated WHEN convertBack THEN returns domain NotCreated`() { + // GIVEN + val dm = PaymentAccountStatusValueDM.NotCreated() + + // WHEN + val result = PaymentAccountStatusValueDMConverter.convertBack(dm) + + // THEN + assertThat(result).isEqualTo(PaymentAccountStatusValue.NotCreated) + } + } +} \ No newline at end of file diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/usecase/IsAccountsModeEnabledUseCase.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/usecase/IsAccountsModeEnabledUseCase.kt index ae529e245c..7357b3d7cf 100644 --- a/domain/account/status/src/main/java/com/tangem/domain/account/status/usecase/IsAccountsModeEnabledUseCase.kt +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/usecase/IsAccountsModeEnabledUseCase.kt @@ -56,6 +56,7 @@ class IsAccountsModeEnabledUseCase( is PaymentAccountStatusValue.Loading, is PaymentAccountStatusValue.Locked, is PaymentAccountStatusValue.UnderReview, + is PaymentAccountStatusValue.Deactivated, -> true } } diff --git a/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/usecase/IsAccountsModeEnabledUseCaseTest.kt b/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/usecase/IsAccountsModeEnabledUseCaseTest.kt index 02aa942198..0366176714 100644 --- a/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/usecase/IsAccountsModeEnabledUseCaseTest.kt +++ b/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/usecase/IsAccountsModeEnabledUseCaseTest.kt @@ -150,6 +150,21 @@ class IsAccountsModeEnabledUseCaseTest { Truth.assertThat(actual).isTrue() } + @Test + fun `GIVEN payment account is Deactivated WHEN invoke THEN returns true`() = runTest { + // GIVEN + val statusList = createAccountStatusList( + statuses = listOf(mockCryptoPortfolio(), mockPayment(mockk())), + ) + every { multiAccountStatusListSupplier.invoke() } returns flowOf(listOf(statusList)) + + // WHEN + val actual = useCase.invoke().first() + + // THEN + Truth.assertThat(actual).isTrue() + } + @Test fun `returns true when multiple status lists and one has two crypto portfolios`() = runTest { val statusList1 = createAccountStatusList(statuses = listOf(mockCryptoPortfolio())) @@ -270,6 +285,21 @@ class IsAccountsModeEnabledUseCaseTest { Truth.assertThat(actual).isTrue() } + @Test + fun `GIVEN payment account is Deactivated WHEN invokeSync THEN returns true`() = runTest { + // GIVEN + val statusList = createAccountStatusList( + statuses = listOf(mockCryptoPortfolio(), mockPayment(mockk())), + ) + coEvery { multiAccountStatusListSupplier.getSyncOrNull(Unit, any()) } returns listOf(statusList) + + // WHEN + val actual = useCase.invokeSync() + + // THEN + Truth.assertThat(actual).isTrue() + } + @Test fun `returns true when multiple status lists and one has loaded payment`() = runTest { val statusList1 = createAccountStatusList(statuses = listOf(mockCryptoPortfolio())) diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/PaymentAccountStatusValue.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/PaymentAccountStatusValue.kt index 542733a235..058d78d061 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/account/PaymentAccountStatusValue.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/PaymentAccountStatusValue.kt @@ -27,6 +27,7 @@ sealed class PaymentAccountStatusValue { is Loading -> TotalFiatBalance.Loading is Locked -> TotalFiatBalance.Loaded(amount = fiatBalance.availableBalance, source = source) is Loaded -> TotalFiatBalance.Loaded(amount = fiatBalance.availableBalance, source = source) + is Deactivated -> TotalFiatBalance.Loaded(amount = fiatBalance.availableBalance, source = source) } /** @@ -40,6 +41,7 @@ sealed class PaymentAccountStatusValue { is Loaded -> copy(source = source) is Locked -> copy(source = source) is UnderReview -> copy(source = source) + is Deactivated -> copy(source = source) is Loading, is Empty, is NotCreated, @@ -88,6 +90,18 @@ sealed class PaymentAccountStatusValue { @Serializable data class IssuingCard(override val source: StatusSource) : PaymentAccountStatusValue() + /** + * Represents a state where the account is deactivated. + * + * @property source The source of the status information. + * @property fiatBalance The fiat balance details. + */ + @Serializable + data class Deactivated( + override val source: StatusSource, + val fiatBalance: FiatBalance, + ) : PaymentAccountStatusValue() + /** * Represents a state where the payment account is locked. * diff --git a/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt index e2424a061a..2906021583 100644 --- a/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt +++ b/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt @@ -11,4 +11,5 @@ data class TangemPayDetailsConfig( val cardFrozenState: TangemPayCardFrozenState, val cardNumberEnd: String, val chainId: Int, + val isTangemPayDeactivated: Boolean, ) \ No newline at end of file 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 1ebfcecd9a..be321d6b33 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,7 +57,6 @@ 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 a35e9adaea..dac433e459 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,8 @@ data class CustomerInfo( val productInstance: ProductInstance?, val kycStatus: KycStatus, val cardInfo: CardInfo?, + val state: State, + val fiatBalance: PaymentAccountStatusValue.FiatBalance?, ) { enum class State { NEW, @@ -49,7 +51,23 @@ data class CustomerInfo( val id: String, val cardId: String, val frozenState: TangemPayCardFrozenState, - ) + val status: Status, + ) { + enum class Status { + NEW, + READY_FOR_MANUFACTURING, + MANUFACTURING, + SENT_TO_DELIVERY, + DELIVERED, + ACTIVATING, + ACTIVE, + BLOCKED, + DEACTIVATING, + DEACTIVATED, + CANCELED, + UNKNOWN, + } + } data class CardInfo( val lastFourDigits: 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 07e4dc7bb8..e2086cfed3 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,5 +6,4 @@ 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/usecase/TangemPayMainScreenCustomerInfoUseCase.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/usecase/TangemPayMainScreenCustomerInfoUseCase.kt index 04780f63c1..2e62f7190e 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 @@ -62,10 +62,6 @@ class TangemPayMainScreenCustomerInfoUseCase( } val result = proceedWithPaeraCustomerResult(userWalletId) - 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 @@ -172,7 +168,6 @@ 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/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt index 8fb38c33bc..eba5584f1c 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt @@ -5,6 +5,7 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.themedColor import com.tangem.core.ui.res.TangemTheme @@ -12,6 +13,7 @@ import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.model.transformers.TangemPayCardFrozenStateConverter import com.tangem.features.tangempay.utils.TangemPayDetailIntents +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList @@ -23,51 +25,13 @@ internal class TangemPayDetailsStateFactory( private val cardFrozenState: TangemPayCardFrozenState, private val converter: TangemPayCardFrozenStateConverter, ) { - @Suppress("LongMethod") - fun getInitialState(): TangemPayDetailsUM { - val cardFrozenStateItem = when (cardFrozenState) { - is TangemPayCardFrozenState.Pending -> null - is TangemPayCardFrozenState.Frozen -> TangemPayDetailsTopBarMenuItem( - type = TangemPayDetailsTopBarMenuItemType.UnfreezeCard, - dropdownItem = TangemDropdownMenuItem( - title = resourceReference(R.string.tangempay_card_details_unfreeze_card), - textColor = themedColor { TangemTheme.colors.text.primary1 }, - onClick = intents::onClickUnfreezeCard, - ), - ) - is TangemPayCardFrozenState.Unfrozen -> TangemPayDetailsTopBarMenuItem( - type = TangemPayDetailsTopBarMenuItemType.FreezeCard, - dropdownItem = TangemDropdownMenuItem( - title = resourceReference(R.string.tangempay_card_details_freeze_card), - textColor = themedColor { TangemTheme.colors.text.primary1 }, - onClick = intents::onClickFreezeCard, - ), - ) - } + fun getInitialState(isTangemPayDeactivated: Boolean): TangemPayDetailsUM { return TangemPayDetailsUM( topBarConfig = TangemPayDetailsTopBarConfig( onBackClick = onBack, onOpenMenu = onOpenMenu, - items = listOfNotNull( - TangemPayDetailsTopBarMenuItem( - type = TangemPayDetailsTopBarMenuItemType.ChangePin, - dropdownItem = TangemDropdownMenuItem( - title = resourceReference(R.string.tangempay_card_details_pin_code), - textColor = themedColor { TangemTheme.colors.text.primary1 }, - onClick = intents::onClickPinCode, - ), - ), - TangemPayDetailsTopBarMenuItem( - type = TangemPayDetailsTopBarMenuItemType.TermsAndLimits, - dropdownItem = TangemDropdownMenuItem( - title = resourceReference(R.string.tangem_pay_terms_limits), - textColor = themedColor { TangemTheme.colors.text.primary1 }, - onClick = intents::onClickTermsAndLimits, - ), - ), - cardFrozenStateItem, - ).toPersistentList(), + items = getTopBarMenuItems().takeIf { !isTangemPayDeactivated }, ), pullToRefreshConfig = PullToRefreshConfig( isRefreshing = false, @@ -92,17 +56,67 @@ internal class TangemPayDetailsStateFactory( addToWalletBlockState = null, isBalanceHidden = false, addFundsEnabled = true, - cardFrozenState = converter.convert(cardFrozenState), - betaNotificationConfig = NotificationConfig( - title = resourceReference(R.string.tangem_pay_beta_notification_title), - subtitle = resourceReference(R.string.tangem_pay_beta_notification_subtitle), - iconResId = R.drawable.img_visa_notification, - buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( - text = resourceReference(R.string.common_contact_support), - onClick = intents::onContactSupportClicked, - ), - iconSize = 36.dp, - ), + cardFrozenState = getCardFrozenState().takeIf { !isTangemPayDeactivated }, + betaNotificationConfig = getBetaNotificationConfig().takeIf { !isTangemPayDeactivated }, + accountDeactivatedNotificationConfig = NotificationConfig( + title = TextReference.Res(R.string.tangempay_account_deactivated_message_title), + subtitle = TextReference.Res(R.string.tangempay_account_deactivated_message_subtitle), + iconResId = R.drawable.img_attention_20, + ).takeIf { isTangemPayDeactivated }, ) } + + private fun getCardFrozenState() = converter.convert(cardFrozenState) + + private fun getBetaNotificationConfig() = NotificationConfig( + title = resourceReference(R.string.tangem_pay_beta_notification_title), + subtitle = resourceReference(R.string.tangem_pay_beta_notification_subtitle), + iconResId = R.drawable.img_visa_notification, + buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( + text = resourceReference(R.string.common_contact_support), + onClick = intents::onContactSupportClicked, + ), + iconSize = 36.dp, + ) + + private fun getTopBarMenuItems(): ImmutableList { + val cardFrozenStateItem = when (cardFrozenState) { + is TangemPayCardFrozenState.Pending -> null + is TangemPayCardFrozenState.Frozen -> TangemPayDetailsTopBarMenuItem( + type = TangemPayDetailsTopBarMenuItemType.UnfreezeCard, + dropdownItem = TangemDropdownMenuItem( + title = resourceReference(R.string.tangempay_card_details_unfreeze_card), + textColor = themedColor { TangemTheme.colors.text.primary1 }, + onClick = intents::onClickUnfreezeCard, + ), + ) + is TangemPayCardFrozenState.Unfrozen -> TangemPayDetailsTopBarMenuItem( + type = TangemPayDetailsTopBarMenuItemType.FreezeCard, + dropdownItem = TangemDropdownMenuItem( + title = resourceReference(R.string.tangempay_card_details_freeze_card), + textColor = themedColor { TangemTheme.colors.text.primary1 }, + onClick = intents::onClickFreezeCard, + ), + ) + } + return listOfNotNull( + TangemPayDetailsTopBarMenuItem( + type = TangemPayDetailsTopBarMenuItemType.ChangePin, + dropdownItem = TangemDropdownMenuItem( + title = resourceReference(R.string.tangempay_card_details_pin_code), + textColor = themedColor { TangemTheme.colors.text.primary1 }, + onClick = intents::onClickPinCode, + ), + ), + TangemPayDetailsTopBarMenuItem( + type = TangemPayDetailsTopBarMenuItemType.TermsAndLimits, + dropdownItem = TangemDropdownMenuItem( + title = resourceReference(R.string.tangem_pay_terms_limits), + textColor = themedColor { TangemTheme.colors.text.primary1 }, + onClick = intents::onClickTermsAndLimits, + ), + ), + cardFrozenStateItem, + ).toPersistentList() + } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt index f67ed6b7b0..000fd9d0b5 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt @@ -15,8 +15,9 @@ internal data class TangemPayDetailsUM( val addToWalletBlockState: AddToWalletBlockState?, val isBalanceHidden: Boolean, val addFundsEnabled: Boolean, - val cardFrozenState: CardFrozenState, - val betaNotificationConfig: NotificationConfig, + val cardFrozenState: CardFrozenState?, + val betaNotificationConfig: NotificationConfig?, + val accountDeactivatedNotificationConfig: NotificationConfig?, ) internal data class TangemPayCardDetailsUM( diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt index 87c07ef8ba..1afea757d7 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt @@ -55,14 +55,11 @@ import com.tangem.features.tokendetails.ExpressTransactionsEventListener import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn +import com.tangem.utils.logging.TangemLogger import com.tangem.utils.transformer.update import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -import com.tangem.utils.logging.TangemLogger import javax.inject.Inject @Suppress("LongParameterList", "LargeClass") @@ -98,7 +95,7 @@ internal class TangemPayDetailsModel @Inject constructor( ) val uiState: StateFlow - field = MutableStateFlow(stateFactory.getInitialState()) + field = MutableStateFlow(stateFactory.getInitialState(params.config.isTangemPayDeactivated)) private val refreshStateJobHolder = JobHolder() private val fetchBalanceJobHolder = JobHolder() @@ -116,9 +113,11 @@ internal class TangemPayDetailsModel @Inject constructor( init { analytics.send(TangemPayAnalyticsEvents.MainScreenOpened()) handleBalanceHiding() - fetchAddToWalletBanner() fetchBalance() - subscribeToCardFrozenState() + if (!params.config.isTangemPayDeactivated) { + fetchAddToWalletBanner() + subscribeToCardFrozenState() + } } fun onResume() { diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt index 1e3a51c9a4..2ecb3a0fc6 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.util.fastForEach import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.buttons.HorizontalActionChips import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig @@ -86,13 +87,16 @@ internal fun TangemPayDetailsScreen( bottom = TangemTheme.dimens.spacing16 + bottomBarHeight, ), ) { - item(TangemPayCardDetailsUM::class.java) { - cardDetailsBlockComponent.CardDetailsBlockContent( - modifier = Modifier - .padding(horizontal = 16.dp) - .padding(top = 8.dp), - state = cardDetailsState, - ) + if (state.accountDeactivatedNotificationConfig == null) { + item(TangemPayCardDetailsUM::class.java) { + cardDetailsBlockComponent.CardDetailsBlockContent( + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(top = 8.dp), + state = cardDetailsState, + ) + SpacerH12() + } } when (state.cardFrozenState) { is CardFrozenState.Frozen -> item(CardFrozenState.Frozen::class.java) { @@ -100,11 +104,11 @@ internal fun TangemPayDetailsScreen( modifier = Modifier .animateItem() .padding(horizontal = 16.dp) - .padding(top = 12.dp) .fillMaxWidth(), text = stringResourceSafe(R.string.tangempay_card_details_unfreeze_card), onClick = state.cardFrozenState.onUnfreeze, ) + SpacerH12() } else -> Unit } @@ -115,9 +119,9 @@ internal fun TangemPayDetailsScreen( TangemPayAddToWalletBlock( state = state.addToWalletBlockState, modifier = Modifier - .padding(top = TangemTheme.dimens.spacing12) .padding(horizontal = TangemTheme.dimens.spacing16), ) + SpacerH12() }, ) } @@ -127,34 +131,52 @@ internal fun TangemPayDetailsScreen( TangemPayDetailsBalanceBlock( modifier = Modifier .padding(horizontal = TangemTheme.dimens.spacing16) - .padding(top = 12.dp) .fillMaxWidth(), state = state.balanceBlockState, isBalanceHidden = state.isBalanceHidden, ) + SpacerH12() }, ) - item( - key = "TANGEM_PAY_IS_IN_BETA", - content = { - TangemPayBetaBlock( - modifier = modifier - .padding(horizontal = TangemTheme.dimens.spacing16) - .padding(top = 12.dp) - .fillMaxWidth(), - config = state.betaNotificationConfig, - ) - }, - ) - with(expressTransactionsComponent) { - expressTransactionsContent( - state = expressState.transactionsToDisplay, - modifier = modifier - .padding(start = 16.dp, end = 16.dp, top = 12.dp) - .fillMaxWidth(), + if (state.accountDeactivatedNotificationConfig != null) { + item( + key = "DEACTIVATION_MESSAGE", + content = { + Notification( + modifier = modifier + .padding(horizontal = TangemTheme.dimens.spacing16) + .fillMaxWidth(), + config = state.accountDeactivatedNotificationConfig, + ) + SpacerH12() + }, ) } - with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryState) } + if (state.betaNotificationConfig != null) { + item( + key = "TANGEM_PAY_IS_IN_BETA", + content = { + TangemPayBetaBlock( + modifier = modifier + .padding(horizontal = TangemTheme.dimens.spacing16) + .fillMaxWidth(), + config = state.betaNotificationConfig, + ) + SpacerH12() + }, + ) + } + if (state.accountDeactivatedNotificationConfig == null) { + with(expressTransactionsComponent) { + expressTransactionsContent( + state = expressState.transactionsToDisplay, + modifier = modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + } + with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryState) } + } } } expressTransactionsBottomSheetState?.content() @@ -349,6 +371,7 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider null } notification?.let(::add) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetWalletNotificationsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetWalletNotificationsFactory.kt index c119fc6251..931e87bff3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetWalletNotificationsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetWalletNotificationsFactory.kt @@ -240,6 +240,7 @@ internal class GetWalletNotificationsFactory @Inject constructor( is PaymentAccountStatusValue.Locked, is PaymentAccountStatusValue.UnderReview, is PaymentAccountStatusValue.Empty, + is PaymentAccountStatusValue.Deactivated, -> null } notification?.let(::add) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayUpdateInfoStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayUpdateInfoStateTransformer.kt index 3e1615a6e2..91e7b3a263 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayUpdateInfoStateTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayUpdateInfoStateTransformer.kt @@ -80,6 +80,7 @@ internal class TangemPayUpdateInfoStateTransformer( cardFrozenState = cardFrozenState, cardNumberEnd = cardInfo.lastFourDigits, chainId = POLYGON_CHAIN_ID, + isTangemPayDeactivated = false, ), ) }, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TangemPayMainBlockConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TangemPayMainBlockConverter.kt index fef7fb08bb..dcad0972b5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TangemPayMainBlockConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TangemPayMainBlockConverter.kt @@ -56,6 +56,31 @@ internal class TangemPayMainBlockConverter( is PaymentAccountStatusValue.Empty -> TangemPayMainUM.Empty is PaymentAccountStatusValue.NotCreated -> TangemPayMainUM.Empty is PaymentAccountStatusValue.Loading -> TangemPayMainUM.Loading + is PaymentAccountStatusValue.Deactivated -> TangemPayMainUM.Content( + subtitle = TextReference.Res(R.string.tangempay_status_deactivated), + isBalanceFlickering = statusValue.source == StatusSource.CACHE, + balance = getBalanceText( + currencyCode = statusValue.fiatBalance.currency, + balance = statusValue.fiatBalance.availableBalance, + ), + balanceSubtitle = stringReference("USDC"), // TODO hardcode for now + shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE, + onClick = { + // Dummy config for deactivated account just to open details screen + tangemPayClickIntents.openDetails( + userWalletId = value.account.userWalletId, + config = TangemPayDetailsConfig( + customerId = "", + cardId = "", + isPinSet = false, + cardFrozenState = TangemPayCardFrozenState.Unfrozen, + cardNumberEnd = "", + chainId = POLYGON_CHAIN_ID, + isTangemPayDeactivated = true, + ), + ) + }, + ) is PaymentAccountStatusValue.Locked -> TangemPayMainUM.Content( subtitle = stringReference("*${statusValue.lastFourDigits}"), isBalanceFlickering = statusValue.source == StatusSource.CACHE, @@ -75,6 +100,7 @@ internal class TangemPayMainBlockConverter( cardFrozenState = TangemPayCardFrozenState.Frozen, cardNumberEnd = statusValue.lastFourDigits, chainId = POLYGON_CHAIN_ID, + isTangemPayDeactivated = false, ), ) }, @@ -98,6 +124,7 @@ internal class TangemPayMainBlockConverter( cardFrozenState = TangemPayCardFrozenState.Unfrozen, cardNumberEnd = statusValue.lastFourDigits, chainId = POLYGON_CHAIN_ID, + isTangemPayDeactivated = false, ), ) }, 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 a800e1a2a7..cc818aa976 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,11 +65,6 @@ 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")