From 467eda6e5ffa361862e7e587524b030a8183fb07 Mon Sep 17 00:00:00 2001 From: MS Date: Wed, 17 May 2023 14:01:52 +0200 Subject: EBICS server emulation. Introducing the EndToEndId for transactions in the database schema. Additionally: introducing the cash-out currency as a configuration value. --- sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt | 13 ++++++++++--- .../kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt | 14 ++++++++++---- sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt | 10 +++++----- sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt | 2 +- 4 files changed, 26 insertions(+), 13 deletions(-) (limited to 'sandbox/src/main/kotlin/tech/libeufin') diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt index 19d4283d..57e00f77 100644 --- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt +++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt @@ -426,13 +426,19 @@ object BankAccountTransactionsTable : LongIdTable() { val currency = text("currency") // Milliseconds since the Epoch. val date = long("date") - // Unique ID for this payment within the bank account. + + /** + * UID assigned to the payment by Sandbox. Despite the camt-looking + * name, this UID is always given, even when no EBICS or camt are being + * served. + */ val accountServicerReference = text("accountServicerReference") /** - * Payment information ID, which is a reference to the payment initiation - * that triggered this transaction. Typically, only available with outgoing transactions. + * The following two values are pain.001 specific. Sandbox stores + * them when it serves EBICS connections. */ val pmtInfId = text("pmtInfId").nullable() + val endToEndId = text("EndToEndId").nullable() val direction = text("direction") /** * Bank account of the party whose 'direction' refers. This version allows @@ -479,6 +485,7 @@ class BankAccountTransactionEntity(id: EntityID) : LongEntity(id) { var date by BankAccountTransactionsTable.date var accountServicerReference by BankAccountTransactionsTable.accountServicerReference var pmtInfId by BankAccountTransactionsTable.pmtInfId + var endToEndId by BankAccountTransactionsTable.endToEndId var direction by BankAccountTransactionsTable.direction var account by BankAccountEntity referencedOn BankAccountTransactionsTable.account var demobank by DemobankConfigEntity referencedOn BankAccountTransactionsTable.demobank diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt index e2c9abf5..d79d0ab2 100644 --- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt +++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt @@ -61,6 +61,7 @@ data class PainParseResult( val amount: String, val currency: String, val pmtInfId: String, + val endToEndId: String, val msgId: String ) @@ -121,8 +122,9 @@ private class EbicsUnsupportedOrderType : EbicsRequestError( * Used here also for "Internal server error". For example, when the * sandbox itself generates a invalid XML response. */ -class EbicsProcessingError(detail: String) : EbicsRequestError( - "[EBICS_PROCESSING_ERROR] $detail", +class EbicsProcessingError(detail: String?) : EbicsRequestError( + // a missing detail is already the bank's fault. + "[EBICS_PROCESSING_ERROR] " + (detail ?: "bank internal error"), "091116" ) @@ -457,7 +459,7 @@ fun buildCamtString( text(it.pmtInfId ?: "NOTPROVIDED") } element("EndToEndId") { - text("NOTPROVIDED") + text(it.endToEndId ?: "NOTPROVIDED") } } element("AmtDtls/TxAmt/Amt") { @@ -675,7 +677,9 @@ private fun parsePain001(paymentRequest: String): PainParseResult { val subject = requireUniqueChildNamed("RmtInf") { requireUniqueChildNamed("Ustrd") { focusElement.textContent } } - + val endToEndId = requireUniqueChildNamed("PmtId") { + requireUniqueChildNamed("EndToEndId") { focusElement.textContent } + } } } /** @@ -699,6 +703,7 @@ private fun parsePain001(paymentRequest: String): PainParseResult { creditorIban = txDetails.creditorIban, creditorBic = txDetails.creditorBic, pmtInfId = pmtInfId, + endToEndId = txDetails.endToEndId, msgId = msgId ) } @@ -772,6 +777,7 @@ private fun handleCct( currency = parseResult.currency date = getUTCnow().toInstant().toEpochMilli() pmtInfId = parseResult.pmtInfId + endToEndId = parseResult.endToEndId accountServicerReference = "sandboxref-${getRandomString(16)}" direction = "DBIT" } diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt index fdea79c2..6529b9d1 100644 --- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt +++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt @@ -36,6 +36,7 @@ import kotlin.reflect.KProperty data class DemobankConfig( val allowRegistrations: Boolean, val currency: String, + val cashoutCurrency: String? = null, val bankDebtLimit: Int, val usersDebtLimit: Int, val withSignupBonus: Boolean, @@ -236,13 +237,12 @@ fun getHistoryElementFromTransactionRow(dbRow: BankAccountTransactionEntity): XL date = dbRow.date.toString(), amount = dbRow.amount, currency = dbRow.currency, - // The line below produces a value too long (>35 chars), - // and dbRow makes the document invalid! - // uid = "${dbRow.pmtInfId}-${it.msgId}" + // UID assigned by the bank itself. uid = dbRow.accountServicerReference, - // Eventually, the _database_ should contain the direction enum: direction = XLibeufinBankDirection.convertCamtDirectionToXLibeufin(dbRow.direction), - pmtInfId = dbRow.pmtInfId + // UIDs as gotten from a pain.001 (from EBICS connections.) + pmtInfId = dbRow.pmtInfId, + endToEndId = dbRow.endToEndId ) } diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt index a6aab42e..aea5a008 100644 --- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt +++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt @@ -1174,7 +1174,7 @@ val sandboxApp: Application.() -> Unit = { } catch (e: Exception) { logger.error(e.stackTraceToString()) - throw EbicsProcessingError("Could not map error to EBICS code: $e") + throw EbicsProcessingError(e.message) } return@post } -- cgit v1.2.3