commit cf71b2da383a9fb86d074eeb15c0c9d5fefd1f05
parent 19b8afec701f83903e01ca03a6a431616edc5e3f
Author: Antoine A <>
Date: Mon, 9 Sep 2024 14:57:51 +0200
nexus: pain001 test
Diffstat:
5 files changed, 106 insertions(+), 20 deletions(-)
diff --git a/nexus/sample/platform/gls_pain001.xml b/nexus/sample/platform/gls_pain001.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><PmtTpInf><SvcLvl><Cd>SEPA</Cd></SvcLvl></PmtTpInf><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>BIC</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="EUR">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="EUR">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="EUR">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
+\ No newline at end of file
diff --git a/nexus/sample/platform/postfinance_pain001.xml b/nexus/sample/platform/postfinance_pain001.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.xsd"><CstmrCdtTrfInitn><GrpHdr><MsgId>MESSAGE_ID</MsgId><CreDtTm>2024-09-09T00:00:00Z</CreDtTm><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><InitgPty><Nm>myname</Nm></InitgPty></GrpHdr><PmtInf><PmtInfId>NOTPROVIDED</PmtInfId><PmtMtd>TRF</PmtMtd><BtchBookg>false</BtchBookg><NbOfTxs>3</NbOfTxs><CtrlSum>47.32</CtrlSum><PmtTpInf><SvcLvl><Cd>SDVA</Cd></SvcLvl></PmtTpInf><ReqdExctnDt><Dt>2024-09-09Z</Dt></ReqdExctnDt><Dbtr><Nm>myname</Nm></Dbtr><DbtrAcct><Id><IBAN>CH7789144474425692816</IBAN></Id></DbtrAcct><DbtrAgt><FinInstnId><BICFI>BIC</BICFI></FinInstnId></DbtrAgt><ChrgBr>SLEV</ChrgBr><CdtTrfTxInf><PmtId><InstrId>TX_FIRST</InstrId><EndToEndId>TX_FIRST</EndToEndId></PmtId><Amt><InstdAmt Ccy="EUR">42</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 42</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_SECOND</InstrId><EndToEndId>TX_SECOND</EndToEndId></PmtId><Amt><InstdAmt Ccy="EUR">5.11</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 5.11</Ustrd></RmtInf></CdtTrfTxInf><CdtTrfTxInf><PmtId><InstrId>TX_THIRD</InstrId><EndToEndId>TX_THIRD</EndToEndId></PmtId><Amt><InstdAmt Ccy="EUR">0.21</InstdAmt></Amt><Cdtr><Nm>Test</Nm></Cdtr><CdtrAcct><Id><IBAN>CH4189144589712575493</IBAN></Id></CdtrAcct><RmtInf><Ustrd>Test 0.21</Ustrd></RmtInf></CdtTrfTxInf></PmtInf></CstmrCdtTrfInitn></Document>
+\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt
@@ -30,21 +30,11 @@ import tech.libeufin.nexus.iso20022.*
import java.time.Instant
import kotlin.time.toKotlinDuration
-/**
- * Submit an initiated payments [batch] using [client].
- *
- * Parse creditor IBAN account metadata then perform an EBICS direct credit
- *
- * Returns the orderID
- */
-private suspend fun submitBatch(
- client: EbicsClient,
- batch: PaymentBatch
-): String {
- val msg = Pain001Msg(
+fun batchToPain001Msg(account: IbanAccountMetadata, batch: PaymentBatch): Pain001Msg {
+ return Pain001Msg(
messageId = batch.messageId,
timestamp = batch.creationDate,
- debtor = client.cfg.account,
+ debtor = account,
sum = batch.sum,
txs = batch.payments.map { payment ->
val payto = Payto.parse(payment.creditPaytoUri).expectIban()
@@ -61,7 +51,20 @@ private suspend fun submitBatch(
)
}
)
-
+}
+
+/**
+ * Submit an initiated payments [batch] using [client].
+ *
+ * Parse creditor IBAN account metadata then perform an EBICS direct credit
+ *
+ * Returns the orderID
+ */
+private suspend fun submitBatch(
+ client: EbicsClient,
+ batch: PaymentBatch
+): String {
+ val msg = batchToPain001Msg(client.cfg.account, batch)
val xml = createPain001(
msg = msg,
dialect = client.cfg.dialect
diff --git a/nexus/src/test/kotlin/IngestionTest.kt b/nexus/src/test/kotlin/IngestionTest.kt
@@ -27,23 +27,25 @@ import tech.libeufin.nexus.ebics.*
import tech.libeufin.nexus.iso20022.*
import java.nio.file.Files
import java.time.Instant
-import kotlin.io.path.Path
+import kotlin.io.path.*
import kotlin.test.*
/** End-to-end test for XML file ingestion */
class IngestionTest {
/** Register batches of initiated payments for reconcciliation */
- suspend fun Database.batches(batches: Map<String, List<InitiatedPayment>>) {
+ suspend fun Database.batches(batches: Map<String, List<InitiatedPayment>>): List<PaymentBatch> {
+ val tmp = mutableListOf<PaymentBatch>()
for ((name, txs) in batches) {
for (tx in txs) {
initiated.create(tx)
}
this.initiated.batch(Instant.now(), name)
- val tmp = this.initiated.submittable("EUR")
- assertEquals(1, tmp.size)
- this.initiated.batchSubmissionSuccess(tmp[0].id, Instant.now(), name.replace("BATCH", "ORDER"))
+ val (batch) = this.initiated.submittable("EUR")
+ this.initiated.batchSubmissionSuccess(batch.id, Instant.now(), name.replace("BATCH", "ORDER"))
+ tmp.add(batch)
}
+ return tmp
}
/** Ingest an XML sample into the database */
@@ -149,6 +151,36 @@ class IngestionTest {
assertContentEquals(outgoing, outgoing_tx)
}
+ @Test
+ fun pain001() = setup { db, cfg ->
+ val (batch) = db.batches(mapOf(
+ "MESSAGE_ID" to listOf(
+ genInitPay(
+ endToEndId = "TX_FIRST",
+ amount = "EUR:42",
+ subject = "Test 42",
+ ),
+ genInitPay(
+ endToEndId = "TX_SECOND",
+ amount = "EUR:5.11",
+ subject = "Test 5.11",
+ ),
+ genInitPay(
+ endToEndId = "TX_THIRD",
+ amount = "EUR:0.21",
+ subject = "Test 0.21",
+ ),
+ ),
+ ))
+ val msg = batchToPain001Msg(cfg.ebics.account, batch).copy(timestamp = dateToInstant("2024-09-09"),)
+ for (dialect in Dialect.entries) {
+ assertContentEquals(
+ Path("sample/platform/${dialect}_pain001.xml").readBytes(),
+ createPain001(msg, dialect)
+ )
+ }
+ }
+
/** HAC order id test */
@Test
fun hac() = setup { db, cfg ->
diff --git a/nexus/src/test/kotlin/Iso20022Test.kt b/nexus/src/test/kotlin/Iso20022Test.kt
@@ -19,13 +19,60 @@
import org.junit.Test
import tech.libeufin.common.*
-import tech.libeufin.nexus.ebics.Dialect
+import tech.libeufin.nexus.*
+import tech.libeufin.nexus.ebics.*
import tech.libeufin.nexus.iso20022.*
import kotlin.io.path.*
import kotlin.test.*
+import java.time.Instant
class Iso20022Test {
@Test
+ fun pain001() {
+ val creditor = IbanAccountMetadata(
+ iban = "CH4189144589712575493",
+ bic = null,
+ name = "Test"
+ )
+ val msg = Pain001Msg(
+ messageId = "MESSAGE_ID",
+ timestamp = dateToInstant("2024-09-09"),
+ debtor = IbanAccountMetadata(
+ iban = "CH7789144474425692816",
+ bic = "BIC",
+ name = "myname"
+ ),
+ sum = TalerAmount("EUR:47.32"),
+ txs = listOf(
+ Pain001Tx(
+ creditor = creditor,
+ amount = TalerAmount("EUR:42"),
+ subject = "Test 42",
+ endToEndId = "TX_FIRST"
+ ),
+ Pain001Tx(
+ creditor = creditor,
+ amount = TalerAmount("EUR:5.11"),
+ subject = "Test 5.11",
+ endToEndId = "TX_SECOND"
+ ),
+ Pain001Tx(
+ creditor = creditor,
+ amount = TalerAmount("EUR:0.21"),
+ subject = "Test 0.21",
+ endToEndId = "TX_THIRD"
+ )
+ )
+ )
+ for (dialect in Dialect.entries) {
+ assertContentEquals(
+ Path("sample/platform/${dialect}_pain001.xml").readBytes(),
+ createPain001(msg, dialect)
+ )
+ }
+ }
+
+ @Test
fun hac() {
assertContentEquals(
parseCustomerAck(Path("sample/platform/hac.xml").inputStream()),