diff options
author | Antoine A <> | 2023-11-17 15:43:13 +0000 |
---|---|---|
committer | Antoine A <> | 2023-11-17 15:43:13 +0000 |
commit | c553a2991ec6aeb910351658092cef1762e413d2 (patch) | |
tree | 0be8f4a8a929aa47d1151f8888ae87045e548154 | |
parent | ffb74ead5035f3218f137b855e85edc91a8e4552 (diff) | |
download | libeufin-c553a2991ec6aeb910351658092cef1762e413d2.tar.gz libeufin-c553a2991ec6aeb910351658092cef1762e413d2.tar.bz2 libeufin-c553a2991ec6aeb910351658092cef1762e413d2.zip |
Add Taler Conversion Info API
-rw-r--r-- | bank/src/main/kotlin/tech/libeufin/bank/ConversionApi.kt | 93 | ||||
-rw-r--r-- | bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt | 45 | ||||
-rw-r--r-- | bank/src/main/kotlin/tech/libeufin/bank/Main.kt | 1 | ||||
-rw-r--r-- | bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt | 20 | ||||
-rw-r--r-- | bank/src/test/kotlin/ConversionApiTest.kt | 118 | ||||
-rw-r--r-- | bank/src/test/kotlin/CoreBankApiTest.kt | 76 | ||||
-rw-r--r-- | bank/src/test/kotlin/helpers.kt | 2 |
7 files changed, 232 insertions, 123 deletions
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/ConversionApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/ConversionApi.kt new file mode 100644 index 00000000..b8d540e0 --- /dev/null +++ b/bank/src/main/kotlin/tech/libeufin/bank/ConversionApi.kt @@ -0,0 +1,93 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2023 Taler Systems S.A. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ +package tech.libeufin.bank + +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import java.util.* +import tech.libeufin.util.* +import net.taler.common.errorcodes.TalerErrorCode + +fun Routing.conversionApi(db: Database, ctx: BankConfig) = conditional(ctx.haveCashout) { + get("/conversion-info/config") { + call.respond( + ConversionConfig( + currency = ctx.currency, + fiat_currency = ctx.fiatCurrency, + cashin_ratio = ctx.conversionInfo!!.cashin_ratio, + cashin_fee = ctx.conversionInfo.cashin_fee, + cashin_tiny_amount = ctx.conversionInfo.cashin_tiny_amount, + cashin_rounding_mode = ctx.conversionInfo.cashin_rounding_mode, + cashin_min_amount = ctx.conversionInfo.cashin_min_amount, + cashout_ratio = ctx.conversionInfo.cashout_ratio, + cashout_fee = ctx.conversionInfo.cashout_fee, + cashout_tiny_amount = ctx.conversionInfo.cashout_tiny_amount, + cashout_rounding_mode = ctx.conversionInfo.cashout_rounding_mode, + cashout_min_amount = ctx.conversionInfo.cashout_min_amount + ) + ) + } + get("/conversion-info/cashout-rate") { + val params = RateParams.extract(call.request.queryParameters) + + params.debit?.let { ctx.checkRegionalCurrency(it) } + params.credit?.let { ctx.checkFiatCurrency(it) } + + if (params.debit != null) { + val credit = db.conversion.toCashout(params.debit) ?: + throw conflict( + "${params.debit} is too small to be converted", + TalerErrorCode.BANK_BAD_CONVERSION + ) + call.respond(ConversionResponse(params.debit, credit)) + } else { + val debit = db.conversion.fromCashout(params.credit!!) ?: + throw conflict( + "${params.debit} is too small to be converted", + TalerErrorCode.BANK_BAD_CONVERSION + ) + call.respond(ConversionResponse(debit, params.credit)) + } + } + get("/conversion-info/cashin-rate") { + val params = RateParams.extract(call.request.queryParameters) + + params.debit?.let { ctx.checkFiatCurrency(it) } + params.credit?.let { ctx.checkRegionalCurrency(it) } + + if (params.debit != null) { + val credit = db.conversion.toCashin(params.debit) ?: + throw conflict( + "${params.debit} is too small to be converted", + TalerErrorCode.BANK_BAD_CONVERSION + ) + call.respond(ConversionResponse(params.debit, credit)) + } else { + val debit = db.conversion.fromCashin(params.credit!!) ?: + throw conflict( + "${params.debit} is too small to be converted", + TalerErrorCode.BANK_BAD_CONVERSION + ) + call.respond(ConversionResponse(debit, params.credit)) + } + } +}
\ No newline at end of file diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt index 0cb7e519..d86e12ce 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt @@ -51,7 +51,6 @@ fun Routing.coreBankApi(db: Database, ctx: BankConfig) { currency = ctx.currencySpecification, have_cashout = ctx.haveCashout, fiat_currency = ctx.fiatCurrency, - conversion_info = ctx.conversionInfo, allow_registrations = !ctx.restrictRegistration, allow_deletions = !ctx.restrictAccountDeletion ) @@ -609,48 +608,4 @@ private fun Routing.coreBankCashoutApi(db: Database, ctx: BankConfig) = conditio } } } - get("/cashout-rate") { - val params = RateParams.extract(call.request.queryParameters) - - params.debit?.let { ctx.checkRegionalCurrency(it) } - params.credit?.let { ctx.checkFiatCurrency(it) } - - if (params.debit != null) { - val credit = db.conversion.toCashout(params.debit) ?: - throw conflict( - "${params.debit} is too small to be converted", - TalerErrorCode.BANK_BAD_CONVERSION - ) - call.respond(ConversionResponse(params.debit, credit)) - } else { - val debit = db.conversion.fromCashout(params.credit!!) ?: - throw conflict( - "${params.debit} is too small to be converted", - TalerErrorCode.BANK_BAD_CONVERSION - ) - call.respond(ConversionResponse(debit, params.credit)) - } - } - get("/cashin-rate") { - val params = RateParams.extract(call.request.queryParameters) - - params.debit?.let { ctx.checkFiatCurrency(it) } - params.credit?.let { ctx.checkRegionalCurrency(it) } - - if (params.debit != null) { - val credit = db.conversion.toCashin(params.debit) ?: - throw conflict( - "${params.debit} is too small to be converted", - TalerErrorCode.BANK_BAD_CONVERSION - ) - call.respond(ConversionResponse(params.debit, credit)) - } else { - val debit = db.conversion.fromCashin(params.credit!!) ?: - throw conflict( - "${params.debit} is too small to be converted", - TalerErrorCode.BANK_BAD_CONVERSION - ) - call.respond(ConversionResponse(debit, params.credit)) - } - } } diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt index 7ec57cf9..3d0bf956 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt @@ -209,6 +209,7 @@ fun Application.corebankWebApp(db: Database, ctx: BankConfig) { } routing { coreBankApi(db, ctx) + conversionApi(db, ctx) bankIntegrationApi(db, ctx) wireGatewayApi(db, ctx) revenueApi(db) diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt index bf9593ab..e97a7a5c 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt @@ -205,7 +205,6 @@ data class Config( val currency: CurrencySpecification, val have_cashout: Boolean, val fiat_currency: String?, - val conversion_info: ConversionInfo?, val allow_registrations: Boolean, val allow_deletions: Boolean ) { @@ -213,6 +212,25 @@ data class Config( val version: String = "0:0:0" } +@Serializable +data class ConversionConfig( + val currency: String, + val fiat_currency: String?, + val cashin_ratio: DecimalNumber, + val cashin_fee: TalerAmount, + val cashin_tiny_amount: TalerAmount, + val cashin_rounding_mode: RoundingMode, + val cashin_min_amount: TalerAmount, + val cashout_ratio: DecimalNumber, + val cashout_fee: TalerAmount, + val cashout_tiny_amount: TalerAmount, + val cashout_rounding_mode: RoundingMode, + val cashout_min_amount: TalerAmount, +) { + val name: String = "taler-conversion-info" + val version: String = "0:0:0" +} + enum class CreditDebitInfo { credit, debit } diff --git a/bank/src/test/kotlin/ConversionApiTest.kt b/bank/src/test/kotlin/ConversionApiTest.kt new file mode 100644 index 00000000..30cc7706 --- /dev/null +++ b/bank/src/test/kotlin/ConversionApiTest.kt @@ -0,0 +1,118 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2023 Taler Systems S.A. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +import io.ktor.client.plugins.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.server.testing.* +import java.util.* +import kotlin.test.* +import kotlinx.coroutines.* +import kotlinx.serialization.json.* +import net.taler.common.errorcodes.TalerErrorCode +import org.junit.Test +import tech.libeufin.bank.* + +class ConversionApiTest { + // GET /conversion-info/config + @Test + fun config() = bankSetup { _ -> + client.get("/conversion-info/config").assertOk() + } + + // GET /conversion-info/cashout-rate + @Test + fun cashoutRate() = bankSetup { _ -> + // Check conversion to + client.get("/conversion-info/cashout-rate?amount_debit=KUDOS:1").assertOkJson<ConversionResponse> { + assertEquals(TalerAmount("KUDOS:1"), it.amount_debit) + assertEquals(TalerAmount("FIAT:1.247"), it.amount_credit) + } + // Check conversion from + client.get("/conversion-info/cashout-rate?amount_credit=FIAT:1.247").assertOkJson<ConversionResponse> { + assertEquals(TalerAmount("KUDOS:1"), it.amount_debit) + assertEquals(TalerAmount("FIAT:1.247"), it.amount_credit) + } + + // Too small + client.get("/conversion-info/cashout-rate?amount_debit=KUDOS:0.08") + .assertConflict(TalerErrorCode.BANK_BAD_CONVERSION) + // No amount + client.get("/conversion-info/cashout-rate") + .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MISSING) + // Both amount + client.get("/conversion-info/cashout-rate?amount_debit=FIAT:1&amount_credit=KUDOS:1") + .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) + // Wrong format + client.get("/conversion-info/cashout-rate?amount_debit=1") + .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) + client.get("/conversion-info/cashout-rate?amount_credit=1") + .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) + // Wrong currency + client.get("/conversion-info/cashout-rate?amount_debit=FIAT:1") + .assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) + client.get("/conversion-info/cashout-rate?amount_credit=KUDOS:1") + .assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) + } + + // GET /conversion-info/cashin-rate + @Test + fun cashinRate() = bankSetup { _ -> + for ((amount, converted) in listOf( + Pair(0.75, 0.58), Pair(0.32, 0.24), Pair(0.66, 0.51) + )) { + // Check conversion to + client.get("/conversion-info/cashin-rate?amount_debit=FIAT:$amount").assertOkJson<ConversionResponse> { + assertEquals(TalerAmount("KUDOS:$converted"), it.amount_credit) + assertEquals(TalerAmount("FIAT:$amount"), it.amount_debit) + } + // Check conversion from + client.get("/conversion-info/cashin-rate?amount_credit=KUDOS:$converted").assertOkJson<ConversionResponse> { + assertEquals(TalerAmount("KUDOS:$converted"), it.amount_credit) + assertEquals(TalerAmount("FIAT:$amount"), it.amount_debit) + } + } + + // No amount + client.get("/conversion-info/cashin-rate") + .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MISSING) + // Both amount + client.get("/conversion-info/cashin-rate?amount_debit=KUDOS:1&amount_credit=FIAT:1") + .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) + // Wrong format + client.get("/conversion-info/cashin-rate?amount_debit=1") + .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) + client.get("/conversion-info/cashin-rate?amount_credit=1") + .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) + // Wrong currency + client.get("/conversion-info/cashin-rate?amount_debit=KUDOS:1") + .assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) + client.get("/conversion-info/cashin-rate?amount_credit=FIAT:1") + .assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) + } + + @Test + fun notImplemented() = bankSetup("test_restrict.conf") { _ -> + client.get("/conversion-info/cashin-rate") + .assertNotImplemented() + client.get("/conversion-info/cashout-rate") + .assertNotImplemented() + } +}
\ No newline at end of file diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt index c6ae9758..f25629ea 100644 --- a/bank/src/test/kotlin/CoreBankApiTest.kt +++ b/bank/src/test/kotlin/CoreBankApiTest.kt @@ -1342,84 +1342,8 @@ class CoreBankCashoutApiTest { }.assertHistory(10) } - // GET /cashout-rate - @Test - fun cashoutRate() = bankSetup { _ -> - // Check conversion to - client.get("/cashout-rate?amount_debit=KUDOS:1").assertOkJson<ConversionResponse> { - assertEquals(TalerAmount("KUDOS:1"), it.amount_debit) - assertEquals(TalerAmount("FIAT:1.247"), it.amount_credit) - } - // Check conversion from - client.get("/cashout-rate?amount_credit=FIAT:1.247").assertOkJson<ConversionResponse> { - assertEquals(TalerAmount("KUDOS:1"), it.amount_debit) - assertEquals(TalerAmount("FIAT:1.247"), it.amount_credit) - } - - // Too small - client.get("/cashout-rate?amount_debit=KUDOS:0.08") - .assertConflict(TalerErrorCode.BANK_BAD_CONVERSION) - // No amount - client.get("/cashout-rate") - .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MISSING) - // Both amount - client.get("/cashout-rate?amount_debit=FIAT:1&amount_credit=KUDOS:1") - .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) - // Wrong format - client.get("/cashout-rate?amount_debit=1") - .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) - client.get("/cashout-rate?amount_credit=1") - .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) - // Wrong currency - client.get("/cashout-rate?amount_debit=FIAT:1") - .assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) - client.get("/cashout-rate?amount_credit=KUDOS:1") - .assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) - } - - // GET /cashin-rate - @Test - fun cashinRate() = bankSetup { _ -> - - for ((amount, converted) in listOf( - Pair(0.75, 0.58), Pair(0.32, 0.24), Pair(0.66, 0.51) - )) { - // Check conversion to - client.get("/cashin-rate?amount_debit=FIAT:$amount").assertOkJson<ConversionResponse> { - assertEquals(TalerAmount("KUDOS:$converted"), it.amount_credit) - assertEquals(TalerAmount("FIAT:$amount"), it.amount_debit) - } - // Check conversion from - client.get("/cashin-rate?amount_credit=KUDOS:$converted").assertOkJson<ConversionResponse> { - assertEquals(TalerAmount("KUDOS:$converted"), it.amount_credit) - assertEquals(TalerAmount("FIAT:$amount"), it.amount_debit) - } - } - - // No amount - client.get("/cashin-rate") - .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MISSING) - // Both amount - client.get("/cashin-rate?amount_debit=KUDOS:1&amount_credit=FIAT:1") - .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) - // Wrong format - client.get("/cashin-rate?amount_debit=1") - .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) - client.get("/cashin-rate?amount_credit=1") - .assertBadRequest(TalerErrorCode.GENERIC_PARAMETER_MALFORMED) - // Wrong currency - client.get("/cashin-rate?amount_debit=KUDOS:1") - .assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) - client.get("/cashin-rate?amount_credit=FIAT:1") - .assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) - } - @Test fun notImplemented() = bankSetup("test_restrict.conf") { _ -> - client.get("/cashin-rate") - .assertNotImplemented() - client.get("/cashout-rate") - .assertNotImplemented() client.get("/accounts/customer/cashouts") .assertNotImplemented() } diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt index 41302ce5..ace1e013 100644 --- a/bank/src/test/kotlin/helpers.kt +++ b/bank/src/test/kotlin/helpers.kt @@ -201,7 +201,7 @@ suspend fun ApplicationTestBuilder.withdrawalSelect(uuid: String) { } suspend fun ApplicationTestBuilder.convert(amount: String): TalerAmount { - return client.get("/cashout-rate?amount_debit=$amount") + return client.get("/conversion-info/cashout-rate?amount_debit=$amount") .assertOkJson<ConversionResponse>().amount_credit } |