Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-24 12:01:22 +05:00
parent bbc732b01f
commit 1037d2ff10
9 changed files with 79 additions and 22 deletions

View file

@ -6,6 +6,7 @@ import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectMapSync
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.getSyncOrNull
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.datasource.local.visa.TangemPayStorage
@ -21,6 +22,7 @@ import javax.inject.Singleton
private const val AUTH_TOKENS_DEFAULT_KEY = "tangem_pay_default_key"
private const val WITHDRAW_ORDER_ID_KEY = "tangem_pay_withdraw_order_id_key"
@Suppress("TooManyFunctions")
@Singleton
internal class DefaultTangemPayStorage @Inject constructor(
@ApplicationContext applicationContext: Context,
@ -119,16 +121,6 @@ internal class DefaultTangemPayStorage @Inject constructor(
return appPreferencesStore.getSyncOrNull(PreferencesKeys.getTangemPayCheckCustomerByWalletId(userWalletId))
}
override suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String) =
withContext(dispatcherProvider.io) {
secureStorage.delete(createAuthTokensKey(customerWalletAddress))
appPreferencesStore.store(PreferencesKeys.getTangemPayCheckCustomerByWalletId(userWalletId), false)
appPreferencesStore.store(PreferencesKeys.getTangemPayCustomerWalletAddressKey(userWalletId), "")
appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), "")
appPreferencesStore.store(PreferencesKeys.getTangemPayAddToWalletKey(customerWalletAddress), false)
appPreferencesStore.store(PreferencesKeys.getTangemPayHideOnboardingKey(userWalletId), false)
}
override suspend fun storeWithdrawOrder(userWalletId: UserWalletId, orderId: String) {
appPreferencesStore.editData { mutablePreferences ->
val orders = mutablePreferences.getObjectMap<String>(PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY)
@ -170,6 +162,28 @@ internal class DefaultTangemPayStorage @Inject constructor(
}
}
override suspend fun storeTangemPayEligibility(eligibility: Boolean) {
withContext(dispatcherProvider.io) {
appPreferencesStore.store(key = PreferencesKeys.TANGEM_PAY_ELIGIBILITY_KEY, value = eligibility)
}
}
override suspend fun getTangemPayEligibility(): Boolean {
return withContext(dispatcherProvider.io) {
appPreferencesStore.getSyncOrDefault(key = PreferencesKeys.TANGEM_PAY_ELIGIBILITY_KEY, default = false)
}
}
override suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String) =
withContext(dispatcherProvider.io) {
secureStorage.delete(createAuthTokensKey(customerWalletAddress))
appPreferencesStore.store(PreferencesKeys.getTangemPayCheckCustomerByWalletId(userWalletId), false)
appPreferencesStore.store(PreferencesKeys.getTangemPayCustomerWalletAddressKey(userWalletId), "")
appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), "")
appPreferencesStore.store(PreferencesKeys.getTangemPayAddToWalletKey(customerWalletAddress), false)
appPreferencesStore.store(PreferencesKeys.getTangemPayHideOnboardingKey(userWalletId), false)
}
private fun createAuthTokensKey(address: String): String = "${AUTH_TOKENS_DEFAULT_KEY}_$address"
private fun createWithdrawOrderIdKey(userWalletId: UserWalletId): String = "${WITHDRAW_ORDER_ID_KEY}_$userWalletId"

View file

@ -154,6 +154,7 @@ object PreferencesKeys {
}
val TANGEM_PAY_WITHDRAW_ORDERS_KEY by lazy { stringPreferencesKey(name = "tangemPayWithdrawOrders") }
val TANGEM_PAY_ELIGIBILITY_KEY by lazy { booleanPreferencesKey(name = "tangemPayEligibility") }
fun getShouldShowNotificationKey(key: String) = booleanPreferencesKey("showShowNotificationUM_$key")
// endregion

View file

@ -3,6 +3,7 @@ package com.tangem.datasource.local.visa
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.TangemPayAuthTokens
@Suppress("TooManyFunctions")
interface TangemPayStorage {
suspend fun storeCustomerWalletAddress(userWalletId: UserWalletId, customerWalletAddress: String)
@ -36,6 +37,8 @@ interface TangemPayStorage {
suspend fun getHideMainOnboardingBanner(userWalletId: UserWalletId): Boolean
suspend fun storeHideOnboardingBanner(userWalletId: UserWalletId, hide: Boolean)
suspend fun storeTangemPayEligibility(eligibility: Boolean)
suspend fun getTangemPayEligibility(): Boolean
suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String)
}

View file

@ -45,6 +45,10 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
}
}
override suspend fun getTangemPayAvailability(): Boolean {
return onboardingRepository.checkCustomerEligibility()
}
private suspend fun getUserWalletsData(): List<UserWalletData> {
cachedEligibleWallets?.let { return it }
@ -69,7 +73,7 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
}
private suspend fun getPossibleWalletsForTangemPay(): List<UserWallet> {
if (!onboardingRepository.checkCustomerEligibility()) {
if (!checkTangemPayEligibility()) {
return emptyList()
}
@ -125,6 +129,10 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
eligibleWalletsDeferred = null
}
private suspend fun checkTangemPayEligibility(): Boolean {
return onboardingRepository.getCustomerEligibility() || onboardingRepository.checkCustomerEligibility()
}
private data class UserWalletData(
val userWallet: UserWallet,
val isPaeraCustomer: Boolean,

View file

@ -197,7 +197,15 @@ internal class DefaultOnboardingRepository @Inject constructor(
val response = requestHelper.performWithoutToken {
tangemPayApi.checkCustomerEligibility()
}.getOrNull()
return response?.result?.isTangemPayAvailable == true
val isAvailable = response?.result?.isTangemPayAvailable == true
tangemPayStorage.storeTangemPayEligibility(eligibility = isAvailable)
return isAvailable
}
override suspend fun getCustomerEligibility(): Boolean {
return tangemPayStorage.getTangemPayEligibility()
}
override suspend fun getHideMainOnboardingBanner(userWalletId: UserWalletId): Boolean {

View file

@ -5,4 +5,5 @@ import com.tangem.domain.models.wallet.UserWallet
interface TangemPayEligibilityManager {
suspend fun getEligibleWallets(shouldExcludePaeraCustomers: Boolean): List<UserWallet>
suspend fun getTangemPayAvailability(): Boolean
}

View file

@ -26,6 +26,7 @@ interface OnboardingRepository {
suspend fun checkCustomerWallet(userWalletId: UserWalletId): Either<VisaApiError, Boolean>
suspend fun checkCustomerEligibility(): Boolean
suspend fun getCustomerEligibility(): Boolean
fun getSavedCustomerInfo(userWalletId: UserWalletId): CustomerInfo?

View file

@ -53,19 +53,19 @@ import javax.inject.Inject
@Suppress("LongParameterList")
internal class DetailsModel @Inject constructor(
socialsBuilder: SocialsBuilder,
paramsContainer: ParamsContainer,
feedbackFeatureToggles: FeedbackFeatureToggles,
private val itemsBuilder: ItemsBuilder,
private val appVersionProvider: AppVersionProvider,
private val checkIsWalletConnectAvailableUseCase: CheckIsWalletConnectAvailableUseCase,
private val router: Router,
private val urlOpener: UrlOpener,
private val appInstanceIdProvider: AppInstanceIdProvider,
paramsContainer: ParamsContainer,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val appStateHolder: ReduxStateHolder,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
private val feedbackFeatureToggles: FeedbackFeatureToggles,
override val dispatchers: CoroutineDispatcherProvider,
private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
@ -254,7 +254,18 @@ internal class DetailsModel @Inject constructor(
.getEligibleWallets(shouldExcludePaeraCustomers = true)
.isNotEmpty()
if (isEligible) {
items.update { itemsBuilder.addVisaItem(it) }
items.update { itemsBuilder.addTangemPayItem(items = it, onClick = ::onTangemPayItemClicked) }
}
}
}
private fun onTangemPayItemClicked() {
modelScope.launch {
val isEligible = tangemPayEligibilityManager.getTangemPayAvailability()
if (isEligible) {
router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.FromBannerInSettings))
} else {
items.update { itemsBuilder.removeTangemPayItem(it) }
}
}
}

View file

@ -14,6 +14,8 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
import javax.inject.Inject
private const val TANGEM_PAY_ITEM_ID = "get_tangem_pay"
@ModelScoped
internal class ItemsBuilder @Inject constructor(private val router: Router) {
@ -37,10 +39,13 @@ internal class ItemsBuilder @Inject constructor(private val router: Router) {
).let(::add)
}.toImmutableList()
fun addVisaItem(items: ImmutableList<DetailsItemUM>): ImmutableList<DetailsItemUM> {
fun addTangemPayItem(items: ImmutableList<DetailsItemUM>, onClick: () -> Unit): ImmutableList<DetailsItemUM> {
return items.toMutableList().map { block ->
if (block.id == "shop" && block is DetailsItemUM.Basic) {
val newItems = block.items.toMutableList().apply { add(getVisaItem()) }
val newItems = block
.items
.toMutableList()
.apply { add(getTangemPayItem(onClick = onClick)) }
block.copy(items = newItems.toImmutableList())
} else {
block
@ -48,6 +53,13 @@ internal class ItemsBuilder @Inject constructor(private val router: Router) {
}.toImmutableList()
}
fun removeTangemPayItem(items: ImmutableList<DetailsItemUM>): ImmutableList<DetailsItemUM> {
val tangemPayItem = items.find { it.id == TANGEM_PAY_ITEM_ID } ?: return items
return items.toMutableList()
.also { list -> list.remove(tangemPayItem) }
.toImmutableList()
}
private fun buildWalletConnectBlock(isWalletConnectAvailable: Boolean, userWalletId: UserWalletId): DetailsItemUM? {
return if (isWalletConnectAvailable) {
DetailsItemUM.WalletConnect(
@ -126,14 +138,12 @@ internal class ItemsBuilder @Inject constructor(private val router: Router) {
}.toPersistentList(),
)
private fun getVisaItem(): DetailsItemUM.Basic.Item = DetailsItemUM.Basic.Item(
id = "get_tangem_visa",
private fun getTangemPayItem(onClick: () -> Unit): DetailsItemUM.Basic.Item = DetailsItemUM.Basic.Item(
id = TANGEM_PAY_ITEM_ID,
block = BlockUM(
text = resourceReference(R.string.tangempay_get_tangem_pay),
iconRes = R.drawable.ic_tangem_pay_24,
onClick = {
router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.FromBannerInSettings))
},
onClick = onClick,
),
)
}