commit 0a39bbc41c5502dd1305c41ed7e28e174223b0e9
parent e06c066d23e4d9434f488e64480b168f524005f1
Author: Antoine A <>
Date: Thu, 11 Sep 2025 14:57:31 +0200
bank: make base_url mandatory
Diffstat:
11 files changed, 38 insertions(+), 61 deletions(-)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
@@ -30,7 +30,7 @@ import java.time.Duration
data class BankConfig(
private val cfg: TalerConfig,
val name: String,
- val baseUrl: BaseURL?,
+ val baseUrl: BaseURL,
val regionalCurrency: String,
val regionalCurrencySpec: CurrencySpecification,
val wireTransferFees: TalerAmount,
@@ -125,36 +125,24 @@ private fun TalerConfig.loadBankConfig(): BankConfig = section("libeufin-bank").
}
}
- val (baseUrl, hostname) = run {
- val baseUrl = baseURL("base_url").orNull()
- val hostname = string("x_taler_bank_payto_hostname").orNull()
- if (baseUrl != null && hostname != null) {
- if (baseUrl.url.host != hostname) {
- throw TalerConfigError.generic("x_taler_bank_payto_hostname must match base_url hostname when both are specified")
- } else {
- Pair(baseUrl, hostname)
- }
- } else if (baseUrl != null) {
- Pair(baseUrl, baseUrl.url.host)
- } else {
- Pair(baseUrl, null)
- }
- }
+ val baseUrl = baseURL("base_url").require()
+ val hostname = baseUrl.url.host
val method = map("wire_type", "payment target type", mapOf(
"iban" to WireMethod.IBAN,
"x-taler-bank" to WireMethod.X_TALER_BANK,
)).default(WireMethod.IBAN, logger, " defaulting to 'iban' but will fail in a future update")
- val payto = when (method) {
- WireMethod.IBAN -> BankPaytoCtx(
- bic = string("iban_payto_bic").orNull(logger, " will fail in a future update"),
- hostname = hostname
- )
- WireMethod.X_TALER_BANK -> BankPaytoCtx(
- bic = string("iban_payto_bic").orNull(),
- hostname = hostname ?: string("x_taler_bank_payto_hostname").orNull(logger, " will fail in a future update")
- )
+
+ if (method == WireMethod.X_TALER_BANK) {
+ val xhostname = string("x_taler_bank_payto_hostname").orNull()
+ if (xhostname != null && xhostname != hostname) {
+ logger.warn("deprecated x_taler_bank_payto_hostname '${xhostname}' does not match base_url hostname '$hostname'")
+ }
}
+ val payto = BankPaytoCtx(
+ bic = string("iban_payto_bic").orNull(),
+ hostname = hostname
+ )
val pwCrypto = map("pwd_hash_algorithm", "password hash algorithm", mapOf(
"bcrypt" to json<PwCrypto.Bcrypt>("pwd_hash_config", "bcrypt JSON config").require()
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/BankIntegrationApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/BankIntegrationApi.kt
@@ -58,7 +58,7 @@ fun Routing.bankIntegrationApi(db: Database, ctx: BankConfig) {
)
call.respond(op.copy(
suggested_exchange = ctx.suggestedWithdrawalExchange,
- confirm_transfer_url = if (op.status == WithdrawalStatus.pending || op.status == WithdrawalStatus.selected) call.request.withdrawConfirmUrl(ctx.baseUrl, uuid) else null
+ confirm_transfer_url = if (op.status == WithdrawalStatus.pending || op.status == WithdrawalStatus.selected) ctx.withdrawConfirmUrl(uuid) else null
))
}
post("/taler-integration/withdrawal-operation/{wopid}") {
@@ -113,7 +113,7 @@ fun Routing.bankIntegrationApi(db: Database, ctx: BankConfig) {
call.respond(BankWithdrawalOperationPostResponse(
transfer_done = res.status == WithdrawalStatus.confirmed,
status = res.status,
- confirm_transfer_url = if (res.status == WithdrawalStatus.pending || res.status == WithdrawalStatus.selected) call.request.withdrawConfirmUrl(ctx.baseUrl, uuid) else null
+ confirm_transfer_url = if (res.status == WithdrawalStatus.pending || res.status == WithdrawalStatus.selected) ctx.withdrawConfirmUrl(uuid) else null
))
}
}
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt
@@ -574,7 +574,7 @@ private fun Routing.coreBankWithdrawalApi(db: Database, cfg: BankConfig) {
call.respond(
BankAccountCreateWithdrawalResponse(
withdrawal_id = opId.toString(),
- taler_withdraw_uri = call.request.talerWithdrawUri(cfg.baseUrl, opId)
+ taler_withdraw_uri = cfg.talerWithdrawUri(opId)
)
)
}
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
@@ -37,6 +37,7 @@ import tech.libeufin.bank.auth.pathUsername
import tech.libeufin.bank.auth.AUTH_INFO
import tech.libeufin.bank.db.AccountDAO.AccountCreationResult
import tech.libeufin.bank.db.Database
+import tech.libeufin.bank.BankConfig
import tech.libeufin.common.*
import tech.libeufin.common.api.intercept
import java.util.*
@@ -67,18 +68,6 @@ suspend fun ApplicationCall.bankInfo(db: Database): BankInfo {
return info
}
-private fun ApplicationRequest.fallbackBase() = BaseURL.parse(url {
- protocol = URLProtocol(
- name = origin.scheme,
- defaultPort = -1
- )
- host = "${origin.serverHost}:${origin.serverPort}"
- headers[X_FORWARD_PREFIX]?.let {
- appendPathSegments(it)
- }
- appendPathSegments("")
-})
-
/**
* Builds the taler://withdraw-URI. Such URI will serve the requests
* from wallets, when they need to manage the operation. For example,
@@ -87,15 +76,15 @@ private fun ApplicationRequest.fallbackBase() = BaseURL.parse(url {
*
* https://$BANK_URL/taler-integration
*/
-fun ApplicationRequest.talerWithdrawUri(baseUrl: BaseURL?, id: UUID): String {
- val base = (baseUrl ?: fallbackBase()).url
+fun BankConfig.talerWithdrawUri(id: UUID): String {
+ val base = this.baseUrl.url
val protocol = if (base.protocol == "http") "taler+http" else "taler"
val port = if (base.port != -1) ":${base.port}" else ""
return "${protocol}://withdraw/${base.host}${port}${base.path}taler-integration/${id}"
}
-fun ApplicationRequest.withdrawConfirmUrl(baseUrl: BaseURL?, id: UUID): String {
- val base = (baseUrl ?: fallbackBase()).url
+fun BankConfig.withdrawConfirmUrl(id: UUID): String {
+ val base = this.baseUrl.url
return "${base}webui/#/operation/${id}"
}
diff --git a/bank/src/test/kotlin/BankIntegrationApiTest.kt b/bank/src/test/kotlin/BankIntegrationApiTest.kt
@@ -111,14 +111,14 @@ class BankIntegrationApiTest {
json(req)
}.assertOkJson<BankWithdrawalOperationPostResponse> {
assertEquals(WithdrawalStatus.selected, it.status)
- assertEquals("http://localhost:80/webui/#/operation/$uuid", it.confirm_transfer_url)
+ assertEquals("http://localhost:8080/webui/#/operation/$uuid", it.confirm_transfer_url)
}
// Check idempotence
client.post("/taler-integration/withdrawal-operation/$uuid") {
json(req)
}.assertOkJson<BankWithdrawalOperationPostResponse> {
assertEquals(WithdrawalStatus.selected, it.status)
- assertEquals("http://localhost:80/webui/#/operation/$uuid", it.confirm_transfer_url)
+ assertEquals("http://localhost:8080/webui/#/operation/$uuid", it.confirm_transfer_url)
}
// Check already selected
client.post("/taler-integration/withdrawal-operation/$uuid") {
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -1526,7 +1526,7 @@ class CoreBankWithdrawalApiTest {
client.postA("/accounts/merchant/withdrawals") {
json(valid)
}.assertOkJson<BankAccountCreateWithdrawalResponse> {
- assertEquals("taler+http://withdraw/localhost:80/taler-integration/${it.withdrawal_id}", it.taler_withdraw_uri)
+ assertEquals("taler+http://withdraw/localhost:8080/taler-integration/${it.withdrawal_id}", it.taler_withdraw_uri)
}
}
diff --git a/bank/src/test/kotlin/PaytoTest.kt b/bank/src/test/kotlin/PaytoTest.kt
@@ -38,7 +38,7 @@ class PaytoTest {
"name" to "John"
}
}.assertOkJson<RegisterAccountResponse> {
- assertEquals("payto://x-taler-bank/bank.hostname.test/john?receiver-name=John", it.internal_payto_uri)
+ assertEquals("payto://x-taler-bank/localhost/john?receiver-name=John", it.internal_payto_uri)
}
// Bad IBAN payto
@@ -56,7 +56,7 @@ class PaytoTest {
"username" to "foo"
"password" to "foo-password"
"name" to "Jane"
- "payto_uri" to "payto://x-taler-bank/bank.hostname.test/not-foo"
+ "payto_uri" to "payto://x-taler-bank/localhost/not-foo"
}
}.assertBadRequest()
// Check Ok
@@ -65,10 +65,10 @@ class PaytoTest {
"username" to "foo"
"password" to "foo-password"
"name" to "Jane"
- "payto_uri" to "payto://x-taler-bank/bank.hostname.test/foo"
+ "payto_uri" to "payto://x-taler-bank/localhost/foo"
}
}.assertOkJson<RegisterAccountResponse> {
- assertEquals("payto://x-taler-bank/bank.hostname.test/foo?receiver-name=Jane", it.internal_payto_uri)
+ assertEquals("payto://x-taler-bank/localhost/foo?receiver-name=Jane", it.internal_payto_uri)
}
// Check payto canonicalisation
@@ -80,8 +80,8 @@ class PaytoTest {
client.getA("/accounts/john/transactions/${it.row_id}")
.assertOkJson<BankAccountTransactionInfo> { tx ->
assertEquals("payout", tx.subject)
- assertEquals("payto://x-taler-bank/bank.hostname.test/foo?receiver-name=Jane", tx.creditor_payto_uri)
- assertEquals("payto://x-taler-bank/bank.hostname.test/john?receiver-name=John", tx.debtor_payto_uri)
+ assertEquals("payto://x-taler-bank/localhost/foo?receiver-name=Jane", tx.creditor_payto_uri)
+ assertEquals("payto://x-taler-bank/localhost/john?receiver-name=John", tx.debtor_payto_uri)
assertEquals(TalerAmount("KUDOS:0.3"), tx.amount)
}
}
diff --git a/common/src/main/kotlin/Constants.kt b/common/src/main/kotlin/Constants.kt
@@ -31,7 +31,6 @@ const val REVENUE_API_VERSION: String = "1:1:1"
// HTTP headers
const val X_CHALLENGE_ID: String = "X-Challenge-Id"
-const val X_FORWARD_PREFIX: String = "X-Forward-Prefix"
// Params
const val MAX_PAGE_SIZE: Int = 1024
diff --git a/common/src/main/kotlin/TalerCommon.kt b/common/src/main/kotlin/TalerCommon.kt
@@ -313,9 +313,8 @@ sealed class Payto {
fun bank(name: String?, ctx: BankPaytoCtx): String = when (this) {
is IbanPayto -> IbanPayto.build(iban.toString(), ctx.bic, name)
is XTalerBankPayto -> {
- val domain = ctx.hostname ?: "localhost"
val name = if (name != null) "?receiver-name=${name.encodeURLParameter()}" else ""
- "payto://x-taler-bank/$domain/$username$name"
+ "payto://x-taler-bank/${ctx.hostname}/$username$name"
}
}
@@ -476,8 +475,8 @@ class XTalerBankPayto internal constructor(
/** Context specific data necessary to create a bank payto URI from a canonical payto URI */
data class BankPaytoCtx(
- val bic: String? = null,
- val hostname: String? = null
+ val bic: String?,
+ val hostname: String
)
diff --git a/common/src/test/kotlin/PaytoTest.kt b/common/src/test/kotlin/PaytoTest.kt
@@ -56,7 +56,8 @@ class PaytoTest {
@Test
fun forms() {
val ctx = BankPaytoCtx(
- bic = "TESTBIC"
+ bic = "TESTBIC",
+ hostname = "test.com"
)
val canonical = "payto://iban/CH9300762011623852957"
val bank = "payto://iban/TESTBIC/CH9300762011623852957?receiver-name=Name"
diff --git a/contrib/bank.conf b/contrib/bank.conf
@@ -6,13 +6,14 @@ CURRENCY =
# Supported payment target type, this can either be iban or x-taler-bank
WIRE_TYPE =
-# The advertised base URL
-# BASE_URL =
+# The bank base URL
+BASE_URL = http://localhost:8080/
# Bank BIC used in generated iban payto URI. Required if WIRE_TYPE = iban
# IBAN_PAYTO_BIC =
# Bank hostname used in generated x-taler-bank payto URI. Required if WIRE_TYPE = x-taler-bank
+# Deprecated, BASE_URL hostname is used instead
# X_TALER_BANK_PAYTO_HOSTNAME = bank.$FOO.taler.net
# Bank display name, used in webui and TAN messages. Default is "Taler Bank"