Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-23 18:54:08 +05:00
parent cea4ba817a
commit 737bb507d9
19 changed files with 221 additions and 75 deletions

View file

@ -53,6 +53,12 @@ internal class DefaultTangemPayStorage @Inject constructor(
} }
} }
override suspend fun clearCustomerWalletAddress(userWalletId: UserWalletId) {
withContext(dispatcherProvider.io) {
appPreferencesStore.store(PreferencesKeys.getTangemPayCustomerWalletAddressKey(userWalletId), "")
}
}
override suspend fun storeAuthTokens(customerWalletAddress: String, tokens: TangemPayAuthTokens) = override suspend fun storeAuthTokens(customerWalletAddress: String, tokens: TangemPayAuthTokens) =
withContext(dispatcherProvider.io) { withContext(dispatcherProvider.io) {
val json = tokensAdapter.toJson(tokens) val json = tokensAdapter.toJson(tokens)
@ -70,6 +76,10 @@ internal class DefaultTangemPayStorage @Inject constructor(
?.let(tokensAdapter::fromJson) ?.let(tokensAdapter::fromJson)
} }
override suspend fun clearAuthTokens(customerWalletAddress: String) {
secureStorage.delete(createAuthTokensKey(customerWalletAddress))
}
override suspend fun storeOrderId(customerWalletAddress: String, orderId: String) { override suspend fun storeOrderId(customerWalletAddress: String, orderId: String) {
withContext(dispatcherProvider.io) { withContext(dispatcherProvider.io) {
appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), orderId) appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), orderId)
@ -112,6 +122,7 @@ internal class DefaultTangemPayStorage @Inject constructor(
override suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String) = override suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String) =
withContext(dispatcherProvider.io) { withContext(dispatcherProvider.io) {
secureStorage.delete(createAuthTokensKey(customerWalletAddress)) secureStorage.delete(createAuthTokensKey(customerWalletAddress))
appPreferencesStore.store(PreferencesKeys.getTangemPayCheckCustomerByWalletId(userWalletId), false)
appPreferencesStore.store(PreferencesKeys.getTangemPayCustomerWalletAddressKey(userWalletId), "") appPreferencesStore.store(PreferencesKeys.getTangemPayCustomerWalletAddressKey(userWalletId), "")
appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), "") appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), "")
appPreferencesStore.store(PreferencesKeys.getTangemPayAddToWalletKey(customerWalletAddress), false) appPreferencesStore.store(PreferencesKeys.getTangemPayAddToWalletKey(customerWalletAddress), false)

View file

@ -29,6 +29,12 @@ interface TangemPayApi {
@Path("customer_wallet_id") customerWalletId: String, @Path("customer_wallet_id") customerWalletId: String,
): ApiResponse<CheckCustomerWalletResponse> ): ApiResponse<CheckCustomerWalletResponse>
@PATCH("v1/customer/pay-enabled")
suspend fun setTangemPayEnabledStatus(
@Header("Authorization") authHeader: String,
@Body body: SetTangemPayEnabledRequest,
): ApiResponse<Any>
@POST("v1/deeplink/validate") @POST("v1/deeplink/validate")
suspend fun validateDeeplink(@Body body: DeeplinkValidityRequest): ApiResponse<DeeplinkValidityResponse> suspend fun validateDeeplink(@Body body: DeeplinkValidityRequest): ApiResponse<DeeplinkValidityResponse>

View file

@ -0,0 +1,9 @@
package com.tangem.datasource.api.pay.models.request
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class SetTangemPayEnabledRequest(
@Json(name = "is_tangem_pay_enabled") val isTangemPayEnabled: Boolean,
)

View file

@ -9,5 +9,6 @@ data class CheckCustomerWalletResponse(
) { ) {
data class Result( data class Result(
@Json(name = "id") val id: String?, @Json(name = "id") val id: String?,
@Json(name = "is_tangem_pay_enabled") val isTangemPayEnabled: Boolean?,
) )
} }

View file

@ -8,9 +8,12 @@ interface TangemPayStorage {
suspend fun storeCustomerWalletAddress(userWalletId: UserWalletId, customerWalletAddress: String) suspend fun storeCustomerWalletAddress(userWalletId: UserWalletId, customerWalletAddress: String)
suspend fun getCustomerWalletAddress(userWalletId: UserWalletId): String? suspend fun getCustomerWalletAddress(userWalletId: UserWalletId): String?
suspend fun clearCustomerWalletAddress(userWalletId: UserWalletId)
suspend fun storeAuthTokens(customerWalletAddress: String, tokens: TangemPayAuthTokens) suspend fun storeAuthTokens(customerWalletAddress: String, tokens: TangemPayAuthTokens)
suspend fun getAuthTokens(customerWalletAddress: String): TangemPayAuthTokens? suspend fun getAuthTokens(customerWalletAddress: String): TangemPayAuthTokens?
suspend fun clearAuthTokens(customerWalletAddress: String)
suspend fun storeOrderId(customerWalletAddress: String, orderId: String) suspend fun storeOrderId(customerWalletAddress: String, orderId: String)

View file

@ -1072,6 +1072,8 @@
<string name="reset_cards_dialog_next_device_description">Bitte setze das nächste Gerät zurück, um fortzufahren.</string> <string name="reset_cards_dialog_next_device_description">Bitte setze das nächste Gerät zurück, um fortzufahren.</string>
<string name="ring_promo_text">Ringbesitzer erhalten bis zum 15.11. 3 provisionsfreie Swaps auf Changelly!</string> <string name="ring_promo_text">Ringbesitzer erhalten bis zum 15.11. 3 provisionsfreie Swaps auf Changelly!</string>
<string name="ring_promo_title">Jetzt mit 0 % Gebühren tauschen!</string> <string name="ring_promo_title">Jetzt mit 0 % Gebühren tauschen!</string>
<string name="root_detected_warning_description">Geräte mit Root-Zugriff gelten als weniger sicher. Deine Daten können zusätzlichen Risiken ausgesetzt sein.</string>
<string name="root_detected_warning_title">Root-Zugriff erkannt</string>
<string name="save_user_wallet_agreement_access_description">Melde dich bei der App an und überprüfe dein Guthaben, ohne die Karte oder Ring zu scannen</string> <string name="save_user_wallet_agreement_access_description">Melde dich bei der App an und überprüfe dein Guthaben, ohne die Karte oder Ring zu scannen</string>
<string name="save_user_wallet_agreement_access_title">Zugriff auf die App</string> <string name="save_user_wallet_agreement_access_title">Zugriff auf die App</string>
<string name="save_user_wallet_agreement_allow_biometrics">Nutzung biometrischer Daten zulassen</string> <string name="save_user_wallet_agreement_allow_biometrics">Nutzung biometrischer Daten zulassen</string>
@ -1990,6 +1992,7 @@
<string name="yield_module_fee_policy_sheet_title">Gebührenpolitik</string> <string name="yield_module_fee_policy_sheet_title">Gebührenpolitik</string>
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem erhebt außerdem eine Servicegebühr von 15% auf den erzielten Ertrag.</string> <string name="yield_module_fee_policy_tangem_service_fee_title">Tangem erhebt außerdem eine Servicegebühr von 15% auf den erzielten Ertrag.</string>
<string name="yield_module_high_fee_error">Deine Gelder werden automatisch an Aave überwiesen, sobald die Netzwerkgebühren niedriger sind oder Dein Guthaben den erforderlichen Mindestbetrag erreicht.</string> <string name="yield_module_high_fee_error">Deine Gelder werden automatisch an Aave überwiesen, sobald die Netzwerkgebühren niedriger sind oder Dein Guthaben den erforderlichen Mindestbetrag erreicht.</string>
<string name="yield_module_high_network_fees_notification_description">Die Gebühren sind aufgrund der hohen Marktaktivität derzeit höher als üblich. Du kannst jetzt fortfahren oder später noch einmal vorbeischauen, wenn die Gebühren niedriger sind.</string>
<string name="yield_module_high_network_fees_notification_title">Hohe Netzwerkgebühren</string> <string name="yield_module_high_network_fees_notification_title">Hohe Netzwerkgebühren</string>
<string name="yield_module_historical_returns">Historische Renditen</string> <string name="yield_module_historical_returns">Historische Renditen</string>
<string name="yield_module_main_screen_promo_banner_message">Aktiviere %1$s%% Jahreszins auf Dein Guthaben</string> <string name="yield_module_main_screen_promo_banner_message">Aktiviere %1$s%% Jahreszins auf Dein Guthaben</string>

View file

@ -916,6 +916,8 @@
<string name="reset_card_without_backup_to_factory_message">El reseteo a valores de fábrica eliminará completamente la billetera de la tarjeta/anillo seleccionado y lo eliminará de la app. No podrá restaurar la billetera actual.</string> <string name="reset_card_without_backup_to_factory_message">El reseteo a valores de fábrica eliminará completamente la billetera de la tarjeta/anillo seleccionado y lo eliminará de la app. No podrá restaurar la billetera actual.</string>
<string name="ring_promo_text">Si tiene un Anillo Tangem, ¡3 swaps sin comisión en Changelly hasta el 15/11!</string> <string name="ring_promo_text">Si tiene un Anillo Tangem, ¡3 swaps sin comisión en Changelly hasta el 15/11!</string>
<string name="ring_promo_title">¡Intercambia con 0% de comisión!</string> <string name="ring_promo_title">¡Intercambia con 0% de comisión!</string>
<string name="root_detected_warning_description">Los dispositivos con jailbreak se consideran menos seguros. Sus datos podrían estar expuestos a riesgos adicionales.</string>
<string name="root_detected_warning_title">Acceso root detectado</string>
<string name="save_user_wallet_agreement_access_description">Inicie sesión en la app y comprueba su saldo sin escanear la tarjeta o el anillo</string> <string name="save_user_wallet_agreement_access_description">Inicie sesión en la app y comprueba su saldo sin escanear la tarjeta o el anillo</string>
<string name="save_user_wallet_agreement_access_title">Acceder a la app</string> <string name="save_user_wallet_agreement_access_title">Acceder a la app</string>
<string name="save_user_wallet_agreement_allow_biometrics">Permitir el uso de biometría</string> <string name="save_user_wallet_agreement_allow_biometrics">Permitir el uso de biometría</string>

View file

@ -1617,7 +1617,7 @@
<string name="visa_unlock_notification_button">ロック解除</string> <string name="visa_unlock_notification_button">ロック解除</string>
<string name="visa_unlock_notification_subtitle">カードをスキャンしてアクセスロックを解除する</string> <string name="visa_unlock_notification_subtitle">カードをスキャンしてアクセスロックを解除する</string>
<string name="visa_unlock_notification_title">ロック解除が必要</string> <string name="visa_unlock_notification_title">ロック解除が必要</string>
<string name="wallet_add_common_title">ウォレットの追加方法を選択してください</string> <string name="wallet_add_common_title">ウォレットの種類を選択します</string>
<string name="wallet_add_hardware_description">Tangemカードまたはリングをスキャンして復元するか、別のウォレットからインポートしてください。</string> <string name="wallet_add_hardware_description">Tangemカードまたはリングをスキャンして復元するか、別のウォレットからインポートしてください。</string>
<string name="wallet_add_hardware_info_create">ハードウェアウォレットを作成</string> <string name="wallet_add_hardware_info_create">ハードウェアウォレットを作成</string>
<string name="wallet_add_hardware_purchase">Tangemウォレットを購入しますか</string> <string name="wallet_add_hardware_purchase">Tangemウォレットを購入しますか</string>

View file

@ -1634,7 +1634,7 @@
<string name="visa_onboarding_pin_not_accepted">ПИН не принят. Попробуйте ещё раз или введите другой код.</string> <string name="visa_onboarding_pin_not_accepted">ПИН не принят. Попробуйте ещё раз или введите другой код.</string>
<string name="visa_onboarding_pin_validation_error_message">Слабый ПИН: не используйте повторы или последовательности.</string> <string name="visa_onboarding_pin_validation_error_message">Слабый ПИН: не используйте повторы или последовательности.</string>
<string name="visa_unlock_notification_button">Разблокировать</string> <string name="visa_unlock_notification_button">Разблокировать</string>
<string name="wallet_add_common_title">Выберите способ добавления кошелька</string> <string name="wallet_add_common_title">Выберите тип кошелька</string>
<string name="wallet_add_hardware_description">Отсканируйте вашу карту или кольцо Tangem, чтобы восстановить её или импортировать из другого кошелька.</string> <string name="wallet_add_hardware_description">Отсканируйте вашу карту или кольцо Tangem, чтобы восстановить её или импортировать из другого кошелька.</string>
<string name="wallet_add_hardware_info_create">Создать аппаратный кошелёк</string> <string name="wallet_add_hardware_info_create">Создать аппаратный кошелёк</string>
<string name="wallet_add_hardware_purchase">Хотите приобрести кошелек Tangem?</string> <string name="wallet_add_hardware_purchase">Хотите приобрести кошелек Tangem?</string>

View file

@ -1501,6 +1501,8 @@
<string name="tangempay_issuing_your_card">Issuing your card</string> <string name="tangempay_issuing_your_card">Issuing your card</string>
<string name="tangempay_issuing_your_card_description">Were getting your card ready. This may take a little time.</string> <string name="tangempay_issuing_your_card_description">Were getting your card ready. This may take a little time.</string>
<string name="tangempay_kyc_card_ready_notification_title">Tangem Pay</string> <string name="tangempay_kyc_card_ready_notification_title">Tangem Pay</string>
<string name="tangempay_kyc_confirm_cancellation_alert_title">Confirm Cancellation</string>
<string name="tangempay_kyc_confirm_cancellation_description">Are you sure you want to stop the KYC process? You can return to it anytime.</string>
<string name="tangempay_kyc_failed_description">We could not verify your profile. If you have any questions, please contact support.</string> <string name="tangempay_kyc_failed_description">We could not verify your profile. If you have any questions, please contact support.</string>
<string name="tangempay_kyc_failed_title">Unfortunately, we couldn\'t verify your identity </string> <string name="tangempay_kyc_failed_title">Unfortunately, we couldn\'t verify your identity </string>
<string name="tangempay_kyc_in_progress">KYC in progress</string> <string name="tangempay_kyc_in_progress">KYC in progress</string>
@ -1686,7 +1688,7 @@
<string name="visa_unlock_notification_button">Unlock</string> <string name="visa_unlock_notification_button">Unlock</string>
<string name="visa_unlock_notification_subtitle">Scan your card to unlock access</string> <string name="visa_unlock_notification_subtitle">Scan your card to unlock access</string>
<string name="visa_unlock_notification_title">Needed unlock</string> <string name="visa_unlock_notification_title">Needed unlock</string>
<string name="wallet_add_common_title">Choose how to add your wallet</string> <string name="wallet_add_common_title">Choose your wallet type</string>
<string name="wallet_add_hardware_description">Scan your Tangem card or ring to restore it or import from another wallet.</string> <string name="wallet_add_hardware_description">Scan your Tangem card or ring to restore it or import from another wallet.</string>
<string name="wallet_add_hardware_info_create">Create Hardware Wallet</string> <string name="wallet_add_hardware_info_create">Create Hardware Wallet</string>
<string name="wallet_add_hardware_purchase">Want to purchase a Tangem Wallet?</string> <string name="wallet_add_hardware_purchase">Want to purchase a Tangem Wallet?</string>

View file

@ -41,6 +41,9 @@ data class MessageBottomSheetUMV2(
} }
} }
@Immutable
data class IconImage(@DrawableRes internal var res: Int) : Element
@Immutable @Immutable
data class Chip( data class Chip(
internal var text: TextReference, internal var text: TextReference,
@ -54,6 +57,7 @@ data class MessageBottomSheetUMV2(
@Immutable @Immutable
data class InfoBlock( data class InfoBlock(
internal var icon: Icon? = null, internal var icon: Icon? = null,
internal var iconImage: IconImage? = null,
internal var chip: Chip? = null, internal var chip: Chip? = null,
var title: TextReference? = null, var title: TextReference? = null,
var body: TextReference? = null, var body: TextReference? = null,
@ -106,6 +110,10 @@ fun MessageBottomSheetUMV2.InfoBlock.icon(@DrawableRes res: Int, init: MessageBo
icon = MessageBottomSheetUMV2.Icon(res).apply(init) icon = MessageBottomSheetUMV2.Icon(res).apply(init)
} }
fun MessageBottomSheetUMV2.InfoBlock.iconImage(@DrawableRes res: Int) = apply {
iconImage = MessageBottomSheetUMV2.IconImage(res)
}
fun MessageBottomSheetUMV2.InfoBlock.chip(text: TextReference, init: MessageBottomSheetUMV2.Chip.() -> Unit = {}) = fun MessageBottomSheetUMV2.InfoBlock.chip(text: TextReference, init: MessageBottomSheetUMV2.Chip.() -> Unit = {}) =
apply { apply {
chip = MessageBottomSheetUMV2.Chip(text).apply(init) chip = MessageBottomSheetUMV2.Chip(text).apply(init)

View file

@ -1,14 +1,18 @@
package com.tangem.core.ui.components.bottomsheets.message package com.tangem.core.ui.components.bottomsheets.message
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -88,9 +92,7 @@ fun MessageBottomSheetV2Content(state: MessageBottomSheetUMV2, modifier: Modifie
@Composable @Composable
private fun ContentContainer(state: MessageBottomSheetUMV2.InfoBlock, modifier: Modifier = Modifier) { private fun ContentContainer(state: MessageBottomSheetUMV2.InfoBlock, modifier: Modifier = Modifier) {
Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) { Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
state.icon?.let { BottomSheetIconContainer(state.icon, state.iconImage)
BottomSheetIcon(it)
}
state.title?.let { title -> state.title?.let { title ->
Text( Text(
modifier = Modifier modifier = Modifier
@ -122,6 +124,26 @@ private fun ContentContainer(state: MessageBottomSheetUMV2.InfoBlock, modifier:
} }
} }
@Suppress("CanBeNonNullable")
@Composable
private fun BottomSheetIconContainer(
icon: MessageBottomSheetUMV2.Icon?,
iconImage: MessageBottomSheetUMV2.IconImage?,
modifier: Modifier = Modifier,
) {
if (icon != null) {
BottomSheetIcon(icon, modifier)
} else if (iconImage != null) {
Image(
modifier = modifier
.size(TangemTheme.dimens.size56)
.clip(CircleShape),
painter = painterResource(id = iconImage.res),
contentDescription = null,
)
}
}
@Composable @Composable
private fun BottomSheetIcon(icon: MessageBottomSheetUMV2.Icon, modifier: Modifier = Modifier) { private fun BottomSheetIcon(icon: MessageBottomSheetUMV2.Icon, modifier: Modifier = Modifier) {
val tint = when (icon.type) { val tint = when (icon.type) {
@ -236,4 +258,33 @@ private fun Preview() {
onDismissRequest = {}, onDismissRequest = {},
) )
} }
}
@Preview
@Composable
private fun Preview2() {
TangemThemePreview {
MessageBottomSheetV2(
messageBottomSheetUM {
infoBlock {
iconImage = MessageBottomSheetUMV2.IconImage(R.drawable.img_visa_notification)
title = TextReference.Str("Title Title Title")
body = TextReference.Str("Body")
chip(text = TextReference.Str("Some chip information"))
}
primaryButton {
text = TextReference.Str("Test")
icon = R.drawable.ic_tangem_24
}
secondaryButton {
icon = R.drawable.ic_tangem_24
text = TextReference.Str("asdasd")
onClick {
closeBs()
}
}
},
onDismissRequest = {},
)
}
} }

View file

@ -4,6 +4,7 @@ import arrow.core.Either
import com.tangem.datasource.api.pay.TangemPayApi import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.models.request.DeeplinkValidityRequest import com.tangem.datasource.api.pay.models.request.DeeplinkValidityRequest
import com.tangem.datasource.api.pay.models.request.OrderRequest import com.tangem.datasource.api.pay.models.request.OrderRequest
import com.tangem.datasource.api.pay.models.request.SetTangemPayEnabledRequest
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
import com.tangem.datasource.local.visa.TangemPayStorage import com.tangem.datasource.local.visa.TangemPayStorage
@ -180,9 +181,10 @@ internal class DefaultOnboardingRepository @Inject constructor(
) )
}.map { response -> }.map { response ->
val id = response.result?.id val id = response.result?.id
val isPaeraCustomer = !id.isNullOrEmpty() val isTangemPayEnabled = response.result?.isTangemPayEnabled == true
tangemPayStorage.storeCheckCustomerWalletResult(userWalletId = userWalletId, isPaeraCustomer) val shouldShowTangemPayBlock = !id.isNullOrEmpty() && isTangemPayEnabled
isPaeraCustomer tangemPayStorage.storeCheckCustomerWalletResult(userWalletId = userWalletId, shouldShowTangemPayBlock)
shouldShowTangemPayBlock
}.mapLeft { error -> }.mapLeft { error ->
if (error is VisaApiError.NotPaeraCustomer) { if (error is VisaApiError.NotPaeraCustomer) {
tangemPayStorage.storeCheckCustomerWalletResult(userWalletId = userWalletId, false) tangemPayStorage.storeCheckCustomerWalletResult(userWalletId = userWalletId, false)
@ -205,4 +207,17 @@ internal class DefaultOnboardingRepository @Inject constructor(
override suspend fun setHideMainOnboardingBanner(userWalletId: UserWalletId) { override suspend fun setHideMainOnboardingBanner(userWalletId: UserWalletId) {
tangemPayStorage.storeHideOnboardingBanner(userWalletId, hide = true) tangemPayStorage.storeHideOnboardingBanner(userWalletId, hide = true)
} }
override suspend fun disableTangemPay(userWalletId: UserWalletId): Either<VisaApiError, Any> {
return requestHelper.performRequest(userWalletId) { authHeader ->
tangemPayApi.setTangemPayEnabledStatus(
authHeader = authHeader,
body = SetTangemPayEnabledRequest(isTangemPayEnabled = false),
)
}.onRight {
val address = requestHelper.getCustomerWalletAddress(userWalletId)
tangemPayStorage.clearAll(userWalletId = userWalletId, customerWalletAddress = address)
setHideMainOnboardingBanner(userWalletId)
}
}
} }

View file

@ -26,9 +26,9 @@ class GetWalletTotalBalanceUseCase(
private val walletBalanceCache = ConcurrentHashMap<UserWalletId, TotalFiatBalance.Loaded>() private val walletBalanceCache = ConcurrentHashMap<UserWalletId, TotalFiatBalance.Loaded>()
operator fun invoke( operator fun invoke(
userTallestIds: Collection<UserWalletId>, userWalletsIds: Collection<UserWalletId>,
): LceFlow<TokenListError, Map<UserWalletId, TotalFiatBalance>> { ): LceFlow<TokenListError, Map<UserWalletId, TotalFiatBalance>> {
val flows = userTallestIds.distinct() val flows = userWalletsIds.distinct()
.map { userWalletId -> .map { userWalletId ->
invoke(userWalletId).map { maybeBalance -> invoke(userWalletId).map { maybeBalance ->
userWalletId to maybeBalance userWalletId to maybeBalance

View file

@ -32,4 +32,6 @@ interface OnboardingRepository {
suspend fun getHideMainOnboardingBanner(userWalletId: UserWalletId): Boolean suspend fun getHideMainOnboardingBanner(userWalletId: UserWalletId): Boolean
suspend fun setHideMainOnboardingBanner(userWalletId: UserWalletId) suspend fun setHideMainOnboardingBanner(userWalletId: UserWalletId)
suspend fun disableTangemPay(userWalletId: UserWalletId): Either<VisaApiError, Any>
} }

View file

@ -6,11 +6,14 @@ import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.res.R import com.tangem.core.res.R
import com.tangem.core.ui.components.bottomsheets.message.* import com.tangem.core.ui.components.bottomsheets.message.*
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.core.ui.message.bottomSheetMessage import com.tangem.core.ui.message.bottomSheetMessage
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.pay.repository.OnboardingRepository import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
@ -26,6 +29,10 @@ internal interface TangemPayIntents {
fun onRefreshPayToken(userWalletId: UserWalletId) fun onRefreshPayToken(userWalletId: UserWalletId)
fun openDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
fun onKycProgressClicked(userWalletId: UserWalletId)
fun onIssuingCardClicked() fun onIssuingCardClicked()
fun onIssuingFailedClicked() fun onIssuingFailedClicked()
@ -47,6 +54,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
private val getWalletMetainfoUseCase: GetWalletMetaInfoUseCase, private val getWalletMetainfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase, private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
private val tangemPayOnboardingRepository: OnboardingRepository,
private val uiMessageSender: UiMessageSender, private val uiMessageSender: UiMessageSender,
) : BaseWalletClickIntents(), TangemPayIntents { ) : BaseWalletClickIntents(), TangemPayIntents {
@ -67,6 +75,51 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
} }
} }
override fun openDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
router.openTangemPayDetails(
userWalletId = userWalletId,
config = config,
)
}
override fun onKycProgressClicked(userWalletId: UserWalletId) {
val cancelKycConfirmDialogMessage = DialogMessage(
title = resourceReference(R.string.tangempay_kyc_confirm_cancellation_alert_title),
message = resourceReference(R.string.tangempay_kyc_confirm_cancellation_description),
firstAction = EventMessageAction(
title = resourceReference(R.string.common_not_now),
onClick = { },
),
secondAction = EventMessageAction(
title = resourceReference(R.string.common_confirm),
onClick = { disableTangemPay(userWalletId) },
),
)
val kycInfoBottomSheet = bottomSheetMessage {
infoBlock {
iconImage(res = com.tangem.core.ui.R.drawable.img_visa_notification)
title = resourceReference(R.string.tangempay_kyc_in_progress)
body = resourceReference(R.string.tangempay_kyc_in_progress_popup_description)
}
primaryButton {
text = resourceReference(R.string.tangempay_kyc_in_progress_notification_button)
onClick = {
router.openTangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding(userWalletId))
closeBs()
}
}
secondaryButton {
text = resourceReference(R.string.tangempay_cancel_kyc)
onClick = {
uiMessageSender.send(cancelKycConfirmDialogMessage)
closeBs()
}
}
}
uiMessageSender.send(kycInfoBottomSheet)
}
override fun onIssuingCardClicked() { override fun onIssuingCardClicked() {
val issuingBottomSheet = bottomSheetMessage { val issuingBottomSheet = bottomSheetMessage {
infoBlock { infoBlock {
@ -132,4 +185,11 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
onboardingRepository.setHideMainOnboardingBanner(userWalletId) onboardingRepository.setHideMainOnboardingBanner(userWalletId)
} }
} }
private fun disableTangemPay(userWalletId: UserWalletId) {
modelScope.launch {
tangemPayOnboardingRepository.disableTangemPay(userWalletId)
.onRight { tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId) }
}
}
} }

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.fiat
@ -11,11 +12,10 @@ import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
import com.tangem.domain.pay.model.MainScreenCustomerInfo import com.tangem.domain.pay.model.MainScreenCustomerInfo
import com.tangem.domain.pay.model.OrderStatus import com.tangem.domain.pay.model.OrderStatus
import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.feature.wallet.child.wallet.model.intents.TangemPayIntents
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createCancelledState
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueProgressState
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createKycInProgressState
import java.util.Currency import java.util.Currency
/** /**
@ -26,12 +26,9 @@ private const val POLYGON_CHAIN_ID = 137
internal class TangemPayUpdateInfoStateTransformer( internal class TangemPayUpdateInfoStateTransformer(
userWalletId: UserWalletId, userWalletId: UserWalletId,
private val value: MainScreenCustomerInfo? = null, private val value: MainScreenCustomerInfo,
private val cardFrozenState: TangemPayCardFrozenState, private val cardFrozenState: TangemPayCardFrozenState,
private val onClickKyc: () -> Unit = {}, private val tangemPayClickIntents: TangemPayIntents,
private val onIssuingCard: () -> Unit = {},
private val onIssuingFailed: () -> Unit = {},
private val openDetails: (config: TangemPayDetailsConfig) -> Unit = {},
) : WalletStateTransformer(userWalletId = userWalletId) { ) : WalletStateTransformer(userWalletId = userWalletId) {
override fun transform(prevState: WalletState): WalletState { override fun transform(prevState: WalletState): WalletState {
@ -44,16 +41,15 @@ internal class TangemPayUpdateInfoStateTransformer(
} }
private fun createInitialState(): TangemPayState { private fun createInitialState(): TangemPayState {
val cardInfo = value?.info?.cardInfo val cardInfo = value.info.cardInfo
val productInstance = value?.info?.productInstance val productInstance = value.info.productInstance
// when statement copied to WalletTangemPayAnalyticsEventSender. Be careful when editing. // when statement copied to WalletTangemPayAnalyticsEventSender. Be careful when editing.
return when { return when {
value == null -> TangemPayState.Empty value.orderStatus == OrderStatus.CANCELED -> createCancelledState()
value.orderStatus == OrderStatus.CANCELED -> createCancelledState(onIssuingFailed) !value.info.isKycApproved -> createKycInProgressState()
!value.info.isKycApproved -> createKycInProgressState(onClickKyc)
cardInfo != null && productInstance != null -> getCardInfoState(cardInfo, productInstance) cardInfo != null && productInstance != null -> getCardInfoState(cardInfo, productInstance)
else -> createIssueProgressState(onIssuingCard) else -> createIssueProgressState()
} }
} }
@ -63,7 +59,8 @@ internal class TangemPayUpdateInfoStateTransformer(
balanceText = TextReference.Str(getBalanceText(cardInfo)), balanceText = TextReference.Str(getBalanceText(cardInfo)),
balanceSymbol = stringReference("USDC"), // TODO hardcode for now balanceSymbol = stringReference("USDC"), // TODO hardcode for now
onClick = { onClick = {
openDetails( tangemPayClickIntents.openDetails(
userWalletId,
TangemPayDetailsConfig( TangemPayDetailsConfig(
cardId = productInstance.cardId, cardId = productInstance.cardId,
isPinSet = cardInfo.isPinSet, isPinSet = cardInfo.isPinSet,
@ -82,4 +79,28 @@ internal class TangemPayUpdateInfoStateTransformer(
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol) fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
} }
} }
private fun createKycInProgressState(): TangemPayState = Progress(
title = TextReference.Res(R.string.tangempay_payment_account),
description = TextReference.Res(R.string.tangempay_kyc_in_progress),
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
iconRes = R.drawable.ic_promo_kyc_36,
onButtonClick = { tangemPayClickIntents.onKycProgressClicked(userWalletId) },
)
private fun createIssueProgressState(): TangemPayState = Progress(
title = TextReference.Res(R.string.tangempay_payment_account),
description = TextReference.Res(R.string.tangempay_issuing_your_card),
buttonText = TextReference.EMPTY,
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
onButtonClick = tangemPayClickIntents::onIssuingCardClicked,
showProgress = true,
)
private fun createCancelledState(): 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,
)
} }

View file

@ -1,33 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.util
import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.TextReference
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
internal object TangemPayStateCreator {
fun createKycInProgressState(onClickKyc: () -> Unit): TangemPayState = Progress(
title = TextReference.Res(R.string.tangempay_payment_account),
description = TextReference.Res(R.string.tangempay_kyc_in_progress),
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
iconRes = R.drawable.ic_promo_kyc_36,
onButtonClick = onClickKyc,
)
fun createIssueProgressState(onIssuingCardClick: () -> Unit): TangemPayState = Progress(
title = TextReference.Res(R.string.tangempay_payment_account),
description = TextReference.Res(R.string.tangempay_issuing_your_card),
buttonText = TextReference.EMPTY,
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
onButtonClick = onIssuingCardClick,
showProgress = true,
)
fun createCancelledState(onIssueFailedClick: () -> Unit): 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 = onIssueFailedClick,
)
}

View file

@ -1,6 +1,5 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.common.routing.AppRoute
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.MainCustomerInfoContentState import com.tangem.domain.pay.model.MainCustomerInfoContentState
@ -10,7 +9,6 @@ import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletTangemPayAnalyticsEventSender import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletTangemPayAnalyticsEventSender
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.* import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
@ -28,7 +26,6 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
@Assisted private val userWallet: UserWallet, @Assisted private val userWallet: UserWallet,
private val stateController: WalletStateController, private val stateController: WalletStateController,
private val clickIntents: WalletClickIntents, private val clickIntents: WalletClickIntents,
private val innerWalletRouter: InnerWalletRouter,
private val cardDetailsRepository: TangemPayCardDetailsRepository, private val cardDetailsRepository: TangemPayCardDetailsRepository,
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase, private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
private val analytics: WalletTangemPayAnalyticsEventSender, private val analytics: WalletTangemPayAnalyticsEventSender,
@ -102,19 +99,7 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
userWalletId = userWalletId, userWalletId = userWalletId,
value = data, value = data,
cardFrozenState = cardFrozenState, cardFrozenState = cardFrozenState,
onClickKyc = { tangemPayClickIntents = clickIntents,
innerWalletRouter.openTangemPayOnboarding(
mode = AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding(userWalletId),
)
},
onIssuingCard = clickIntents::onIssuingCardClicked,
onIssuingFailed = clickIntents::onIssuingFailedClicked,
openDetails = { config ->
innerWalletRouter.openTangemPayDetails(
userWalletId = userWalletId,
config = config,
)
},
), ),
) )
} }