libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit ae80d1c867465365451ea683667728b277d1fb91
parent 05c52309ee2453836063c864241a5ec174a77786
Author: MS <ms@taler.net>
Date:   Sat, 28 Oct 2023 11:10:37 +0200

nexus config

turning to plain IBAN, BIC, name triple in the configuration
to identify the EBICS subscriber bank account.  That simplifies,
as opposed to use a Payto value and then restrict its content.

Diffstat:
Mcontrib/libeufin-nexus.conf | 5+++--
Mnexus/src/main/kotlin/tech/libeufin/nexus/Database.kt | 20++++++++++++--------
Mnexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt | 9+--------
Mnexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt | 10++++------
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 20++++++++++++++------
Mnexus/src/test/kotlin/Common.kt | 8+++++---
Mnexus/src/test/kotlin/DatabaseTest.kt | 2+-
Mnexus/src/test/kotlin/PostFinance.kt | 2+-
8 files changed, 41 insertions(+), 35 deletions(-)

diff --git a/contrib/libeufin-nexus.conf b/contrib/libeufin-nexus.conf @@ -20,8 +20,9 @@ PARTNER_ID = myorg # EBICS partner ID, as assigned by the bank. SYSTEM_ID = banksys -# Name given by the bank to the bank account driven by Nexus. -ACCOUNT_NUMBER = payto://iban/BIC/DE1234567890?receiver-name=Nexus-User +IBAN = CH9789144829733648596 +BIC = POFICHBE +NAME = LibEuFin # File that holds the bank EBICS keys. BANK_PUBLIC_KEYS_FILE = enc-auth-keys.json diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt @@ -3,11 +3,8 @@ package tech.libeufin.nexus import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.postgresql.jdbc.PgConnection -import tech.libeufin.util.pgDataSource import com.zaxxer.hikari.* -import tech.libeufin.util.microsToJavaInstant -import tech.libeufin.util.stripIbanPayto -import tech.libeufin.util.toDbMicros +import tech.libeufin.util.* import java.sql.PreparedStatement import java.sql.SQLException import java.time.Instant @@ -71,6 +68,13 @@ enum class PaymentInitiationOutcome { * The Payto address to send the payment to was invalid. */ BAD_CREDIT_PAYTO, + + /** + * The receiver payto address lacks the name, that would + * cause the bank to reject the pain.001. + */ + RECEIVER_NAME_MISSING, + /** * The row contains a client_request_uid that exists * already in the database. @@ -406,11 +410,11 @@ class Database(dbConfig: String): java.io.Closeable { stmt.setLong(1, paymentData.amount.value) stmt.setInt(2, paymentData.amount.fraction) stmt.setString(3, paymentData.wireTransferSubject) - val paytoOnlyIban = stripIbanPayto(paymentData.creditPaytoUri) ?: run { - logger.error("Credit Payto address is invalid.") - return@runConn PaymentInitiationOutcome.BAD_CREDIT_PAYTO // client fault. + parsePayto(paymentData.creditPaytoUri).apply { + if (this == null) return@runConn PaymentInitiationOutcome.BAD_CREDIT_PAYTO + if (this.receiverName == null) return@runConn PaymentInitiationOutcome.RECEIVER_NAME_MISSING } - stmt.setString(4, paytoOnlyIban) + stmt.setString(4, paymentData.creditPaytoUri) val initiationTime = paymentData.initiationTime.toDbMicros() ?: run { throw Exception("Initiation time could not be converted to microseconds for the database.") } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt @@ -23,9 +23,7 @@ import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.parameters.options.option import io.ktor.client.* import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.time.delay import tech.libeufin.nexus.ebics.submitPayment -import tech.libeufin.util.getDatabaseName import tech.libeufin.util.parsePayto import java.util.* import kotlin.concurrent.fixedRateTimer @@ -57,11 +55,6 @@ private suspend fun submitInitiatedPayment( logger.error("Won't create pain.001 without the receiver name") return false } - val debtor = cfg.accountNumber - if (debtor.bic == null || debtor.receiverName == null) { - logger.error("Won't create pain.001 without the debtor BIC and name") - return false - } if (initiatedPayment.wireTransferSubject == null) { logger.error("Won't create pain.001 without the wire transfer subject") return false @@ -71,7 +64,7 @@ private suspend fun submitInitiatedPayment( initiationTimestamp = initiatedPayment.initiationTime, amount = initiatedPayment.amount, creditAccount = creditor, - debitAccount = debtor, + debitAccount = cfg.myIbanAccount, wireTransferSubject = initiatedPayment.wireTransferSubject ) submitPayment(xml, cfg, clientPrivateKeysFile, bankPublicKeysFile, httpClient) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt @@ -33,7 +33,7 @@ data class Pain001Namespaces( fun createPain001( requestUid: String, initiationTimestamp: Instant, - debitAccount: IbanPayto, + debitAccount: IbanAccountMetadata, amount: TalerAmount, wireTransferSubject: String, creditAccount: IbanPayto @@ -47,8 +47,6 @@ fun createPain001( if (this.size != 2) throw Exception("Invalid stringified amount: $amount") return@run this[1] } - val debtorBic: String = debitAccount.bic ?: throw Exception("Cannot operate without the debtor BIC") - val debtorName: String = debitAccount.receiverName ?: throw Exception("Cannot operate without the debtor name") val creditorName: String = creditAccount.receiverName ?: throw Exception("Cannot operate without the creditor name") return constructXml(indent = true) { root("Document") { @@ -74,7 +72,7 @@ fun createPain001( text(amountWithoutCurrency) } element("InitgPty/Nm") { - text(debtorName) + text(debitAccount.name) } } element("PmtInf") { @@ -102,14 +100,14 @@ fun createPain001( } } element("Dbtr/Nm") { - text(debtorName) + text(debitAccount.name) } element("DbtrAcct/Id/IBAN") { text(debitAccount.iban) } element("DbtrAgt/FinInstnId") { element("BICFI") { - text(debtorBic) + text(debitAccount.bic) } } element("ChrgBr") { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -60,6 +60,15 @@ val myJson = Json { } /** + * Triple identifying one IBAN bank account. + */ +data class IbanAccountMetadata( + val iban: String, + val bic: String, + val name: String +) + +/** * Keeps all the options of the ebics-setup subcommand. The * caller has to handle TalerConfigError if values are missing. * If even one of the fields could not be instantiated, then @@ -103,12 +112,11 @@ class EbicsSetupConfig(val config: TalerConfig) { /** * Bank account metadata. */ - val accountNumber: IbanPayto = ebicsSetupRequireString("account_number").run { - val maybeAccount = parsePayto(this) - if (maybeAccount?.bic == null) throw Exception("ACCOUNT_NUMBER lacks the BIC") - if (maybeAccount.receiverName == null) throw Exception("ACCOUNT_NUMBER lacks the name") - return@run maybeAccount - } + val myIbanAccount = IbanAccountMetadata( + iban = ebicsSetupRequireString("iban"), + bic = ebicsSetupRequireString("bic"), + name = ebicsSetupRequireString("name") + ) /** * Filename where we store the bank public keys. */ diff --git a/nexus/src/test/kotlin/Common.kt b/nexus/src/test/kotlin/Common.kt @@ -68,7 +68,9 @@ fun getPofiConfig( USER_ID = $userId PARTNER_ID = $partnerId SYSTEM_ID = not-used - ACCOUNT_NUMBER = payto://iban/POFICHBE/CH9789144829733648596?receiver-name=$accountOwner + IBAN = CH9789144829733648596 + BIC = POFICHBE + NAME = LibEuFin BANK_PUBLIC_KEYS_FILE = /tmp/enc-auth-keys.json CLIENT_PRIVATE_KEYS_FILE = /tmp/my-private-keys.json ACCOUNT_META_DATA_FILE = /tmp/ebics-meta.json @@ -79,7 +81,7 @@ fun getPofiConfig( fun genInitPay(subject: String? = null, rowUid: String = "unique") = InitiatedPayment( amount = TalerAmount(44, 0, "KUDOS"), - creditPaytoUri = "payto://iban/not-used", + creditPaytoUri = "payto://iban/TEST-IBAN?receiver-name=Test", wireTransferSubject = subject, initiationTime = Instant.now(), requestUid = rowUid @@ -99,7 +101,7 @@ fun genIncPay(subject: String? = null) = fun genOutPay(subject: String? = null) = OutgoingPayment( amount = TalerAmount(44, 0, "KUDOS"), - creditPaytoUri = "payto://iban/not-used", + creditPaytoUri = "payto://iban/TEST-IBAN?receiver-name=Test", wireTransferSubject = subject, executionTime = Instant.now(), bankTransferId = "entropic" diff --git a/nexus/src/test/kotlin/DatabaseTest.kt b/nexus/src/test/kotlin/DatabaseTest.kt @@ -169,7 +169,7 @@ class PaymentInitiationsTest { } val initPay = InitiatedPayment( amount = TalerAmount(44, 0, "KUDOS"), - creditPaytoUri = "payto://iban/not-used", + creditPaytoUri = "payto://iban/TEST-IBAN?receiver-name=Test", wireTransferSubject = "test", requestUid = "unique", initiationTime = Instant.now() diff --git a/nexus/src/test/kotlin/PostFinance.kt b/nexus/src/test/kotlin/PostFinance.kt @@ -34,7 +34,7 @@ class Iso20022 { val xml = createPain001( "random", Instant.now(), - parsePayto("payto://iban/POFICHBE/CH9789144829733648596?receiver-name=NotGiven")!!, + cfg.myIbanAccount, TalerAmount(4, 0, "CHF"), "Test reimbursement, part 2", parsePayto("payto://iban/CH9300762011623852957?receiver-name=NotGiven")!!