Updated on 2026-08-14
This commit is contained in:
parent
10584c84a8
commit
096d1e40c9
11 changed files with 79 additions and 53 deletions
|
|
@ -4,6 +4,11 @@ import com.squareup.moshi.Json
|
|||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OrderRequest(
|
||||
@Json(name = "wallet_address") val walletAddress: String,
|
||||
)
|
||||
data class OrderRequest(@Json(name = "data") val data: Data) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Data(
|
||||
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
|
||||
@Json(name = "specification_name") val specificationName: String = "SP_000004",
|
||||
@Json(name = "type") val type: String = "CARD_ISSUE_VIRTUAL_RAIN_KYC",
|
||||
)
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ data class CustomerMeResponse(
|
|||
@JsonClass(generateAdapter = true)
|
||||
data class PaymentAccount(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "address") val address: String,
|
||||
@Json(name = "address") val address: String?,
|
||||
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -92,8 +92,7 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
|
|||
}
|
||||
|
||||
private fun UserWallet.isCompatible(): Boolean = when (this) {
|
||||
is UserWallet.Cold ->
|
||||
scanResponse.card.firmwareVersion >= FirmwareVersion.HDWalletAvailable
|
||||
is UserWallet.Cold -> scanResponse.card.firmwareVersion >= FirmwareVersion.HDWalletAvailable
|
||||
is UserWallet.Hot -> hotWalletId.authType != HotWalletId.AuthType.NoPassword
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,9 +104,9 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
ifRight = { customerInfo ->
|
||||
Timber.tag(TAG).i("proceedWithoutOrder data customerInfo $userWalletId")
|
||||
val status = customerInfo.mapToPaymentAccountStatus()
|
||||
if (status is PaymentAccountStatus.IssuingCard && customerInfo.kycStatus == KycStatus.APPROVED) {
|
||||
// If order id wasn't saved -> start order creation and get customer info
|
||||
if (customerInfo.productInstance == null) {
|
||||
onboardingRepository.createOrder(userWalletId)
|
||||
.onLeft { Timber.tag(TAG).e("createOrder failed: $it") }
|
||||
}
|
||||
status
|
||||
},
|
||||
|
|
@ -128,9 +128,6 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
-> PaymentAccountStatus.IssuingCard(source = StatusSource.ACTUAL)
|
||||
|
||||
OrderStatus.CANCELED -> {
|
||||
// If order was cancelled -> clear previous order from local storage and start order creation
|
||||
onboardingRepository.clearOrderId(userWalletId)
|
||||
onboardingRepository.createOrder(userWalletId)
|
||||
PaymentAccountStatus.Error.CardIssueFailed
|
||||
}
|
||||
OrderStatus.COMPLETED -> {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ internal class DefaultCustomerOrderRepository @Inject constructor(
|
|||
OrderResponse.Result.Status.CANCELED -> OrderStatus.CANCELED
|
||||
}
|
||||
OrderData(
|
||||
customerId = response.result?.customerId.orEmpty(),
|
||||
status = status,
|
||||
withdrawTxHash = response.result?.data?.transactionHash?.ifEmpty { null },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
package com.tangem.data.pay.repository
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.right
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
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.request.SetTangemPayEnabledRequest
|
||||
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
|
||||
import com.tangem.datasource.api.pay.models.response.OrderResponse
|
||||
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
|
||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
|
|
@ -23,14 +24,11 @@ import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
|||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val VALID_STATUS = "valid"
|
||||
private const val TAG = "TangemPay: OnboardingRepository"
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultOnboardingRepository @Inject constructor(
|
||||
|
|
@ -109,25 +107,31 @@ internal class DefaultOnboardingRepository @Inject constructor(
|
|||
return lastFetchedCustomerInfoMap[userWalletId]
|
||||
}
|
||||
|
||||
override suspend fun createOrder(userWalletId: UserWalletId) = withContext(dispatcherProvider.io) {
|
||||
launch {
|
||||
catch(
|
||||
block = {
|
||||
val walletAddress = requestHelper.getCustomerWalletAddress(userWalletId)
|
||||
val response = requestHelper.performRequest(userWalletId) { authHeader ->
|
||||
tangemPayApi.createOrder(authHeader, body = OrderRequest(walletAddress))
|
||||
}.getOrNull()
|
||||
override suspend fun createOrder(userWalletId: UserWalletId): Either<VisaApiError, String> =
|
||||
withContext(dispatcherProvider.io) {
|
||||
val existingOrderId = getOrderId(userWalletId)
|
||||
if (existingOrderId != null) {
|
||||
val orderStatus = requestHelper.performRequest(userWalletId) { authHeader ->
|
||||
tangemPayApi.getOrder(authHeader, existingOrderId)
|
||||
}.map { it.result?.status }.getOrNull()
|
||||
|
||||
val result = requireNotNull(response?.result)
|
||||
val customerWalletAddress = requireNotNull(result.data.customerWalletAddress)
|
||||
tangemPayStorage.storeOrderId(customerWalletAddress, result.id)
|
||||
},
|
||||
catch = {
|
||||
Timber.tag(TAG).e("createOrder: $it")
|
||||
},
|
||||
)
|
||||
if (orderStatus == OrderResponse.Result.Status.NEW ||
|
||||
orderStatus == OrderResponse.Result.Status.PROCESSING
|
||||
) {
|
||||
return@withContext existingOrderId.right()
|
||||
}
|
||||
}
|
||||
|
||||
val walletAddress = requestHelper.getCustomerWalletAddress(userWalletId)
|
||||
requestHelper.performRequest(userWalletId) { authHeader ->
|
||||
val data = OrderRequest.Data(customerWalletAddress = walletAddress)
|
||||
tangemPayApi.createOrder(authHeader, body = OrderRequest(data = data))
|
||||
}.map { response ->
|
||||
val result = requireNotNull(response.result)
|
||||
tangemPayStorage.storeOrderId(walletAddress, result.id)
|
||||
result.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUserWallet(userWalletId: UserWalletId): UserWallet {
|
||||
return userWalletsListRepository.userWallets.value?.firstOrNull { it.walletId == userWalletId }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.pay.model
|
||||
|
||||
data class OrderData(
|
||||
val customerId: String,
|
||||
val status: OrderStatus,
|
||||
val withdrawTxHash: String?,
|
||||
)
|
||||
|
|
@ -5,7 +5,6 @@ import com.tangem.core.error.UniversalError
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.model.CustomerInfo
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import kotlinx.coroutines.Job
|
||||
|
||||
interface OnboardingRepository {
|
||||
|
||||
|
|
@ -17,7 +16,7 @@ interface OnboardingRepository {
|
|||
|
||||
suspend fun getCustomerInfo(userWalletId: UserWalletId): Either<VisaApiError, CustomerInfo>
|
||||
|
||||
suspend fun createOrder(userWalletId: UserWalletId): Job
|
||||
suspend fun createOrder(userWalletId: UserWalletId): Either<VisaApiError, String>
|
||||
|
||||
suspend fun clearOrderId(userWalletId: UserWalletId)
|
||||
|
||||
|
|
|
|||
|
|
@ -126,11 +126,13 @@ class TangemPayMainScreenCustomerInfoUseCase(
|
|||
}
|
||||
.map { customerInfo ->
|
||||
Timber.tag(TAG).i("customerInfo")
|
||||
if (customerInfo.cardInfo == null && customerInfo.kycStatus == KycStatus.APPROVED) {
|
||||
// If order id wasn't saved -> start order creation and get customer info
|
||||
if (customerInfo.productInstance == null) {
|
||||
onboardingRepository.createOrder(userWalletId)
|
||||
Timber.tag("ddk9499").d("TangemPayMainScreenCustomerInfoUseCase.proceedWithoutOrder: ")
|
||||
MainScreenCustomerInfo(info = customerInfo, orderStatus = OrderStatus.NEW)
|
||||
} else {
|
||||
MainScreenCustomerInfo(info = customerInfo, orderStatus = OrderStatus.COMPLETED)
|
||||
}
|
||||
MainScreenCustomerInfo(info = customerInfo, orderStatus = OrderStatus.UNKNOWN)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,27 +147,33 @@ class TangemPayMainScreenCustomerInfoUseCase(
|
|||
},
|
||||
ifRight = { orderData ->
|
||||
when (orderData.status) {
|
||||
// Kyc is passed and user waits for order creation -> no need to get customer info
|
||||
OrderStatus.NEW,
|
||||
OrderStatus.PROCESSING,
|
||||
-> MainScreenCustomerInfo(
|
||||
info = CustomerInfo(
|
||||
customerId = null,
|
||||
productInstance = null,
|
||||
kycStatus = KycStatus.APPROVED,
|
||||
cardInfo = null,
|
||||
),
|
||||
orderStatus = orderData.status,
|
||||
).right()
|
||||
-> {
|
||||
onboardingRepository.getCustomerInfo(userWalletId = userWalletId)
|
||||
.mapLeft { it.mapErrorForCustomer() }
|
||||
.map { customerInfo ->
|
||||
MainScreenCustomerInfo(info = customerInfo, orderStatus = orderData.status)
|
||||
}
|
||||
}
|
||||
|
||||
// Order cancelled. No need to get customer info
|
||||
OrderStatus.CANCELED -> {
|
||||
MainScreenCustomerInfo(
|
||||
info = CustomerInfo(
|
||||
customerId = null,
|
||||
productInstance = null,
|
||||
kycStatus = KycStatus.INIT,
|
||||
cardInfo = null,
|
||||
),
|
||||
orderStatus = OrderStatus.CANCELED,
|
||||
).right()
|
||||
}
|
||||
|
||||
// Order was created/cancelled -> clear order id and get customer info
|
||||
OrderStatus.COMPLETED,
|
||||
OrderStatus.CANCELED,
|
||||
OrderStatus.UNKNOWN,
|
||||
-> {
|
||||
onboardingRepository.clearOrderId(userWalletId)
|
||||
// If order was cancelled -> start order creation
|
||||
if (orderData.status == OrderStatus.CANCELED) onboardingRepository.createOrder(userWalletId)
|
||||
onboardingRepository.getCustomerInfo(userWalletId = userWalletId)
|
||||
.mapLeft { it.mapErrorForCustomer() }
|
||||
.map { customerInfo ->
|
||||
|
|
|
|||
|
|
@ -94,9 +94,14 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = true))
|
||||
repository.getCustomerInfo(userWalletId = userWalletId)
|
||||
.onRight { customerInfo ->
|
||||
uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = false))
|
||||
when {
|
||||
customerInfo.kycStatus != KycStatus.APPROVED -> {
|
||||
if (customerInfo.productInstance == null) {
|
||||
repository.createOrder(userWalletId)
|
||||
.onLeft { error ->
|
||||
Timber.e("Error creating order before KYC: $error")
|
||||
}
|
||||
}
|
||||
when (params) {
|
||||
is TangemPayOnboardingComponent.Params.ContinueOnboarding -> openKyc(userWalletId)
|
||||
else -> startOnboarding(userWalletId)
|
||||
|
|
@ -104,6 +109,7 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
}
|
||||
else -> back()
|
||||
}
|
||||
uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = false))
|
||||
}
|
||||
.onLeft { startOnboarding(userWalletId) }
|
||||
}
|
||||
|
|
@ -174,6 +180,12 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
if (customerInfo.kycStatus == KycStatus.APPROVED) {
|
||||
back()
|
||||
} else {
|
||||
if (customerInfo.productInstance == null) {
|
||||
repository.createOrder(userWalletId)
|
||||
.onLeft { error ->
|
||||
Timber.e("Error creating order before KYC: $error")
|
||||
}
|
||||
}
|
||||
openKyc(userWalletId)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ internal class TangemPayUpdateInfoStateTransformer(
|
|||
value.orderStatus == OrderStatus.CANCELED -> createCancelledState(customerId)
|
||||
value.info.kycStatus != KycStatus.APPROVED && !value.info.customerId.isNullOrEmpty() ->
|
||||
createKycInProgressState(kycStatus = value.info.kycStatus, customerId = customerId)
|
||||
cardInfo != null && productInstance != null ->
|
||||
cardInfo != null && productInstance != null && value.orderStatus == OrderStatus.COMPLETED ->
|
||||
getCardInfoState(customerId, cardInfo, productInstance)
|
||||
else -> createIssueProgressState()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue