Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-16 10:38:00 +05:00
parent 0bd23be25f
commit 2c754243f5
16 changed files with 378 additions and 29 deletions

View file

@ -2,6 +2,8 @@ package com.tangem.domain.pay.flow
import arrow.core.Either
import com.tangem.domain.core.flow.FlowFetcher
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.account.VirtualAccountOnramp
import com.tangem.domain.models.wallet.UserWalletId
interface PaymentAccountStatusFetcher : FlowFetcher<PaymentAccountStatusFetcher.Params> {
@ -10,5 +12,12 @@ interface PaymentAccountStatusFetcher : FlowFetcher<PaymentAccountStatusFetcher.
return invoke(Params(userWalletId))
}
/**
* Optimistically marks the cached VA on-ramp as [VirtualAccountOnramp.Processing] so the UI reflects a
* cached [PaymentAccountStatusValue.Loaded].
*/
suspend fun markVirtualAccountProcessing(userWalletId: UserWalletId)
data class Params(val userWalletId: UserWalletId)
}

View file

@ -42,6 +42,8 @@ interface OnboardingRepository {
suspend fun storeVirtualAccountOrderId(userWalletId: UserWalletId, vaOrderId: String)
suspend fun clearVirtualAccountOrderId(userWalletId: UserWalletId)
suspend fun hasTangemPayInWallet(userWalletId: UserWalletId): Either<VisaApiError, Boolean>
suspend fun checkCustomerEligibility(): List<TangemPayEligibilityType>

View file

@ -3,6 +3,7 @@ package com.tangem.domain.pay.usecase
import arrow.core.Either
import arrow.core.raise.either
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
import com.tangem.domain.pay.model.OrderStatus
import com.tangem.domain.pay.model.TangemPayOrderInfo
import com.tangem.domain.pay.repository.OnboardingRepository
@ -22,6 +23,7 @@ import java.util.UUID
class CreateVirtualAccountOrderUseCase(
private val onboardingRepository: OnboardingRepository,
private val pollingUseCase: StartTangemPayOrderPollingUseCase,
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
private val appCoroutineScope: AppCoroutineScope,
) {
suspend operator fun invoke(
@ -36,6 +38,9 @@ class CreateVirtualAccountOrderUseCase(
idempotencyKey = UUID.randomUUID().toString(),
).bind()
onboardingRepository.storeVirtualAccountOrderId(userWalletId = userWalletId, vaOrderId = vaOrderId)
// Optimistically flip the cached on-ramp to Processing so the UI shows "Preparing" immediately
// (no wait for the poll/refetch to confirm).
paymentAccountStatusFetcher.markVirtualAccountProcessing(userWalletId)
appCoroutineScope.launch {
pollingUseCase.invoke(
order = TangemPayOrderInfo(orderId = vaOrderId, orderStatus = OrderStatus.NEW),

View file

@ -4,6 +4,7 @@ import arrow.core.left
import arrow.core.right
import com.google.common.truth.Truth.assertThat
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.test.core.TestAppCoroutineScope
@ -17,9 +18,12 @@ internal class CreateVirtualAccountOrderUseCaseTest {
private val onboardingRepository: OnboardingRepository = mockk(relaxUnitFun = true)
private val pollingUseCase: StartTangemPayOrderPollingUseCase = mockk(relaxed = true)
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher = mockk(relaxUnitFun = true)
private val useCase = CreateVirtualAccountOrderUseCase(
onboardingRepository = onboardingRepository,
pollingUseCase = pollingUseCase,
paymentAccountStatusFetcher = paymentAccountStatusFetcher,
appCoroutineScope = TestAppCoroutineScope(),
)
@ -36,6 +40,7 @@ internal class CreateVirtualAccountOrderUseCaseTest {
coVerify(exactly = 0) { onboardingRepository.createVirtualAccountOrder(any(), any(), any()) }
coVerify(exactly = 0) { onboardingRepository.storeVirtualAccountOrderId(any(), any()) }
coVerify(exactly = 0) { pollingUseCase.invoke(any(), any()) }
coVerify(exactly = 0) { paymentAccountStatusFetcher.markVirtualAccountProcessing(any()) }
}
@Test
@ -50,6 +55,7 @@ internal class CreateVirtualAccountOrderUseCaseTest {
assertThat(result.isRight()).isTrue()
coVerify(exactly = 1) { onboardingRepository.storeVirtualAccountOrderId(userWalletId, "new-id") }
coVerify(exactly = 1) { pollingUseCase.invoke(any(), userWalletId) }
coVerify(exactly = 1) { paymentAccountStatusFetcher.markVirtualAccountProcessing(userWalletId) }
}
@Test
@ -64,5 +70,6 @@ internal class CreateVirtualAccountOrderUseCaseTest {
assertThat(result.leftOrNull()).isEqualTo(VisaApiError.Unspecified)
coVerify(exactly = 0) { onboardingRepository.storeVirtualAccountOrderId(any(), any()) }
coVerify(exactly = 0) { pollingUseCase.invoke(any(), any()) }
coVerify(exactly = 0) { paymentAccountStatusFetcher.markVirtualAccountProcessing(any()) }
}
}