Updated on 2026-08-14
This commit is contained in:
parent
8cff4c1bba
commit
ab6072e12d
18 changed files with 219 additions and 57 deletions
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.tangem.tap.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.tangem.datasource.local.visa.TangemPayStorage
|
||||||
|
import com.tangem.sdk.storage.AndroidSecureStorageV2
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
private const val DEFAULT_KEY = "tangem_pay_default_key"
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
internal class DefaultTangemPayStorage @Inject constructor(
|
||||||
|
@ApplicationContext applicationContext: Context,
|
||||||
|
private val dispatcherProvider: CoroutineDispatcherProvider,
|
||||||
|
) : TangemPayStorage {
|
||||||
|
|
||||||
|
private val secureStorage by lazy {
|
||||||
|
AndroidSecureStorageV2(
|
||||||
|
appContext = applicationContext,
|
||||||
|
useStrongBox = false,
|
||||||
|
name = "tangem_pay_storage",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun store(authHeader: String) = withContext(dispatcherProvider.io) {
|
||||||
|
secureStorage.store(authHeader.encodeToByteArray(throwOnInvalidSequence = true), DEFAULT_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun get(): String? = withContext(dispatcherProvider.io) {
|
||||||
|
secureStorage.get(DEFAULT_KEY)?.decodeToString(throwOnInvalidSequence = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clear() = withContext(dispatcherProvider.io) {
|
||||||
|
secureStorage.delete(DEFAULT_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package com.tangem.tap.di.data
|
package com.tangem.tap.di.data
|
||||||
|
|
||||||
|
import com.tangem.datasource.local.visa.TangemPayStorage
|
||||||
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
|
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
|
||||||
import com.tangem.datasource.local.visa.VisaOTPStorage
|
import com.tangem.datasource.local.visa.VisaOTPStorage
|
||||||
|
import com.tangem.tap.data.DefaultTangemPayStorage
|
||||||
import com.tangem.tap.data.DefaultVisaAuthTokenStorage
|
import com.tangem.tap.data.DefaultVisaAuthTokenStorage
|
||||||
import com.tangem.tap.data.DefaultVisaOTPStorage
|
import com.tangem.tap.data.DefaultVisaOTPStorage
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
|
|
@ -21,4 +23,8 @@ internal interface VisaStorageModule {
|
||||||
@Binds
|
@Binds
|
||||||
@Singleton
|
@Singleton
|
||||||
fun bindVisaOTPStorage(impl: DefaultVisaOTPStorage): VisaOTPStorage
|
fun bindVisaOTPStorage(impl: DefaultVisaOTPStorage): VisaOTPStorage
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
fun bindTangemPayStorage(impl: DefaultTangemPayStorage): TangemPayStorage
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ import com.tangem.domain.models.scan.CardDTO
|
||||||
import com.tangem.domain.visa.error.VisaActivationError
|
import com.tangem.domain.visa.error.VisaActivationError
|
||||||
import com.tangem.domain.visa.model.*
|
import com.tangem.domain.visa.model.*
|
||||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
|
||||||
import com.tangem.operations.GenerateOTPCommand
|
import com.tangem.operations.GenerateOTPCommand
|
||||||
import com.tangem.operations.attestation.AttestCardKeyCommand
|
import com.tangem.operations.attestation.AttestCardKeyCommand
|
||||||
import com.tangem.operations.pins.SetUserCodeCommand
|
import com.tangem.operations.pins.SetUserCodeCommand
|
||||||
|
|
@ -46,7 +46,7 @@ class VisaCardActivationTask @AssistedInject constructor(
|
||||||
@Assisted private val coroutineScope: CoroutineScope,
|
@Assisted private val coroutineScope: CoroutineScope,
|
||||||
private val otpStorage: VisaOTPStorage,
|
private val otpStorage: VisaOTPStorage,
|
||||||
private val visaAuthTokenStorage: VisaAuthTokenStorage,
|
private val visaAuthTokenStorage: VisaAuthTokenStorage,
|
||||||
private val visaAuthRepository: VisaAuthRepository,
|
private val visaAuthRemoteDataSource: VisaAuthRemoteDataSource,
|
||||||
private val visaActivationRepositoryFactory: VisaActivationRepository.Factory,
|
private val visaActivationRepositoryFactory: VisaActivationRepository.Factory,
|
||||||
) : CardSessionRunnable<VisaCardActivationResponse> {
|
) : CardSessionRunnable<VisaCardActivationResponse> {
|
||||||
|
|
||||||
|
|
@ -168,7 +168,7 @@ class VisaCardActivationTask @AssistedInject constructor(
|
||||||
signedChallenge: VisaAuthSignedChallenge,
|
signedChallenge: VisaAuthSignedChallenge,
|
||||||
cardWalletAddress: String,
|
cardWalletAddress: String,
|
||||||
): Either<TangemError, VisaDataToSignByCardWallet> = either {
|
): Either<TangemError, VisaDataToSignByCardWallet> = either {
|
||||||
val tokens = visaAuthRepository.getAccessTokens(signedChallenge)
|
val tokens = visaAuthRemoteDataSource.getAccessTokens(signedChallenge)
|
||||||
.getOrElse { raise(it.tangemError) }
|
.getOrElse { raise(it.tangemError) }
|
||||||
|
|
||||||
visaAuthTokenStorage.store(cardId, tokens)
|
visaAuthTokenStorage.store(cardId, tokens)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import com.tangem.domain.visa.error.VisaApiError
|
||||||
import com.tangem.domain.visa.error.VisaCardScanError
|
import com.tangem.domain.visa.error.VisaCardScanError
|
||||||
import com.tangem.domain.visa.model.*
|
import com.tangem.domain.visa.model.*
|
||||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
|
||||||
import com.tangem.operations.attestation.AttestCardKeyCommand
|
import com.tangem.operations.attestation.AttestCardKeyCommand
|
||||||
import com.tangem.operations.attestation.AttestCardKeyResponse
|
import com.tangem.operations.attestation.AttestCardKeyResponse
|
||||||
import com.tangem.operations.attestation.AttestWalletKeyResponse
|
import com.tangem.operations.attestation.AttestWalletKeyResponse
|
||||||
|
|
@ -26,7 +26,7 @@ import javax.inject.Inject
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
internal class VisaCardScanHandler @Inject constructor(
|
internal class VisaCardScanHandler @Inject constructor(
|
||||||
private val visaAuthRepository: VisaAuthRepository,
|
private val visaAuthRemoteDataSource: VisaAuthRemoteDataSource,
|
||||||
private val visaActivationRepositoryFactory: VisaActivationRepository.Factory,
|
private val visaActivationRepositoryFactory: VisaActivationRepository.Factory,
|
||||||
private val visaAuthTokenStorage: VisaAuthTokenStorage,
|
private val visaAuthTokenStorage: VisaAuthTokenStorage,
|
||||||
) {
|
) {
|
||||||
|
|
@ -83,7 +83,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
||||||
|
|
||||||
Timber.i("Requesting challenge for wallet authorization")
|
Timber.i("Requesting challenge for wallet authorization")
|
||||||
|
|
||||||
val challengeResponse = visaAuthRepository.getCardWalletAuthChallenge(
|
val challengeResponse = visaAuthRemoteDataSource.getCardWalletAuthChallenge(
|
||||||
cardId = card.cardId,
|
cardId = card.cardId,
|
||||||
// This is the wallet public key, not the address and it's alright, as the API expects it in this format
|
// This is the wallet public key, not the address and it's alright, as the API expects it in this format
|
||||||
cardWalletAddress = wallet.publicKey.toHexString(),
|
cardWalletAddress = wallet.publicKey.toHexString(),
|
||||||
|
|
@ -122,7 +122,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
||||||
cardWalletAddress: String,
|
cardWalletAddress: String,
|
||||||
signedChallenge: VisaAuthSignedChallenge,
|
signedChallenge: VisaAuthSignedChallenge,
|
||||||
): CompletionResult<VisaCardActivationStatus> {
|
): CompletionResult<VisaCardActivationStatus> {
|
||||||
val authorizationTokensResponse = visaAuthRepository.getAccessTokens(signedChallenge = signedChallenge)
|
val authorizationTokensResponse = visaAuthRemoteDataSource.getAccessTokens(signedChallenge = signedChallenge)
|
||||||
.getOrElse {
|
.getOrElse {
|
||||||
Timber.i("Failed to get Access token for Wallet public key authorization.")
|
Timber.i("Failed to get Access token for Wallet public key authorization.")
|
||||||
return if (
|
return if (
|
||||||
|
|
@ -149,7 +149,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
||||||
|
|
||||||
Timber.i("Requesting authorization challenge to sign")
|
Timber.i("Requesting authorization challenge to sign")
|
||||||
|
|
||||||
val challengeResponse = visaAuthRepository.getCardAuthChallenge(
|
val challengeResponse = visaAuthRemoteDataSource.getCardAuthChallenge(
|
||||||
cardId = card.cardId,
|
cardId = card.cardId,
|
||||||
cardPublicKey = card.cardPublicKey.toHexString(),
|
cardPublicKey = card.cardPublicKey.toHexString(),
|
||||||
).getOrElse {
|
).getOrElse {
|
||||||
|
|
@ -174,7 +174,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val authorizationTokensResponse = visaAuthRepository.getAccessTokens(
|
val authorizationTokensResponse = visaAuthRemoteDataSource.getAccessTokens(
|
||||||
signedChallenge = challengeResponse.toSignedChallenge(
|
signedChallenge = challengeResponse.toSignedChallenge(
|
||||||
signedChallenge = attestCardKeyResponse.cardSignature.toHexString(),
|
signedChallenge = attestCardKeyResponse.cardSignature.toHexString(),
|
||||||
salt = attestCardKeyResponse.salt.toHexString(),
|
salt = attestCardKeyResponse.salt.toHexString(),
|
||||||
|
|
|
||||||
|
|
@ -103,4 +103,7 @@ interface TangemPayApi {
|
||||||
|
|
||||||
@GET("v1/customer/kyc")
|
@GET("v1/customer/kyc")
|
||||||
suspend fun getKycAccess(@Header("Authorization") authHeader: String): ApiResponse<KycAccessInfoResponse>
|
suspend fun getKycAccess(@Header("Authorization") authHeader: String): ApiResponse<KycAccessInfoResponse>
|
||||||
|
|
||||||
|
@GET("v1/customer/me")
|
||||||
|
suspend fun getCustomerMe(@Header("Authorization") authHeader: String): ApiResponse<CustomerMeResponse>
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.tangem.datasource.api.pay.models.response
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class CustomerMeResponse(
|
||||||
|
@Json(name = "result") val result: Result?,
|
||||||
|
@Json(name = "error") val error: String?,
|
||||||
|
) {
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class Result(
|
||||||
|
@Json(name = "id") val id: String,
|
||||||
|
@Json(name = "state") val state: String,
|
||||||
|
@Json(name = "createdAt") val createdAt: String,
|
||||||
|
@Json(name = "product_instance") val productInstance: ProductInstance,
|
||||||
|
@Json(name = "payment_account") val paymentAccount: PaymentAccount,
|
||||||
|
@Json(name = "kyc") val kyc: Kyc,
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class ProductInstance(
|
||||||
|
@Json(name = "id") val id: String,
|
||||||
|
@Json(name = "cid") val cid: String,
|
||||||
|
@Json(name = "card_id") val cardId: String,
|
||||||
|
@Json(name = "card_wallet_address") val cardWalletAddress: String,
|
||||||
|
@Json(name = "status") val status: String,
|
||||||
|
@Json(name = "updated_at") val updatedAt: String,
|
||||||
|
@Json(name = "payment_account_id") val paymentAccountId: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class PaymentAccount(
|
||||||
|
@Json(name = "id") val id: String,
|
||||||
|
@Json(name = "address") val address: String,
|
||||||
|
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class Kyc(
|
||||||
|
@Json(name = "id") val id: String,
|
||||||
|
@Json(name = "provider") val provider: String,
|
||||||
|
@Json(name = "status") val status: String,
|
||||||
|
@Json(name = "risk") val risk: String,
|
||||||
|
@Json(name = "review_answer") val reviewAnswer: String,
|
||||||
|
@Json(name = "created_at") val createdAt: String,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.tangem.datasource.local.visa
|
||||||
|
|
||||||
|
interface TangemPayStorage {
|
||||||
|
|
||||||
|
suspend fun store(authHeader: String)
|
||||||
|
|
||||||
|
suspend fun get(): String?
|
||||||
|
|
||||||
|
suspend fun clear()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.tangem.data.pay.datasource
|
||||||
|
|
||||||
|
import arrow.core.Either
|
||||||
|
import arrow.core.raise.either
|
||||||
|
import com.tangem.common.CompletionResult
|
||||||
|
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
|
||||||
|
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||||
|
import com.tangem.domain.visa.model.VisaDataToSignByCustomerWallet
|
||||||
|
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
|
||||||
|
import com.tangem.sdk.api.TangemSdkManager
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class DefaultTangemPayAuthDataSource @Inject constructor(
|
||||||
|
private val visaAuthRemoteDataSource: VisaAuthRemoteDataSource,
|
||||||
|
private val tangemSdkManager: TangemSdkManager,
|
||||||
|
) : TangemPayAuthDataSource {
|
||||||
|
|
||||||
|
override suspend fun generateNewAuthHeader(address: String, cardId: String): Either<Throwable, String> = either {
|
||||||
|
val challenge = visaAuthRemoteDataSource
|
||||||
|
.getCustomerWalletAuthChallenge(address)
|
||||||
|
.mapLeft { IllegalStateException("TangemPay challenge failed. Error code: ${it.errorCode}") }
|
||||||
|
.bind()
|
||||||
|
|
||||||
|
val signed = tangemSdkManager.visaCustomerWalletApprove(
|
||||||
|
VisaDataForApprove(
|
||||||
|
customerWalletCardId = cardId,
|
||||||
|
targetAddress = address,
|
||||||
|
dataToSign = VisaDataToSignByCustomerWallet(hashToSign = challenge.challenge),
|
||||||
|
),
|
||||||
|
).toEither { IllegalStateException("TangemPay signing failed: $it") }.bind()
|
||||||
|
|
||||||
|
visaAuthRemoteDataSource.getTokenWithCustomerWallet(
|
||||||
|
sessionId = challenge.session.sessionId,
|
||||||
|
signature = signed.signature,
|
||||||
|
nonce = signed.dataToSign.hashToSign,
|
||||||
|
)
|
||||||
|
.mapLeft { IllegalStateException("TangemPay token fetch failed. Error code: ${it.errorCode}") }
|
||||||
|
.bind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> CompletionResult<T>.toEither(map: (Throwable) -> Throwable) = when (this) {
|
||||||
|
is CompletionResult.Success -> Either.Right(data)
|
||||||
|
is CompletionResult.Failure -> Either.Left(map(error))
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.tangem.data.pay.di
|
package com.tangem.data.pay.di
|
||||||
|
|
||||||
import com.tangem.data.pay.DefaultKycRepository
|
import com.tangem.data.pay.repository.DefaultKycRepository
|
||||||
import com.tangem.domain.pay.repository.KycRepository
|
import com.tangem.domain.pay.repository.KycRepository
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,48 @@
|
||||||
package com.tangem.data.pay
|
package com.tangem.data.pay.repository
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import com.tangem.common.map
|
|
||||||
import com.tangem.core.error.UniversalError
|
import com.tangem.core.error.UniversalError
|
||||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||||
import com.tangem.datasource.api.common.response.getOrThrow
|
import com.tangem.datasource.api.common.response.getOrThrow
|
||||||
import com.tangem.datasource.api.pay.TangemPayApi
|
import com.tangem.datasource.api.pay.TangemPayApi
|
||||||
import com.tangem.datasource.api.pay.models.response.VisaErrorResponseJsonAdapter
|
import com.tangem.datasource.api.pay.models.response.VisaErrorResponseJsonAdapter
|
||||||
import com.tangem.datasource.di.NetworkMoshi
|
import com.tangem.datasource.di.NetworkMoshi
|
||||||
|
import com.tangem.datasource.local.visa.TangemPayStorage
|
||||||
import com.tangem.domain.pay.KycStartInfo
|
import com.tangem.domain.pay.KycStartInfo
|
||||||
|
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
|
||||||
import com.tangem.domain.pay.repository.KycRepository
|
import com.tangem.domain.pay.repository.KycRepository
|
||||||
import com.tangem.domain.visa.error.VisaApiError
|
import com.tangem.domain.visa.error.VisaApiError
|
||||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
|
||||||
import com.tangem.domain.visa.model.VisaDataToSignByCustomerWallet
|
|
||||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
|
||||||
import com.tangem.sdk.api.TangemSdkManager
|
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
|
||||||
class DefaultKycRepository @AssistedInject constructor(
|
class DefaultKycRepository @AssistedInject constructor(
|
||||||
@NetworkMoshi moshi: Moshi,
|
@NetworkMoshi moshi: Moshi,
|
||||||
private val tangemPayApi: TangemPayApi,
|
private val tangemPayApi: TangemPayApi,
|
||||||
private val visaAuthRepository: VisaAuthRepository,
|
private val authDataSource: TangemPayAuthDataSource,
|
||||||
private val tangemSdkManager: TangemSdkManager,
|
private val tangemPayStorage: TangemPayStorage,
|
||||||
) : KycRepository {
|
) : KycRepository {
|
||||||
|
|
||||||
private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi)
|
private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi)
|
||||||
|
|
||||||
override suspend fun getKycStartInfo(address: String, cardId: String): Either<UniversalError, KycStartInfo> {
|
override suspend fun getKycStartInfo(address: String, cardId: String): Either<UniversalError, KycStartInfo> {
|
||||||
var authHeader = ""
|
val authHeader = authDataSource.generateNewAuthHeader(address, cardId)
|
||||||
visaAuthRepository.getCustomerWalletAuthChallenge(address).getOrNull()?.let { result ->
|
.getOrNull()
|
||||||
tangemSdkManager.visaCustomerWalletApprove(
|
.takeIf { !it.isNullOrEmpty() }
|
||||||
VisaDataForApprove(
|
?: return Either.Left(VisaApiError.UnknownWithoutCode)
|
||||||
customerWalletCardId = cardId,
|
tangemPayStorage.store(authHeader)
|
||||||
targetAddress = address,
|
return getKycInfo(authHeader)
|
||||||
dataToSign = VisaDataToSignByCustomerWallet(hashToSign = result.challenge),
|
}
|
||||||
),
|
|
||||||
).map { signResult ->
|
override suspend fun getKycStartInfo(authHeader: String): Either<UniversalError, KycStartInfo> {
|
||||||
visaAuthRepository.getTokenWithCustomerWallet(
|
return getKycInfo(authHeader)
|
||||||
sessionId = result.session.sessionId,
|
}
|
||||||
signature = signResult.signature,
|
|
||||||
nonce = signResult.dataToSign.hashToSign,
|
private suspend fun getKycInfo(authHeader: String): Either<UniversalError, KycStartInfo> {
|
||||||
).getOrNull()?.let { authHeader = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return request {
|
return request {
|
||||||
authHeader.ifEmpty { error("Cannot get auth header for KYC") }
|
|
||||||
tangemPayApi.getKycAccess(authHeader = authHeader).getOrThrow().result
|
tangemPayApi.getKycAccess(authHeader = authHeader).getOrThrow().result
|
||||||
}.map {
|
}.map {
|
||||||
KycStartInfo(
|
KycStartInfo(token = it.token, locale = it.locale)
|
||||||
token = it.token,
|
|
||||||
locale = it.locale,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ import com.tangem.datasource.local.visa.VisaAuthTokenStorage
|
||||||
import com.tangem.domain.visa.error.VisaApiError
|
import com.tangem.domain.visa.error.VisaApiError
|
||||||
import com.tangem.domain.visa.model.*
|
import com.tangem.domain.visa.model.*
|
||||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
|
|
@ -32,7 +32,7 @@ internal class DefaultVisaActivationRepository @AssistedInject constructor(
|
||||||
private val visaApi: TangemPayApi,
|
private val visaApi: TangemPayApi,
|
||||||
private val dispatcherProvider: CoroutineDispatcherProvider,
|
private val dispatcherProvider: CoroutineDispatcherProvider,
|
||||||
private val visaAuthTokenStorage: VisaAuthTokenStorage,
|
private val visaAuthTokenStorage: VisaAuthTokenStorage,
|
||||||
private val visaAuthRepository: VisaAuthRepository,
|
private val visaAuthRemoteDataSource: VisaAuthRemoteDataSource,
|
||||||
private val visaLibLoader: VisaLibLoader,
|
private val visaLibLoader: VisaLibLoader,
|
||||||
private val apiConfigsManager: ApiConfigsManager,
|
private val apiConfigsManager: ApiConfigsManager,
|
||||||
) : VisaActivationRepository {
|
) : VisaActivationRepository {
|
||||||
|
|
@ -194,7 +194,7 @@ internal class DefaultVisaActivationRepository @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val authTokens = visaAuthTokenStorage.get(visaCardId.cardId) ?: error("Auth tokens are not stored")
|
val authTokens = visaAuthTokenStorage.get(visaCardId.cardId) ?: error("Auth tokens are not stored")
|
||||||
val newTokens = visaAuthRepository.refreshAccessTokens(authTokens.refreshToken).getOrElse {
|
val newTokens = visaAuthRemoteDataSource.refreshAccessTokens(authTokens.refreshToken).getOrElse {
|
||||||
return Either.Left(VisaApiError.RefreshTokenExpired)
|
return Either.Left(VisaApiError.RefreshTokenExpired)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,16 @@ import com.tangem.domain.visa.model.VisaAuthChallenge
|
||||||
import com.tangem.domain.visa.model.VisaAuthSession
|
import com.tangem.domain.visa.model.VisaAuthSession
|
||||||
import com.tangem.domain.visa.model.VisaAuthSignedChallenge
|
import com.tangem.domain.visa.model.VisaAuthSignedChallenge
|
||||||
import com.tangem.domain.visa.model.VisaAuthTokens
|
import com.tangem.domain.visa.model.VisaAuthTokens
|
||||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Suppress("UnusedPrivateMember")
|
internal class DefaultVisaAuthRemoteDataSource @Inject constructor(
|
||||||
internal class DefaultVisaAuthRepository @Inject constructor(
|
|
||||||
@NetworkMoshi private val moshi: Moshi,
|
@NetworkMoshi private val moshi: Moshi,
|
||||||
private val visaAuthApi: TangemPayApi,
|
private val visaAuthApi: TangemPayApi,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : VisaAuthRepository {
|
) : VisaAuthRemoteDataSource {
|
||||||
|
|
||||||
private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi)
|
private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi)
|
||||||
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package com.tangem.data.visa.di
|
package com.tangem.data.visa.di
|
||||||
|
|
||||||
|
import com.tangem.data.pay.datasource.DefaultTangemPayAuthDataSource
|
||||||
import com.tangem.data.visa.DefaultVisaActivationRepository
|
import com.tangem.data.visa.DefaultVisaActivationRepository
|
||||||
import com.tangem.data.visa.DefaultVisaAuthRepository
|
import com.tangem.data.visa.DefaultVisaAuthRemoteDataSource
|
||||||
import com.tangem.data.visa.MockVisaRepository
|
import com.tangem.data.visa.MockVisaRepository
|
||||||
|
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
|
||||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
|
||||||
import com.tangem.domain.visa.repository.VisaRepository
|
import com.tangem.domain.visa.repository.VisaRepository
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
|
@ -18,7 +20,7 @@ internal interface VisaDataModule {
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@Singleton
|
@Singleton
|
||||||
fun bindVisaAuthRepository(repository: DefaultVisaAuthRepository): VisaAuthRepository
|
fun bindVisaAuthRemoteDataSource(repository: DefaultVisaAuthRemoteDataSource): VisaAuthRemoteDataSource
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@Singleton
|
@Singleton
|
||||||
|
|
@ -39,4 +41,8 @@ internal interface VisaDataModule {
|
||||||
// Mocked
|
// Mocked
|
||||||
@Binds
|
@Binds
|
||||||
fun bindVisaRepository(repository: MockVisaRepository): VisaRepository
|
fun bindVisaRepository(repository: MockVisaRepository): VisaRepository
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
fun bindTangemPayAuthDataSource(repository: DefaultTangemPayAuthDataSource): TangemPayAuthDataSource
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.tangem.domain.pay.datasource
|
||||||
|
|
||||||
|
import arrow.core.Either
|
||||||
|
|
||||||
|
interface TangemPayAuthDataSource {
|
||||||
|
|
||||||
|
suspend fun generateNewAuthHeader(address: String, cardId: String): Either<Throwable, String>
|
||||||
|
}
|
||||||
|
|
@ -6,8 +6,16 @@ import com.tangem.domain.pay.KycStartInfo
|
||||||
|
|
||||||
interface KycRepository {
|
interface KycRepository {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns fresh KYC data to start the survey. Used only for first time launch
|
||||||
|
*/
|
||||||
suspend fun getKycStartInfo(address: String, cardId: String): Either<UniversalError, KycStartInfo>
|
suspend fun getKycStartInfo(address: String, cardId: String): Either<UniversalError, KycStartInfo>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns KYC data to continue the survey. Used when KYC wasn't finished by the user
|
||||||
|
*/
|
||||||
|
suspend fun getKycStartInfo(authHeader: String): Either<UniversalError, KycStartInfo>
|
||||||
|
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(): KycRepository
|
fun create(): KycRepository
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.tangem.domain.visa.repository
|
package com.tangem.domain.visa.datasource
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.domain.visa.error.VisaApiError
|
import com.tangem.domain.visa.error.VisaApiError
|
||||||
|
|
@ -6,7 +6,7 @@ import com.tangem.domain.visa.model.VisaAuthChallenge
|
||||||
import com.tangem.domain.visa.model.VisaAuthSignedChallenge
|
import com.tangem.domain.visa.model.VisaAuthSignedChallenge
|
||||||
import com.tangem.domain.visa.model.VisaAuthTokens
|
import com.tangem.domain.visa.model.VisaAuthTokens
|
||||||
|
|
||||||
interface VisaAuthRepository {
|
interface VisaAuthRemoteDataSource {
|
||||||
|
|
||||||
suspend fun getCardAuthChallenge(
|
suspend fun getCardAuthChallenge(
|
||||||
cardId: String,
|
cardId: String,
|
||||||
|
|
@ -18,7 +18,7 @@ import com.tangem.domain.visa.model.VisaCardActivationStatus
|
||||||
import com.tangem.domain.visa.model.VisaCardId
|
import com.tangem.domain.visa.model.VisaCardId
|
||||||
import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest
|
import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest
|
||||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
|
||||||
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.OnboardingVisaAccessCodeComponent
|
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.OnboardingVisaAccessCodeComponent
|
||||||
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.ui.state.OnboardingVisaAccessCodeUM
|
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.ui.state.OnboardingVisaAccessCodeUM
|
||||||
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.model.analytics.OnboardingVisaAnalyticsEvent
|
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.model.analytics.OnboardingVisaAnalyticsEvent
|
||||||
|
|
@ -44,7 +44,7 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor(
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
@Suppress("UnusedPrivateMember")
|
@Suppress("UnusedPrivateMember")
|
||||||
private val tangemSdkManager: TangemSdkManager,
|
private val tangemSdkManager: TangemSdkManager,
|
||||||
private val visaAuthRepository: VisaAuthRepository,
|
private val visaAuthRemoteDataSource: VisaAuthRemoteDataSource,
|
||||||
private val uiMessageSender: UiMessageSender,
|
private val uiMessageSender: UiMessageSender,
|
||||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
@ -155,7 +155,7 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor(
|
||||||
loading(true)
|
loading(true)
|
||||||
|
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
val challengeToSign = visaAuthRepository.getCardAuthChallenge(
|
val challengeToSign = visaAuthRemoteDataSource.getCardAuthChallenge(
|
||||||
cardId = activationInput.cardId,
|
cardId = activationInput.cardId,
|
||||||
cardPublicKey = activationInput.cardPublicKey,
|
cardPublicKey = activationInput.cardPublicKey,
|
||||||
).getOrElse {
|
).getOrElse {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import com.tangem.domain.visa.model.VisaAuthTokens
|
||||||
import com.tangem.domain.visa.model.VisaCardActivationStatus
|
import com.tangem.domain.visa.model.VisaCardActivationStatus
|
||||||
import com.tangem.domain.visa.model.VisaCardId
|
import com.tangem.domain.visa.model.VisaCardId
|
||||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
|
||||||
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
|
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
|
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
|
||||||
|
|
@ -42,7 +42,7 @@ internal class OnboardingVisaInProgressModel @Inject constructor(
|
||||||
paramsContainer: ParamsContainer,
|
paramsContainer: ParamsContainer,
|
||||||
visaActivationRepositoryFactory: VisaActivationRepository.Factory,
|
visaActivationRepositoryFactory: VisaActivationRepository.Factory,
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val visaAuthRepository: VisaAuthRepository,
|
private val visaAuthRemoteDataSource: VisaAuthRemoteDataSource,
|
||||||
private val visaAuthTokenStorage: VisaAuthTokenStorage,
|
private val visaAuthTokenStorage: VisaAuthTokenStorage,
|
||||||
private val otpStorage: VisaOTPStorage,
|
private val otpStorage: VisaOTPStorage,
|
||||||
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
|
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
|
||||||
|
|
@ -166,7 +166,7 @@ internal class OnboardingVisaInProgressModel @Inject constructor(
|
||||||
val authTokens = visaAuthTokenStorage.get(params.scanResponse.card.cardId)
|
val authTokens = visaAuthTokenStorage.get(params.scanResponse.card.cardId)
|
||||||
?: error("Auth tokens are not found. This should not happen.")
|
?: error("Auth tokens are not found. This should not happen.")
|
||||||
|
|
||||||
val newTokens = visaAuthRepository.exchangeAccessToken(authTokens)
|
val newTokens = visaAuthRemoteDataSource.exchangeAccessToken(authTokens)
|
||||||
.getOrElse {
|
.getOrElse {
|
||||||
uiMessageSender.showErrorDialog(it)
|
uiMessageSender.showErrorDialog(it)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue