-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathBuyInteractor.kt
153 lines (136 loc) · 6.12 KB
/
BuyInteractor.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package org.p2p.wallet.moonpay.interactor
import timber.log.Timber
import java.math.BigDecimal
import javax.net.ssl.HttpsURLConnection
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext
import org.p2p.core.dispatchers.CoroutineDispatchers
import org.p2p.core.token.Token
import org.p2p.core.utils.isLessThan
import org.p2p.core.utils.isMoreThan
import org.p2p.wallet.infrastructure.network.interceptor.MoonpayRequestException
import org.p2p.wallet.moonpay.clientsideapi.response.MoonpayBuyCurrencyResponse
import org.p2p.wallet.moonpay.model.MoonpayBuyQuote
import org.p2p.wallet.moonpay.model.MoonpayBuyResult
import org.p2p.wallet.moonpay.repository.buy.MoonpayApiMapper
import org.p2p.wallet.moonpay.repository.buy.NewMoonpayBuyRepository
import org.p2p.wallet.moonpay.repository.sell.FiatCurrency
// amount in pounds does not accept getting currency rate of 1, it triggers minimal amount error
// so we use 100 as a workaround
private const val CURRENCY_AMOUNT_FOR_PRICE_REQUEST = "100"
private const val DEFAULT_MAX_CURRENCY_AMOUNT = 10000
private const val DEFAULT_PAYMENT_TYPE = "credit_debit_card"
private val FIAT_CURRENCY_CODES = listOf("eur", "usd", "gbp")
class BuyInteractor(
private val moonpayRepository: NewMoonpayBuyRepository,
private val moonpayApiMapper: MoonpayApiMapper,
private val dispatchers: CoroutineDispatchers
) {
companion object {
const val HARDCODED_MIN_BUY_CURRENCY_AMOUNT = 40
}
private val quotes = mutableListOf<MoonpayBuyQuote>()
fun getQuotes(): List<MoonpayBuyQuote> = quotes.toList()
suspend fun loadQuotes(
currencies: List<FiatCurrency>,
tokensToBuy: List<Token>
): Unit = withContext(dispatchers.io) {
Timber.i("Loading quotes for buy: currencies=$currencies; tokens=${tokensToBuy.map(Token::mintAddress)}")
currencies.flatMap { currency ->
tokensToBuy.map { token ->
async { loadQuote(currency, token) }
}
}
.awaitAll()
}
fun getQuotesByCurrency(currency: FiatCurrency): List<MoonpayBuyQuote> {
return quotes.filter { it.currency == currency }
}
suspend fun getMoonpayBuyResult(
baseCurrencyAmount: String?,
quoteCurrencyAmount: String?,
tokenToBuy: Token,
baseCurrencyCode: FiatCurrency,
paymentMethod: String,
): MoonpayBuyResult {
val minBuyAmount = getMinAmountForPair(baseCurrencyCode.abbreviation, tokenToBuy.tokenSymbol)
return try {
val response = moonpayRepository.getBuyCurrencyData(
baseCurrencyAmount = baseCurrencyAmount,
quoteCurrencyAmount = quoteCurrencyAmount,
tokenToBuy = tokenToBuy,
baseCurrencyCode = baseCurrencyCode.abbreviation,
paymentMethod = paymentMethod
)
when {
!isMinAmountValid(response, tokenToBuy.tokenSymbol) -> {
MoonpayBuyResult.MinAmountError(minBuyAmount)
}
!isMaxAmountValid(response) -> {
MoonpayBuyResult.MaxAmountError(DEFAULT_MAX_CURRENCY_AMOUNT.toBigDecimal())
}
else -> {
MoonpayBuyResult.Success(moonpayApiMapper.fromNetworkToDomain(response))
}
}
} catch (error: MoonpayRequestException) {
when {
isMinimumAmountException(error) -> {
MoonpayBuyResult.MinAmountError(minBuyAmount)
}
error.httpCode == HttpsURLConnection.HTTP_BAD_REQUEST -> {
MoonpayBuyResult.Error(moonpayApiMapper.fromNetworkErrorToDomainMessage(error), cause = error)
}
else -> {
throw error
}
}
}
}
private fun getMinAmountForPair(
currencyCode: String,
tokenSymbol: String
): BigDecimal {
val quote = quotes.find {
it.currency == FiatCurrency.getFromAbbreviation(currencyCode) && it.tokenToBuy.tokenSymbol == tokenSymbol
}
return HARDCODED_MIN_BUY_CURRENCY_AMOUNT.toBigDecimal()
}
private suspend fun loadQuote(currency: FiatCurrency, tokenToBuy: Token) {
Timber.d("Load quote for currency=$currency; token=${tokenToBuy.tokenSymbol}")
try {
val response = moonpayRepository.getBuyCurrencyData(
baseCurrencyAmount = CURRENCY_AMOUNT_FOR_PRICE_REQUEST,
quoteCurrencyAmount = null,
tokenToBuy = tokenToBuy,
baseCurrencyCode = currency.abbreviation.lowercase(),
paymentMethod = DEFAULT_PAYMENT_TYPE
)
quotes += MoonpayBuyQuote(
currency = currency,
tokenToBuy = tokenToBuy,
price = response.quoteCurrencyPrice,
minAmount = HARDCODED_MIN_BUY_CURRENCY_AMOUNT.toBigDecimal()
)
} catch (e: Throwable) {
Timber.e(e, "Error while loading quote for currency=$currency; token=${tokenToBuy.tokenSymbol}")
}
}
private fun isMinAmountValid(response: MoonpayBuyCurrencyResponse, tokenSymbol: String): Boolean {
val buyCurrency = response.baseCurrency
val isFiatCurrency = buyCurrency.code in FIAT_CURRENCY_CODES
val minBuyAmount = getMinAmountForPair(response.baseCurrency.code, tokenSymbol)
val isLessThenMin = response.totalAmount.isLessThan(minBuyAmount)
return if (isFiatCurrency) !isLessThenMin else true
}
private fun isMaxAmountValid(response: MoonpayBuyCurrencyResponse): Boolean {
val buyCurrency = response.baseCurrency
val isFiatCurrency = buyCurrency.code in FIAT_CURRENCY_CODES
val isMoreThenMax = response.totalAmount.isMoreThan(DEFAULT_MAX_CURRENCY_AMOUNT.toBigDecimal())
return if (isFiatCurrency) !isMoreThenMax else true
}
private fun isMinimumAmountException(error: MoonpayRequestException): Boolean {
return error.httpCode == HttpsURLConnection.HTTP_BAD_REQUEST && error.message.startsWith("Minimum purchase")
}
}