Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-20 20:38:05 +05:00
parent 74fa237c0d
commit 3b269e6f6a
13 changed files with 158 additions and 11 deletions

View file

@ -1,5 +1,6 @@
package com.tangem.tap.di.domain
import com.tangem.domain.onramp.repositories.LegacyTopUpRepository
import com.tangem.domain.onramp.*
import com.tangem.domain.onramp.repositories.HotCryptoRepository
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
@ -242,4 +243,10 @@ internal object OnrampDomainModule {
fun provideFetchHotCryptoUseCase(hotCryptoRepository: HotCryptoRepository): FetchHotCryptoUseCase {
return FetchHotCryptoUseCase(hotCryptoRepository)
}
@Provides
@Singleton
fun provideGetLegacyTopUpUrlUseCase(legacyTopUpRepository: LegacyTopUpRepository): GetLegacyTopUpUrlUseCase {
return GetLegacyTopUpUrlUseCase(legacyTopUpRepository)
}
}

View file

@ -26,6 +26,7 @@ import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
@Deprecated("Remove when navigation refactoring will be implemented")
class OnboardingManager(var scanResponse: ScanResponse) {
private val cardRepository: CardRepository by lazy { store.inject(DaggerGraphState::cardRepository) }

View file

@ -6,6 +6,7 @@ import com.tangem.common.extensions.calculateSha512
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.common.services.performRequest
import com.tangem.data.onramp.legacy.mercuryoNetwork
import com.tangem.domain.core.utils.lceContent
import com.tangem.domain.core.utils.lceError
import com.tangem.domain.core.utils.lceLoading

View file

@ -8,6 +8,8 @@ import com.tangem.data.onramp.DefaultOnrampErrorResolver
import com.tangem.data.onramp.DefaultOnrampRepository
import com.tangem.data.onramp.DefaultOnrampTransactionRepository
import com.tangem.data.onramp.converters.error.OnrampErrorConverter
import com.tangem.domain.onramp.repositories.LegacyTopUpRepository
import com.tangem.data.onramp.legacy.MercuryoTopUpRepository
import com.tangem.datasource.api.express.TangemExpressApi
import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
import com.tangem.datasource.api.onramp.OnrampApi
@ -15,6 +17,7 @@ import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.crypto.DataSignatureVerifier
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.exchangeservice.hotcrypto.HotCryptoResponseStore
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.datasource.local.onramp.countries.OnrampCountriesStore
import com.tangem.datasource.local.onramp.currencies.OnrampCurrenciesStore
import com.tangem.datasource.local.onramp.pairs.OnrampPairsStore
@ -112,4 +115,16 @@ internal object OnrampDataModule {
analyticsEventHandler = analyticsEventHandler,
)
}
@Provides
@Singleton
fun provideMercuryoRepository(
environmentConfigStorage: EnvironmentConfigStorage,
dispatchersProvider: CoroutineDispatcherProvider,
): LegacyTopUpRepository {
return MercuryoTopUpRepository(
environmentConfigStorage = environmentConfigStorage,
dispatchersProvider = dispatchersProvider,
)
}
}

View file

@ -1,12 +1,13 @@
package com.tangem.tap.network.exchangeServices.mercuryo
package com.tangem.data.onramp.legacy
import com.tangem.blockchain.common.Blockchain
/**
* Legacy onboarding note/twins to get top up URL from mercuryo
* Map [Blockchain] to ids from [link](https://api.mercuryo.io/v1.6/lib/currencies) [crypto_currencies]
*/
@Suppress("CyclomaticComplexMethod")
internal val Blockchain.mercuryoNetwork: String?
public val Blockchain.mercuryoNetwork: String?
get() {
return when (this) {
Blockchain.Algorand -> "ALGORAND"

View file

@ -0,0 +1,47 @@
package com.tangem.data.onramp.legacy
import android.net.Uri
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.calculateSha512
import com.tangem.common.extensions.toHexString
import com.tangem.datasource.local.config.environment.EnvironmentConfig
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.domain.onramp.repositories.LegacyTopUpRepository
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import javax.inject.Inject
internal class MercuryoTopUpRepository @Inject constructor(
private val environmentConfigStorage: EnvironmentConfigStorage,
private val dispatchersProvider: CoroutineDispatcherProvider,
) : LegacyTopUpRepository {
override suspend fun getTopUpUrl(cryptoCurrency: CryptoCurrency, walletAddress: String): String =
withContext(dispatchersProvider.default) {
val blockchain = Blockchain.fromId(cryptoCurrency.network.id.value)
val environmentConfig = environmentConfigStorage.getConfigSync()
val builder = Uri.Builder()
.scheme(LegacyTopUpRepository.SCHEME)
.authority("exchange.mercuryo.io")
.appendQueryParameter("widget_id", environmentConfig.mercuryoWidgetId)
.appendQueryParameter("type", "buy")
.appendQueryParameter("currency", cryptoCurrency.symbol)
.appendQueryParameter("address", walletAddress)
.appendQueryParameter("signature", environmentConfig.signature(walletAddress))
.appendQueryParameter("fix_currency", "true")
.appendQueryParameter("redirect_url", LegacyTopUpRepository.SUCCESS_URL)
blockchain.mercuryoNetwork?.let {
builder.appendQueryParameter("network", it)
}
builder.build().toString()
}
private fun EnvironmentConfig.signature(address: String) = (address + mercuryoSecret)
.calculateSha512()
.toHexString()
.lowercase()
}

View file

@ -0,0 +1,22 @@
package com.tangem.domain.onramp
import arrow.core.Either
import arrow.core.raise.either
import com.tangem.domain.onramp.repositories.LegacyTopUpRepository
import com.tangem.domain.onramp.model.error.OnrampError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
class GetLegacyTopUpUrlUseCase(
private val legacyTopUpRepository: LegacyTopUpRepository,
) {
suspend operator fun invoke(cryptoCurrencyStatus: CryptoCurrencyStatus): Either<OnrampError.DomainError, String> =
either {
val walletAddress = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
?: raise(OnrampError.DomainError("Wallet address is null"))
legacyTopUpRepository.getTopUpUrl(
cryptoCurrency = cryptoCurrencyStatus.currency,
walletAddress = walletAddress,
)
}
}

View file

@ -0,0 +1,23 @@
package com.tangem.domain.onramp.repositories
import com.tangem.domain.tokens.model.CryptoCurrency
/**
* Support legacy onboarding note/twins to get top up URL from mercuryo
*
*/
interface LegacyTopUpRepository {
/**
* Get top up URL from mercuryo
*
* @property cryptoCurrency crypto currency
* @property walletAddress address of wallet
*/
suspend fun getTopUpUrl(cryptoCurrency: CryptoCurrency, walletAddress: String): String
companion object {
const val SCHEME = "https"
const val SUCCESS_URL = "https://tangem.com/success"
}
}

View file

@ -45,6 +45,7 @@ dependencies {
implementation(projects.domain.visa)
implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models)
implementation(projects.domain.onramp)
/** Tangem libraries */
implementation(projects.libs.tangemSdkApi)

View file

@ -4,9 +4,11 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.onramp.GetLegacyTopUpUrlUseCase
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -20,7 +22,7 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("UnusedPrivateMember")
@Suppress("LongParameterList")
@ModelScoped
internal class OnboardingNoteTopUpModel @Inject constructor(
paramsContainer: ParamsContainer,
@ -28,9 +30,12 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
private val userWalletBuilderFactory: UserWalletBuilder.Factory,
private val getLegacyTopUpUrlUseCase: GetLegacyTopUpUrlUseCase,
private val urlOpener: UrlOpener,
) : Model() {
private val params = paramsContainer.require<OnboardingNoteTopUpComponent.Params>()
private val commonState = params.childParams.commonState
private val scanResponse = params.childParams.commonState.value.scanResponse
private var userWallet: UserWallet? = null
@ -68,7 +73,12 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
}
private fun onBuyCryptoClick() {
// TODO [REDACTED_TASK_KEY]
val cryptoCurrencyStatus = params.childParams.commonState.value.cryptoCurrencyStatus ?: return
modelScope.launch {
getLegacyTopUpUrlUseCase(cryptoCurrencyStatus).onRight {
urlOpener.openUrl(it)
}
}
}
private fun onShowWalletAddressClick() {
@ -100,6 +110,9 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
}
private fun applyCryptoCurrencyStatusToState(status: CryptoCurrencyStatus) {
commonState.update {
it.copy(cryptoCurrencyStatus = status)
}
_uiState.update {
it.copy(
amountToCreateAccount = (status.value as? CryptoCurrencyStatus.NoAccount)
@ -112,12 +125,15 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
)
},
),
balance = (status.value as? CryptoCurrencyStatus.Loaded)?.amount?.format({
crypto(
symbol = status.currency.symbol,
decimals = status.currency.decimals,
)
},),
balance = (status.value as? CryptoCurrencyStatus.Loaded)?.amount?.format(
{
crypto(
symbol = status.currency.symbol,
decimals = status.currency.decimals,
)
},
),
isTopUpDataLoading = status.value.networkAddress == null,
)
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.features.onboarding.v2.note.impl.child.topup.ui
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -26,6 +27,7 @@ fun OnboardingNoteTopUp(state: OnboardingNoteTopUpUM, modifier: Modifier = Modif
.fillMaxSize()
.navigationBarsPadding(),
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
) {
OnboardingNoteTopUpHeader(
balance = state.balance,
@ -64,7 +66,15 @@ fun OnboardingNoteTopUp(state: OnboardingNoteTopUpUM, modifier: Modifier = Modif
)
}
BottomButtons(state)
if (state.isTopUpDataLoading) {
CircularProgressIndicator(
modifier = Modifier
.padding(all = 32.dp)
.size(36.dp),
)
} else {
BottomButtons(state)
}
}
}

View file

@ -5,6 +5,7 @@ data class OnboardingNoteTopUpUM(
val availableForBuy: Boolean = true,
val balance: String? = null,
val isRefreshing: Boolean = false,
val isTopUpDataLoading: Boolean = true,
val amountToCreateAccount: String? = null,
val onBuyCryptoClick: () -> Unit = {},
val onShowWalletAddressClick: () -> Unit = {},

View file

@ -1,10 +1,12 @@
package com.tangem.features.onboarding.v2.note.impl.model
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
internal data class OnboardingNoteCommonState(
val scanResponse: ScanResponse,
val cardArtworkUrl: String? = null,
val userWallet: UserWallet? = null,
val cryptoCurrencyStatus: CryptoCurrencyStatus? = null,
)