Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-25 22:45:58 +04:00
parent 84af4950a1
commit ff2559df3e
27 changed files with 904 additions and 20 deletions

View file

@ -20,6 +20,7 @@ import com.tangem.features.onramp.deeplink.SwapDeepLinkHandler
import com.tangem.features.send.v2.api.deeplink.SellRedirectDeepLinkHandler
import com.tangem.features.staking.api.deeplink.StakingDeepLinkHandler
import com.tangem.features.tangempay.deeplink.OnboardVisaDeepLinkHandler
import com.tangem.features.tangempay.deeplink.TangemPayMainDeepLinkHandler
import com.tangem.features.tokendetails.deeplink.TokenDetailsDeepLinkHandler
import com.tangem.features.wallet.deeplink.PromoDeeplinkHandler
import com.tangem.features.wallet.deeplink.WalletDeepLinkHandler
@ -56,6 +57,7 @@ internal class DeepLinkFactory @Inject constructor(
private val promoDeepLink: PromoDeeplinkHandler.Factory,
private val onboardVisaDeepLink: OnboardVisaDeepLinkHandler.Factory,
private val marketsTokenExchangesDeepLink: MarketsTokenExchangesDeepLinkHandler.Factory,
private val tangemPayMainDeepLink: TangemPayMainDeepLinkHandler.Factory,
private val newsDetailsDeepLink: NewsDetailsDeepLinkHandler.Factory,
private val newsDeepLink: NewsDeepLinkHandler.Factory,
private val earnDeepLink: EarnDeepLinkHandler.Factory,
@ -129,6 +131,10 @@ internal class DeepLinkFactory @Inject constructor(
private fun handleHttpDeepLinks(deeplinkUri: Uri, coroutineScope: CoroutineScope) {
if (deeplinkUri.host == DeepLinkRoute.PayApp.host) {
when {
deeplinkUri.path?.startsWith("/pay-app-main") == true -> {
tangemPayMainDeepLink.create(coroutineScope, getQueryParams(deeplinkUri))
return
}
deeplinkUri.path?.startsWith("/pay-app") == true -> {
onboardVisaDeepLink.create(deeplinkUri)
return
@ -168,6 +174,7 @@ internal class DeepLinkFactory @Inject constructor(
DeepLinkRoute.News.host -> newsDeepLink.create(queryParams)
DeepLinkRoute.Earn.host -> earnDeepLink.create(queryParams)
DeepLinkRoute.Yield.host -> yieldDeepLink.create(coroutineScope, queryParams)
DeepLinkRoute.PayAppMain.host -> tangemPayMainDeepLink.create(coroutineScope, queryParams)
else -> {
TangemLogger.i(
"""

View file

@ -18,6 +18,7 @@ import com.tangem.features.onramp.deeplink.SwapDeepLinkHandler
import com.tangem.features.send.v2.api.deeplink.SellRedirectDeepLinkHandler
import com.tangem.features.staking.api.deeplink.StakingDeepLinkHandler
import com.tangem.features.tangempay.deeplink.OnboardVisaDeepLinkHandler
import com.tangem.features.tangempay.deeplink.TangemPayMainDeepLinkHandler
import com.tangem.features.tokendetails.deeplink.TokenDetailsDeepLinkHandler
import com.tangem.features.wallet.deeplink.PromoDeeplinkHandler
import com.tangem.features.wallet.deeplink.WalletDeepLinkHandler
@ -82,6 +83,10 @@ class DeepLinkFactoryTest {
every { create(any()) } returns mockk()
}
private val tangemPayMainDeepLink = mockk<TangemPayMainDeepLinkHandler.Factory>(relaxed = true) {
every { create(any(), any()) } returns mockk()
}
private val cardSdkProvider = mockk<CardSdkProvider>(relaxed = true) {
every { sdk.uiVisibility() } returns MutableStateFlow(false)
}
@ -130,6 +135,7 @@ class DeepLinkFactoryTest {
swapDeepLink = swapDeepLinkFactory,
promoDeepLink = promoDeepLinkFactory,
onboardVisaDeepLink = onboardVisaDeepLink,
tangemPayMainDeepLink = tangemPayMainDeepLink,
newsDetailsDeepLink = newsDeeplink,
newsDeepLink = newsDeepLinkFactory,
earnDeepLink = earnDeepLinkFactory,
@ -358,6 +364,14 @@ class DeepLinkFactoryTest {
deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent)
advanceUntilIdle()
verify { promoDeepLinkFactory.create(eq(testScope), eq(emptyMap())) }
// Test TangemPay
every { mockedUri.host } returns "pay-app-main"
every { mockedUri.queryParameterNames } returns setOf("param")
every { mockedUri.getQueryParameter("param") } returns "value"
deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent)
advanceUntilIdle()
verify { tangemPayMainDeepLink.create(eq(testScope), any()) }
}
@Test
@ -381,6 +395,7 @@ class DeepLinkFactoryTest {
sellDeepLinkFactory.create()
swapDeepLinkFactory.create()
promoDeepLinkFactory.create(any(), any())
tangemPayMainDeepLink.create(any(), any())
}
}

View file

@ -83,6 +83,10 @@ sealed class DeepLinkRoute {
data object Yield : DeepLinkRoute() {
override val host: String = "yield"
}
data object PayAppMain : DeepLinkRoute() {
override val host: String = "pay-app-main"
}
}
enum class DeepLinkScheme(val scheme: String) {

View file

@ -6,6 +6,8 @@ object DeeplinkConst {
const val TANGEM_SCHEME = "tangem"
const val WALLET_ID_KEY = "user_wallet_id"
const val CUSTOMER_WALLET_ID_KEY = "customer_wallet_id"
const val CUSTOMER_ID_KEY = "customer_id"
const val NETWORK_ID_KEY = "network_id"
const val TYPE_KEY = "type"
const val TOKEN_ID_KEY = "token_id"

View file

@ -3,6 +3,7 @@ package com.tangem.common.routing.deeplink
import android.os.Bundle
import com.tangem.common.routing.DeepLinkRoute
import com.tangem.common.routing.DeepLinkScheme
import com.tangem.common.routing.deeplink.DeeplinkConst.CUSTOMER_WALLET_ID_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.DEEPLINK_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.DERIVATION_PATH_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.NAME_KEY
@ -11,6 +12,7 @@ import com.tangem.common.routing.deeplink.DeeplinkConst.TOKEN_ID_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.TRANSACTION_ID_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.TYPE_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.WALLET_ID_KEY
import com.tangem.domain.visa.model.TangemPayPushNotificationType
import com.tangem.utils.converter.Converter
object PayloadToDeeplinkConverter : Converter<Map<String, String>, String?> {
@ -18,6 +20,7 @@ object PayloadToDeeplinkConverter : Converter<Map<String, String>, String?> {
override fun convert(value: Map<String, String>): String? {
return when {
value[DEEPLINK_KEY] != null -> value[DEEPLINK_KEY]
isTangemPayPushNotificationPayload(value) -> buildTangemPayNotificationDeeplink(value)
isTangemPushNotificationPayload(value) -> buildNotificationDeeplink(value)
else -> null
}
@ -70,4 +73,19 @@ object PayloadToDeeplinkConverter : Converter<Map<String, String>, String?> {
payload.containsKey(TOKEN_ID_KEY) &&
payload.containsKey(WALLET_ID_KEY)
}
private fun isTangemPayPushNotificationPayload(payload: Map<String, String>): Boolean {
return payload.containsKey(CUSTOMER_WALLET_ID_KEY) && payload[TYPE_KEY] in TangemPayPushNotificationType.all
}
private fun buildTangemPayNotificationDeeplink(payload: Map<String, String>): String? {
val walletId = payload[CUSTOMER_WALLET_ID_KEY]
val type = payload[TYPE_KEY]
if (walletId.isNullOrEmpty() || type.isNullOrEmpty()) return null
return DeepLinkBuilder().setScheme(DeepLinkScheme.Tangem.scheme).apply {
setAction(DeepLinkRoute.PayAppMain.host)
payload.forEach { (key, value) -> addQueryParam(key, value) }
}.build()
}
}

View file

@ -2,11 +2,14 @@ package com.tangem.common.routing.deeplink
import com.google.common.truth.Truth.assertThat
import com.tangem.common.routing.deeplink.DeeplinkConst.DEEPLINK_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.CUSTOMER_WALLET_ID_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.DERIVATION_PATH_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.NETWORK_ID_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.TOKEN_ID_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.TRANSACTION_ID_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.TYPE_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.WALLET_ID_KEY
import com.tangem.domain.visa.model.TangemPayPushNotificationType
import org.junit.Test
internal class PayloadToDeeplinkConverterTest {
@ -145,4 +148,88 @@ internal class PayloadToDeeplinkConverterTest {
// THEN
assertThat(result).isNull()
}
@Test
fun `GIVEN tangem pay card_ready push payload WHEN convert THEN should return pay-app-main deeplink`() {
// GIVEN
val payload = mapOf(
TYPE_KEY to TangemPayPushNotificationType.CARD_READY.value,
CUSTOMER_WALLET_ID_KEY to "wallet123",
)
// WHEN
val result = PayloadToDeeplinkConverter.convert(payload)
// THEN
assertThat(result).isEqualTo(
"tangem://pay-app-main?type=card_ready&customer_wallet_id=wallet123",
)
}
@Test
fun `GIVEN tangem pay transaction_spend push payload WHEN convert THEN should return pay-app-main deeplink with transaction_id`() {
// GIVEN
val payload = mapOf(
TYPE_KEY to TangemPayPushNotificationType.TRANSACTION_SPEND.value,
CUSTOMER_WALLET_ID_KEY to "wallet123",
TRANSACTION_ID_KEY to "test456",
)
// WHEN
val result = PayloadToDeeplinkConverter.convert(payload)
// THEN
assertThat(result).isEqualTo(
"tangem://pay-app-main?type=transaction_spend&customer_wallet_id=wallet123&transaction_id=test456",
)
}
@Test
fun `GIVEN tangem pay top_up push payload WHEN convert THEN should return pay-app-main deeplink`() {
// GIVEN
val payload = mapOf(
TYPE_KEY to TangemPayPushNotificationType.TOP_UP.value,
CUSTOMER_WALLET_ID_KEY to "wallet123",
TRANSACTION_ID_KEY to "test456",
)
// WHEN
val result = PayloadToDeeplinkConverter.convert(payload)
// THEN
assertThat(result).isEqualTo(
"tangem://pay-app-main?type=declined_top_up&customer_wallet_id=wallet123&transaction_id=test456",
)
}
@Test
fun `GIVEN tangem pay collateral push payload WHEN convert THEN should return pay-app-main deeplink`() {
// GIVEN
val payload = mapOf(
TYPE_KEY to TangemPayPushNotificationType.COLLATERAL.value,
CUSTOMER_WALLET_ID_KEY to "wallet123",
)
// WHEN
val result = PayloadToDeeplinkConverter.convert(payload)
// THEN
assertThat(result).isEqualTo(
"tangem://pay-app-main?type=collateral&customer_wallet_id=wallet123",
)
}
@Test
fun `GIVEN tangem pay push payload with missing customer_wallet_id WHEN convert THEN should return null`() {
// GIVEN
val payload = mapOf(
TYPE_KEY to TangemPayPushNotificationType.CARD_READY.value,
)
// WHEN
val result = PayloadToDeeplinkConverter.convert(payload)
// THEN
assertThat(result).isNull()
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.data.visa.utils
import com.squareup.moshi.Moshi
import com.tangem.datasource.api.pay.models.response.TangemPayTxHistoryResponse
import com.tangem.domain.pay.utils.TangemPayTxHistoryItemStatusConverter
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.isPositive

View file

@ -0,0 +1,17 @@
package com.tangem.domain.visa.model
enum class TangemPayPushNotificationType(val value: String) {
CARD_READY("card_ready"),
TRANSACTION_SPEND("transaction_spend"),
TOP_UP("declined_top_up"),
COLLATERAL("collateral"),
;
companion object {
private val map = entries.associateBy { it.value }
val all: Set<String> = entries.map { it.value }.toSet()
fun fromValue(value: String): TangemPayPushNotificationType? = map[value]
}
}

View file

@ -1,9 +1,9 @@
package com.tangem.data.visa.utils
package com.tangem.domain.pay.utils
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.utils.converter.Converter
internal object TangemPayTxHistoryItemStatusConverter : Converter<String, TangemPayTxHistoryItem.Status> {
object TangemPayTxHistoryItemStatusConverter : Converter<String, TangemPayTxHistoryItem.Status> {
override fun convert(value: String): TangemPayTxHistoryItem.Status {
return when (value.uppercase()) {
"PENDING" -> TangemPayTxHistoryItem.Status.PENDING

View file

@ -0,0 +1,19 @@
package com.tangem.features.tangempay.components
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
interface TangemPayTransactionBottomSheetComponent : ComposableBottomSheetComponent {
data class Params(
val isBalanceHidden: Boolean,
val transaction: TangemPayTxHistoryItem,
val userWalletId: UserWalletId,
val customerId: String,
val onDismiss: () -> Unit,
)
interface Factory : ComponentFactory<Params, TangemPayTransactionBottomSheetComponent>
}

View file

@ -94,7 +94,7 @@ internal class TangemPayDetailsComponent(
)
is TangemPayDetailsNavigation.TransactionDetails -> TangemPayTxHistoryDetailsComponent(
appComponentContext = context,
params = TangemPayTxHistoryDetailsComponent.Params(
params = TangemPayTransactionBottomSheetComponent.Params(
transaction = navigation.transaction,
isBalanceHidden = navigation.isBalanceHidden,
userWalletId = params.initialStatus.userWalletId,

View file

@ -5,16 +5,17 @@ import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.features.tangempay.components.TangemPayTransactionBottomSheetComponent
import com.tangem.features.tangempay.model.TangemPayTxHistoryDetailsModel
import com.tangem.features.tangempay.ui.TangemPayTxHistoryDetailsContent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class TangemPayTxHistoryDetailsComponent(
appComponentContext: AppComponentContext,
params: Params,
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
internal class TangemPayTxHistoryDetailsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted private val params: TangemPayTransactionBottomSheetComponent.Params,
) : TangemPayTransactionBottomSheetComponent, AppComponentContext by appComponentContext {
private val model: TangemPayTxHistoryDetailsModel = getOrCreateModel(params = params)
@ -28,11 +29,11 @@ internal class TangemPayTxHistoryDetailsComponent(
TangemPayTxHistoryDetailsContent(state = state)
}
data class Params(
val transaction: TangemPayTxHistoryItem,
val isBalanceHidden: Boolean,
val userWalletId: UserWalletId,
val customerId: String,
val onDismiss: () -> Unit,
)
@AssistedFactory
interface Factory : TangemPayTransactionBottomSheetComponent.Factory {
override fun create(
context: AppComponentContext,
params: TangemPayTransactionBottomSheetComponent.Params,
): TangemPayTxHistoryDetailsComponent
}
}

View file

@ -2,6 +2,8 @@ package com.tangem.features.tangempay.di
import com.tangem.features.tangempay.components.DefaultTangemPayDetailsContainerComponent
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
import com.tangem.features.tangempay.components.TangemPayTransactionBottomSheetComponent
import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryDetailsComponent
import com.tangem.features.tangempay.model.listener.CardDetailsEventListener
import com.tangem.features.tangempay.model.listener.DefaultCardDetailsEventListener
import dagger.Binds
@ -23,4 +25,10 @@ internal interface TangemPayDetailsFeatureModule {
@Binds
@Singleton
fun bindCardDetailsEventListener(impl: DefaultCardDetailsEventListener): CardDetailsEventListener
@Binds
@Singleton
fun bindTangemPayTransactionBottomSheetComponentFactory(
factory: TangemPayTxHistoryDetailsComponent.Factory,
): TangemPayTransactionBottomSheetComponent.Factory
}

View file

@ -13,7 +13,7 @@ import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryDetailsComponent
import com.tangem.features.tangempay.components.TangemPayTransactionBottomSheetComponent
import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM
import com.tangem.features.tangempay.model.transformers.TangemPayTxHistoryDetailsConverter
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -34,7 +34,7 @@ internal class TangemPayTxHistoryDetailsModel @Inject constructor(
paramsContainer: ParamsContainer,
) : Model() {
private val params = paramsContainer.require<TangemPayTxHistoryDetailsComponent.Params>()
private val params = paramsContainer.require<TangemPayTransactionBottomSheetComponent.Params>()
val uiState: StateFlow<TangemPayTxHistoryDetailsUM>
field = MutableStateFlow(
value = TangemPayTxHistoryDetailsConverter.convert(

View file

@ -0,0 +1,10 @@
package com.tangem.features.tangempay.deeplink
import kotlinx.coroutines.CoroutineScope
interface TangemPayMainDeepLinkHandler {
interface Factory {
fun create(scope: CoroutineScope, payload: Map<String, String>): TangemPayMainDeepLinkHandler
}
}

View file

@ -0,0 +1,118 @@
package com.tangem.features.tangempay.deeplink
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.routing.deeplink.DeeplinkConst.CUSTOMER_ID_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.CUSTOMER_WALLET_ID_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.TYPE_KEY
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
import com.tangem.domain.visa.model.TangemPayPushNotificationType
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.SelectWalletUseCase
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionTrigger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
@Suppress("LongParameterList")
internal class DefaultTangemPayMainDeepLinkHandler @AssistedInject constructor(
@Assisted private val scope: CoroutineScope,
@Assisted private val payload: Map<String, String>,
private val appRouter: AppRouter,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val selectWalletUseCase: SelectWalletUseCase,
private val walletDeepLinkActionTrigger: WalletDeepLinkActionTrigger,
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
private val paymentAccountSupplier: PaymentAccountStatusSupplier,
) : TangemPayMainDeepLinkHandler {
init {
handleDeepLink()
}
private fun handleDeepLink() {
val walletId = payload[CUSTOMER_WALLET_ID_KEY]
scope.launch {
val userWalletId = walletId?.let(::UserWalletId) ?: run {
appRouter.popTo(AppRoute.Wallet)
return@launch
}
val userWallet = getUserWalletUseCase(userWalletId).getOrNull()
if (userWallet == null || userWallet.isLocked) {
appRouter.popTo(AppRoute.Wallet)
return@launch
}
if (selectWalletUseCase(userWalletId).getOrNull() == null) {
appRouter.popTo(AppRoute.Wallet)
return@launch
}
val pushAction = buildPushAction()
appRouter.popTo(
route = AppRoute.Wallet,
onComplete = {
walletDeepLinkActionTrigger.selectWallet(userWalletId)
when (pushAction) {
is TangemPayPushAction.CardReady,
is TangemPayPushAction.TopUp,
-> navigateToTangemPayDetails(userWalletId)
is TangemPayPushAction.TransactionSpend -> {
walletDeepLinkActionTrigger.showTangemPayTransaction(
transaction = pushAction.transaction,
customerId = pushAction.customerId,
)
}
is TangemPayPushAction.CollateralTransaction -> {
walletDeepLinkActionTrigger.showTangemPayTransaction(
transaction = pushAction.transaction,
customerId = pushAction.customerId,
)
}
null -> Unit
}
},
)
}
}
private fun buildPushAction(): TangemPayPushAction? {
val type = payload[TYPE_KEY]?.let(TangemPayPushNotificationType::fromValue) ?: return null
val customerId = payload[CUSTOMER_ID_KEY].orEmpty()
return when (type) {
TangemPayPushNotificationType.CARD_READY -> TangemPayPushAction.CardReady
TangemPayPushNotificationType.TRANSACTION_SPEND -> {
val transaction = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
if (transaction != null) TangemPayPushAction.TransactionSpend(transaction, customerId) else null
}
TangemPayPushNotificationType.TOP_UP -> TangemPayPushAction.TopUp
TangemPayPushNotificationType.COLLATERAL -> {
val transaction = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(payload)
if (transaction != null) TangemPayPushAction.CollateralTransaction(transaction, customerId) else null
}
}
}
private fun navigateToTangemPayDetails(walletId: UserWalletId) {
scope.launch {
paymentAccountStatusFetcher.invoke(PaymentAccountStatusFetcher.Params(walletId))
val paymentAccountStatus = paymentAccountSupplier.invoke(userWalletId = walletId)
.firstOrNull()
?: return@launch
appRouter.push(route = AppRoute.TangemPayDetails(status = paymentAccountStatus))
}
}
@AssistedFactory
interface Factory : TangemPayMainDeepLinkHandler.Factory {
override fun create(scope: CoroutineScope, payload: Map<String, String>): DefaultTangemPayMainDeepLinkHandler
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.features.tangempay.deeplink
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
internal sealed class TangemPayPushAction {
data object CardReady : TangemPayPushAction()
data class TransactionSpend(
val transaction: TangemPayTxHistoryItem,
val customerId: String,
) : TangemPayPushAction()
data object TopUp : TangemPayPushAction()
data class CollateralTransaction(
val transaction: TangemPayTxHistoryItem,
val customerId: String,
) : TangemPayPushAction()
}

View file

@ -0,0 +1,90 @@
package com.tangem.features.tangempay.deeplink
import com.tangem.domain.pay.utils.TangemPayTxHistoryItemStatusConverter
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.utils.extensions.orZero
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import java.math.BigDecimal
import java.util.Currency
object TangemPayPushPayloadToTxHistoryItemConverter {
private const val KEY_ID = "transaction_id"
private const val KEY_AMOUNT = "amount"
private const val KEY_CURRENCY = "currency"
private const val KEY_LOCAL_AMOUNT = "local_amount"
private const val KEY_LOCAL_CURRENCY = "local_currency"
private const val KEY_AUTHORIZED_AMOUNT = "authorized_amount"
private const val KEY_MERCHANT_NAME = "merchant_name"
private const val KEY_ENRICHED_MERCHANT_NAME = "enriched_merchant_name"
private const val KEY_ENRICHED_MERCHANT_ICON = "enriched_merchant_icon"
private const val KEY_ENRICHED_MERCHANT_CATEGORY = "enriched_merchant_category"
private const val KEY_MERCHANT_CATEGORY = "merchant_category"
private const val KEY_MERCHANT_CATEGORY_CODE = "merchant_category_code"
private const val KEY_STATUS = "status"
private const val KEY_DECLINED_REASON = "declined_reason"
private const val KEY_AUTHORIZED_AT = "authorized_at"
private const val KEY_POSTED_AT = "posted_at"
private const val KEY_TRANSACTION_HASH = "transaction_hash"
@Suppress("ComplexCondition")
fun convertSpend(payload: Map<String, String>): TangemPayTxHistoryItem.Spend? {
val id = payload[KEY_ID]?.ifEmpty { null } ?: return null
val amount = payload[KEY_AMOUNT]?.toBigDecimalOrNull() ?: return null
val currency = payload[KEY_CURRENCY]?.let(::parseCurrency) ?: return null
val merchantName = payload[KEY_MERCHANT_NAME] ?: payload[KEY_ENRICHED_MERCHANT_NAME] ?: return null
val status = payload[KEY_STATUS]?.ifEmpty { null } ?: return null
val authorizedAt = payload[KEY_AUTHORIZED_AT]?.let(::parseDateTime) ?: return null
return TangemPayTxHistoryItem.Spend(
id = id,
jsonRepresentation = payload.toString(),
date = authorizedAt.withZone(DateTimeZone.getDefault()),
amount = amount,
currency = currency,
authorizedAmount = payload[KEY_AUTHORIZED_AMOUNT]?.toBigDecimalOrNull().orZero(),
localAmount = payload[KEY_LOCAL_AMOUNT]?.toBigDecimalOrNull(),
localCurrency = payload[KEY_LOCAL_CURRENCY]?.let(::parseCurrency),
enrichedMerchantName = payload[KEY_ENRICHED_MERCHANT_NAME],
merchantName = merchantName,
enrichedMerchantCategory = payload[KEY_ENRICHED_MERCHANT_CATEGORY],
merchantCategoryCode = payload[KEY_MERCHANT_CATEGORY_CODE],
merchantCategory = payload[KEY_MERCHANT_CATEGORY],
status = TangemPayTxHistoryItemStatusConverter.convert(status),
enrichedMerchantIconUrl = payload[KEY_ENRICHED_MERCHANT_ICON],
declinedReason = payload[KEY_DECLINED_REASON],
)
}
@Suppress("ComplexCondition")
fun convertCollateral(payload: Map<String, String>): TangemPayTxHistoryItem.Collateral? {
val id = payload[KEY_ID]?.ifEmpty { null } ?: return null
val amount = payload[KEY_AMOUNT]?.toBigDecimalOrNull() ?: return null
val transactionHash = payload[KEY_TRANSACTION_HASH]?.ifEmpty { null } ?: return null
val postedAt = payload[KEY_POSTED_AT]?.let(::parseDateTime) ?: return null
val currency = payload[KEY_CURRENCY]?.let(::parseCurrency) ?: return null
return TangemPayTxHistoryItem.Collateral(
id = id,
jsonRepresentation = payload.toString(),
date = postedAt.withZone(DateTimeZone.getDefault()),
currency = currency,
amount = amount,
transactionHash = transactionHash,
type = if (amount >= BigDecimal.ZERO) {
TangemPayTxHistoryItem.Type.Deposit
} else {
TangemPayTxHistoryItem.Type.Withdrawal
},
)
}
private fun parseCurrency(code: String): Currency? = runCatching {
return Currency.getInstance(code.uppercase())
}.getOrNull()
private fun parseDateTime(value: String): DateTime? = runCatching {
return DateTime.parse(value).withZone(DateTimeZone.getDefault())
}.getOrNull()
}

View file

@ -1,7 +1,9 @@
package com.tangem.features.tangempay.di
import com.tangem.features.tangempay.deeplink.DefaultOnboardVisaDeepLinkHandler
import com.tangem.features.tangempay.deeplink.DefaultTangemPayMainDeepLinkHandler
import com.tangem.features.tangempay.deeplink.OnboardVisaDeepLinkHandler
import com.tangem.features.tangempay.deeplink.TangemPayMainDeepLinkHandler
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@ -15,4 +17,10 @@ internal interface TangemPayDeeplinkModule {
@Binds
@Singleton
fun bindDeepLinkHandlerFactory(impl: DefaultOnboardVisaDeepLinkHandler.Factory): OnboardVisaDeepLinkHandler.Factory
@Binds
@Singleton
fun bindTangemPayMainDeepLinkHandlerFactory(
impl: DefaultTangemPayMainDeepLinkHandler.Factory,
): TangemPayMainDeepLinkHandler.Factory
}

View file

@ -0,0 +1,400 @@
package com.tangem.features.tangempay.deeplink
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.google.common.truth.Truth.assertThat
import org.junit.jupiter.api.Test
import java.math.BigDecimal
class TangemPayPushPayloadToTxHistoryItemConverterTest {
@Test
fun `convertSpend returns Spend tx with all fields when payload is complete`() {
val payload = mapOf(
"transaction_id" to "txn-123",
"amount" to "5.24",
"currency" to "usd",
"local_amount" to "5.24",
"local_currency" to "usd",
"authorized_amount" to "5.24",
"merchant_name" to "PLAYSTATION NETWORK",
"enriched_merchant_name" to "Playstation",
"enriched_merchant_icon" to "https://example.com/icon.png",
"enriched_merchant_category" to "Gaming",
"merchant_category" to "Digital Goods",
"merchant_category_code" to "5818",
"status" to "completed",
"declined_reason" to "",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNotNull()
assertThat(result!!.id).isEqualTo("txn-123")
assertThat(result.amount).isEqualTo(BigDecimal("5.24"))
assertThat(result.currency.currencyCode).isEqualTo("USD")
assertThat(result.localAmount).isEqualTo(BigDecimal("5.24"))
assertThat(result.localCurrency?.currencyCode).isEqualTo("USD")
assertThat(result.authorizedAmount).isEqualTo(BigDecimal("5.24"))
assertThat(result.merchantName).isEqualTo("PLAYSTATION NETWORK")
assertThat(result.enrichedMerchantName).isEqualTo("Playstation")
assertThat(result.enrichedMerchantIconUrl).isEqualTo("https://example.com/icon.png")
assertThat(result.enrichedMerchantCategory).isEqualTo("Gaming")
assertThat(result.merchantCategory).isEqualTo("Digital Goods")
assertThat(result.merchantCategoryCode).isEqualTo("5818")
assertThat(result.status).isEqualTo(TangemPayTxHistoryItem.Status.COMPLETED)
}
@Test
fun `convertSpend returns null when transaction_id is missing`() {
val payload = mapOf(
"amount" to "5.24",
"currency" to "usd",
"merchant_name" to "Test",
"status" to "completed",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNull()
}
@Test
fun `convertSpend returns null when amount is missing`() {
val payload = mapOf(
"transaction_id" to "txn-123",
"currency" to "usd",
"merchant_name" to "Test",
"status" to "completed",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNull()
}
@Test
fun `convertSpend returns null when currency is missing`() {
val payload = mapOf(
"transaction_id" to "txn-123",
"amount" to "5.24",
"merchant_name" to "Test",
"status" to "completed",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNull()
}
@Test
fun `convertSpend returns null when merchant_name is missing`() {
val payload = mapOf(
"transaction_id" to "txn-123",
"amount" to "5.24",
"currency" to "usd",
"status" to "completed",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNull()
}
@Test
fun `convertSpend falls back to enriched_merchant_name when merchant_name is absent`() {
val payload = mapOf(
"transaction_id" to "txn-123",
"amount" to "5.24",
"currency" to "usd",
"enriched_merchant_name" to "Playstation",
"status" to "completed",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNotNull()
assertThat(result!!.merchantName).isEqualTo("Playstation")
}
@Test
fun `convertSpend returns null when status is missing`() {
val payload = mapOf(
"transaction_id" to "txn-123",
"amount" to "5.24",
"currency" to "usd",
"merchant_name" to "Test",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNull()
}
@Test
fun `convertSpend returns null when authorized_at is missing`() {
val payload = mapOf(
"transaction_id" to "txn-123",
"amount" to "5.24",
"currency" to "usd",
"merchant_name" to "Test",
"status" to "completed",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNull()
}
@Test
fun `convertSpend returns null when amount is not a number`() {
val payload = mapOf(
"transaction_id" to "txn-123",
"amount" to "abc",
"currency" to "usd",
"merchant_name" to "Test",
"status" to "completed",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNull()
}
@Test
fun `convertSpend returns null when currency is invalid`() {
val payload = mapOf(
"transaction_id" to "txn-123",
"amount" to "5.24",
"currency" to "INVALID",
"merchant_name" to "Test",
"status" to "completed",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNull()
}
@Test
fun `convertSpend maps all statuses correctly`() {
fun spendPayloadWithStatus(status: String) = mapOf(
"transaction_id" to "txn-123",
"amount" to "1.00",
"currency" to "usd",
"merchant_name" to "Test",
"status" to status,
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
assertThat(TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(spendPayloadWithStatus("pending"))!!.status)
.isEqualTo(TangemPayTxHistoryItem.Status.PENDING)
assertThat(TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(spendPayloadWithStatus("reserved"))!!.status)
.isEqualTo(TangemPayTxHistoryItem.Status.RESERVED)
assertThat(TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(spendPayloadWithStatus("completed"))!!.status)
.isEqualTo(TangemPayTxHistoryItem.Status.COMPLETED)
assertThat(TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(spendPayloadWithStatus("declined"))!!.status)
.isEqualTo(TangemPayTxHistoryItem.Status.DECLINED)
assertThat(TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(spendPayloadWithStatus("reversed"))!!.status)
.isEqualTo(TangemPayTxHistoryItem.Status.REVERSED)
assertThat(TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(spendPayloadWithStatus("unknown_value"))!!.status)
.isEqualTo(TangemPayTxHistoryItem.Status.UNKNOWN)
}
@Test
fun `convertSpend returns null when transaction_id is empty`() {
val payload = mapOf(
"transaction_id" to "",
"amount" to "5.24",
"currency" to "usd",
"merchant_name" to "Test",
"status" to "completed",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNull()
}
@Test
fun `convertCollateral returns Collateral with all fields when payload is complete`() {
val payload = mapOf(
"transaction_id" to "col-123",
"amount" to "50.00",
"currency" to "usd",
"transaction_hash" to "0xabc123",
"posted_at" to "2025-10-25T19:22:22.597Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(payload)
assertThat(result).isNotNull()
assertThat(result!!.id).isEqualTo("col-123")
assertThat(result.amount).isEqualTo(BigDecimal("50.00"))
assertThat(result.currency.currencyCode).isEqualTo("USD")
assertThat(result.transactionHash).isEqualTo("0xabc123")
assertThat(result.type).isEqualTo(TangemPayTxHistoryItem.Type.Deposit)
}
@Test
fun `convertCollateral returns Withdrawal type for negative amount`() {
val payload = mapOf(
"transaction_id" to "col-456",
"amount" to "-10.00",
"currency" to "usd",
"transaction_hash" to "0xdef789",
"posted_at" to "2025-10-25T19:22:22.597Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(payload)
assertThat(result).isNotNull()
assertThat(result!!.type).isEqualTo(TangemPayTxHistoryItem.Type.Withdrawal)
}
@Test
fun `convertCollateral returns null when transaction_id is missing`() {
val payload = mapOf(
"amount" to "50.00",
"currency" to "usd",
"transaction_hash" to "0xabc123",
"posted_at" to "2025-10-25T19:22:22.597Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(payload)
assertThat(result).isNull()
}
@Test
fun `convertCollateral returns null when amount is missing`() {
val payload = mapOf(
"transaction_id" to "col-123",
"currency" to "usd",
"transaction_hash" to "0xabc123",
"posted_at" to "2025-10-25T19:22:22.597Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(payload)
assertThat(result).isNull()
}
@Test
fun `convertCollateral returns null when transaction_hash is missing`() {
val payload = mapOf(
"transaction_id" to "col-123",
"amount" to "50.00",
"currency" to "usd",
"posted_at" to "2025-10-25T19:22:22.597Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(payload)
assertThat(result).isNull()
}
@Test
fun `convertCollateral returns null when posted_at is missing`() {
val payload = mapOf(
"transaction_id" to "col-123",
"amount" to "50.00",
"currency" to "usd",
"transaction_hash" to "0xabc123",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(payload)
assertThat(result).isNull()
}
@Test
fun `convertCollateral returns null when currency is missing`() {
val payload = mapOf(
"transaction_id" to "col-123",
"amount" to "50.00",
"transaction_hash" to "0xabc123",
"posted_at" to "2025-10-25T19:22:22.597Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(payload)
assertThat(result).isNull()
}
@Test
fun `convertCollateral returns null when transaction_hash is empty`() {
val payload = mapOf(
"transaction_id" to "col-123",
"amount" to "50.00",
"currency" to "usd",
"transaction_hash" to "",
"posted_at" to "2025-10-25T19:22:22.597Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(payload)
assertThat(result).isNull()
}
@Test
fun `convertSpend handles optional fields as null`() {
val payload = mapOf(
"transaction_id" to "txn-123",
"amount" to "5.24",
"currency" to "usd",
"merchant_name" to "Test",
"status" to "completed",
"authorized_at" to "2025-10-24T10:32:24.496Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(payload)
assertThat(result).isNotNull()
assertThat(result!!.localAmount).isNull()
assertThat(result.localCurrency).isNull()
assertThat(result.enrichedMerchantName).isNull()
assertThat(result.enrichedMerchantIconUrl).isNull()
assertThat(result.enrichedMerchantCategory).isNull()
assertThat(result.merchantCategory).isNull()
assertThat(result.merchantCategoryCode).isNull()
assertThat(result.declinedReason).isNull()
}
@Test
fun `convertCollateral returns Deposit type for zero amount`() {
val payload = mapOf(
"transaction_id" to "col-789",
"amount" to "0",
"currency" to "usd",
"transaction_hash" to "0xdef",
"posted_at" to "2025-10-25T19:22:22.597Z",
)
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(payload)
assertThat(result).isNotNull()
assertThat(result!!.type).isEqualTo(TangemPayTxHistoryItem.Type.Deposit)
}
@Test
fun `convertSpend returns null for empty payload`() {
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertSpend(emptyMap())
assertThat(result).isNull()
}
@Test
fun `convertCollateral returns null for empty payload`() {
val result = TangemPayPushPayloadToTxHistoryItemConverter.convertCollateral(emptyMap())
assertThat(result).isNull()
}
}

View file

@ -14,6 +14,7 @@ dependencies {
/** Project - Domain */
implementation(projects.domain.models)
implementation(projects.domain.visa.models)
/** Tangem libraries */
implementation(tangemDeps.card.core)

View file

@ -1,12 +1,20 @@
package com.tangem.features.wallet.deeplink
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import kotlinx.coroutines.flow.Flow
interface WalletDeepLinkActionTrigger {
fun selectWallet(userWalletId: UserWalletId)
fun showTangemPayTransaction(transaction: TangemPayTxHistoryItem, customerId: String)
}
interface WalletDeepLinkActionListener {
val selectWalletFlow: Flow<UserWalletId>
val showTangemPayTransactionFlow: Flow<TangemPayTransactionDeepLinkData>
}
data class TangemPayTransactionDeepLinkData(
val transaction: TangemPayTxHistoryItem,
val customerId: String,
)

View file

@ -152,6 +152,7 @@ dependencies {
implementation(projects.features.feed.api)
implementation(projects.features.promoBanners.api)
implementation(projects.features.tangempay.main.api)
implementation(projects.features.tangempay.details.api)
/** Common modules */
implementation(projects.common)

View file

@ -42,6 +42,7 @@ import com.tangem.features.pushnotifications.api.PushNotificationsBottomSheetCom
import com.tangem.features.pushnotifications.api.PushNotificationsParams
import com.tangem.features.send.v2.api.NetworkSelectionComponent
import com.tangem.features.tangempay.component.TangemPayMainBlockComponent
import com.tangem.features.tangempay.components.TangemPayTransactionBottomSheetComponent
import com.tangem.features.tokenreceive.TokenReceiveComponent
import com.tangem.features.yield.supply.api.YieldSupplyDepositedWarningComponent
import dagger.assisted.Assisted
@ -56,6 +57,7 @@ internal class WalletComponent @AssistedInject constructor(
@Assisted navigate: (WalletRoute) -> Unit,
feedEntryComponentFactory: FeedEntryComponent.Factory,
tangemPayMainBlockComponentFactory: TangemPayMainBlockComponent.Factory,
private val tangemPayTransactionBottomSheetComponentFactory: TangemPayTransactionBottomSheetComponent.Factory,
private val renameWalletComponentFactory: RenameWalletComponent.Factory,
private val askBiometryComponentFactory: AskBiometryComponent.Factory,
private val pushNotificationsBottomSheetComponent: PushNotificationsBottomSheetComponent.Factory,
@ -242,6 +244,18 @@ internal class WalletComponent @AssistedInject constructor(
),
)
}
is WalletDialogConfig.TangemPayTransactionDetails -> {
tangemPayTransactionBottomSheetComponentFactory.create(
context = childByContext(componentContext),
params = TangemPayTransactionBottomSheetComponent.Params(
isBalanceHidden = dialogConfig.isBalanceHidden,
transaction = dialogConfig.transaction,
userWalletId = dialogConfig.walletId,
customerId = dialogConfig.customerId,
onDismiss = model.innerWalletRouter.dialogNavigation::dismiss,
),
)
}
}
},
)

View file

@ -158,6 +158,7 @@ internal class WalletModel @Inject constructor(
subscribeToScreenBackgroundState()
subscribeOnPushNotificationsPermission()
subscribeTangemPayOnWalletState()
subscribeToTangemPayTransactionDeepLink()
subscribeToMainScreenQrScanning()
enableNotificationsIfNeeded()
applyPendingAssetsDiscovery()
@ -780,6 +781,21 @@ internal class WalletModel @Inject constructor(
.launchIn(modelScope)
}
private fun subscribeToTangemPayTransactionDeepLink() {
walletDeepLinkActionListener.showTangemPayTransactionFlow
.onEach { data ->
innerWalletRouter.dialogNavigation.activate(
WalletDialogConfig.TangemPayTransactionDetails(
isBalanceHidden = stateHolder.value.isHidingMode,
transaction = data.transaction,
walletId = stateHolder.getSelectedWalletId(),
customerId = data.customerId,
),
)
}
.launchIn(modelScope)
}
private suspend fun handleQrResult(qrCode: String, resultSource: QrResultSource) {
val target = resolveQrSendTargetsUseCase(qrCode)
handleQrTarget(target, resultSource)

View file

@ -1,6 +1,8 @@
package com.tangem.feature.wallet.deeplink
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.features.wallet.deeplink.TangemPayTransactionDeepLinkData
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionTrigger
import kotlinx.coroutines.channels.Channel
@ -18,7 +20,15 @@ internal class DefaultWalletDeepLinkActionTrigger @Inject constructor() :
override val selectWalletFlow: Flow<UserWalletId>
get() = _selectWalletFlow.receiveAsFlow()
private val _showTangemPayTransactionFlow = Channel<TangemPayTransactionDeepLinkData>()
override val showTangemPayTransactionFlow: Flow<TangemPayTransactionDeepLinkData>
get() = _showTangemPayTransactionFlow.receiveAsFlow()
override fun selectWallet(userWalletId: UserWalletId) {
_selectWalletFlow.trySend(userWalletId)
}
override fun showTangemPayTransaction(transaction: TangemPayTxHistoryItem, customerId: String) {
_showTangemPayTransactionFlow.trySend(TangemPayTransactionDeepLinkData(transaction, customerId))
}
}

View file

@ -8,6 +8,7 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.serialization.BigDecimalSerializer
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.model.details.TokenAction
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import kotlinx.collections.immutable.ImmutableList
import kotlinx.serialization.Serializable
import java.math.BigDecimal
@ -53,6 +54,14 @@ internal sealed interface WalletDialogConfig {
@Serializable
data class AddAndManage(val userWalletId: UserWalletId) : WalletDialogConfig
@Serializable
data class TangemPayTransactionDetails(
val isBalanceHidden: Boolean,
val transaction: TangemPayTxHistoryItem,
val walletId: UserWalletId,
val customerId: String,
) : WalletDialogConfig
@Serializable
data class OrganizeTokens(val userWalletId: UserWalletId) : WalletDialogConfig