Updated on 2026-08-14
This commit is contained in:
parent
e037233aa5
commit
804e4eca23
31 changed files with 635 additions and 147 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.data.pay
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.Either.Companion.catch
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.blockchains.ethereum.Chain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
|
|
@ -16,7 +17,6 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.pay.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.TangemPayTopUpDataFactory
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
|
@ -68,10 +68,10 @@ internal class DefaultTangemPayTopUpDataFactory @Inject constructor(
|
|||
cryptoBalance: BigDecimal,
|
||||
fiatBalance: BigDecimal,
|
||||
): Either<UniversalError, TangemPayTopUpData> {
|
||||
return try {
|
||||
return catch {
|
||||
val wallet = tangemPayWalletsManager.getDefaultWalletForTangemPayBlocking()
|
||||
val currency = getCurrency(wallet, chainId)
|
||||
val result = TangemPayTopUpData(
|
||||
TangemPayTopUpData(
|
||||
currency = currency,
|
||||
walletId = wallet.walletId,
|
||||
cryptoBalance = cryptoBalance,
|
||||
|
|
@ -79,18 +79,9 @@ internal class DefaultTangemPayTopUpDataFactory @Inject constructor(
|
|||
depositAddress = depositAddress,
|
||||
receiveAddress = listOf(ReceiveAddressModel(nameService = NameService.Default, value = depositAddress)),
|
||||
)
|
||||
|
||||
Either.Right(result)
|
||||
} catch (exception: Exception) {
|
||||
when (exception) {
|
||||
is CancellationException -> {
|
||||
throw exception
|
||||
}
|
||||
else -> {
|
||||
Timber.tag(TAG).e(exception)
|
||||
Either.Left(errorConverter.convert(exception))
|
||||
}
|
||||
}
|
||||
}.mapLeft { exception ->
|
||||
Timber.tag(TAG).e(exception)
|
||||
errorConverter.convert(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
package com.tangem.data.pay.di
|
||||
|
||||
import com.tangem.data.pay.DefaultTangemPayTopUpDataFactory
|
||||
import com.tangem.data.pay.repository.DefaultCardDetailsRepository
|
||||
import com.tangem.data.pay.repository.DefaultTangemPayCardDetailsRepository
|
||||
import com.tangem.data.pay.repository.DefaultKycRepository
|
||||
import com.tangem.data.pay.repository.DefaultTangemPayTxHistoryRepository
|
||||
import com.tangem.data.pay.repository.DefaultOnboardingRepository
|
||||
import com.tangem.data.pay.usecase.DefaultGetTangemPayCurrencyStatusUseCase
|
||||
import com.tangem.domain.pay.TangemPayTopUpDataFactory
|
||||
import com.tangem.domain.pay.repository.CardDetailsRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||
import com.tangem.domain.pay.repository.KycRepository
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
|
||||
|
|
@ -40,7 +40,7 @@ internal interface TangemPayDataModule {
|
|||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindCardDetailsRepository(repository: DefaultCardDetailsRepository): CardDetailsRepository
|
||||
fun bindCardDetailsRepository(repository: DefaultTangemPayCardDetailsRepository): TangemPayCardDetailsRepository
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
|
|
|
|||
|
|
@ -137,8 +137,18 @@ internal class DefaultOnboardingRepository @Inject constructor(
|
|||
} else {
|
||||
null
|
||||
}
|
||||
val productInstance = response?.productInstance?.let { instance ->
|
||||
ProductInstance(
|
||||
id = instance.id,
|
||||
cardId = instance.cardId,
|
||||
status = when (instance.status) {
|
||||
CustomerMeResponse.ProductInstance.Status.ACTIVE -> ProductInstance.Status.ACTIVE
|
||||
else -> ProductInstance.Status.INACTIVE
|
||||
},
|
||||
)
|
||||
}
|
||||
return CustomerInfo(
|
||||
productInstance = response?.productInstance?.let { ProductInstance(id = it.id, status = it.status) },
|
||||
productInstance = productInstance,
|
||||
isKycApproved = response?.kyc?.status == APPROVED_KYC_STATUS,
|
||||
cardInfo = cardInfo,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,24 +9,29 @@ import com.tangem.datasource.api.common.config.ApiEnvironment
|
|||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.datasource.api.pay.TangemPayApi
|
||||
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.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.CardDetailsRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val TAG = "TangemPay: CardDetailsRepository"
|
||||
|
||||
internal class DefaultCardDetailsRepository @Inject constructor(
|
||||
@Suppress("OptionalUnit")
|
||||
internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
|
||||
private val tangemPayApi: TangemPayApi,
|
||||
private val requestHelper: TangemPayRequestPerformer,
|
||||
private val visaLibLoader: VisaLibLoader,
|
||||
private val apiConfigsManager: ApiConfigsManager,
|
||||
private val rainCryptoUtil: RainCryptoUtil,
|
||||
private val storage: TangemPayStorage,
|
||||
) : CardDetailsRepository {
|
||||
) : TangemPayCardDetailsRepository {
|
||||
|
||||
override suspend fun getCardBalance(): Either<UniversalError, TangemPayCardBalance> {
|
||||
return requestHelper.runWithErrorLogs(TAG) {
|
||||
|
|
@ -112,6 +117,34 @@ internal class DefaultCardDetailsRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun freezeCard(cardId: String): Either<UniversalError, Unit> {
|
||||
return requestHelper.makeSafeRequest {
|
||||
tangemPayApi.freezeCard(authHeader = it, body = FreezeUnfreezeCardRequest(cardId = cardId))
|
||||
}.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun unfreezeCard(cardId: String): Either<UniversalError, Unit> {
|
||||
return requestHelper.makeSafeRequest {
|
||||
tangemPayApi.unfreezeCard(authHeader = it, body = FreezeUnfreezeCardRequest(cardId = cardId))
|
||||
}.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getPublicKeyBase64(): String {
|
||||
val env = apiConfigsManager.getEnvironmentConfig(ApiConfig.ID.TangemPay).environment
|
||||
return when (env) {
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.data.pay.repository
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.Either.Companion.catch
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.data.pay.util.TangemPayErrorConverter
|
||||
|
|
@ -11,6 +12,7 @@ import com.tangem.datasource.api.common.response.getOrThrow
|
|||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
||||
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import com.tangem.domain.visa.model.VisaAuthTokens
|
||||
import com.tangem.domain.visa.model.getAuthHeader
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -21,7 +23,6 @@ import kotlinx.coroutines.sync.withLock
|
|||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class TangemPayRequestPerformer @Inject constructor(
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -54,6 +55,16 @@ internal class TangemPayRequestPerformer @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun <T : Any> makeSafeRequest(
|
||||
requestBlock: suspend (header: String) -> ApiResponse<T>,
|
||||
): Either<VisaApiError, T> {
|
||||
return catch { request(requestBlock) }
|
||||
.mapLeft { exception ->
|
||||
Timber.tag("TangemPayRequestPerformer").e(exception)
|
||||
errorConverter.convert(exception)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <T : Any> request(requestBlock: suspend (header: String) -> ApiResponse<T>): T =
|
||||
withContext(dispatchers.io) {
|
||||
performRequest(
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
package com.tangem.data.pay.util
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.pay.models.response.VisaErrorResponseJsonAdapter
|
||||
import com.tangem.datasource.api.pay.models.response.VisaErrorResponse
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
class TangemPayErrorConverter(moshi: Moshi) : Converter<Throwable, UniversalError> {
|
||||
class TangemPayErrorConverter(moshi: Moshi) : Converter<Throwable, VisaApiError> {
|
||||
|
||||
private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi)
|
||||
private val visaErrorAdapter by lazy { moshi.adapter(VisaErrorResponse::class.java) }
|
||||
|
||||
override fun convert(value: Throwable): UniversalError {
|
||||
override fun convert(value: Throwable): VisaApiError {
|
||||
return if (value is ApiResponseError.HttpException) {
|
||||
val errorBody = value.errorBody ?: return VisaApiError.UnknownWithoutCode
|
||||
return runCatching {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue