Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-22 20:07:04 +05:00
parent 616386c345
commit b81ea5cf6e
9 changed files with 133 additions and 59 deletions

View file

@ -9,6 +9,7 @@ import com.tangem.domain.pay.DataForReceiveFactory
import com.tangem.domain.pay.repository.CardDetailsRepository
import com.tangem.domain.pay.repository.KycRepository
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
import com.tangem.domain.pay.usecase.TangemPayIssueOrderUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
@ -52,6 +53,14 @@ internal interface TangemPayDataModule {
return TangemPayMainScreenCustomerInfoUseCase(repository = repository)
}
@Provides
@Singleton
fun provideProduceTangemPayInitialDataUseCase(
repository: OnboardingRepository,
): ProduceTangemPayInitialDataUseCase {
return ProduceTangemPayInitialDataUseCase(repository = repository)
}
@Provides
@Singleton
fun provideTangemPayIssueOrderUseCase(repository: OnboardingRepository): TangemPayIssueOrderUseCase {

View file

@ -2,12 +2,14 @@ package com.tangem.data.pay.repository
import arrow.core.Either
import com.tangem.core.error.UniversalError
import com.tangem.data.pay.util.TangemPayWalletsManager
import com.tangem.datasource.api.common.response.getOrThrow
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.TangemPayStorage
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
import com.tangem.domain.pay.model.CustomerInfo
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
@ -27,6 +29,8 @@ internal class DefaultOnboardingRepository @Inject constructor(
private val tangemPayApi: TangemPayApi,
private val requestHelper: TangemPayRequestPerformer,
private val tangemPayStorage: TangemPayStorage,
private val authDataSource: TangemPayAuthDataSource,
private val tangemPayWalletsManager: TangemPayWalletsManager,
) : OnboardingRepository {
override suspend fun validateDeeplink(link: String): Either<UniversalError, Boolean> {
@ -38,6 +42,31 @@ internal class DefaultOnboardingRepository @Inject constructor(
}
}
override suspend fun isTangemPayInitialDataProduced(): Boolean {
val walletId = tangemPayWalletsManager.getDefaultWalletForTangemPay().walletId
val customerWalletAddress = tangemPayStorage.getCustomerWalletAddress(walletId) ?: return false
tangemPayStorage.getAuthTokens(customerWalletAddress) ?: return false
return true
}
override suspend fun produceInitialData() {
val wallet = tangemPayWalletsManager.getDefaultWalletForTangemPay()
val initialCredentials = authDataSource.produceInitialCredentials(cardId = wallet.cardId)
.fold(
ifLeft = { error -> error("Can not produce initial data: ${error.message}") },
ifRight = { it },
)
tangemPayStorage.storeCustomerWalletAddress(
userWalletId = wallet.walletId,
customerWalletAddress = initialCredentials.customerWalletAddress,
)
tangemPayStorage.storeAuthTokens(
customerWalletAddress = initialCredentials.customerWalletAddress,
tokens = initialCredentials.authTokens,
)
}
override suspend fun getCustomerInfo(): Either<UniversalError, CustomerInfo> {
return requestHelper.runWithErrorLogs(TAG) {
val result = requestHelper.request { authHeader ->
@ -85,7 +114,7 @@ internal class DefaultOnboardingRepository @Inject constructor(
override suspend fun createOrder(): Either<UniversalError, Unit> = withContext(dispatcherProvider.io) {
requestHelper.runWithErrorLogs(TAG) {
val walletAddress = requestHelper.getCustomerWalletAddress()
val result = requestHelper.requestWithPersistedToken { authHeader ->
val result = requestHelper.request { authHeader ->
tangemPayApi.createOrder(authHeader, body = OrderRequest(walletAddress))
}.result ?: error("Create order result is null")
@ -129,7 +158,7 @@ internal class DefaultOnboardingRepository @Inject constructor(
}
private suspend fun getCustomerInfoWithPersistedToken(): CustomerInfo {
val result = requestHelper.requestWithPersistedToken { authHeader ->
val result = requestHelper.request { authHeader ->
tangemPayApi.getCustomerMe(authHeader)
}.result
return getCustomerInfo(result)

View file

@ -63,15 +63,6 @@ internal class TangemPayRequestPerformer @Inject constructor(
)
}
suspend fun <T : Any> requestWithPersistedToken(requestBlock: suspend (header: String) -> ApiResponse<T>): T =
withContext(dispatchers.io) {
performRequest(
requestBlock = requestBlock,
getTokens = { getAccessTokensIfSaved() ?: error("Cannot get saved access tokens") },
refreshTokens = ::refreshAuthTokens,
)
}
private suspend fun <T : Any> performRequest(
requestBlock: suspend (header: String) -> ApiResponse<T>,
getTokens: (suspend () -> VisaAuthTokens),
@ -128,40 +119,20 @@ internal class TangemPayRequestPerformer @Inject constructor(
}
private suspend fun getAccessTokens(): VisaAuthTokens {
return getAccessTokensIfSaved() ?: fetchTokens()
}
private suspend fun getAccessTokensIfSaved(): VisaAuthTokens? {
return tangemPayStorage.getAuthTokens(getCustomerWalletAddress())
}
private suspend fun fetchTokens(): VisaAuthTokens {
val wallet = tangemPayWalletsManager.getDefaultWalletForTangemPay()
val initialCredentials = authDataSource.produceInitialCredentials(cardId = wallet.cardId)
.getOrThrowWithMessage("Can not produce initial data:")
tangemPayStorage.storeCustomerWalletAddress(
userWalletId = wallet.walletId,
customerWalletAddress = initialCredentials.customerWalletAddress,
)
tangemPayStorage.storeAuthTokens(
customerWalletAddress = initialCredentials.customerWalletAddress,
tokens = initialCredentials.authTokens,
)
return initialCredentials.authTokens
val walletAddress = getCustomerWalletAddress()
val tokens = tangemPayStorage.getAuthTokens(walletAddress) ?: error("Auth tokens are not stored")
return tokens
}
private suspend fun refreshAuthTokens(): VisaAuthTokens {
val customerWalletAddress = getCustomerWalletAddress()
val refreshToken = getAccessTokens().refreshToken.value
val tokens = authDataSource.refreshAuthTokens(refreshToken).getOrThrowWithMessage("Cannot refresh tokens:")
val tokens = authDataSource.refreshAuthTokens(refreshToken)
.fold(
ifLeft = { error -> error("Cannot refresh tokens: ${error.message}") },
ifRight = { it },
)
tangemPayStorage.storeAuthTokens(customerWalletAddress, tokens)
return tokens
}
private fun <A : Throwable, B> Either<A, B>.getOrThrowWithMessage(message: String): B {
return this.fold(
ifLeft = { error -> throw IllegalStateException("$message ${error.message}") },
ifRight = { it },
)
}
}