Updated on 2026-08-14

This commit is contained in:
Tangem 2025-11-20 14:05:32 +05:00
parent 9aa3bf7541
commit 3ea0fbb8a3
32 changed files with 618 additions and 421 deletions

View file

@ -8,6 +8,7 @@ import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.models.request.DeeplinkValidityRequest
import com.tangem.datasource.api.pay.models.request.OrderRequest
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
import com.tangem.domain.pay.model.CustomerInfo
@ -16,6 +17,7 @@ import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
import com.tangem.domain.pay.model.MainScreenCustomerInfo
import com.tangem.domain.pay.model.OrderStatus
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import javax.inject.Inject
@ -24,6 +26,7 @@ private const val VALID_STATUS = "valid"
private const val APPROVED_KYC_STATUS = "APPROVED"
private const val TAG = "TangemPay: OnboardingRepository"
@Suppress("LongParameterList")
internal class DefaultOnboardingRepository @Inject constructor(
private val dispatcherProvider: CoroutineDispatcherProvider,
private val tangemPayApi: TangemPayApi,
@ -31,6 +34,7 @@ internal class DefaultOnboardingRepository @Inject constructor(
private val tangemPayStorage: TangemPayStorage,
private val authDataSource: TangemPayAuthDataSource,
private val tangemPayWalletsManager: TangemPayWalletsManager,
private val cardFrozenStateStore: TangemPayCardFrozenStateStore,
) : OnboardingRepository {
override suspend fun validateDeeplink(link: String): Either<UniversalError, Boolean> {
@ -122,7 +126,7 @@ internal class DefaultOnboardingRepository @Inject constructor(
}
}
private fun getCustomerInfo(response: CustomerMeResponse.Result?): CustomerInfo {
private suspend fun getCustomerInfo(response: CustomerMeResponse.Result?): CustomerInfo {
val card = response?.card
val balance = response?.balance
val paymentAccount = response?.paymentAccount
@ -138,6 +142,13 @@ internal class DefaultOnboardingRepository @Inject constructor(
null
}
val productInstance = response?.productInstance?.let { instance ->
cardFrozenStateStore.store(
key = instance.cardId,
value = when (instance.status) {
CustomerMeResponse.ProductInstance.Status.ACTIVE -> TangemPayCardFrozenState.Unfrozen
else -> TangemPayCardFrozenState.Frozen
},
)
ProductInstance(
id = instance.id,
cardId = instance.cardId,

View file

@ -12,18 +12,19 @@ import com.tangem.datasource.api.pay.models.request.CardDetailsRequest
import com.tangem.datasource.api.pay.models.request.FreezeUnfreezeCardRequest
import com.tangem.datasource.api.pay.models.request.SetPinRequest
import com.tangem.datasource.api.pay.models.response.FreezeUnfreezeCardResponse
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.domain.pay.model.SetPinResult
import com.tangem.domain.pay.model.TangemPayCardBalance
import com.tangem.domain.pay.model.TangemPayCardDetails
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.visa.error.VisaApiError
import timber.log.Timber
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
private const val TAG = "TangemPay: CardDetailsRepository"
@Suppress("OptionalUnit")
@Suppress("LongParameterList")
internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
private val tangemPayApi: TangemPayApi,
private val requestHelper: TangemPayRequestPerformer,
@ -31,6 +32,7 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
private val apiConfigsManager: ApiConfigsManager,
private val rainCryptoUtil: RainCryptoUtil,
private val storage: TangemPayStorage,
private val cardFrozenStateStore: TangemPayCardFrozenStateStore,
) : TangemPayCardDetailsRepository {
override suspend fun getCardBalance(): Either<UniversalError, TangemPayCardBalance> {
@ -117,34 +119,58 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
}
}
override suspend fun freezeCard(cardId: String): Either<UniversalError, Unit> {
override suspend fun freezeCard(cardId: String): Either<UniversalError, TangemPayCardFrozenState> {
cardFrozenStateStore.store(cardId, TangemPayCardFrozenState.Pending)
return requestHelper.makeSafeRequest {
tangemPayApi.freezeCard(authHeader = it, body = FreezeUnfreezeCardRequest(cardId = cardId))
}.onLeft {
cardFrozenStateStore.store(cardId, TangemPayCardFrozenState.Unfrozen)
}.map { response ->
val result = response.result
if (result != null && result.status == FreezeUnfreezeCardResponse.Status.COMPLETED) {
Either.Right(Unit)
} else {
Timber.e("Card freeze failed with status: ${response.result?.status}")
Either.Left(VisaApiError.UnknownWithoutCode)
val state = when (response.result?.status) {
FreezeUnfreezeCardResponse.Status.COMPLETED -> TangemPayCardFrozenState.Frozen
FreezeUnfreezeCardResponse.Status.NEW,
FreezeUnfreezeCardResponse.Status.PROCESSING,
-> TangemPayCardFrozenState.Pending
FreezeUnfreezeCardResponse.Status.CANCELED,
null,
-> TangemPayCardFrozenState.Unfrozen
}
cardFrozenStateStore.store(cardId, state)
state
}
}
override suspend fun unfreezeCard(cardId: String): Either<UniversalError, Unit> {
override suspend fun unfreezeCard(cardId: String): Either<UniversalError, TangemPayCardFrozenState> {
cardFrozenStateStore.store(cardId, TangemPayCardFrozenState.Pending)
return requestHelper.makeSafeRequest {
tangemPayApi.unfreezeCard(authHeader = it, body = FreezeUnfreezeCardRequest(cardId = cardId))
}.onLeft {
cardFrozenStateStore.store(cardId, TangemPayCardFrozenState.Frozen)
}.map { response ->
val result = response.result
if (result != null && result.status == FreezeUnfreezeCardResponse.Status.COMPLETED) {
Either.Right(Unit)
} else {
Timber.e("Card unfreeze failed with status: ${response.result?.status}")
Either.Left(VisaApiError.UnknownWithoutCode)
val state = when (response.result?.status) {
FreezeUnfreezeCardResponse.Status.COMPLETED -> TangemPayCardFrozenState.Unfrozen
FreezeUnfreezeCardResponse.Status.NEW,
FreezeUnfreezeCardResponse.Status.PROCESSING,
-> TangemPayCardFrozenState.Pending
FreezeUnfreezeCardResponse.Status.CANCELED,
null,
-> TangemPayCardFrozenState.Frozen
}
cardFrozenStateStore.store(cardId, state)
state
}
}
override fun cardFrozenState(cardId: String): Flow<TangemPayCardFrozenState> {
return cardFrozenStateStore.get(cardId)
}
override suspend fun cardFrozenStateSync(cardId: String): TangemPayCardFrozenState? {
return cardFrozenStateStore.getSyncOrNull(cardId)
}
private suspend fun getPublicKeyBase64(): String {
val env = apiConfigsManager.getEnvironmentConfig(ApiConfig.ID.TangemPay).environment
return when (env) {