diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/visa/models/response/CardActivationRemoteStateResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/visa/models/response/CardActivationRemoteStateResponse.kt
index e0ee6cc4f0..aeb498dac8 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/api/visa/models/response/CardActivationRemoteStateResponse.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/api/visa/models/response/CardActivationRemoteStateResponse.kt
@@ -7,10 +7,13 @@ import com.squareup.moshi.JsonClass
data class CardActivationRemoteStateResponse(
@Json(name = "activation_status") val status: String,
@Json(name = "activation_order") val activationOrder: ActivationOrder?,
+ @Json(name = "stepChangeCode") val stepChangeCode: Int?,
+ @Json(name = "updatedAt") val updatedAt: String?,
) {
@JsonClass(generateAdapter = true)
data class ActivationOrder(
@Json(name = "id") val id: String,
+ @Json(name = "customer_id") val customerId: String,
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
)
}
\ No newline at end of file
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index 874225b0fe..b59492d1ed 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -109,6 +109,8 @@
報酬を受け取る
閉じる
確認
+ Tangemサポートへ問い合わせる
+ Visaサポートへ問い合わせる
続ける
コピー
アドレスをコピー
@@ -515,6 +517,7 @@
トークン標準
特徴
結果がありません。別のリクエストをお試しください。
+ ネットワークを選択
私のウォレットへ
NFTを受け取る
%1$dコレクションの%2$dNFT
@@ -1031,12 +1034,14 @@
長くはかかりません。アカウントを設定しています。
長くはかかりません。アクティベーションを完了しています。
準備完了です!
+ PINの認証に失敗しました。もう一度お試しいただくか、別のコードを使用してください。
無効な暗証番号:連続や繰り返しを避けてください
ウェブサイトに移動
アカウントの設定を続けましょう。
お帰りなさい!
手順に従ってアカウントを設定してください。
ようこそ!
+ この取引に異議を唱える
ロック解除
カードをスキャンしてアクセスロックを解除する
ロック解除が必要
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index de090f81f6..3ea8b2da0c 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -1020,6 +1020,7 @@
Переименование кошелька
Разблокировать все
Разблокировать все с %s
+ ПИН не принят. Попробуйте ещё раз или введите другой код.
Блокчейн недоступен. Попробуйте позже.
Отсканируйте карту или кольцо
Этот кошелек уже был активирован ранее.\nЕсли это сделали не вы, свяжитесь со службой поддержки.\nTangem никогда не продает кошелек вместе с предустановленным кодом доступа.
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index b2b7ab6c75..440dac2a29 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -528,11 +528,11 @@
Choose network
To My wallet
Receive NFT
+ Traits
%1$d NFTs in %2$d collection
Tap here to receive first NFT
NFT collections
Unable to load the data
- Traits
Set up a single access code to protect all your devices.
Protect
Set an individual access code for each card or ring later.
@@ -1070,6 +1070,7 @@
Set up a 4-digit code.
It will be used for payments.
PIN code
Create PIN Code
+ PIN was not accepted. Try again or use a different code.
Invalid PIN: avoid sequences or repeats
You\'re good to go!
Prepare the Tangem card and tap to approve
diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/converter/VisaActivationStatusConverter.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/converter/VisaActivationStatusConverter.kt
index d82a09116a..b67a8484e1 100644
--- a/data/visa/src/main/kotlin/com/tangem/data/visa/converter/VisaActivationStatusConverter.kt
+++ b/data/visa/src/main/kotlin/com/tangem/data/visa/converter/VisaActivationStatusConverter.kt
@@ -1,15 +1,60 @@
package com.tangem.data.visa.converter
import com.tangem.datasource.api.visa.models.response.CardActivationRemoteStateResponse
+import com.tangem.domain.visa.model.VisaActivationOrderInfo
import com.tangem.domain.visa.model.VisaActivationRemoteState
import com.tangem.utils.converter.Converter
+import kotlinx.coroutines.flow.MutableStateFlow
import javax.inject.Inject
+import javax.inject.Singleton
+@Singleton
class VisaActivationStatusConverter @Inject constructor() :
Converter {
+ private val lastUpdatedAt = MutableStateFlow(null)
+
override fun convert(value: CardActivationRemoteStateResponse): VisaActivationRemoteState {
+ // handle pin code error
+ // either we entered pin code before with an error (WasError) or after receiving an error (InProgress)
+ if (
+ value.status == Status.AwaitingPin.stringValue &&
+ value.stepChangeCode != null &&
+ value.stepChangeCode == PIN_CODE_VALIDATION_ERROR
+ ) {
+ return if (lastUpdatedAt.value != value.updatedAt) {
+ lastUpdatedAt.value == value.updatedAt
+
+ VisaActivationRemoteState.AwaitingPinCode(
+ activationOrderInfo = value.activationOrder!!.convert(),
+ status = VisaActivationRemoteState.AwaitingPinCode.Status.WasError,
+ )
+ } else {
+ VisaActivationRemoteState.AwaitingPinCode(
+ activationOrderInfo = value.activationOrder!!.convert(),
+ status = VisaActivationRemoteState.AwaitingPinCode.Status.InProgress,
+ )
+ }
+ }
+
// TODO Will be implemented in the future
return VisaActivationRemoteState.Activated
}
+
+ private fun CardActivationRemoteStateResponse.ActivationOrder.convert(): VisaActivationOrderInfo {
+ return VisaActivationOrderInfo(
+ orderId = id,
+ customerId = customerId,
+ customerWalletAddress = customerWalletAddress,
+ )
+ }
+
+ private enum class Status(val stringValue: String) {
+ AwaitingPin("AWAITING_PIN"),
+ // TODO complete statuses list when backend is ready
+ }
+
+ private companion object {
+ const val PIN_CODE_VALIDATION_ERROR = 1000
+ }
}
\ No newline at end of file
diff --git a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationOrderInfo.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationOrderInfo.kt
index 78bbd9692b..76389945fd 100644
--- a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationOrderInfo.kt
+++ b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationOrderInfo.kt
@@ -8,6 +8,6 @@ import kotlinx.serialization.Serializable
@JsonClass(generateAdapter = true)
data class VisaActivationOrderInfo(
@Json(name = "orderId") val orderId: String,
- @Json(name = "customer_id") val customerId: String,
- @Json(name = "customer_wallet_address") val customerWalletAddress: String,
+ @Json(name = "customerId") val customerId: String,
+ @Json(name = "customerWalletAddress") val customerWalletAddress: String,
)
\ No newline at end of file
diff --git a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationRemoteState.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationRemoteState.kt
index 88e4389287..889037bee3 100644
--- a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationRemoteState.kt
+++ b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaActivationRemoteState.kt
@@ -20,9 +20,14 @@ sealed class VisaActivationRemoteState {
data object PaymentAccountDeploying : VisaActivationRemoteState()
@Serializable
- data class WaitingPinCode(
+ data class AwaitingPinCode(
val activationOrderInfo: VisaActivationOrderInfo,
- ) : VisaActivationRemoteState()
+ val status: Status,
+ ) : VisaActivationRemoteState() {
+ enum class Status {
+ WaitingForPinCode, InProgress, WasError
+ }
+ }
@Serializable
data object WaitingForActivationFinishing : VisaActivationRemoteState()
@@ -43,6 +48,7 @@ class VisaActivationRemoteState_Json(
@Json(name = "type") val type: VisaActivationRemoteState_Type,
@Json(name = "requestCardWallet") val requestCardWallet: VisaCardWalletDataToSignRequest? = null,
@Json(name = "activationOrderInfo") val activationOrderInfo: VisaActivationOrderInfo? = null,
+ @Json(name = "awaiting_pin_code_status") val awaitingPinCodeStatus: AwaitingPinCodeStatus_Type? = null,
)
@Suppress("ClassNaming")
@@ -58,6 +64,12 @@ enum class VisaActivationRemoteState_Type {
BlockedForActivation,
}
+@Suppress("ClassNaming")
+@JsonClass(generateAdapter = false)
+enum class AwaitingPinCodeStatus_Type {
+ WaitingForPinCode, InProgress, WasError
+}
+
@Suppress("ClassNaming")
class VisaActivationRemoteState_JsonAdapter {
@@ -70,7 +82,17 @@ class VisaActivationRemoteState_JsonAdapter {
VisaActivationRemoteState.CustomerWalletSignatureRequired(value.activationOrderInfo!!)
VisaActivationRemoteState_Type.PaymentAccountDeploying -> VisaActivationRemoteState.PaymentAccountDeploying
VisaActivationRemoteState_Type.WaitingPinCode ->
- VisaActivationRemoteState.WaitingPinCode(value.activationOrderInfo!!)
+ VisaActivationRemoteState.AwaitingPinCode(
+ activationOrderInfo = value.activationOrderInfo!!,
+ status = when (value.awaitingPinCodeStatus!!) {
+ AwaitingPinCodeStatus_Type.WaitingForPinCode ->
+ VisaActivationRemoteState.AwaitingPinCode.Status.WaitingForPinCode
+ AwaitingPinCodeStatus_Type.InProgress ->
+ VisaActivationRemoteState.AwaitingPinCode.Status.InProgress
+ AwaitingPinCodeStatus_Type.WasError ->
+ VisaActivationRemoteState.AwaitingPinCode.Status.WasError
+ },
+ )
VisaActivationRemoteState_Type.WaitingForActivationFinishing ->
VisaActivationRemoteState.WaitingForActivationFinishing
VisaActivationRemoteState_Type.Activated -> VisaActivationRemoteState.Activated
@@ -93,10 +115,18 @@ class VisaActivationRemoteState_JsonAdapter {
)
is VisaActivationRemoteState.PaymentAccountDeploying ->
VisaActivationRemoteState_Json(VisaActivationRemoteState_Type.PaymentAccountDeploying)
- is VisaActivationRemoteState.WaitingPinCode ->
+ is VisaActivationRemoteState.AwaitingPinCode ->
VisaActivationRemoteState_Json(
VisaActivationRemoteState_Type.WaitingPinCode,
activationOrderInfo = value.activationOrderInfo,
+ awaitingPinCodeStatus = when (value.status) {
+ VisaActivationRemoteState.AwaitingPinCode.Status.WaitingForPinCode ->
+ AwaitingPinCodeStatus_Type.WaitingForPinCode
+ VisaActivationRemoteState.AwaitingPinCode.Status.InProgress ->
+ AwaitingPinCodeStatus_Type.InProgress
+ VisaActivationRemoteState.AwaitingPinCode.Status.WasError ->
+ AwaitingPinCodeStatus_Type.WasError
+ },
)
is VisaActivationRemoteState.WaitingForActivationFinishing ->
VisaActivationRemoteState_Json(VisaActivationRemoteState_Type.WaitingForActivationFinishing)
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/DefaultOnboardingVisaComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/DefaultOnboardingVisaComponent.kt
index b3cc93357a..c18f5585e7 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/DefaultOnboardingVisaComponent.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/DefaultOnboardingVisaComponent.kt
@@ -167,7 +167,9 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
params = OnboardingVisaOtherWalletComponent.Params(
childParams = childParams,
onDone = {
- model.stackNavigation.pushNew(OnboardingVisaRoute.PinCode(activationOrderInfo = it))
+ model.stackNavigation.pushNew(
+ OnboardingVisaRoute.PinCode(activationOrderInfo = it, pinCodeValidationError = false),
+ )
},
),
)
@@ -176,6 +178,7 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
config = OnboardingVisaPinCodeComponent.Config(
scanResponse = model.currentScanResponse.value,
activationOrderInfo = route.activationOrderInfo,
+ wasValidationError = route.pinCodeValidationError,
),
params = OnboardingVisaPinCodeComponent.Params(
childParams = childParams,
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/model/OnboardingVisaAccessCodeModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/model/OnboardingVisaAccessCodeModel.kt
index 7feeafe67c..a07b1acd3a 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/model/OnboardingVisaAccessCodeModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/model/OnboardingVisaAccessCodeModel.kt
@@ -92,6 +92,7 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor(
_uiState.update {
it.copy(
accessCodeFirst = textFieldValue,
+ accessCodeSecond = TextFieldValue(),
atLeastMinCharsError = false,
)
}
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt
index b409af7757..a138341670 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt
@@ -75,9 +75,10 @@ internal class OnboardingVisaInProgressModel @Inject constructor(
VisaActivationRemoteState.WaitingForActivationFinishing,
-> {
}
- is VisaActivationRemoteState.WaitingPinCode -> {
- navigateToPinCode(result.activationOrderInfo)
- return@launch
+ is VisaActivationRemoteState.AwaitingPinCode -> {
+ navigateToPinCodeIfNeeded(result) {
+ return@launch
+ }
}
VisaActivationRemoteState.Activated -> {
finishActivation()
@@ -90,8 +91,37 @@ internal class OnboardingVisaInProgressModel @Inject constructor(
}
}
- private suspend fun navigateToPinCode(activationOrderInfo: VisaActivationOrderInfo) {
- onDone.emit(Params.DoneEvent.NavigateTo(OnboardingVisaRoute.PinCode(activationOrderInfo)))
+ private suspend inline fun navigateToPinCodeIfNeeded(
+ remoteState: VisaActivationRemoteState.AwaitingPinCode,
+ returnBlock: () -> Unit,
+ ) {
+ when (remoteState.status) {
+ VisaActivationRemoteState.AwaitingPinCode.Status.WaitingForPinCode -> {
+ onDone.emit(
+ Params.DoneEvent.NavigateTo(
+ OnboardingVisaRoute.PinCode(
+ activationOrderInfo = remoteState.activationOrderInfo,
+ pinCodeValidationError = false,
+ ),
+ ),
+ )
+ returnBlock()
+ }
+ VisaActivationRemoteState.AwaitingPinCode.Status.WasError -> {
+ onDone.emit(
+ Params.DoneEvent.NavigateTo(
+ OnboardingVisaRoute.PinCode(
+ activationOrderInfo = remoteState.activationOrderInfo,
+ pinCodeValidationError = true,
+ ),
+ ),
+ )
+ returnBlock()
+ }
+ VisaActivationRemoteState.AwaitingPinCode.Status.InProgress -> {
+ /** waiting for new state */
+ }
+ }
}
private suspend fun finishActivation() {
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/otherwallet/model/OnboardingVisaOtherWalletModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/otherwallet/model/OnboardingVisaOtherWalletModel.kt
index 4d89e3baa0..fa1adb5d3c 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/otherwallet/model/OnboardingVisaOtherWalletModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/otherwallet/model/OnboardingVisaOtherWalletModel.kt
@@ -54,7 +54,7 @@ internal class OnboardingVisaOtherWalletModel @Inject constructor(
visaActivationRepository.getActivationRemoteStateLongPoll()
}.getOrNull()
- if (result is VisaActivationRemoteState.WaitingPinCode) {
+ if (result is VisaActivationRemoteState.AwaitingPinCode) {
onDone.emit(result.activationOrderInfo)
break
}
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/pincode/OnboardingVisaPinCodeComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/pincode/OnboardingVisaPinCodeComponent.kt
index b70d2995a1..baf52ede33 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/pincode/OnboardingVisaPinCodeComponent.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/pincode/OnboardingVisaPinCodeComponent.kt
@@ -48,6 +48,7 @@ internal class OnboardingVisaPinCodeComponent(
data class Config(
val scanResponse: ScanResponse,
val activationOrderInfo: VisaActivationOrderInfo,
+ val wasValidationError: Boolean,
)
data class Params(
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/pincode/model/OnboardingVisaPinCodeModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/pincode/model/OnboardingVisaPinCodeModel.kt
index f3919153a9..432a024889 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/pincode/model/OnboardingVisaPinCodeModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/pincode/model/OnboardingVisaPinCodeModel.kt
@@ -61,6 +61,11 @@ internal class OnboardingVisaPinCodeModel @Inject constructor(
submitButtonEnabled = false,
onPinCodeChange = ::onPinCodeChange,
onSubmitClick = ::onSubmitClick,
+ error = if (params.wasValidationError) {
+ resourceReference(R.string.visa_onboarding_pin_not_accepted)
+ } else {
+ null
+ },
)
}
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/model/OnboardingVisaModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/model/OnboardingVisaModel.kt
index f93540c8ee..8bc8b14032 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/model/OnboardingVisaModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/model/OnboardingVisaModel.kt
@@ -159,8 +159,11 @@ internal class OnboardingVisaModel @Inject constructor(
VisaActivationRemoteState.WaitingForActivationFinishing -> {
OnboardingVisaRoute.InProgress(from = OnboardingVisaRoute.InProgress.From.PinCode)
}
- is VisaActivationRemoteState.WaitingPinCode -> {
- OnboardingVisaRoute.PinCode(activationOrderInfo = remoteState.activationOrderInfo)
+ is VisaActivationRemoteState.AwaitingPinCode -> {
+ OnboardingVisaRoute.PinCode(
+ activationOrderInfo = remoteState.activationOrderInfo,
+ pinCodeValidationError = false,
+ )
}
VisaActivationRemoteState.Activated,
VisaActivationRemoteState.BlockedForActivation,
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/route/OnboardingVisaRoute.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/route/OnboardingVisaRoute.kt
index 9dca6484e9..762fc96544 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/route/OnboardingVisaRoute.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/route/OnboardingVisaRoute.kt
@@ -48,5 +48,8 @@ internal sealed class OnboardingVisaRoute {
}
@Serializable
- data class PinCode(val activationOrderInfo: VisaActivationOrderInfo) : OnboardingVisaRoute()
+ data class PinCode(
+ val activationOrderInfo: VisaActivationOrderInfo,
+ val pinCodeValidationError: Boolean,
+ ) : OnboardingVisaRoute()
}
\ No newline at end of file