From fa86588528ae7165e91330018c2f006323e9d5f5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 5 Feb 2026 16:27:54 +0700 Subject: [PATCH] Updated on 2026-08-14 --- .../models/PromocodeActivationBody.kt | 1 + .../data/wallets/DefaultWalletsRepository.kt | 2 ++ .../wallets/DefaultWalletsRepositoryTest.kt | 13 +++++++--- .../wallets/repository/WalletsRepository.kt | 6 ++++- .../ActivateBitcoinPromocodeUseCase.kt | 12 +++++++-- .../deeplink/DefaultPromoDeeplinkHandler.kt | 14 +++++++--- .../DefaultPromoDeeplinkHandlerTest.kt | 26 +++++++++---------- 7 files changed, 52 insertions(+), 22 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/PromocodeActivationBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/PromocodeActivationBody.kt index 9d28f527e7..9651ae0c5c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/PromocodeActivationBody.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/PromocodeActivationBody.kt @@ -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, ) \ No newline at end of file diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt index d2e807c047..ea7ba4b3b1 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt @@ -435,6 +435,7 @@ internal class DefaultWalletsRepository( } override suspend fun activatePromoCode( + userWalletId: UserWalletId, promoCode: String, bitcoinAddress: String, ): Either = withContext(dispatchers.io) { @@ -442,6 +443,7 @@ internal class DefaultWalletsRepository( body = PromocodeActivationBody( promoCode = promoCode, address = bitcoinAddress, + walletId = userWalletId.stringValue, ), ).fold( onSuccess = { it.status.right() }, diff --git a/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt b/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt index 9fbe71c7a6..30dd508d5b 100644 --- a/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt +++ b/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt @@ -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 // 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 // WHEN - val result = repository.activatePromoCode(promoCode = "PROMO", bitcoinAddress = "addr") + val result = repository.activatePromoCode(userWalletId = walletId, promoCode = "PROMO", bitcoinAddress = "addr") // THEN var error: ActivatePromoCodeError? = null diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt index 1431dc5e6f..3757a72ea5 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt @@ -75,5 +75,9 @@ interface WalletsRepository { @Throws suspend fun associateWallets(applicationId: String, wallets: List) - suspend fun activatePromoCode(promoCode: String, bitcoinAddress: String): Either + suspend fun activatePromoCode( + userWalletId: UserWalletId, + promoCode: String, + bitcoinAddress: String, + ): Either } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ActivateBitcoinPromocodeUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ActivateBitcoinPromocodeUseCase.kt index 0acd401fcb..638cfa438a 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ActivateBitcoinPromocodeUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ActivateBitcoinPromocodeUseCase.kt @@ -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 = - walletsRepository.activatePromoCode(promoCode = promoCode, bitcoinAddress = address) + suspend operator fun invoke( + userWalletId: UserWalletId, + address: String, + promoCode: String, + ): Either = walletsRepository.activatePromoCode( + userWalletId = userWalletId, + promoCode = promoCode, + bitcoinAddress = address, + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt index 0cb63b07c9..e2c99b4aa3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt @@ -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) diff --git a/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt index 602c3cb5be..28fd3f1aec 100644 --- a/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt +++ b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt @@ -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,