Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-14 15:51:30 +05:00
parent 500bc90ca3
commit a821732662
16 changed files with 113 additions and 112 deletions

View file

@ -9,7 +9,6 @@ import com.tangem.common.core.TangemError
import com.tangem.common.core.TangemSdkError
import com.tangem.common.deserialization.WalletDataDeserializer
import com.tangem.common.extensions.*
import com.tangem.common.map
import com.tangem.common.tlv.Tlv
import com.tangem.common.tlv.TlvDecoder
import com.tangem.crypto.CryptoUtils
@ -145,11 +144,11 @@ internal class ScanProductTask(
card = cardDto,
session = session,
) { scanResponseResult ->
callback(
scanResponseResult.map { scanResponse ->
scanResponse.copy(visaCardActivationStatus = result.data)
},
)
// callback(
// scanResponseResult.map { scanResponse ->
// scanResponse.copy(visaCardActivationStatus = result.data)
// },
// )
}
}
is CompletionResult.Failure -> {

View file

@ -10,7 +10,7 @@ internal val UserWallet.sensitiveInformation: UserWalletSensitiveInformation
get() = when (this) {
is UserWallet.Cold -> UserWalletSensitiveInformation(
wallets = scanResponse.card.wallets,
visaCardActivationStatus = scanResponse.visaCardActivationStatus,
// visaCardActivationStatus = scanResponse.visaCardActivationStatus,
mobileWallets = null,
)
is UserWallet.Hot -> UserWalletSensitiveInformation(
@ -30,7 +30,7 @@ internal val UserWallet.publicInformation: UserWalletPublicInformation
card = scanResponse.card.copy(
wallets = emptyList(),
),
visaCardActivationStatus = null,
// visaCardActivationStatus = null,
),
hasBackupError = hasBackupError,
hotWalletId = null,
@ -80,7 +80,7 @@ internal fun UserWallet.updateWith(sensitiveInformation: UserWalletSensitiveInfo
card = scanResponse.card.copy(
wallets = requireNotNull(sensitiveInformation.wallets),
),
visaCardActivationStatus = sensitiveInformation.visaCardActivationStatus,
// visaCardActivationStatus = sensitiveInformation.visaCardActivationStatus,
),
)
}
@ -113,7 +113,7 @@ internal fun UserWallet.lock(): UserWallet = when (this) {
card = scanResponse.card.copy(
wallets = emptyList(),
),
visaCardActivationStatus = null,
// visaCardActivationStatus = null,
),
)
}

View file

@ -10,6 +10,7 @@ import com.tangem.common.extensions.toHexString
import com.tangem.core.error.ext.tangemError
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.domain.visa.error.VisaActivationError
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.domain.visa.error.VisaCardScanError

View file

@ -4,7 +4,6 @@ import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.common.util.twinsIsTwinned
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.proxy.redux.DaggerGraphState
@ -20,9 +19,10 @@ object OnboardingHelper {
return when {
response.cardTypesResolver.isVisaWallet() -> {
if (response.visaCardActivationStatus == null) error("Visa card activation status is null")
response.visaCardActivationStatus !is VisaCardActivationStatus.Activated
// if (response.visaCardActivationStatus == null) error("Visa card activation status is null")
//
// response.visaCardActivationStatus !is VisaCardActivationStatus.Activated
return true
}
response.cardTypesResolver.isTangemTwins() -> {

View file

@ -41,6 +41,7 @@ dependencies {
implementation(projects.domain.nft.models)
implementation(projects.domain.walletConnect.models)
implementation(projects.domain.yieldSupply.models)
implementation(projects.domain.visa.models)
/** Tangem libraries */
implementation(tangemDeps.blockchain)

View file

@ -11,8 +11,6 @@ import com.tangem.datasource.local.config.providers.models.ProviderModel
import com.tangem.datasource.local.network.entity.NetworkStatusDM
import com.tangem.datasource.utils.SerializeNullsFactory
import com.tangem.domain.models.scan.serialization.*
import com.tangem.domain.visa.model.VisaActivationRemoteState
import com.tangem.domain.visa.model.VisaCardActivationStatus
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -40,8 +38,8 @@ class MoshiModule {
.add(BigIntegerAdapter())
.add(LocalDateAdapter())
.add(DateTimeAdapter())
.add(VisaActivationRemoteState.jsonAdapter)
.add(VisaCardActivationStatus.jsonAdapter)
// .add(VisaActivationRemoteState.jsonAdapter)
// .add(VisaCardActivationStatus.jsonAdapter)
.add(
NamePolymorphicAdapterFactory.of(NetworkStatusDM::class.java)
.withSubtype(NetworkStatusDM.Verified::class.java, "amounts")
@ -86,8 +84,8 @@ class MoshiModule {
val typedAdapters = MoshiJsonConverter.getTangemSdkTypedAdapters()
return Moshi.Builder().apply {
add(VisaActivationRemoteState.jsonAdapter)
add(VisaCardActivationStatus.jsonAdapter)
// add(VisaActivationRemoteState.jsonAdapter)
// add(VisaCardActivationStatus.jsonAdapter)
adapters.forEach { this.add(it) }
typedAdapters.forEach { add(it.key, it.value) }
addLast(KotlinJsonAdapterFactory())

View file

@ -54,7 +54,7 @@ internal class VisaApiRequestMaker @Inject constructor(
userWalletsStore.update(userWalletId) { userWallet ->
userWallet.requireColdWallet().copy(
scanResponse = userWallet.scanResponse.copy(
visaCardActivationStatus = VisaCardActivationStatus.RefreshTokenExpired,
// visaCardActivationStatus = VisaCardActivationStatus.RefreshTokenExpired,
),
)
}
@ -65,9 +65,9 @@ internal class VisaApiRequestMaker @Inject constructor(
userWalletsStore.update(userWalletId) { userWallet ->
userWallet.requireColdWallet().copy(
scanResponse = userWallet.scanResponse.copy(
visaCardActivationStatus = VisaCardActivationStatus.Activated(
visaAuthTokens = newTokens,
),
// visaCardActivationStatus = VisaCardActivationStatus.Activated(
// visaAuthTokens = newTokens,
// ),
),
)
}
@ -93,8 +93,9 @@ internal class VisaApiRequestMaker @Inject constructor(
@Throws
private fun getAuthTokens(userWalletId: UserWalletId): VisaAuthTokens {
val userWallet = findVisaUserWallet(userWalletId)
val status = userWallet.requireColdWallet().scanResponse.visaCardActivationStatus
?: error("Visa card activation status not found")
// val status = userWallet.requireColdWallet().scanResponse.visaCardActivationStatus
// ?: error("Visa card activation status not found")
val status: VisaCardActivationStatus = TODO("Fix visaCardActivationStatus retrieval")
if (status is VisaCardActivationStatus.RefreshTokenExpired) {
throw RefreshTokenExpiredException()

View file

@ -22,6 +22,7 @@ dependencies {
implementation(projects.domain.models)
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets.models)
implementation(projects.domain.visa.models)
implementation(deps.timber)

View file

@ -10,7 +10,6 @@ tasks.withType<Test>().configureEach {
}
dependencies {
api(projects.domain.visa.models)
api(projects.domain.core)
api(projects.core.utils)

View file

@ -5,7 +5,6 @@ import com.squareup.moshi.JsonClass
import com.tangem.common.card.WalletData
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.domain.models.scan.serialization.ScanResponseAsStringSerializer
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.operations.backup.PrimaryCard
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import kotlinx.serialization.Serializable
@ -21,7 +20,7 @@ data class ScanResponse(
@Json(name = "productType") val productType: ProductType,
@Json(name = "walletData") val walletData: WalletData?,
@Json(name = "secondTwinPublicKey") val secondTwinPublicKey: String? = null,
@Json(name = "visaCardActivationStatus") val visaCardActivationStatus: VisaCardActivationStatus? = null,
// @Json(name = "visaCardActivationStatus") val visaCardActivationStatus: VisaCardActivationStatus? = null,
@Json(name = "derivedKeys") val derivedKeys: Map<KeyWalletPublicKey, ExtendedPublicKeysMap> = mapOf(),
@Json(name = "primaryCard") val primaryCard: PrimaryCard? = null,
)

View file

@ -4,8 +4,6 @@ import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.tangem.common.json.TangemSdkAdapter
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.visa.model.VisaActivationRemoteState
import com.tangem.domain.visa.model.VisaCardActivationStatus
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
@ -24,8 +22,8 @@ internal object ScanResponseAsStringSerializer : KSerializer<ScanResponse> {
.add(TangemSdkAdapter.DateAdapter())
.add(TangemSdkAdapter.DerivationNodeAdapter())
.add(TangemSdkAdapter.FirmwareVersionAdapter()) // For PrimaryCard model
.add(VisaActivationRemoteState.jsonAdapter)
.add(VisaCardActivationStatus.jsonAdapter)
// .add(VisaActivationRemoteState.jsonAdapter)
// .add(VisaCardActivationStatus.jsonAdapter)
.addLast(KotlinJsonAdapterFactory())
.build()

View file

@ -12,4 +12,7 @@ dependencies {
implementation(deps.kotlin.serialization)
implementation(deps.jodatime)
implementation(projects.core.error)
/** Domain models */
implementation(projects.domain.models)
}

View file

@ -14,11 +14,11 @@ import com.tangem.core.error.UniversalError
import com.tangem.core.error.ext.universalError
import com.tangem.core.ui.utils.showErrorDialog
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.domain.visa.model.VisaCardId
import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest
import com.tangem.domain.visa.repository.VisaActivationRepository
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
import com.tangem.domain.visa.model.VisaActivationInput
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.OnboardingVisaAccessCodeComponent
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.ui.state.OnboardingVisaAccessCodeUM
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.model.analytics.OnboardingVisaAnalyticsEvent
@ -57,11 +57,12 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor(
),
)
private val activationInput = when (val status = params.scanResponse.visaCardActivationStatus) {
is VisaCardActivationStatus.NotStartedActivation -> status.activationInput
is VisaCardActivationStatus.ActivationStarted -> status.activationInput
else -> error("Visa activation status is not set or incorrect for this step")
}
private val activationInput: VisaActivationInput = TODO("Fix visaCardActivationStatus retrieval")
// when (val status = params.scanResponse.visaCardActivationStatus) {
// is VisaCardActivationStatus.NotStartedActivation -> status.activationInput
// is VisaCardActivationStatus.ActivationStarted -> status.activationInput
// else -> error("Visa activation status is not set or incorrect for this step")
// }
private val _uiState = MutableStateFlow(getInitialState())

View file

@ -12,15 +12,15 @@ import com.tangem.core.ui.utils.showErrorDialog
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.datasource.local.visa.VisaOTPStorage
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
import com.tangem.domain.visa.error.VisaActivationError
import com.tangem.domain.visa.model.VisaActivationRemoteState
import com.tangem.domain.visa.model.VisaAuthTokens
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.domain.visa.model.VisaCardId
import com.tangem.domain.visa.repository.VisaActivationRepository
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.OnboardingVisaInProgressComponent.Config
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.OnboardingVisaInProgressComponent.Params
@ -180,13 +180,15 @@ internal class OnboardingVisaInProgressModel @Inject constructor(
onDone.emit(Params.DoneEvent.Activated)
}
@Suppress("UnusedPrivateProperty")
private suspend fun createUserWallet(scanResponse: ScanResponse, authTokens: VisaAuthTokens): UserWallet =
withContext(dispatchers.io) {
val newActivationStatus = VisaCardActivationStatus.Activated(visaAuthTokens = authTokens)
requireNotNull(
value = coldUserWalletBuilderFactory.create(
scanResponse = scanResponse.copy(visaCardActivationStatus = newActivationStatus),
// scanResponse = scanResponse.copy(visaCardActivationStatus = newActivationStatus),
scanResponse = scanResponse,
).build(),
lazyMessage = { "User wallet not created" },
)

View file

@ -10,10 +10,6 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.card.common.visa.VisaUtilities
import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility
import com.tangem.domain.visa.model.VisaActivationRemoteState
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.domain.visa.model.VisaCardWalletDataToSignRequest
import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
@ -143,74 +139,75 @@ internal class OnboardingVisaModel @Inject constructor(
}
private fun initializeRoute(): OnboardingVisaRoute {
return when (val activationStatus = params.scanResponse.visaCardActivationStatus) {
is VisaCardActivationStatus.ActivationStarted -> {
when (val remoteState = activationStatus.remoteState) {
is VisaActivationRemoteState.CardWalletSignatureRequired -> {
if (activationStatus.activationInput.isAccessCodeSet) {
OnboardingVisaRoute.WelcomeBack(
activationInput = activationStatus.activationInput,
dataToSignByCardWalletRequest = VisaCardWalletDataToSignRequest(
activationOrderInfo = remoteState.activationOrderInfo,
cardWalletAddress = activationStatus.cardWalletAddress,
),
)
} else {
OnboardingVisaRoute.AccessCode
}
}
is VisaActivationRemoteState.CustomerWalletSignatureRequired -> {
remoteState.getRoute(activationStatus)
}
VisaActivationRemoteState.PaymentAccountDeploying -> {
OnboardingVisaRoute.InProgress(from = OnboardingVisaRoute.InProgress.From.Approve)
}
VisaActivationRemoteState.WaitingForActivationFinishing -> {
OnboardingVisaRoute.InProgress(from = OnboardingVisaRoute.InProgress.From.PinCode)
}
is VisaActivationRemoteState.AwaitingPinCode -> {
OnboardingVisaRoute.PinCode(
activationOrderInfo = remoteState.activationOrderInfo,
pinCodeValidationError = false,
)
}
VisaActivationRemoteState.Activated,
VisaActivationRemoteState.BlockedForActivation,
VisaActivationRemoteState.Failed,
-> error("Activation status is not correct for onboarding flow")
}
}
is VisaCardActivationStatus.NotStartedActivation -> OnboardingVisaRoute.Welcome
else -> error("Visa activation status is not correct for onboarding flow")
}
TODO("Fix visaCardActivationStatus retrieval")
// return when (val activationStatus = params.scanResponse.visaCardActivationStatus) {
// is VisaCardActivationStatus.ActivationStarted -> {
// when (val remoteState = activationStatus.remoteState) {
// is VisaActivationRemoteState.CardWalletSignatureRequired -> {
// if (activationStatus.activationInput.isAccessCodeSet) {
// OnboardingVisaRoute.WelcomeBack(
// activationInput = activationStatus.activationInput,
// dataToSignByCardWalletRequest = VisaCardWalletDataToSignRequest(
// activationOrderInfo = remoteState.activationOrderInfo,
// cardWalletAddress = activationStatus.cardWalletAddress,
// ),
// )
// } else {
// OnboardingVisaRoute.AccessCode
// }
// }
// is VisaActivationRemoteState.CustomerWalletSignatureRequired -> {
// remoteState.getRoute(activationStatus)
// }
// VisaActivationRemoteState.PaymentAccountDeploying -> {
// OnboardingVisaRoute.InProgress(from = OnboardingVisaRoute.InProgress.From.Approve)
// }
// VisaActivationRemoteState.WaitingForActivationFinishing -> {
// OnboardingVisaRoute.InProgress(from = OnboardingVisaRoute.InProgress.From.PinCode)
// }
// is VisaActivationRemoteState.AwaitingPinCode -> {
// OnboardingVisaRoute.PinCode(
// activationOrderInfo = remoteState.activationOrderInfo,
// pinCodeValidationError = false,
// )
// }
// VisaActivationRemoteState.Activated,
// VisaActivationRemoteState.BlockedForActivation,
// VisaActivationRemoteState.Failed,
// -> error("Activation status is not correct for onboarding flow")
// }
// }
// is VisaCardActivationStatus.NotStartedActivation -> OnboardingVisaRoute.Welcome
// else -> error("Visa activation status is not correct for onboarding flow")
// }
}
private fun VisaActivationRemoteState.CustomerWalletSignatureRequired.getRoute(
activationStatus: VisaCardActivationStatus.ActivationStarted,
): OnboardingVisaRoute {
val foundWalletCardId = tryToFindExistingWalletCardId(this.activationOrderInfo.customerWalletAddress)
val request = VisaCustomerWalletDataToSignRequest(
orderId = this.activationOrderInfo.orderId,
cardWalletAddress = activationStatus.cardWalletAddress,
customerWalletAddress = this.activationOrderInfo.customerWalletAddress,
)
val preparationDataForApprove = PreparationDataForApprove(
customerWalletAddress = this.activationOrderInfo.customerWalletAddress,
request = request,
)
return if (foundWalletCardId != null) {
OnboardingVisaRoute.TangemWalletApproveOption(
preparationDataForApprove = preparationDataForApprove,
foundWalletCardId = foundWalletCardId,
allowNavigateBack = false,
)
} else {
OnboardingVisaRoute.ChooseWallet(
preparationDataForApprove = preparationDataForApprove,
)
}
}
// private fun VisaActivationRemoteState.CustomerWalletSignatureRequired.getRoute(
// activationStatus: VisaCardActivationStatus.ActivationStarted,
// ): OnboardingVisaRoute {
// val foundWalletCardId = tryToFindExistingWalletCardId(this.activationOrderInfo.customerWalletAddress)
// val request = VisaCustomerWalletDataToSignRequest(
// orderId = this.activationOrderInfo.orderId,
// cardWalletAddress = activationStatus.cardWalletAddress,
// customerWalletAddress = this.activationOrderInfo.customerWalletAddress,
// )
// val preparationDataForApprove = PreparationDataForApprove(
// customerWalletAddress = this.activationOrderInfo.customerWalletAddress,
// request = request,
// )
//
// return if (foundWalletCardId != null) {
// OnboardingVisaRoute.TangemWalletApproveOption(
// preparationDataForApprove = preparationDataForApprove,
// foundWalletCardId = foundWalletCardId,
// allowNavigateBack = false,
// )
// } else {
// OnboardingVisaRoute.ChooseWallet(
// preparationDataForApprove = preparationDataForApprove,
// )
// }
// }
private fun tryToFindExistingWalletCardId(targetAddress: String): String? {
val wallets = getWalletsUseCase.invokeSync().filter { it.isLocked.not() }

View file

@ -15,6 +15,7 @@ dependencies {
/** Domain */
implementation(projects.domain.models)
implementation(projects.domain.visa.models)
/** Compose */
implementation(deps.compose.runtime)