Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-18 13:50:25 +05:00
parent 64efc8af01
commit e11e2d953d
9 changed files with 40 additions and 18 deletions

View file

@ -79,15 +79,17 @@ interface TangemPayApi {
@Body body: CardDetailsRequest,
): ApiResponse<CardDetailsResponse>
@GET("v1/customer/card/pin")
@GET("v1/customer/card/{card_id}/pin")
suspend fun getPin(
@Header("Authorization") authHeader: String,
@Path("card_id") cardId: String,
@Header("X-Session-Id") sessionId: String,
): ApiResponse<GetPinResponse>
@PUT("v1/customer/card/pin")
@PUT("v1/customer/card/{card_id}/pin")
suspend fun setPin(
@Header("Authorization") authHeader: String,
@Path("card_id") cardId: String,
@Body body: SetPinRequest,
): ApiResponse<SetPinResponse>

View file

@ -123,7 +123,7 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
val publicKeyBase64 = getPublicKeyBase64()
val (secretKeyBytes, sessionId) = rainCryptoUtil.generateSecretKeyAndSessionId(publicKeyBase64)
val response = requestHelper.performRequest(userWalletId = userWalletId) { authHeader ->
tangemPayApi.getPin(authHeader = authHeader, sessionId = sessionId)
tangemPayApi.getPin(authHeader = authHeader, cardId = cardId, sessionId = sessionId)
}.getOrNull()
val result = requireNotNull(response?.result)
@ -140,7 +140,11 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
)
}
override suspend fun setPin(userWalletId: UserWalletId, pin: String): Either<UniversalError, SetPinResult> {
override suspend fun setPin(
userWalletId: UserWalletId,
cardId: String,
pin: String,
): Either<UniversalError, SetPinResult> {
return catch(
block = {
val publicKeyBase64 = getPublicKeyBase64()
@ -151,6 +155,7 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
val response = requestHelper.performRequest(userWalletId) { authHeader ->
tangemPayApi.setPin(
authHeader = authHeader,
cardId = cardId,
body = SetPinRequest(
sessionId = sessionId,
pin = encryptedData.encryptedBase64,

View file

@ -53,9 +53,13 @@ internal class MockAwareTangemPayCardDetailsRepository @Inject constructor(
return real.getPin(userWalletId, cardId)
}
override suspend fun setPin(userWalletId: UserWalletId, pin: String): Either<UniversalError, SetPinResult> {
override suspend fun setPin(
userWalletId: UserWalletId,
cardId: String,
pin: String,
): Either<UniversalError, SetPinResult> {
if (isMockMode) return SetPinResult.SUCCESS.right()
return real.setPin(userWalletId, pin)
return real.setPin(userWalletId, cardId, pin)
}
override suspend fun isAddToWalletDone(userWalletId: UserWalletId): Either<UniversalError, Boolean> =

View file

@ -22,7 +22,7 @@ interface TangemPayCardDetailsRepository {
suspend fun getPin(userWalletId: UserWalletId, cardId: String): Either<UniversalError, String?>
suspend fun setPin(userWalletId: UserWalletId, pin: String): Either<UniversalError, SetPinResult>
suspend fun setPin(userWalletId: UserWalletId, cardId: String, pin: String): Either<UniversalError, SetPinResult>
suspend fun isAddToWalletDone(userWalletId: UserWalletId): Either<UniversalError, Boolean>

View file

@ -70,9 +70,12 @@ internal class TangemPayCardPageComponent @AssistedInject constructor(
params = params,
tokenReceiveComponentFactory = tokenReceiveComponentFactory,
)
TangemPayCardDetailsInnerRoute.ChangePIN -> TangemPayChangePinComponent(
is TangemPayCardDetailsInnerRoute.ChangePIN -> TangemPayChangePinComponent(
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
params = TangemPayDetailsContainerComponent.Params(initialStatus = params.initialStatus),
params = TangemPayChangePinComponent.Params(
card = config.card,
userWalletId = params.initialStatus.userWalletId,
),
)
TangemPayCardDetailsInnerRoute.ChangePINSuccess -> TangemPayChangePinSuccessComponent(
appComponentContext = childByContext(

View file

@ -9,13 +9,15 @@ import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
import com.tangem.domain.models.pay.TangemPayCard
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.tangempay.model.TangemPayChangePinModel
import com.tangem.features.tangempay.ui.TangemPayChangePinScreen
import com.tangem.features.tangempay.ui.TangemPayChangePinScreenV2
internal class TangemPayChangePinComponent(
private val appComponentContext: AppComponentContext,
params: TangemPayDetailsContainerComponent.Params,
params: Params,
) : AppComponentContext by appComponentContext, ComposableContentComponent {
private val model: TangemPayChangePinModel = getOrCreateModel(params)
@ -37,4 +39,6 @@ internal class TangemPayChangePinComponent(
)
}
}
data class Params(val card: TangemPayCard, val userWalletId: UserWalletId)
}

View file

@ -379,10 +379,10 @@ internal class TangemPayCardPageModel @Inject constructor(
}
private fun onClickChangePIN(isPinSet: Boolean) {
val card = selectedCard() ?: return
if (!isPinSet) {
router.push(TangemPayCardDetailsInnerRoute.ChangePIN)
router.push(TangemPayCardDetailsInnerRoute.ChangePIN(card))
} else {
val card = selectedCard() ?: return
bottomSheetNavigation.activate(
TangemPayCardNavigation.ViewPinCode(
userWalletId = userWalletId,
@ -551,7 +551,8 @@ internal class TangemPayCardPageModel @Inject constructor(
override fun onClickChangePin() {
bottomSheetNavigation.dismiss()
router.push(TangemPayCardDetailsInnerRoute.ChangePIN)
val card = selectedCard() ?: return
router.push(TangemPayCardDetailsInnerRoute.ChangePIN(card))
}
override fun onDismissViewPin() {

View file

@ -9,16 +9,16 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.ToastMessage
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
import com.tangem.domain.pay.model.SetPinResult
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
import com.tangem.features.tangempay.components.TangemPayChangePinComponent
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayChangePinUM
import com.tangem.features.tangempay.model.transformers.PinCodeChangeTransformer
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
import com.tangem.features.tangempay.utils.userWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
import com.tangem.utils.transformer.update
@ -39,9 +39,10 @@ internal class TangemPayChangePinModel @Inject constructor(
private val cardDetailsRepository: TangemPayCardDetailsRepository,
private val analytics: AnalyticsEventHandler,
private val featureToggles: TangemPayFeatureToggles,
private val fetcher: PaymentAccountStatusFetcher,
) : Model() {
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
private val params: TangemPayChangePinComponent.Params = paramsContainer.require()
val uiState: StateFlow<TangemPayChangePinUM>
field = MutableStateFlow(getInitialState())
@ -66,7 +67,8 @@ internal class TangemPayChangePinModel @Inject constructor(
uiState.update { it.copy(submitButtonLoading = true) }
val result = try {
cardDetailsRepository.setPin(
userWalletId = params.initialStatus.userWalletId,
userWalletId = params.userWalletId,
cardId = params.card.id,
pin = uiState.value.pinCode,
).getOrNull()
} catch (e: Exception) {
@ -83,6 +85,7 @@ internal class TangemPayChangePinModel @Inject constructor(
)
}
SetPinResult.SUCCESS -> {
fetcher.invoke(params = PaymentAccountStatusFetcher.Params(userWalletId = params.userWalletId))
analytics.send(TangemPayAnalyticsEvents.ChangePinSuccessShown())
router.push(TangemPayCardDetailsInnerRoute.ChangePINSuccess)
}

View file

@ -11,7 +11,7 @@ internal sealed class TangemPayCardDetailsInnerRoute : Route {
data object Details : TangemPayCardDetailsInnerRoute()
@Serializable
data object ChangePIN : TangemPayCardDetailsInnerRoute()
data class ChangePIN(val card: TangemPayCard) : TangemPayCardDetailsInnerRoute()
@Serializable
data object ChangePINSuccess : TangemPayCardDetailsInnerRoute()