Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-14 17:43:51 +03:00
parent 15a518c576
commit 78cb0fc750
18 changed files with 747 additions and 15 deletions

View file

@ -3,11 +3,13 @@ package com.tangem.data.pay.repository
import arrow.core.Either
import com.tangem.data.pay.store.TangemPayStorage
import com.tangem.data.pay.util.CashbackAccrualDocsConverter
import com.tangem.data.pay.util.CashbackHistoryConverter
import com.tangem.data.pay.util.CashbackPromotionsConverter
import com.tangem.data.pay.util.CashbackSummaryConverter
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.CashbackDocument
import com.tangem.domain.pay.model.CashbackHistory
import com.tangem.domain.pay.model.CashbackPromotions
import com.tangem.domain.pay.model.CashbackSummary
import com.tangem.domain.pay.repository.CashbackRepository
@ -47,6 +49,15 @@ internal class DefaultCashbackRepository @Inject constructor(
}.map(CashbackAccrualDocsConverter::convert)
}
override suspend fun getCashbackHistory(
userWalletId: UserWalletId,
months: Int,
): Either<VisaApiError, CashbackHistory> {
return requestHelper.performRequest(userWalletId) { authHeader ->
tangemPayApi.getCashbackHistory(authHeader = authHeader, months = months)
}.map(CashbackHistoryConverter::convert)
}
override suspend fun isDeactivationBannerDismissed(userWalletId: UserWalletId): Boolean {
val customerWalletAddress = requestHelper.getCustomerWalletAddress(userWalletId)
return storage.getCashbackDeactivationDismissed(customerWalletAddress)

View file

@ -0,0 +1,23 @@
package com.tangem.data.pay.util
import com.tangem.datasource.api.pay.models.response.CashbackHistoryResponse
import com.tangem.domain.pay.model.CashbackHistory
import com.tangem.utils.converter.Converter
import java.math.BigDecimal
/** Maps [CashbackHistoryResponse] (BFF) to the domain [CashbackHistory]. */
internal object CashbackHistoryConverter : Converter<CashbackHistoryResponse, CashbackHistory> {
override fun convert(value: CashbackHistoryResponse): CashbackHistory {
return CashbackHistory(
currency = value.currency.orEmpty(),
months = value.items.orEmpty().map { item ->
CashbackHistory.MonthlyCashback(
year = item.year,
month = item.month,
confirmedAmount = item.confirmedAmount ?: BigDecimal.ZERO,
)
},
)
}
}

View file

@ -8,6 +8,7 @@ import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.CashbackDisplayMode
import com.tangem.domain.pay.model.CashbackDocument
import com.tangem.domain.pay.model.CashbackHistory
import com.tangem.domain.pay.model.CashbackPromotions
import com.tangem.domain.pay.model.CashbackSummary
import com.tangem.domain.pay.model.TangemPayCashback
@ -49,6 +50,14 @@ internal class MockAwareCashbackRepository @Inject constructor(
return real.getCashbackAccrualDocs(userWalletId)
}
override suspend fun getCashbackHistory(
userWalletId: UserWalletId,
months: Int,
): Either<VisaApiError, CashbackHistory> {
if (isMockMode) return MOCK_HISTORY.copy(months = MOCK_HISTORY.months.takeLast(months)).right()
return real.getCashbackHistory(userWalletId, months)
}
override suspend fun isDeactivationBannerDismissed(userWalletId: UserWalletId): Boolean =
real.isDeactivationBannerDismissed(userWalletId)
@ -104,5 +113,16 @@ internal class MockAwareCashbackRepository @Inject constructor(
url = "https://tangem.com/docs/en/tangem-pay-cashback-terms.pdf",
),
)
val MOCK_HISTORY = CashbackHistory(
currency = "USD",
months = listOf(
CashbackHistory.MonthlyCashback(year = 2026, month = 2, confirmedAmount = BigDecimal("12.02")),
CashbackHistory.MonthlyCashback(year = 2026, month = 3, confirmedAmount = BigDecimal("44.22")),
CashbackHistory.MonthlyCashback(year = 2026, month = 4, confirmedAmount = BigDecimal("38.52")),
CashbackHistory.MonthlyCashback(year = 2026, month = 5, confirmedAmount = BigDecimal("26.10")),
CashbackHistory.MonthlyCashback(year = 2026, month = 6, confirmedAmount = BigDecimal("22.54")),
),
)
}
}

View file

@ -0,0 +1,104 @@
package com.tangem.data.pay.util
import com.google.common.truth.Truth.assertThat
import com.tangem.datasource.api.pay.models.response.CashbackHistoryResponse
import com.tangem.domain.pay.model.CashbackHistory
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import java.math.BigDecimal
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class CashbackHistoryConverterTest {
@ParameterizedTest
@MethodSource("provideTestModels")
fun convert(model: ConvertModel) {
// Act
val actual = CashbackHistoryConverter.convert(model.response)
// Assert
assertThat(actual).isEqualTo(model.expected)
}
private fun provideTestModels() = listOf(
ConvertModel(
name = "full response -> mapped history ordered oldest to newest",
response = createResponse(
currency = "USD",
items = listOf(
createItem(year = 2026, month = 2, confirmedAmount = BigDecimal("5.00")),
createItem(year = 2026, month = 6, confirmedAmount = BigDecimal("22.54")),
),
),
expected = CashbackHistory(
currency = "USD",
months = listOf(
CashbackHistory.MonthlyCashback(year = 2026, month = 2, confirmedAmount = BigDecimal("5.00")),
CashbackHistory.MonthlyCashback(year = 2026, month = 6, confirmedAmount = BigDecimal("22.54")),
),
),
),
ConvertModel(
name = "negative amount preserved for current-month refund",
response = createResponse(
items = listOf(createItem(year = 2026, month = 6, confirmedAmount = BigDecimal("-2.15"))),
),
expected = CashbackHistory(
currency = "USD",
months = listOf(
CashbackHistory.MonthlyCashback(year = 2026, month = 6, confirmedAmount = BigDecimal("-2.15")),
),
),
),
ConvertModel(
name = "null confirmed_amount -> ZERO",
response = createResponse(
items = listOf(createItem(year = 2026, month = 6, confirmedAmount = null)),
),
expected = CashbackHistory(
currency = "USD",
months = listOf(
CashbackHistory.MonthlyCashback(year = 2026, month = 6, confirmedAmount = BigDecimal.ZERO),
),
),
),
ConvertModel(
name = "null currency -> empty string",
response = createResponse(currency = null, items = emptyList()),
expected = CashbackHistory(currency = "", months = emptyList()),
),
ConvertModel(
name = "null items -> empty months",
response = createResponse(items = null),
expected = CashbackHistory(currency = "USD", months = emptyList()),
),
)
internal data class ConvertModel(
val name: String,
val response: CashbackHistoryResponse,
val expected: CashbackHistory,
) {
override fun toString(): String = name
}
private companion object {
fun createResponse(
currency: String? = "USD",
items: List<CashbackHistoryResponse.Item>? = listOf(
createItem(year = 2026, month = 6, confirmedAmount = BigDecimal("22.54")),
),
) = CashbackHistoryResponse(
currency = currency,
items = items,
)
fun createItem(year: Int, month: Int, confirmedAmount: BigDecimal?) = CashbackHistoryResponse.Item(
year = year,
month = month,
confirmedAmount = confirmedAmount,
)
}
}