Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-17 14:44:06 +05:00
parent 5618f44c8b
commit 084bad17ab
18 changed files with 452 additions and 192 deletions

View file

@ -75,6 +75,10 @@ internal interface TangemPayDataModule {
@Singleton
fun bindCloseCardRepository(repository: DefaultCloseCardRepository): TangemPayCloseCardRepository
@Binds
@Singleton
fun bindIssueCardRepository(repository: DefaultIssueCardRepository): TangemPayIssueCardRepository
@Binds
@Singleton
fun bindTangemPayCryptoCurrencyFactory(factory: DefaultTangemPayCurrencyFactory): TangemPayCurrencyFactory
@ -269,8 +273,17 @@ internal interface TangemPayDataModule {
fun provideIssueAdditionalCardUseCase(
customerOffersRepository: CustomerOffersRepository,
customerOrderRepository: CustomerOrderRepository,
issueCardRepository: TangemPayIssueCardRepository,
startTangemPayOrderPollingUseCase: StartTangemPayOrderPollingUseCase,
appCoroutineScope: AppCoroutineScope,
): IssueAdditionalCardUseCase {
return IssueAdditionalCardUseCase(customerOffersRepository, customerOrderRepository)
return IssueAdditionalCardUseCase(
customerOffersRepository = customerOffersRepository,
customerOrderRepository = customerOrderRepository,
issueCardRepository = issueCardRepository,
startTangemPayOrderPollingUseCase = startTangemPayOrderPollingUseCase,
appCoroutineScope = appCoroutineScope,
)
}
}
}

View file

@ -53,6 +53,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
private val singleQuoteSupplier: SingleQuoteStatusSupplier,
private val closeCardRepository: TangemPayCloseCardRepository,
private val cardDetailsRepository: TangemPayCardDetailsRepository,
private val issueCardRepository: TangemPayIssueCardRepository,
) : PaymentAccountStatusFetcher {
private val logger = TangemLogger.withTag(TAG)
@ -360,13 +361,17 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
if (tangemPayCards.isEmpty()) return PaymentAccountStatusValue.IssuingCard(source = StatusSource.ACTUAL)
// Additional-card issuance: the backend omits the new card until it is provisioned, so surface a
// placeholder for every locally tracked in-flight issuance order alongside the real cards.
val issuingCards = buildIssuingCards(userWalletId)
return PaymentAccountStatusValue.Loaded(
source = StatusSource.ACTUAL,
customerId = customerId,
depositAddress = cryptoBalance.depositAddress,
cryptoCurrency = tangemPayCurrencyFactory.create(userWalletId),
fiatRate = fiatRate,
cards = tangemPayCards,
cards = tangemPayCards + issuingCards,
balance = PaymentAccountStatusValue.Balance(
fiatBalance = fiatBalance,
cryptoBalance = cryptoBalance,
@ -399,6 +404,37 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
}
}
/**
* Builds the issuing placeholder cards from locally tracked additional-card orders. Each order is
* re-checked against the backend; terminal orders are dropped (and forgotten) because the real card
* is now part of [CustomerInfo], while in-flight orders surface as an issuing placeholder card.
*/
private suspend fun buildIssuingCards(userWalletId: UserWalletId): List<TangemPayCard> {
val orderIds = issueCardRepository.getIssueOrderIds(userWalletId)
return orderIds.mapNotNull { orderId ->
val order = cardDetailsRepository.getOrderInfo(userWalletId, orderId).getOrNull()
if (order != null && order.orderStatus.isTerminal) {
issueCardRepository.removeIssueOrderId(userWalletId, orderId)
null
} else {
issuingPlaceholderCard(orderId)
}
}
}
/** Placeholder card for an additional card that is still being issued (no backend card yet). */
private fun issuingPlaceholderCard(orderId: String): TangemPayCard = TangemPayCard(
id = orderId,
productInstanceId = orderId,
cardStatus = TangemPayCard.Status.INACTIVE,
hasPinCode = false,
displayName = null,
limit = null,
frozenState = TangemPayCardFrozenState.Unfrozen,
lastDigits = "",
state = TangemPayCardState.Issuing,
)
private suspend fun VisaApiError.mapToPaymentAccountStatus(userWalletId: UserWalletId): PaymentAccountStatusValue {
return when (this) {
is VisaApiError.RefreshTokenExpired -> PaymentAccountStatusValue.Error.NotSynced

View file

@ -0,0 +1,25 @@
package com.tangem.data.pay.repository
import com.tangem.datasource.local.visa.TangemPayIssueCardStore
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.repository.TangemPayIssueCardRepository
import com.tangem.utils.coroutines.runSuspendCatching
import javax.inject.Inject
internal class DefaultIssueCardRepository @Inject constructor(
private val tangemPayIssueCardStore: TangemPayIssueCardStore,
) : TangemPayIssueCardRepository {
override suspend fun storeIssueOrderId(userWalletId: UserWalletId, orderId: String) {
runSuspendCatching { tangemPayIssueCardStore.addIssueOrderId(userWalletId, orderId) }
}
override suspend fun getIssueOrderIds(userWalletId: UserWalletId): List<String> {
return runSuspendCatching { tangemPayIssueCardStore.getIssueOrderIds(userWalletId) }
.getOrDefault(emptyList())
}
override suspend fun removeIssueOrderId(userWalletId: UserWalletId, orderId: String) {
runSuspendCatching { tangemPayIssueCardStore.removeIssueOrderId(userWalletId, orderId) }
}
}