Updated on 2026-08-14
This commit is contained in:
parent
5618f44c8b
commit
084bad17ab
18 changed files with 452 additions and 192 deletions
|
|
@ -20,18 +20,24 @@ enum class TangemPayCardState {
|
|||
/** A close order is in progress; the card is being closed. */
|
||||
@SerialName("Closing")
|
||||
Closing,
|
||||
|
||||
/** An issue order is in progress; the (additional) card is being issued and not yet provisioned. */
|
||||
@SerialName("Issuing")
|
||||
Issuing,
|
||||
;
|
||||
|
||||
override fun toString() = when (this) {
|
||||
Active -> "Active"
|
||||
Reissuing -> "Reissuing"
|
||||
Closing -> "Closing"
|
||||
Issuing -> "Issuing"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromString(value: String) = when (value.lowercase(Locale.US)) {
|
||||
"reissuing" -> Reissuing
|
||||
"closing" -> Closing
|
||||
"issuing" -> Issuing
|
||||
else -> Active
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.domain.pay.repository
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
/**
|
||||
* Tracks in-flight additional-card issuance orders locally.
|
||||
*
|
||||
* The backend does not return a card while it is still being issued, so the issuance order id is
|
||||
* stored locally to surface an "issuing" placeholder card until the order reaches a terminal state.
|
||||
*/
|
||||
interface TangemPayIssueCardRepository {
|
||||
|
||||
suspend fun storeIssueOrderId(userWalletId: UserWalletId, orderId: String)
|
||||
|
||||
suspend fun getIssueOrderIds(userWalletId: UserWalletId): List<String>
|
||||
|
||||
suspend fun removeIssueOrderId(userWalletId: UserWalletId, orderId: String)
|
||||
}
|
||||
|
|
@ -6,12 +6,15 @@ import arrow.core.raise.catch
|
|||
import arrow.core.raise.either
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.model.Offer
|
||||
import com.tangem.domain.pay.model.Order
|
||||
import com.tangem.domain.pay.model.TangemPayOrderInfo
|
||||
import com.tangem.domain.pay.repository.CustomerOffersRepository
|
||||
import com.tangem.domain.pay.repository.CustomerOrderRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayIssueCardRepository
|
||||
import com.tangem.domain.pay.util.OrderResolver
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
|
|
@ -33,8 +36,11 @@ import java.util.UUID
|
|||
class IssueAdditionalCardUseCase(
|
||||
private val customerOffersRepository: CustomerOffersRepository,
|
||||
private val customerOrderRepository: CustomerOrderRepository,
|
||||
private val issueCardRepository: TangemPayIssueCardRepository,
|
||||
private val startTangemPayOrderPollingUseCase: StartTangemPayOrderPollingUseCase,
|
||||
private val appCoroutineScope: AppCoroutineScope,
|
||||
) {
|
||||
suspend operator fun invoke(userWalletId: UserWalletId): Either<VisaApiError, Result> = either {
|
||||
suspend operator fun invoke(userWalletId: UserWalletId): Either<VisaApiError, Unit> = either {
|
||||
val offer = catch(
|
||||
block = {
|
||||
customerOffersRepository.getOffers(userWalletId)
|
||||
|
|
@ -61,20 +67,18 @@ class IssueAdditionalCardUseCase(
|
|||
idempotencyKey = UUID.randomUUID().toString(),
|
||||
).bind()
|
||||
|
||||
Result(order = order, offer = offer)
|
||||
issueCardRepository.storeIssueOrderId(userWalletId = userWalletId, orderId = order.id)
|
||||
|
||||
appCoroutineScope.launch {
|
||||
startTangemPayOrderPollingUseCase(
|
||||
order = TangemPayOrderInfo(orderId = order.id, orderStatus = order.status),
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Raise<VisaApiError>.handleError(throwable: Throwable): Nothing {
|
||||
TangemLogger.e("Error in IssueAdditionalCardUseCase", throwable)
|
||||
raise(VisaApiError.Unspecified)
|
||||
}
|
||||
|
||||
/**
|
||||
* Outcome of a successful run.
|
||||
*
|
||||
|
||||
* @property offer the offer that authorised issuance, carried back so the caller can show pricing
|
||||
* without an extra round trip.
|
||||
*/
|
||||
data class Result(val order: Order, val offer: Offer)
|
||||
}
|
||||
|
|
@ -10,7 +10,9 @@ import com.tangem.domain.pay.model.OrderStatus
|
|||
import com.tangem.domain.pay.model.OrderType
|
||||
import com.tangem.domain.pay.repository.CustomerOffersRepository
|
||||
import com.tangem.domain.pay.repository.CustomerOrderRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayIssueCardRepository
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import com.tangem.test.core.TestAppCoroutineScope
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
|
|
@ -23,7 +25,15 @@ internal class IssueAdditionalCardUseCaseTest {
|
|||
|
||||
private val offersRepository: CustomerOffersRepository = mockk()
|
||||
private val orderRepository: CustomerOrderRepository = mockk()
|
||||
private val useCase = IssueAdditionalCardUseCase(offersRepository, orderRepository)
|
||||
private val issueCardRepository: TangemPayIssueCardRepository = mockk(relaxed = true)
|
||||
private val startTangemPayOrderPollingUseCase: StartTangemPayOrderPollingUseCase = mockk(relaxed = true)
|
||||
private val useCase = IssueAdditionalCardUseCase(
|
||||
customerOffersRepository = offersRepository,
|
||||
customerOrderRepository = orderRepository,
|
||||
issueCardRepository = issueCardRepository,
|
||||
startTangemPayOrderPollingUseCase = startTangemPayOrderPollingUseCase,
|
||||
appCoroutineScope = TestAppCoroutineScope(),
|
||||
)
|
||||
private val userWalletId = UserWalletId("1234567890ABCDEF")
|
||||
private val spec = "SP_000004"
|
||||
|
||||
|
|
@ -62,10 +72,9 @@ internal class IssueAdditionalCardUseCaseTest {
|
|||
|
||||
val result = useCase(userWalletId)
|
||||
|
||||
val resultValue = result.getOrNull()
|
||||
assertThat(resultValue?.order).isEqualTo(existing)
|
||||
assertThat(resultValue?.offer).isEqualTo(offer)
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 0) { orderRepository.createOrder(any(), any(), any(), any()) }
|
||||
coVerify(exactly = 1) { issueCardRepository.storeIssueOrderId(userWalletId, existing.id) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -118,7 +127,8 @@ internal class IssueAdditionalCardUseCaseTest {
|
|||
|
||||
val result = useCase(userWalletId)
|
||||
|
||||
assertThat(result.getOrNull()?.order).isEqualTo(newOrder)
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 1) { issueCardRepository.storeIssueOrderId(userWalletId, newOrder.id) }
|
||||
}
|
||||
|
||||
private fun order(id: String, type: OrderType, status: OrderStatus): Order = Order(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue