From 161477c16a489db2aa5d483366a831f67bf909c9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Oct 2025 12:41:59 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/data/DefaultTangemPayStorage.kt | 6 +- .../datasource/local/visa/TangemPayStorage.kt | 4 +- .../repository/DefaultOnboardingRepository.kt | 67 +++++++++++++------ .../tangem/domain/pay/model/CustomerInfo.kt | 6 +- .../tangem/domain/pay/model/OrderStatus.kt | 2 +- .../model/TangemPayOnboardingModel.kt | 2 +- .../wallet/child/wallet/model/WalletModel.kt | 10 +-- ...kt => TangemPayInitialStateTransformer.kt} | 47 +++---------- ...TangemPayIssueAvailableStateTransformer.kt | 12 ++++ .../TangemPayIssueProgressStateTransformer.kt | 10 +++ .../state/util/TangemPayStateCreator.kt | 31 +++++++++ 11 files changed, 125 insertions(+), 72 deletions(-) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/{TangemPayStateTransformer.kt => TangemPayInitialStateTransformer.kt} (50%) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayIssueAvailableStateTransformer.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayIssueProgressStateTransformer.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/util/TangemPayStateCreator.kt 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 58e979c7dc..ce88071554 100644 --- a/app/src/main/java/com/tangem/tap/data/DefaultTangemPayStorage.kt +++ b/app/src/main/java/com/tangem/tap/data/DefaultTangemPayStorage.kt @@ -67,7 +67,11 @@ internal class DefaultTangemPayStorage @Inject constructor( secureStorage.get(createOrderIdKey(customerWalletAddress))?.decodeToString(throwOnInvalidSequence = true) } - override suspend fun clear(customerWalletAddress: String) = withContext(dispatcherProvider.io) { + override suspend fun clearOrderId(customerWalletAddress: String) = withContext(dispatcherProvider.io) { + secureStorage.delete(createOrderIdKey(customerWalletAddress)) + } + + override suspend fun clearAll(customerWalletAddress: String) = withContext(dispatcherProvider.io) { secureStorage.delete(createKey(customerWalletAddress)) secureStorage.delete(createOrderIdKey(customerWalletAddress)) } 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 1ed58f2220..bf8bf7fa78 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 @@ -12,5 +12,7 @@ interface TangemPayStorage { suspend fun getOrderId(customerWalletAddress: String): String? - suspend fun clear(customerWalletAddress: String) + suspend fun clearOrderId(customerWalletAddress: String) + + suspend fun clearAll(customerWalletAddress: String) } \ No newline at end of file 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 b005177a40..d0d1af52ab 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 @@ -18,6 +18,7 @@ import kotlinx.coroutines.withContext import javax.inject.Inject private const val VALID_STATUS = "valid" +private const val APPROVED_KYC_STATUS = "APPROVED" private const val TAG = "TangemPay: OnboardingRepository" internal class DefaultOnboardingRepository @Inject constructor( @@ -47,13 +48,36 @@ internal class DefaultOnboardingRepository @Inject constructor( override suspend fun getMainScreenCustomerInfo(): Either { return requestHelper.runWithErrorLogs(TAG) { - val result = requestHelper.requestWithPersistedToken { authHeader -> - tangemPayApi.getCustomerMe(authHeader) - }.result + val customerWalletAddress = requestHelper.getCustomerWalletAddress() - val orderStatus = getOrderStatus().getOrNull() ?: error("Order status is null") + when (val orderId = tangemPayStorage.getOrderId(customerWalletAddress)) { + // If order id wasn't saved -> get customer info + null -> { + MainScreenCustomerInfo( + info = getCustomerInfoWithPersistedToken(), + orderStatus = OrderStatus.UNKNOWN, + ) + } + // If order id was saved -> check its status + else -> { + val orderStatus = getOrderStatus(orderId) + val customerInfo = when (orderStatus) { + // Kyc is passed and user waits for order creation -> no need to get customer info + OrderStatus.NEW, + OrderStatus.PROCESSING, + -> CustomerInfo(productInstance = null, isKycApproved = true, cardInfo = null) - MainScreenCustomerInfo(info = getCustomerInfo(result), orderStatus = orderStatus) + // Order was created/cancelled -> clear order id and get customer info + OrderStatus.UNKNOWN, + OrderStatus.COMPLETED, + OrderStatus.CANCELED, + -> getCustomerInfoWithPersistedToken().also { + tangemPayStorage.clearOrderId(customerWalletAddress) + } + } + MainScreenCustomerInfo(info = customerInfo, orderStatus = orderStatus) + } + } } } @@ -84,27 +108,28 @@ internal class DefaultOnboardingRepository @Inject constructor( } return CustomerInfo( productInstance = response?.productInstance?.let { ProductInstance(id = it.id, status = it.status) }, - kycStatus = response?.kyc?.status, + isKycApproved = response?.kyc?.status == APPROVED_KYC_STATUS, cardInfo = cardInfo, ) } - private suspend fun getOrderStatus(): Either { - return requestHelper.runWithErrorLogs(TAG) { - val walletAddress = requestHelper.getCustomerWalletAddress() - val orderId: String = tangemPayStorage.getOrderId(walletAddress) - ?: return@runWithErrorLogs OrderStatus.NOT_ISSUED + private suspend fun getOrderStatus(orderId: String): OrderStatus { + val result = requestHelper.request { authHeader -> + tangemPayApi.getOrder(authHeader, orderId) + }.result ?: error("Order result is null") - val result = requestHelper.request { authHeader -> - tangemPayApi.getOrder(authHeader, orderId) - }.result ?: error("Order result is null") - - when (result.status) { - OrderStatus.NEW.apiName -> OrderStatus.NEW - OrderStatus.PROCESSING.apiName -> OrderStatus.PROCESSING - OrderStatus.COMPLETED.apiName -> OrderStatus.COMPLETED - else -> OrderStatus.CANCELED - } + return when (result.status) { + OrderStatus.NEW.apiName -> OrderStatus.NEW + OrderStatus.PROCESSING.apiName -> OrderStatus.PROCESSING + OrderStatus.COMPLETED.apiName -> OrderStatus.COMPLETED + else -> OrderStatus.CANCELED } } + + private suspend fun getCustomerInfoWithPersistedToken(): CustomerInfo { + val result = requestHelper.requestWithPersistedToken { authHeader -> + tangemPayApi.getCustomerMe(authHeader) + }.result + return getCustomerInfo(result) + } } \ No newline at end of file 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 9a5438f259..cb32317055 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 @@ -2,8 +2,6 @@ package com.tangem.domain.pay.model import java.math.BigDecimal -private const val APPROVED_KYC_STATUS = "APPROVED" - data class MainScreenCustomerInfo( val info: CustomerInfo, val orderStatus: OrderStatus, @@ -11,7 +9,7 @@ data class MainScreenCustomerInfo( data class CustomerInfo( val productInstance: ProductInstance?, - val kycStatus: String?, + val isKycApproved: Boolean, val cardInfo: CardInfo?, ) { @@ -26,6 +24,4 @@ data class CustomerInfo( val currencyCode: String, val customerWalletAddress: String, ) - - fun isKycApproved() = kycStatus == APPROVED_KYC_STATUS } \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/OrderStatus.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/OrderStatus.kt index 6cab68a6fd..6ae706a0e1 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/OrderStatus.kt +++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/OrderStatus.kt @@ -1,7 +1,7 @@ package com.tangem.domain.pay.model enum class OrderStatus(val apiName: String) { - NOT_ISSUED(""), + UNKNOWN(""), NEW("NEW"), PROCESSING("PROCESSING"), COMPLETED("COMPLETED"), diff --git a/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt b/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt index 65caca46f0..4b41785218 100644 --- a/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt +++ b/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt @@ -56,7 +56,7 @@ internal class TangemPayOnboardingModel @Inject constructor( repository.getCustomerInfo() .onRight { customerInfo -> when { - !customerInfo.isKycApproved() -> { + !customerInfo.isKycApproved -> { when (params) { is TangemPayOnboardingComponent.Params.Deeplink -> screenState.value = screenState.value.copy(fullScreenLoading = false) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt index c843cb1153..c2d9b76a0c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt @@ -359,10 +359,10 @@ internal class WalletModel @Inject constructor( val info = tangemPayMainScreenCustomerInfoUseCase() if (info != null) { stateHolder.update( - transformer = TangemPayStateTransformer( + transformer = TangemPayInitialStateTransformer( value = info, - onIssueOrderClick = ::issueOrder, - onContinueKycClick = innerWalletRouter::openTangemPayOnboarding, + onClickIssue = ::issueOrder, + onClickKyc = innerWalletRouter::openTangemPayOnboarding, openDetails = innerWalletRouter::openTangemPayDetails, ), ) @@ -371,9 +371,9 @@ internal class WalletModel @Inject constructor( private fun issueOrder() { modelScope.launch { - stateHolder.update(TangemPayStateTransformer(issueProgressState = true)) + stateHolder.update(TangemPayIssueProgressStateTransformer()) tangemPayIssueOrderUseCase().onLeft { - stateHolder.update(TangemPayStateTransformer(issueState = true, onIssueOrderClick = ::issueOrder)) + stateHolder.update(TangemPayIssueAvailableStateTransformer(onClickIssue = ::issueOrder)) } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt similarity index 50% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayStateTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt index 9e367377ba..4af0c5bdc7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayStateTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt @@ -1,33 +1,28 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers -import com.tangem.common.ui.R import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.pay.model.CustomerInfo.CardInfo import com.tangem.domain.pay.model.MainScreenCustomerInfo import com.tangem.domain.pay.model.OrderStatus.CANCELED -import com.tangem.domain.pay.model.OrderStatus.NOT_ISSUED +import com.tangem.domain.pay.model.OrderStatus.UNKNOWN import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState -import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueAvailableState +import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueProgressState +import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createKycInProgressState import java.util.Currency -internal class TangemPayStateTransformer( +internal class TangemPayInitialStateTransformer( private val value: MainScreenCustomerInfo? = null, - private val onIssueOrderClick: () -> Unit = {}, - private val onContinueKycClick: () -> Unit = {}, + private val onClickIssue: () -> Unit = {}, + private val onClickKyc: () -> Unit = {}, private val openDetails: (customerWalletAddress: String, cardNumberEnd: String) -> Unit = { _, _ -> }, - private val issueProgressState: Boolean = false, - private val issueState: Boolean = false, ) : WalletScreenStateTransformer { override fun transform(prevState: WalletScreenState): WalletScreenState { - val tangemPayState = when { - issueProgressState -> createIssueProgressState() - issueState -> createIssueState() - else -> createInitialState() - } + val tangemPayState = createInitialState() return prevState.copy(tangemPayState = tangemPayState) } @@ -35,35 +30,13 @@ internal class TangemPayStateTransformer( val cardInfo = value?.info?.cardInfo return when { value == null -> TangemPayState.Empty - !value.info.isKycApproved() -> createKycInProgressState(onContinueKycClick) + !value.info.isKycApproved -> createKycInProgressState(onClickKyc) cardInfo != null -> getCardInfoState(cardInfo) - value.orderStatus == NOT_ISSUED || value.orderStatus == CANCELED -> createIssueState() + value.orderStatus == UNKNOWN || value.orderStatus == CANCELED -> createIssueAvailableState(onClickIssue) else -> createIssueProgressState() } } - private fun createIssueProgressState(): TangemPayState = Progress( - title = TextReference.Res(R.string.tangempay_issue_card_notification_title), - buttonText = TextReference.EMPTY, - iconRes = R.drawable.ic_tangem_pay_promo_card_36, - onButtonClick = {}, - showProgress = true, - ) - - private fun createIssueState() = Progress( - title = TextReference.Res(R.string.tangempay_issue_card_notification_title), - buttonText = TextReference.Res(R.string.common_continue), - iconRes = R.drawable.ic_tangem_pay_promo_card_36, - onButtonClick = onIssueOrderClick, - ) - - private fun createKycInProgressState(onContinueKycClick: () -> Unit): TangemPayState = Progress( - title = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_title), - buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button), - iconRes = R.drawable.ic_promo_kyc_36, - onButtonClick = onContinueKycClick, - ) - private fun getCardInfoState(cardInfo: CardInfo): TangemPayState = TangemPayState.Card( lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"), balanceText = TextReference.Str(getBalanceText(cardInfo)), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayIssueAvailableStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayIssueAvailableStateTransformer.kt new file mode 100644 index 0000000000..3f66ab33ad --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayIssueAvailableStateTransformer.kt @@ -0,0 +1,12 @@ +package com.tangem.feature.wallet.presentation.wallet.state.transformers + +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueAvailableState + +internal class TangemPayIssueAvailableStateTransformer( + private val onClickIssue: () -> Unit = {}, +) : WalletScreenStateTransformer { + + override fun transform(prevState: WalletScreenState): WalletScreenState = + prevState.copy(tangemPayState = createIssueAvailableState(onClickIssue)) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayIssueProgressStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayIssueProgressStateTransformer.kt new file mode 100644 index 0000000000..b7bc76806e --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayIssueProgressStateTransformer.kt @@ -0,0 +1,10 @@ +package com.tangem.feature.wallet.presentation.wallet.state.transformers + +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueProgressState + +internal class TangemPayIssueProgressStateTransformer : WalletScreenStateTransformer { + + override fun transform(prevState: WalletScreenState): WalletScreenState = + prevState.copy(tangemPayState = createIssueProgressState()) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/util/TangemPayStateCreator.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/util/TangemPayStateCreator.kt new file mode 100644 index 0000000000..f8b3293d44 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/util/TangemPayStateCreator.kt @@ -0,0 +1,31 @@ +package com.tangem.feature.wallet.presentation.wallet.state.util + +import com.tangem.common.ui.R +import com.tangem.core.ui.extensions.TextReference +import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState +import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress + +internal object TangemPayStateCreator { + + fun createKycInProgressState(onClickKyc: () -> Unit): TangemPayState = Progress( + title = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_title), + buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button), + iconRes = R.drawable.ic_promo_kyc_36, + onButtonClick = onClickKyc, + ) + + fun createIssueAvailableState(onClickIssue: () -> Unit) = Progress( + title = TextReference.Res(R.string.tangempay_issue_card_notification_title), + buttonText = TextReference.Res(R.string.common_continue), + iconRes = R.drawable.ic_tangem_pay_promo_card_36, + onButtonClick = onClickIssue, + ) + + fun createIssueProgressState(): TangemPayState = Progress( + title = TextReference.Res(R.string.tangempay_issue_card_notification_title), + buttonText = TextReference.EMPTY, + iconRes = R.drawable.ic_tangem_pay_promo_card_36, + onButtonClick = {}, + showProgress = true, + ) +} \ No newline at end of file