Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-25 17:45:47 +05:00
parent 774f25bf4a
commit 319f62f008
11 changed files with 49 additions and 16 deletions

View file

@ -39,6 +39,10 @@
"name": "TANGEM_PAY_ENABLED",
"version": "5.31.0"
},
{
"name": "TANGEM_PAY_ENTRYPOINT_ENABLED",
"version": "undefined"
},
{
"name": "NEW_TOKEN_RECEIVE_ENABLED",
"version": "5.28.0"

View file

@ -39,6 +39,9 @@ dependencies {
/** Feature API - remove after removing [HotWalletFeatureToggles] */
implementation(projects.features.hotWallet.api)
/** Feature API - remove after removing [TangemPayFeatureToggles] */
implementation(projects.features.tangempay.details.api)
/** Project - Utils */
implementation(projects.core.utils)
implementation(projects.domain.legacy)

View file

@ -9,15 +9,10 @@ import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import javax.inject.Inject
@ -27,6 +22,7 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val onboardingRepository: OnboardingRepository,
) : TangemPayEligibilityManager {
@ -130,6 +126,8 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
}
private suspend fun checkTangemPayEligibility(): Boolean {
if (!tangemPayFeatureToggles.isEntryPointsEnabled) return true
return onboardingRepository.getCustomerEligibility() || onboardingRepository.checkCustomerEligibility()
}

View file

@ -13,6 +13,7 @@ import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.tangempay.GetTangemPayCurrencyStatusUseCase
import com.tangem.domain.tangempay.TangemPayWithdrawUseCase
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.security.DeviceSecurityInfoProvider
import dagger.Binds
import dagger.Module
@ -77,12 +78,14 @@ internal interface TangemPayDataModule {
customerOrderRepository: CustomerOrderRepository,
tangemPayOnboardingRepository: OnboardingRepository,
eligibilityManager: TangemPayEligibilityManager,
tangemPayFeatureToggles: TangemPayFeatureToggles,
deviceSecurity: DeviceSecurityInfoProvider,
): TangemPayMainScreenCustomerInfoUseCase {
return TangemPayMainScreenCustomerInfoUseCase(
onboardingRepository = repository,
customerOrderRepository = customerOrderRepository,
eligibilityManager = eligibilityManager,
tangemPayFeatureToggles = tangemPayFeatureToggles,
deviceSecurity = deviceSecurity,
)
}

View file

@ -25,6 +25,9 @@ dependencies {
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets.models)
/** Feature API - remove after removing [TangemPayFeatureToggles] */
implementation(projects.features.tangempay.details.api)
/** Security */
implementation(deps.spongecastle.core)

View file

@ -9,6 +9,7 @@ import com.tangem.domain.pay.model.*
import com.tangem.domain.pay.repository.CustomerOrderRepository
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.security.DeviceSecurityInfoProvider
import com.tangem.security.isSecurityExposed
import kotlinx.coroutines.flow.*
@ -24,6 +25,7 @@ class TangemPayMainScreenCustomerInfoUseCase(
private val onboardingRepository: OnboardingRepository,
private val customerOrderRepository: CustomerOrderRepository,
private val eligibilityManager: TangemPayEligibilityManager,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val deviceSecurity: DeviceSecurityInfoProvider,
) {
@ -46,7 +48,7 @@ class TangemPayMainScreenCustomerInfoUseCase(
.fold(
ifLeft = { error ->
Timber.tag(TAG).e("Failed checkCustomerWallet for $userWalletId: ${error.javaClass.simpleName}")
if (error is VisaApiError.NotPaeraCustomer) {
if (error is VisaApiError.NotPaeraCustomer && tangemPayFeatureToggles.isEntryPointsEnabled) {
showOnboardingBannerIfEligible(userWalletId)
} else {
updateState(userWalletId, TangemPayCustomerInfoError.UnknownError.left())
@ -64,8 +66,13 @@ class TangemPayMainScreenCustomerInfoUseCase(
.map(MainCustomerInfoContentState::Content)
updateState(userWalletId, result)
} else {
// if there's no tangem pay, check eligibility and show onboarding banner
showOnboardingBannerIfEligible(userWalletId)
if (tangemPayFeatureToggles.isEntryPointsEnabled) {
// if there's no tangem pay, check eligibility and show onboarding banner
showOnboardingBannerIfEligible(userWalletId)
} else {
// ignore if there's no TangemPay
updateState(userWalletId, TangemPayCustomerInfoError.UnknownError.left())
}
}
},
)

View file

@ -248,7 +248,7 @@ internal class DetailsModel @Inject constructor(
}
private fun addTangemPayItemIfEligible() {
if (!tangemPayFeatureToggles.isTangemPayEnabled) return
if (!tangemPayFeatureToggles.isEntryPointsEnabled) return
modelScope.launch {
val isEligible = tangemPayEligibilityManager
.getEligibleWallets(shouldExcludePaeraCustomers = true)

View file

@ -54,10 +54,13 @@ internal class ItemsBuilder @Inject constructor(private val router: Router) {
}
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()
return items.map { block ->
if (block is DetailsItemUM.Basic && block.items.any { it.id == TANGEM_PAY_ITEM_ID }) {
block.copy(items = block.items.filter { it.id != TANGEM_PAY_ITEM_ID }.toImmutableList())
} else {
block
}
}.toImmutableList()
}
private fun buildWalletConnectBlock(isWalletConnectAvailable: Boolean, userWalletId: UserWalletId): DetailsItemUM? {

View file

@ -2,4 +2,5 @@ package com.tangem.features.tangempay
interface TangemPayFeatureToggles {
val isTangemPayEnabled: Boolean
val isEntryPointsEnabled: Boolean
}

View file

@ -7,4 +7,6 @@ internal class DefaultTangemPayFeatureToggles(
) : TangemPayFeatureToggles {
override val isTangemPayEnabled
get() = featureTogglesManager.isFeatureEnabled("TANGEM_PAY_ENABLED")
override val isEntryPointsEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled("TANGEM_PAY_ENTRYPOINT_ENABLED")
}

View file

@ -14,6 +14,7 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
@ -55,6 +56,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
private val tangemPayOnboardingRepository: OnboardingRepository,
private val tangemPayEligibilityManager: TangemPayEligibilityManager,
private val uiMessageSender: UiMessageSender,
) : BaseWalletClickIntents(), TangemPayIntents {
@ -176,7 +178,14 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
}
override fun onOnboardingBannerClick(userWalletId: UserWalletId) {
router.openTangemPayOnboarding(mode = AppRoute.TangemPayOnboarding.Mode.FromBannerOnMain(userWalletId))
modelScope.launch {
val isEligible = tangemPayEligibilityManager.getTangemPayAvailability()
if (isEligible) {
router.openTangemPayOnboarding(mode = AppRoute.TangemPayOnboarding.Mode.FromBannerOnMain(userWalletId))
} else {
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
}
}
}
override fun onOnboardingBannerCloseClick(userWalletId: UserWalletId) {