Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-05 16:49:02 +04:00
parent a5389365f4
commit 5346881a57
17 changed files with 158 additions and 55 deletions

View file

@ -4,6 +4,7 @@ import com.tangem.data.pay.DefaultTangemPayCryptoCurrencyFactory
import com.tangem.data.pay.DefaultTangemPayEligibilityManager
import com.tangem.data.pay.repository.*
import com.tangem.data.pay.usecase.DefaultGetTangemPayCurrencyStatusUseCase
import com.tangem.data.pay.usecase.DefaultGetTangemPayCustomerIdUseCase
import com.tangem.data.pay.usecase.DefaultTangemPayWithdrawUseCase
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
import com.tangem.domain.pay.TangemPayEligibilityManager
@ -11,6 +12,7 @@ import com.tangem.domain.pay.repository.*
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.tangempay.GetTangemPayCurrencyStatusUseCase
import com.tangem.domain.tangempay.GetTangemPayCustomerIdUseCase
import com.tangem.domain.tangempay.TangemPayWithdrawUseCase
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
import com.tangem.security.DeviceSecurityInfoProvider
@ -65,6 +67,10 @@ internal interface TangemPayDataModule {
@Singleton
fun bindTangemPayWithdrawUseCase(impl: DefaultTangemPayWithdrawUseCase): TangemPayWithdrawUseCase
@Binds
@Singleton
fun bindGetTangemPayCustomerIdUseCase(impl: DefaultGetTangemPayCustomerIdUseCase): GetTangemPayCustomerIdUseCase
@Binds
@Singleton
fun bindTangemPayEligibilityManager(impl: DefaultTangemPayEligibilityManager): TangemPayEligibilityManager

View file

@ -0,0 +1,25 @@
package com.tangem.data.pay.usecase
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.tangem.core.error.UniversalError
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.tangempay.GetTangemPayCustomerIdUseCase
import com.tangem.domain.visa.error.VisaApiError
import javax.inject.Inject
internal class DefaultGetTangemPayCustomerIdUseCase @Inject constructor(
private val tangemPayOnboardingRepository: OnboardingRepository,
) : GetTangemPayCustomerIdUseCase {
override fun invoke(userWalletId: UserWalletId): Either<UniversalError, String> {
val customerId = tangemPayOnboardingRepository.getSavedCustomerInfo(userWalletId)?.customerId
return if (customerId.isNullOrEmpty()) {
VisaApiError.CustomerIdUnavailable.left()
} else {
customerId.right()
}
}
}

View file

@ -60,31 +60,50 @@ sealed interface FeedbackEmailType {
}
sealed class Visa : FeedbackEmailType {
abstract val customerId: String
data class DirectUserRequest(override val walletMetaInfo: WalletMetaInfo) : Visa()
data class DirectUserRequest(
override val walletMetaInfo: WalletMetaInfo,
override val customerId: String,
) : Visa()
data class Activation(override val walletMetaInfo: WalletMetaInfo) : Visa()
data class Activation(
override val walletMetaInfo: WalletMetaInfo,
override val customerId: String,
) : Visa()
data class Dispute(
val visaTxDetails: VisaTxDetails,
override val walletMetaInfo: WalletMetaInfo,
override val customerId: String,
) : Visa()
data class FailedIssueCard(override val walletMetaInfo: WalletMetaInfo) : Visa()
data class FailedIssueCard(
override val walletMetaInfo: WalletMetaInfo,
override val customerId: String,
) : Visa()
data class DisputeV2(
val item: TangemPayTxHistoryItem,
override val walletMetaInfo: WalletMetaInfo,
override val customerId: String,
) : Visa()
data class Withdrawal(
override val walletMetaInfo: WalletMetaInfo,
override val customerId: String,
val providerName: String,
val txId: String,
) : Visa()
data class FeatureIsBeta(override val walletMetaInfo: WalletMetaInfo) : Visa()
data class FeatureIsBeta(
override val walletMetaInfo: WalletMetaInfo,
override val customerId: String,
) : Visa()
data class KycRejected(override val walletMetaInfo: WalletMetaInfo, val customerId: String) : Visa()
data class KycRejected(
override val walletMetaInfo: WalletMetaInfo,
override val customerId: String,
) : Visa()
}
}

View file

@ -55,7 +55,7 @@ internal class FeedbackDataBuilder {
}
fun addCustomerId(customerId: String) {
builder.appendKeyValue("ID: ", customerId)
builder.appendKeyValue("Tangem Pay Customer ID", customerId)
}
fun addUserWalletMetaInfo(walletMetaInfo: WalletMetaInfo) {

View file

@ -34,7 +34,11 @@ class EmailMessageBodyResolver(
-> addPhoneInfoBody()
is FeedbackEmailType.Visa.Activation -> addUserRequestBody(type.walletMetaInfo)
is FeedbackEmailType.Visa.DirectUserRequest -> addUserRequestBody(type.walletMetaInfo)
is FeedbackEmailType.Visa.Dispute -> addVisaRequestBody(type.walletMetaInfo, type.visaTxDetails)
is FeedbackEmailType.Visa.Dispute -> addVisaRequestBody(
walletMetaInfo = type.walletMetaInfo,
customerId = type.customerId,
visaTxDetails = type.visaTxDetails,
)
is FeedbackEmailType.Visa.FailedIssueCard -> addTangemPayFailedIssuingCardBody(type)
is FeedbackEmailType.Visa.DisputeV2 -> addTangemPayDisputeRequestBody(type)
is FeedbackEmailType.Visa.Withdrawal -> addTangemPayWithdrawalRequestBody(type)
@ -48,6 +52,7 @@ class EmailMessageBodyResolver(
private fun FeedbackDataBuilder.addTangemPayFailedIssuingCardBody(type: FeedbackEmailType.Visa.FailedIssueCard) {
addTangemPayPhoneInfoBody(type)
addDelimiter()
addCustomerId(customerId = type.customerId)
type.walletMetaInfo.userWalletId?.let { userWalletId ->
addUserWalletId(userWalletId = userWalletId.stringValue)
}
@ -58,6 +63,7 @@ class EmailMessageBodyResolver(
addDelimiter()
addTangemPayTxInfo(type.item)
addDelimiter()
addCustomerId(customerId = type.customerId)
type.walletMetaInfo.userWalletId?.let { userWalletId ->
addUserWalletId(userWalletId = userWalletId.stringValue)
}
@ -66,6 +72,7 @@ class EmailMessageBodyResolver(
private fun FeedbackDataBuilder.addTangemPayBetaRequestBody(type: FeedbackEmailType.Visa) {
addTangemPayPhoneInfoBody(type)
addDelimiter()
addCustomerId(customerId = type.customerId)
type.walletMetaInfo?.userWalletId?.let { userWalletId ->
addUserWalletId(userWalletId = userWalletId.stringValue)
}
@ -82,6 +89,7 @@ class EmailMessageBodyResolver(
) {
addUserWalletMetaInfo(type.walletMetaInfo)
addDelimiter()
addCustomerId(customerId = type.customerId)
val userWalletId = requireNotNull(type.walletMetaInfo.userWalletId) { "UserWalletId must be not null" }
val blockchainError = feedbackRepository.getBlockchainErrorInfo(userWalletId = userWalletId)
@ -107,9 +115,11 @@ class EmailMessageBodyResolver(
private suspend fun FeedbackDataBuilder.addVisaRequestBody(
walletMetaInfo: WalletMetaInfo,
visaTxDetails: VisaTxDetails,
customerId: String,
) {
addUserRequestBody(walletMetaInfo)
addDelimiter()
addCustomerId(customerId = customerId)
addVisaTxInfo(visaTxDetails)
}

View file

@ -5,6 +5,7 @@ import kotlinx.serialization.Serializable
@Serializable
data class TangemPayDetailsConfig(
val customerId: String,
val cardId: String,
val isPinSet: Boolean,
val cardFrozenState: TangemPayCardFrozenState,

View file

@ -66,6 +66,7 @@ sealed class VisaApiError(
data object SignWithdrawError : VisaApiError(104004004)
data object WithdrawError : VisaApiError(104004005)
data object ServerUnavailable : VisaApiError(104004006)
data object CustomerIdUnavailable : VisaApiError(104004007)
companion object {
fun fromBackendError(backendErrorCode: Int): VisaApiError {

View file

@ -0,0 +1,10 @@
package com.tangem.domain.tangempay
import arrow.core.Either
import com.tangem.core.error.UniversalError
import com.tangem.domain.models.wallet.UserWalletId
interface GetTangemPayCustomerIdUseCase {
operator fun invoke(userWalletId: UserWalletId): Either<UniversalError, String>
}

View file

@ -23,6 +23,7 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.redux.LegacyAction
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.tangempay.GetTangemPayCustomerIdUseCase
import com.tangem.domain.walletconnect.CheckIsWalletConnectAvailableUseCase
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
@ -63,6 +64,7 @@ internal class DetailsModel @Inject constructor(
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val appStateHolder: ReduxStateHolder,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val getTangemPayCustomerIdUseCase: GetTangemPayCustomerIdUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
override val dispatchers: CoroutineDispatcherProvider,
@ -134,14 +136,20 @@ internal class DetailsModel @Inject constructor(
?: error("Selected wallet is null")
val metaInfo = getWalletMetaInfoUseCase(selectedUserWallet.walletId).getOrNull() ?: return@launch
val visaCustomerId = getTangemPayCustomerIdUseCase(selectedUserWallet.walletId).getOrNull()
val feedbackType = when {
userWallets.all { it is UserWallet.Cold && it.scanResponse.card.isVisa } ->
FeedbackEmailType.Visa.DirectUserRequest(metaInfo)
userWallets.all {
it is UserWallet.Cold && it.scanResponse.card.isVisa && !visaCustomerId.isNullOrEmpty()
} ->
FeedbackEmailType.Visa.DirectUserRequest(
walletMetaInfo = metaInfo,
customerId = requireNotNull(visaCustomerId),
)
userWallets.all { it !is UserWallet.Cold || it.scanResponse.card.isVisa.not() } ->
FeedbackEmailType.DirectUserRequest(metaInfo)
else -> {
showFeedbackEmailTypeOptionBS(metaInfo)
showFeedbackEmailTypeOptionBS(selectedWalletMetaInfo = metaInfo, visaCustomerId = visaCustomerId)
return@launch
}
}
@ -158,16 +166,16 @@ internal class DetailsModel @Inject constructor(
}
}
private fun showFeedbackEmailTypeOptionBS(selectedWalletMetaInfo: WalletMetaInfo) {
state.update {
it.copy(
private fun showFeedbackEmailTypeOptionBS(selectedWalletMetaInfo: WalletMetaInfo, visaCustomerId: String?) {
state.update { current ->
current.copy(
selectFeedbackEmailTypeBSConfig = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = {
state.update {
it.copy(
current.copy(
selectFeedbackEmailTypeBSConfig =
it.selectFeedbackEmailTypeBSConfig.copy(isShown = false),
current.selectFeedbackEmailTypeBSConfig.copy(isShown = false),
)
}
},
@ -176,6 +184,7 @@ internal class DetailsModel @Inject constructor(
onEmailFeedbackTypeOptionSelected(
selectedWalletMetaInfo = selectedWalletMetaInfo,
option = option,
visaCustomerId = visaCustomerId,
)
state.update {
@ -194,6 +203,7 @@ internal class DetailsModel @Inject constructor(
private fun onEmailFeedbackTypeOptionSelected(
selectedWalletMetaInfo: WalletMetaInfo,
option: SelectEmailFeedbackTypeBS.Option,
visaCustomerId: String?,
) {
modelScope.launch {
val feedbackType = when (option) {
@ -211,14 +221,15 @@ internal class DetailsModel @Inject constructor(
}
}
SelectEmailFeedbackTypeBS.Option.Visa -> {
if (selectedWalletMetaInfo.isVisa == true) {
FeedbackEmailType.Visa.DirectUserRequest(selectedWalletMetaInfo)
if (selectedWalletMetaInfo.isVisa == true && !visaCustomerId.isNullOrEmpty()) {
FeedbackEmailType.Visa.DirectUserRequest(selectedWalletMetaInfo, visaCustomerId)
} else {
val userWallet = getWalletsUseCase.invokeSync()
.firstOrNull { it is UserWallet.Cold && it.scanResponse.card.isVisa }
?: return@launch
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
FeedbackEmailType.Visa.DirectUserRequest(metaInfo)
val customerId = getTangemPayCustomerIdUseCase(userWallet.walletId).getOrNull() ?: return@launch
FeedbackEmailType.Visa.DirectUserRequest(metaInfo, customerId)
}
}
}

View file

@ -13,6 +13,7 @@ import com.tangem.domain.card.common.TapWorkarounds.isVisa
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.tangempay.GetTangemPayCustomerIdUseCase
import com.tangem.features.onboarding.v2.stepper.api.OnboardingStepperComponent
import com.tangem.features.onboarding.v2.stepper.impl.ui.OnboardingStepper
import dagger.assisted.Assisted
@ -27,6 +28,7 @@ internal class DefaultOnboardingStepperComponent @AssistedInject constructor(
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val analyticsHandler: AnalyticsEventHandler,
private val getTangemPayCustomerIdUseCase: GetTangemPayCustomerIdUseCase,
) : OnboardingStepperComponent, AppComponentContext by context {
override val state = instanceKeeper.getOrCreateSimple { MutableStateFlow(params.initState) }
@ -41,11 +43,13 @@ internal class DefaultOnboardingStepperComponent @AssistedInject constructor(
componentScope.launch {
val cardInfo = getWalletMetaInfoUseCase(params.scanResponse).getOrNull() ?: return@launch
val userWalletId = cardInfo.userWalletId ?: return@launch
val visaCustomerId = getTangemPayCustomerIdUseCase(userWalletId).getOrNull()
sendFeedbackEmailUseCase(
if (params.scanResponse.card.isVisa) {
FeedbackEmailType.Visa.Activation(cardInfo)
if (params.scanResponse.card.isVisa && !visaCustomerId.isNullOrEmpty()) {
FeedbackEmailType.Visa.Activation(walletMetaInfo = cardInfo, customerId = visaCustomerId)
} else {
FeedbackEmailType.DirectUserRequest(cardInfo)
FeedbackEmailType.DirectUserRequest(walletMetaInfo = cardInfo)
},
)
}

View file

@ -56,6 +56,7 @@ import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
import com.tangem.domain.tangempay.GetTangemPayCurrencyStatusUseCase
import com.tangem.domain.tangempay.GetTangemPayCustomerIdUseCase
import com.tangem.domain.tangempay.TangemPayWithdrawUseCase
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
@ -156,6 +157,7 @@ internal class SwapModel @Inject constructor(
private val getTokenMarketInfoUseCase: GetTokenMarketInfoUseCase,
private val excludedBlockchains: ExcludedBlockchains,
private val getUserWalletsUseCase: GetWalletsUseCase,
private val getTangemPayCustomerIdUseCase: GetTangemPayCustomerIdUseCase,
) : Model() {
private val params = paramsContainer.require<SwapComponent.Params>()
@ -1995,16 +1997,20 @@ internal class SwapModel @Inject constructor(
uiState = uiState,
error = SwapTransactionState.Error.TangemPayWithdrawalError(txId.orEmpty()),
onDismiss = { uiState = stateBuilder.clearAlert(uiState) },
onSupportClick = ::onTangemPaySupportClick,
onSupportClick = {
val customerId = getTangemPayCustomerIdUseCase(userWallet.walletId).getOrNull() ?: "Unknown"
onTangemPaySupportClick(customerId = customerId, txId = txId)
},
isReverseSwapPossible = isReverseSwapPossible(),
)
}
private fun onTangemPaySupportClick(txId: String?) {
private fun onTangemPaySupportClick(customerId: String, txId: String?) {
modelScope.launch {
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
val email = FeedbackEmailType.Visa.Withdrawal(
walletMetaInfo = metaInfo,
customerId = customerId,
providerName = dataState.selectedProvider?.name.orEmpty(),
txId = txId.orEmpty(),
)

View file

@ -105,6 +105,7 @@ internal class TangemPayDetailsComponent(
transaction = navigation.transaction,
isBalanceHidden = navigation.isBalanceHidden,
userWalletId = params.userWalletId,
customerId = params.config.customerId,
onDismiss = model.bottomSheetNavigation::dismiss,
),
)

View file

@ -32,6 +32,7 @@ internal class TangemPayTxHistoryDetailsComponent(
val transaction: TangemPayTxHistoryItem,
val isBalanceHidden: Boolean,
val userWalletId: UserWalletId,
val customerId: String,
val onDismiss: () -> Unit,
)
}

View file

@ -371,6 +371,7 @@ internal class TangemPayDetailsModel @Inject constructor(
sendFeedbackEmailUseCase.invoke(
type = FeedbackEmailType.Visa.FeatureIsBeta(
walletMetaInfo = WalletMetaInfo(userWalletId = params.userWalletId),
customerId = params.config.customerId,
),
)
}

View file

@ -40,7 +40,7 @@ internal class TangemPayTxHistoryDetailsModel @Inject constructor(
item = params.transaction,
isBalanceHidden = params.isBalanceHidden,
onExplorerClick = ::openExplorer,
onDisputeClick = ::dispute,
onDisputeClick = { dispute(customerId = params.customerId) },
onDismiss = ::dismiss,
),
),
@ -64,7 +64,7 @@ internal class TangemPayTxHistoryDetailsModel @Inject constructor(
txHash?.let(urlOpener::openUrlExternalBrowser)
}
private fun dispute() {
private fun dispute(customerId: String) {
analytics.send(TangemPayAnalyticsEvents.SupportOnTransactionPopupClicked())
modelScope.launch {
val walletMetaInfo = getWalletMetaInfoUseCase.invoke(params.userWalletId).getOrNull() ?: return@launch
@ -73,6 +73,7 @@ internal class TangemPayTxHistoryDetailsModel @Inject constructor(
FeedbackEmailType.Visa.DisputeV2(
item = params.transaction,
walletMetaInfo = walletMetaInfo,
customerId = customerId,
),
)
}

View file

@ -51,9 +51,9 @@ internal interface TangemPayIntents {
fun onIssuingCardClicked()
fun onIssuingFailedClicked()
fun onIssuingFailedClicked(customerId: String)
fun onPaySupportClick()
fun onPaySupportClick(customerId: String)
fun onOnboardingBannerClick(userWalletId: UserWalletId)
@ -203,7 +203,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
uiMessageSender.send(issuingBottomSheet)
}
override fun onIssuingFailedClicked() {
override fun onIssuingFailedClicked(customerId: String) {
val issuingBottomSheet = bottomSheetMessage {
infoBlock {
icon(com.tangem.core.ui.R.drawable.ic_alert_24) {
@ -216,7 +216,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
secondaryButton {
text = resourceReference(R.string.tangempay_go_to_support)
onClick {
onPaySupportClick()
onPaySupportClick(customerId)
closeBs()
}
}
@ -225,7 +225,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
uiMessageSender.send(issuingBottomSheet)
}
override fun onPaySupportClick() {
override fun onPaySupportClick(customerId: String) {
modelScope.launch {
val cardInfo = getWalletMetainfoUseCase.invoke(
userWalletId = stateHolder.getSelectedWalletId(),
@ -234,6 +234,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
sendFeedbackEmailUseCase(
FeedbackEmailType.Visa.FailedIssueCard(
walletMetaInfo = cardInfo,
customerId = customerId,
),
)
}

View file

@ -45,37 +45,42 @@ internal class TangemPayUpdateInfoStateTransformer(
private fun createInitialState(): TangemPayState {
val cardInfo = value.info.cardInfo
val productInstance = value.info.productInstance
val customerId = value.info.customerId
val customerId = value.info.customerId ?: "Unknown"
// when statement copied to WalletTangemPayAnalyticsEventSender. Be careful when editing.
return when {
value.orderStatus == OrderStatus.CANCELED -> createCancelledState()
value.info.kycStatus != APPROVED && !customerId.isNullOrEmpty() ->
value.orderStatus == OrderStatus.CANCELED -> createCancelledState(customerId)
value.info.kycStatus != APPROVED && !value.info.customerId.isNullOrEmpty() ->
createKycInProgressState(kycStatus = value.info.kycStatus, customerId = customerId)
cardInfo != null && productInstance != null -> getCardInfoState(cardInfo, productInstance)
cardInfo != null && productInstance != null ->
getCardInfoState(customerId, cardInfo, productInstance)
else -> createIssueProgressState()
}
}
private fun getCardInfoState(cardInfo: CardInfo, productInstance: ProductInstance): TangemPayState =
TangemPayState.Card(
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
balanceText = TextReference.Str(getBalanceText(cardInfo)),
balanceSymbol = stringReference("USDC"), // TODO hardcode for now
onClick = {
tangemPayClickIntents.openDetails(
userWalletId,
TangemPayDetailsConfig(
cardId = productInstance.cardId,
isPinSet = cardInfo.isPinSet,
cardFrozenState = cardFrozenState,
customerWalletAddress = cardInfo.customerWalletAddress,
cardNumberEnd = cardInfo.lastFourDigits,
chainId = POLYGON_CHAIN_ID,
),
)
},
)
private fun getCardInfoState(
customerId: String,
cardInfo: CardInfo,
productInstance: ProductInstance,
): TangemPayState = TangemPayState.Card(
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
balanceText = TextReference.Str(getBalanceText(cardInfo)),
balanceSymbol = stringReference("USDC"), // TODO hardcode for now
onClick = {
tangemPayClickIntents.openDetails(
userWalletId,
TangemPayDetailsConfig(
customerId = customerId,
cardId = productInstance.cardId,
isPinSet = cardInfo.isPinSet,
cardFrozenState = cardFrozenState,
customerWalletAddress = cardInfo.customerWalletAddress,
cardNumberEnd = cardInfo.lastFourDigits,
chainId = POLYGON_CHAIN_ID,
),
)
},
)
private fun getBalanceText(cardInfo: CardInfo): String {
val currency = Currency.getInstance(cardInfo.currencyCode)
@ -113,10 +118,10 @@ internal class TangemPayUpdateInfoStateTransformer(
showProgress = true,
)
private fun createCancelledState(): TangemPayState = TangemPayState.FailedIssue(
private fun createCancelledState(customerId: String): TangemPayState = TangemPayState.FailedIssue(
title = TextReference.Res(R.string.tangempay_payment_account),
description = TextReference.Res(R.string.tangempay_failed_to_issue_card),
iconRes = R.drawable.ic_alert_24,
onButtonClick = tangemPayClickIntents::onIssuingFailedClicked,
onButtonClick = { tangemPayClickIntents.onIssuingFailedClicked(customerId) },
)
}