From 16b8957165eea23364b9f14730303eea2bc69b3a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 5 Dec 2025 14:53:27 +0500 Subject: [PATCH] Updated on 2026-08-14 --- domain/visa/build.gradle.kts | 1 + .../tangempay/TangemPayAnalyticsEvents.kt | 50 +++++++++++++++++++ .../tangem/features/kyc/DefaultKycModel.kt | 4 ++ .../tangempay/details/impl/build.gradle.kts | 5 +- .../tangempay/model/TangemPayDetailsModel.kt | 5 ++ .../onboarding/impl/build.gradle.kts | 6 +-- .../model/TangemPayOnboardingModel.kt | 8 ++- .../WalletTangemPayAnalyticsEventSender.kt | 38 ++++++++++++++ .../TangemPayUpdateInfoStateTransformer.kt | 2 + .../subscribers/TangemPayMainSubscriber.kt | 8 ++- 10 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 domain/visa/src/main/kotlin/com/tangem/domain/tangempay/TangemPayAnalyticsEvents.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletTangemPayAnalyticsEventSender.kt diff --git a/domain/visa/build.gradle.kts b/domain/visa/build.gradle.kts index 22faf074b1..1f83cc44b1 100644 --- a/domain/visa/build.gradle.kts +++ b/domain/visa/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { api(projects.core.pagination) implementation(projects.core.utils) implementation(projects.core.error) + implementation(projects.core.analytics.models) /** Project - Domain */ api(projects.domain.models) diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/TangemPayAnalyticsEvents.kt b/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/TangemPayAnalyticsEvents.kt new file mode 100644 index 0000000000..af92401e5b --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/TangemPayAnalyticsEvents.kt @@ -0,0 +1,50 @@ +package com.tangem.domain.tangempay + +import com.tangem.core.analytics.models.AnalyticsEvent + +sealed class TangemPayAnalyticsEvents( + categoryName: String, + event: String, + params: Map = emptyMap(), +) : AnalyticsEvent(category = categoryName, event = event, params = params) { + + data object ActivationScreenOpened : TangemPayAnalyticsEvents( + categoryName = "Visa Onboarding", + event = "Activation Screen Opened", + ) + + data object ViewTermsClicked : TangemPayAnalyticsEvents( + categoryName = "Visa Onboarding", + event = "Button - Visa View Terms", + ) + + data object GetCardClicked : TangemPayAnalyticsEvents( + categoryName = "Visa Onboarding", + event = "Button - Visa Get Card", + ) + + data object KycFlowOpened : TangemPayAnalyticsEvents( + categoryName = "Visa Onboarding", + event = "Visa KYC Flow Opened", + ) + + data object IssuingBannerDisplayed : TangemPayAnalyticsEvents( + categoryName = "Visa Onboarding", + event = "Visa Issuing Banner Displayed", + ) + + data object MainScreenOpened : TangemPayAnalyticsEvents( + categoryName = "Visa Screen", + event = "Visa Main Screen Opened", + ) + + data object ReceiveFundsClicked : TangemPayAnalyticsEvents( + categoryName = "Visa Screen", + event = "Button - Visa Receive", + ) + + data object SwapClicked : TangemPayAnalyticsEvents( + categoryName = "Visa Screen", + event = "Button - Visa Swap", + ) +} \ No newline at end of file diff --git a/features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/DefaultKycModel.kt b/features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/DefaultKycModel.kt index 6b44954fab..2d7b7e1558 100644 --- a/features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/DefaultKycModel.kt +++ b/features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/DefaultKycModel.kt @@ -1,11 +1,13 @@ package com.tangem.features.kyc import androidx.compose.runtime.Stable +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.domain.pay.KycStartInfo import com.tangem.domain.pay.repository.KycRepository +import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -17,6 +19,7 @@ import javax.inject.Inject @ModelScoped class DefaultKycModel @Inject constructor( paramsContainer: ParamsContainer, + analytics: AnalyticsEventHandler, override val dispatchers: CoroutineDispatcherProvider, private val kycRepository: KycRepository, ) : Model() { @@ -27,6 +30,7 @@ class DefaultKycModel @Inject constructor( val uiState = _uiState.asStateFlow() init { + analytics.send(TangemPayAnalyticsEvents.KycFlowOpened) modelScope.launch { try { kycRepository.getKycStartInfo(params.userWalletId).getOrNull()?.let { _uiState.emit(it) } diff --git a/features/tangempay/details/impl/build.gradle.kts b/features/tangempay/details/impl/build.gradle.kts index 6a0e6c5358..52a85dad6b 100644 --- a/features/tangempay/details/impl/build.gradle.kts +++ b/features/tangempay/details/impl/build.gradle.kts @@ -13,12 +13,13 @@ android { dependencies { /** Core */ + implementation(projects.core.analytics) + implementation(projects.core.analytics.models) implementation(projects.core.configToggles) implementation(projects.core.decompose) implementation(projects.core.error) implementation(projects.core.navigation) implementation(projects.core.ui) - implementation(projects.core.analytics.models) /** Common */ implementation(projects.common.ui) @@ -26,8 +27,8 @@ dependencies { /** Features api */ implementation(projects.features.tangempay.details.api) - implementation(projects.features.txhistory.api) implementation(projects.features.tokenRecieve.api) + implementation(projects.features.txhistory.api) /** Domain */ implementation(projects.domain.balanceHiding) diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt index e016749d7a..b3a558564e 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt @@ -5,6 +5,7 @@ import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate import com.arkivanov.decompose.router.slot.dismiss import com.tangem.common.routing.AppRoute +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model @@ -22,6 +23,7 @@ import com.tangem.domain.pay.TangemPaySwapDataFactory import com.tangem.domain.pay.model.TangemPayCardBalance import com.tangem.domain.pay.repository.CustomerOrderRepository import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository +import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.domain.visa.model.TangemPayTxHistoryItem import com.tangem.features.tangempay.TangemPayConstants @@ -59,6 +61,7 @@ import javax.inject.Inject internal class TangemPayDetailsModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, + private val analytics: AnalyticsEventHandler, private val router: Router, private val urlOpener: UrlOpener, private val cardDetailsRepository: TangemPayCardDetailsRepository, @@ -328,6 +331,7 @@ internal class TangemPayDetailsModel @Inject constructor( } override fun onClickSwap(data: TangemPayTopUpData) { + analytics.send(TangemPayAnalyticsEvents.SwapClicked) bottomSheetNavigation.dismiss() router.push( AppRoute.Swap( @@ -346,6 +350,7 @@ internal class TangemPayDetailsModel @Inject constructor( } override fun onClickReceive(data: TangemPayTopUpData) { + analytics.send(TangemPayAnalyticsEvents.ReceiveFundsClicked) bottomSheetNavigation.dismiss() val config = TokenReceiveConfig( shouldShowWarning = false, diff --git a/features/tangempay/onboarding/impl/build.gradle.kts b/features/tangempay/onboarding/impl/build.gradle.kts index a35a3e4657..0d109bd508 100644 --- a/features/tangempay/onboarding/impl/build.gradle.kts +++ b/features/tangempay/onboarding/impl/build.gradle.kts @@ -13,12 +13,11 @@ android { dependencies { /** Core */ + implementation(projects.core.analytics) implementation(projects.core.decompose) - implementation(projects.core.ui) implementation(projects.core.error) implementation(projects.core.navigation) - implementation(deps.arrow.core) - implementation(deps.arrow.core) + implementation(projects.core.ui) /** Common */ implementation(projects.common.routing) @@ -47,4 +46,5 @@ dependencies { /** Other */ implementation(deps.timber) + implementation(deps.arrow.core) } \ No newline at end of file diff --git a/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt b/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt index 688336b8ca..de4d3018a3 100644 --- a/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt +++ b/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt @@ -2,6 +2,7 @@ package com.tangem.features.tangempay.model import androidx.compose.runtime.Stable import com.tangem.common.routing.AppRoute +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -10,6 +11,7 @@ import com.tangem.core.navigation.url.UrlOpener import com.tangem.data.pay.util.TangemPayWalletsManager import com.tangem.domain.pay.repository.OnboardingRepository import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase +import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.features.tangempay.TangemPayConstants import com.tangem.features.tangempay.components.TangemPayOnboardingComponent import com.tangem.features.tangempay.model.transformers.TangemPayOnboardingButtonLoadingTransformer @@ -28,8 +30,9 @@ import com.tangem.utils.transformer.update as transformerUpdate @Suppress("LongParameterList") internal class TangemPayOnboardingModel @Inject constructor( paramsContainer: ParamsContainer, - private val router: Router, override val dispatchers: CoroutineDispatcherProvider, + private val analytics: AnalyticsEventHandler, + private val router: Router, private val repository: OnboardingRepository, private val produceInitialDataUseCase: ProduceTangemPayInitialDataUseCase, private val urlOpener: UrlOpener, @@ -41,6 +44,7 @@ internal class TangemPayOnboardingModel @Inject constructor( field = MutableStateFlow(getInitialState()) init { + analytics.send(TangemPayAnalyticsEvents.ActivationScreenOpened) modelScope.launch { when (params) { is TangemPayOnboardingComponent.Params.ContinueOnboarding -> { @@ -89,10 +93,12 @@ internal class TangemPayOnboardingModel @Inject constructor( } private fun onTermsClick() { + analytics.send(TangemPayAnalyticsEvents.ViewTermsClicked) urlOpener.openUrl(TangemPayConstants.TERMS_AND_LIMITS_LINK) } private fun onGetCardClick() { + analytics.send(TangemPayAnalyticsEvents.GetCardClicked) uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = true)) modelScope.launch { // TODO implement selector diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletTangemPayAnalyticsEventSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletTangemPayAnalyticsEventSender.kt new file mode 100644 index 0000000000..ff945a2974 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletTangemPayAnalyticsEventSender.kt @@ -0,0 +1,38 @@ +package com.tangem.feature.wallet.presentation.wallet.analytics.utils + +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.domain.pay.model.MainScreenCustomerInfo +import com.tangem.domain.pay.model.OrderStatus +import com.tangem.domain.tangempay.TangemPayAnalyticsEvents +import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider +import javax.inject.Inject + +@ModelScoped +internal class WalletTangemPayAnalyticsEventSender @Inject constructor( + private val analyticsEventHandler: AnalyticsEventHandler, + private val screenLifecycleProvider: ScreenLifecycleProvider, +) { + + private val sentEvents = mutableSetOf() + + fun send(customerInfo: MainScreenCustomerInfo) { + if (screenLifecycleProvider.isBackgroundState.value) return + + val cardInfo = customerInfo.info.cardInfo + val productInstance = customerInfo.info.productInstance + + // TODO: TangemPay refactor analytics + // when statement copied from TangemPayUpdateInfoStateTransformer. Be careful when editing + val event = when { + customerInfo.orderStatus == OrderStatus.CANCELED -> return // ignore cancelled state on analytics + !customerInfo.info.isKycApproved -> return // ignore kyc not approved state on analytics + cardInfo != null && productInstance != null -> TangemPayAnalyticsEvents.MainScreenOpened + else -> TangemPayAnalyticsEvents.IssuingBannerDisplayed + } + + if (sentEvents.add(event)) { + analyticsEventHandler.send(event) + } + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayUpdateInfoStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayUpdateInfoStateTransformer.kt index f3f6852b13..f9127d04ef 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayUpdateInfoStateTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayUpdateInfoStateTransformer.kt @@ -46,6 +46,8 @@ internal class TangemPayUpdateInfoStateTransformer( private fun createInitialState(): TangemPayState { val cardInfo = value?.info?.cardInfo val productInstance = value?.info?.productInstance + + // when statement copied to WalletTangemPayAnalyticsEventSender. Be careful when editing. return when { value == null -> TangemPayState.Empty value.orderStatus == OrderStatus.CANCELED -> createCancelledState(onIssuingFailed) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TangemPayMainSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TangemPayMainSubscriber.kt index 7488723664..0664439d54 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TangemPayMainSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TangemPayMainSubscriber.kt @@ -9,6 +9,7 @@ import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.feature.wallet.child.wallet.model.TangemPayMainInfoManager 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.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayHiddenStateTransformer import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayRefreshNeededStateTransformer @@ -21,6 +22,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* import timber.log.Timber +@Suppress("LongParameterList") internal class TangemPayMainSubscriber @AssistedInject constructor( @Assisted private val userWallet: UserWallet, private val stateController: WalletStateController, @@ -28,6 +30,7 @@ internal class TangemPayMainSubscriber @AssistedInject constructor( private val innerWalletRouter: InnerWalletRouter, private val cardDetailsRepository: TangemPayCardDetailsRepository, private val tangemPayMainInfoManager: TangemPayMainInfoManager, + private val analytics: WalletTangemPayAnalyticsEventSender, ) : WalletSubscriber() { override fun create(coroutineScope: CoroutineScope): Flow<*> { @@ -63,7 +66,10 @@ internal class TangemPayMainSubscriber @AssistedInject constructor( ) } } - }.onRight { data -> updateTangemPay(data, userWalletId) } + }.onRight { data -> + updateTangemPay(data, userWalletId) + analytics.send(customerInfo = data) + } } }