Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-05 16:27:54 +07:00
parent a40e6e7e28
commit fa86588528
7 changed files with 52 additions and 22 deletions

View file

@ -7,4 +7,5 @@ import com.squareup.moshi.JsonClass
data class PromocodeActivationBody(
@Json(name = "promoCode") val promoCode: String,
@Json(name = "address") val address: String,
@Json(name = "walletId") val walletId: String,
)

View file

@ -435,6 +435,7 @@ internal class DefaultWalletsRepository(
}
override suspend fun activatePromoCode(
userWalletId: UserWalletId,
promoCode: String,
bitcoinAddress: String,
): Either<ActivatePromoCodeError, String> = withContext(dispatchers.io) {
@ -442,6 +443,7 @@ internal class DefaultWalletsRepository(
body = PromocodeActivationBody(
promoCode = promoCode,
address = bitcoinAddress,
walletId = userWalletId.stringValue,
),
).fold(
onSuccess = { it.status.right() },

View file

@ -268,6 +268,7 @@ class DefaultWalletsRepositoryTest {
@Test
fun `GIVEN valid data WHEN activatePromoCode THEN returns Right with status and calls API`() = runTest {
// GIVEN
val walletId = UserWalletId("1234567890abcdef")
val promoCode = "PROMO123"
val address = "bc1qexampleaddress"
coEvery { tangemTechApi.activatePromoCode(any()) } returns ApiResponse.Success(
@ -275,7 +276,11 @@ class DefaultWalletsRepositoryTest {
)
// WHEN
val result = repository.activatePromoCode(promoCode = promoCode, bitcoinAddress = address)
val result = repository.activatePromoCode(
userWalletId = walletId,
promoCode = promoCode,
bitcoinAddress = address
)
// THEN
var right: String? = null
@ -294,13 +299,14 @@ class DefaultWalletsRepositoryTest {
@Test
fun `GIVEN NOT_FOUND error WHEN activatePromoCode THEN returns Left InvalidPromoCode`() = runTest {
// GIVEN
val walletId = UserWalletId("1234567890abcdef")
coEvery { tangemTechApi.activatePromoCode(any()) } returns
ApiResponse.Error(
HttpException(code = HttpException.Code.NOT_FOUND, message = null, errorBody = null),
) as ApiResponse<PromocodeActivationResponse>
// WHEN
val result = repository.activatePromoCode(promoCode = "PROMO", bitcoinAddress = "addr")
val result = repository.activatePromoCode(userWalletId = walletId, promoCode = "PROMO", bitcoinAddress = "addr")
// THEN
var error: ActivatePromoCodeError? = null
@ -311,13 +317,14 @@ class DefaultWalletsRepositoryTest {
@Test
fun `GIVEN CONFLICT error WHEN activatePromoCode THEN returns Left PromocodeAlreadyUsed`() = runTest {
// GIVEN
val walletId = UserWalletId("1234567890abcdef")
coEvery { tangemTechApi.activatePromoCode(any()) } returns
ApiResponse.Error(
HttpException(code = HttpException.Code.CONFLICT, message = null, errorBody = null),
) as ApiResponse<PromocodeActivationResponse>
// WHEN
val result = repository.activatePromoCode(promoCode = "PROMO", bitcoinAddress = "addr")
val result = repository.activatePromoCode(userWalletId = walletId, promoCode = "PROMO", bitcoinAddress = "addr")
// THEN
var error: ActivatePromoCodeError? = null

View file

@ -75,5 +75,9 @@ interface WalletsRepository {
@Throws
suspend fun associateWallets(applicationId: String, wallets: List<UserWallet>)
suspend fun activatePromoCode(promoCode: String, bitcoinAddress: String): Either<ActivatePromoCodeError, String>
suspend fun activatePromoCode(
userWalletId: UserWalletId,
promoCode: String,
bitcoinAddress: String,
): Either<ActivatePromoCodeError, String>
}

View file

@ -1,6 +1,7 @@
package com.tangem.domain.wallets.usecase
import arrow.core.Either
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.models.errors.ActivatePromoCodeError
import com.tangem.domain.wallets.repository.WalletsRepository
import javax.inject.Inject
@ -9,6 +10,13 @@ class ActivateBitcoinPromocodeUseCase @Inject constructor(
private val walletsRepository: WalletsRepository,
) {
suspend operator fun invoke(address: String, promoCode: String): Either<ActivatePromoCodeError, String> =
walletsRepository.activatePromoCode(promoCode = promoCode, bitcoinAddress = address)
suspend operator fun invoke(
userWalletId: UserWalletId,
address: String,
promoCode: String,
): Either<ActivatePromoCodeError, String> = walletsRepository.activatePromoCode(
userWalletId = userWalletId,
promoCode = promoCode,
bitcoinAddress = address,
)
}

View file

@ -130,7 +130,11 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
"Start activation promoCode ${promoCode.mask()} address ${bitcoinAddress.mask()}",
)
activatePromoCode(bitcoinAddress = bitcoinAddress, promoCode = promoCode)
activatePromoCode(
userWallet = userWallet,
bitcoinAddress = bitcoinAddress,
promoCode = promoCode,
)
} else {
uiMessageSender.send(GlobalLoadingMessage(false))
delay(DEFAULT_MESSAGE_SENDER_DELAY)
@ -141,9 +145,13 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
}
}
private suspend fun activatePromoCode(bitcoinAddress: String, promoCode: String) {
private suspend fun activatePromoCode(userWallet: UserWallet, bitcoinAddress: String, promoCode: String) {
uiMessageSender.send(GlobalLoadingMessage(true))
activateBitcoinPromocodeUseCase(bitcoinAddress, promoCode).onRight {
activateBitcoinPromocodeUseCase(
userWalletId = userWallet.walletId,
address = bitcoinAddress,
promoCode = promoCode,
).onRight {
delay(DEFAULT_MESSAGE_SENDER_DELAY)
uiMessageSender.send(GlobalLoadingMessage(false))
delay(DEFAULT_MESSAGE_SENDER_DELAY)

View file

@ -102,7 +102,7 @@ class DefaultPromoDeeplinkHandlerTest {
}
val btcCoin = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id)
coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(btcCoin)
coEvery { activateBitcoinPromocodeUseCase.invoke("bc1qxyz", promoCode) } returns Either.Right("ok")
coEvery { activateBitcoinPromocodeUseCase.invoke(any(), "bc1qxyz", promoCode) } returns Either.Right("ok")
val dispatcherProvider = testDispatcherProvider(testScheduler)
DefaultPromoDeeplinkHandler(
@ -251,7 +251,7 @@ class DefaultPromoDeeplinkHandlerTest {
coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatus))
val btcCoin = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id)
coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(btcCoin)
coEvery { activateBitcoinPromocodeUseCase.invoke("bc1qxyz", promoCode) } returns Either.Right("ok")
coEvery { activateBitcoinPromocodeUseCase.invoke(any(), "bc1qxyz", promoCode) } returns Either.Right("ok")
val dispatcherProvider = testDispatcherProvider(testScheduler)
DefaultPromoDeeplinkHandler(
@ -369,7 +369,7 @@ class DefaultPromoDeeplinkHandlerTest {
coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatus))
val btcCurrency = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id)
coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(btcCurrency)
coEvery { activateBitcoinPromocodeUseCase.invoke("bc1qxyz", promoCode) } returns Either.Left(error)
coEvery { activateBitcoinPromocodeUseCase.invoke(any(), "bc1qxyz", promoCode) } returns Either.Left(error)
val dispatcherProvider = testDispatcherProvider(testScheduler)
DefaultPromoDeeplinkHandler(
@ -506,7 +506,7 @@ class DefaultPromoDeeplinkHandlerTest {
btcCoinCustom,
btcCoinCard,
)
coEvery { activateBitcoinPromocodeUseCase.invoke("bc1qcustom", promoCode) } returns Either.Right("ok")
coEvery { activateBitcoinPromocodeUseCase.invoke(any(), "bc1qcustom", promoCode) } returns Either.Right("ok")
val dispatcherProvider = testDispatcherProvider(testScheduler)
@ -530,8 +530,8 @@ class DefaultPromoDeeplinkHandlerTest {
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success_title))
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success))
coVerify(exactly = 1) { activateBitcoinPromocodeUseCase.invoke("bc1qcustom", promoCode) }
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke("bc1qcard", promoCode) }
coVerify(exactly = 1) { activateBitcoinPromocodeUseCase.invoke(any(), "bc1qcustom", promoCode) }
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), "bc1qcard", promoCode) }
}
@Test
@ -560,7 +560,7 @@ class DefaultPromoDeeplinkHandlerTest {
val btcCoinCard = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCard)
coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(btcCoinCard)
coEvery { activateBitcoinPromocodeUseCase.invoke("bc1qcard", promoCode) } returns Either.Right("ok")
coEvery { activateBitcoinPromocodeUseCase.invoke(any(), "bc1qcard", promoCode) } returns Either.Right("ok")
val dispatcherProvider = testDispatcherProvider(testScheduler)
@ -584,8 +584,8 @@ class DefaultPromoDeeplinkHandlerTest {
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success_title))
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success))
coVerify(exactly = 1) { activateBitcoinPromocodeUseCase.invoke("bc1qcard", promoCode) }
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke("bc1qcustom", promoCode) }
coVerify(exactly = 1) { activateBitcoinPromocodeUseCase.invoke(any(), "bc1qcard", promoCode) }
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), "bc1qcustom", promoCode) }
verify(
exactly = 1,
@ -644,7 +644,7 @@ class DefaultPromoDeeplinkHandlerTest {
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title))
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address))
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) }
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(),any(), any()) }
verify(
exactly = 1,
@ -703,7 +703,7 @@ class DefaultPromoDeeplinkHandlerTest {
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title))
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address))
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) }
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any(), any()) }
verify(
exactly = 1,
@ -758,7 +758,7 @@ class DefaultPromoDeeplinkHandlerTest {
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title))
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address))
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) }
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any(), any()) }
verify(
exactly = 1,
@ -813,7 +813,7 @@ class DefaultPromoDeeplinkHandlerTest {
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title))
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address))
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) }
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any(), any()) }
verify(
exactly = 1,