Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-13 16:07:12 +05:00
parent 71507af6c3
commit 589805424e
9 changed files with 129 additions and 66 deletions

View file

@ -10,6 +10,7 @@ import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.kyc.KycStatus
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.repository.CustomerOrderRepository
import com.tangem.domain.pay.repository.OnboardingRepository
@ -19,7 +20,11 @@ import com.tangem.security.DeviceSecurityInfoProvider
import com.tangem.security.isSecurityExposed
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import javax.inject.Inject
import kotlin.time.Duration.Companion.minutes
private const val TAG = "PaymentAccountStatusFetcher"
@ -144,53 +149,103 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
}
private suspend fun proceedWithOrderId(account: Account.Payment, orderId: String): PaymentAccountStatusValue {
// Step 1: Check KYC status first
val customerInfo = onboardingRepository.getCustomerInfo(account.userWalletId).fold(
ifLeft = { error ->
logger.e("proceedWithOrderId KYC check ${account.userWalletId} error: $error")
return error.mapToPaymentAccountStatus()
},
ifRight = { it },
)
logger.i("proceedWithOrderId ${account.userWalletId} kycStatus: ${customerInfo.kycStatus}")
when (customerInfo.kycStatus) {
KycStatus.PENDING,
KycStatus.INIT,
KycStatus.REJECTED,
-> return customerInfo.mapToPaymentAccountStatus()
KycStatus.APPROVED -> Unit // proceed to order check
}
// Step 2: Check order status
return customerOrderRepository.getOrderData(userWalletId = account.userWalletId, orderId = orderId).fold(
ifLeft = { error ->
logger.e("proceedWithOrderId ${account.userWalletId} orderId: $orderId error: $error")
error.mapToPaymentAccountStatus()
},
ifRight = { orderData ->
logger.i("proceedWithOrderId $account.userWalletId: $orderId status: ${orderData.status}")
logger.i("proceedWithOrderId ${account.userWalletId}: $orderId status: ${orderData.status}")
when (orderData.status) {
// Kyc is passed and user waits for order creation -> no need to get customer info
OrderStatus.CANCELED -> handleCanceledOrder(account, orderData)
OrderStatus.COMPLETED -> handleCompletedOrder(account)
OrderStatus.UNKNOWN -> PaymentAccountStatusValue.Error.Unavailable
OrderStatus.NEW,
OrderStatus.PROCESSING,
-> PaymentAccountStatusValue.IssuingCard(source = StatusSource.ACTUAL)
OrderStatus.CANCELED -> {
onboardingRepository.getCustomerInfo(userWalletId = account.userWalletId)
.fold(
ifLeft = {
PaymentAccountStatusValue.Error.CardIssueFailed(
customerId = orderData.customerId,
)
},
ifRight = { customerInfo ->
if (customerInfo.kycStatus == KycStatus.REJECTED) {
customerInfo.mapToPaymentAccountStatus()
} else {
PaymentAccountStatusValue.Error.CardIssueFailed(
customerId = orderData.customerId,
)
}
},
)
-> {
paymentAccountStatusesStore.store(
userWalletId = account.userWalletId,
status = AccountStatus.Payment(
account = account,
value = PaymentAccountStatusValue.IssuingCard(source = StatusSource.ACTUAL),
),
)
// Start polling for terminal state
pollOrderStatus(account = account, orderId = orderId)
}
OrderStatus.COMPLETED -> {
// Order was completed -> clear order id and get customer info
onboardingRepository.clearOrderId(account.userWalletId)
onboardingRepository.getCustomerInfo(userWalletId = account.userWalletId)
.fold(
ifLeft = { it.mapToPaymentAccountStatus() },
ifRight = { customerInfo -> customerInfo.mapToPaymentAccountStatus() },
)
}
OrderStatus.UNKNOWN -> PaymentAccountStatusValue.Error.Unavailable
}
},
)
}
private suspend fun pollOrderStatus(account: Account.Payment, orderId: String): PaymentAccountStatusValue {
while (currentCoroutineContext().isActive) {
delay(1.minutes)
val result = customerOrderRepository.getOrderData(
userWalletId = account.userWalletId,
orderId = orderId,
)
result.fold(
ifLeft = { error ->
logger.e("pollOrderStatus ${account.userWalletId} orderId: $orderId error: $error")
// Continue polling on transient errors
},
ifRight = { orderData ->
logger.i("pollOrderStatus ${account.userWalletId}: $orderId status: ${orderData.status}")
when (orderData.status) {
OrderStatus.CANCELED -> return handleCanceledOrder(account, orderData)
OrderStatus.COMPLETED -> return handleCompletedOrder(account)
OrderStatus.NEW,
OrderStatus.PROCESSING,
OrderStatus.UNKNOWN,
-> Unit // Continue polling
}
},
)
}
return PaymentAccountStatusValue.IssuingCard(source = StatusSource.ACTUAL)
}
private suspend fun handleCanceledOrder(
account: Account.Payment,
orderData: OrderData,
): PaymentAccountStatusValue {
onboardingRepository.clearOrderId(account.userWalletId)
return PaymentAccountStatusValue.Error.CardIssueFailed(orderData.customerId)
}
private suspend fun handleCompletedOrder(account: Account.Payment): PaymentAccountStatusValue {
onboardingRepository.clearOrderId(account.userWalletId)
return onboardingRepository.getCustomerInfo(userWalletId = account.userWalletId)
.fold(
ifLeft = { it.mapToPaymentAccountStatus() },
ifRight = { customerInfo -> customerInfo.mapToPaymentAccountStatus() },
)
}
private fun CustomerInfo.mapToPaymentAccountStatus(): PaymentAccountStatusValue {
val cardInfo = this.cardInfo
val productInstance = this.productInstance