commit c928ab9e189020c2d150d42ecb51ceb618412c19
parent 4725b87198c315c57a2d1f0f4a917715a69b4928
Author: Antoine A <>
Date: Tue, 30 Apr 2024 11:53:15 +0900
Parse camt052 files for GLS bank
Diffstat:
6 files changed, 371 insertions(+), 11 deletions(-)
diff --git a/nexus/sample/platform/gls_camt052.xml b/nexus/sample/platform/gls_camt052.xml
@@ -0,0 +1,314 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.052.001.02"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:camt.052.001.02 camt.052.001.02.xsd">
+ <BkToCstmrAcctRpt>
+ <Rpt>
+ <Ntry>
+ <Amt Ccy="EUR">2.00</Amt>
+ <CdtDbtInd>DBIT</CdtDbtInd>
+ <Sts>BOOK</Sts>
+ <BookgDt>
+ <Dt>2024-04-18</Dt>
+ </BookgDt>
+ <AcctSvcrRef>2024041801514102000</AcctSvcrRef>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>ICDT</Cd>
+ <SubFmlyCd>ESCT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ </BkTxCd>
+ <NtryDtls>
+ <TxDtls>
+ <Refs>
+ <MsgId>G059N0SR5V0WZ0XSFY1H92QBZ0</MsgId>
+ <PmtInfId>NOTPROVIDED</PmtInfId>
+ <EndToEndId>NOTPROVIDED</EndToEndId>
+ <TxId>2024041785403105090200000010000001</TxId>
+ </Refs>
+ <AmtDtls>
+ <TxAmt>
+ <Amt Ccy="EUR">2.00</Amt>
+ </TxAmt>
+ </AmtDtls>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>ICDT</Cd>
+ <SubFmlyCd>ESCT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ <Prtry>
+ <Cd>NTRF+177+08381</Cd>
+ <Issr>DK</Issr>
+ </Prtry>
+ </BkTxCd>
+ <RltdPties>
+ <Dbtr>
+ <Nm>Mr Test</Nm>
+ </Dbtr>
+ <DbtrAcct>
+ <Id>
+ <IBAN>DE84500105177118117964</IBAN>
+ </Id>
+ </DbtrAcct>
+ <Cdtr>
+ <Nm>John Smith</Nm>
+ </Cdtr>
+ <CdtrAcct>
+ <Id>
+ <IBAN>DE20500105172419259181</IBAN>
+ </Id>
+ </CdtrAcct>
+ </RltdPties>
+ <RltdAgts>
+ <CdtrAgt>
+ <FinInstnId>
+ <BIC>BYLADEM1WOR</BIC>
+ </FinInstnId>
+ </CdtrAgt>
+ </RltdAgts>
+ <RmtInf>
+ <Ustrd>TestABC123</Ustrd>
+ </RmtInf>
+ </TxDtls>
+ </NtryDtls>
+ <AddtlNtryInf>Überweisungsauftrag</AddtlNtryInf>
+ </Ntry>
+ <Ntry>
+ <Amt Ccy="EUR">1.10</Amt>
+ <CdtDbtInd>DBIT</CdtDbtInd>
+ <Sts>BOOK</Sts>
+ <BookgDt>
+ <Dt>2024-04-18</Dt>
+ </BookgDt>
+ <ValDt>
+ <Dt>2024-04-18</Dt>
+ </ValDt>
+ <AcctSvcrRef>2024041810552821000</AcctSvcrRef>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>ICDT</Cd>
+ <SubFmlyCd>ESCT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ <Prtry>
+ <Cd>NTRF+177+08381</Cd>
+ <Issr>DK</Issr>
+ </Prtry>
+ </BkTxCd>
+ <NtryDtls>
+ <TxDtls>
+ <Refs>
+ <MsgId>YF5QBARGQ0MNY0VK59S477VDG4</MsgId>
+ <PmtInfId>NOTPROVIDED</PmtInfId>
+ <EndToEndId>NOTPROVIDED</EndToEndId>
+ <TxId>2024041885917775090200000010000001</TxId>
+ </Refs>
+ <AmtDtls>
+ <TxAmt>
+ <Amt Ccy="EUR">1.10</Amt>
+ </TxAmt>
+ </AmtDtls>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>ICDT</Cd>
+ <SubFmlyCd>ESCT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ <Prtry>
+ <Cd>NTRF+177+08381</Cd>
+ <Issr>DK</Issr>
+ </Prtry>
+ </BkTxCd>
+ <RltdPties>
+ <Dbtr>
+ <Nm>Mr Test</Nm>
+ </Dbtr>
+ <DbtrAcct>
+ <Id>
+ <IBAN>DE84500105177118117964</IBAN>
+ </Id>
+ </DbtrAcct>
+ <Cdtr>
+ <Nm>John Smith</Nm>
+ </Cdtr>
+ <CdtrAcct>
+ <Id>
+ <IBAN>DE20500105172419259181</IBAN>
+ </Id>
+ </CdtrAcct>
+ </RltdPties>
+ <RltdAgts>
+ <CdtrAgt>
+ <FinInstnId>
+ <BIC>INGDDEFFXXX</BIC>
+ </FinInstnId>
+ </CdtrAgt>
+ </RltdAgts>
+ <RmtInf>
+ <Ustrd>This should fail because dummy</Ustrd>
+ </RmtInf>
+ </TxDtls>
+ </NtryDtls>
+ <AddtlNtryInf>Überweisungsauftrag</AddtlNtryInf>
+ </Ntry>
+ <Ntry>
+ <Amt Ccy="EUR">3.00</Amt>
+ <CdtDbtInd>CRDT</CdtDbtInd>
+ <Sts>BOOK</Sts>
+ <BookgDt>
+ <Dt>2024-04-12</Dt>
+ </BookgDt>
+ <AcctSvcrRef>2024041210041357000</AcctSvcrRef>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>RCDT</Cd>
+ <SubFmlyCd>ESCT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ </BkTxCd>
+ <NtryDtls>
+ <TxDtls>
+ <Refs>
+ <EndToEndId>NOTPROVIDED</EndToEndId>
+ <TxId>BYLADEM1WOR-G2910276709458A2</TxId>
+ </Refs>
+ <AmtDtls>
+ <TxAmt>
+ <Amt Ccy="EUR">3.00</Amt>
+ </TxAmt>
+ </AmtDtls>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>RCDT</Cd>
+ <SubFmlyCd>ESCT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ </BkTxCd>
+ <RltdPties>
+ <Dbtr>
+ <Nm>John Smith</Nm>
+ </Dbtr>
+ <DbtrAcct>
+ <Id>
+ <IBAN>DE84500105177118117964</IBAN>
+ </Id>
+ </DbtrAcct>
+ <Cdtr>
+ <Nm>Mr Test</Nm>
+ </Cdtr>
+ <CdtrAcct>
+ <Id>
+ <IBAN>DE20500105172419259181</IBAN>
+ </Id>
+ </CdtrAcct>
+ </RltdPties>
+ <RltdAgts>
+ <DbtrAgt>
+ <FinInstnId>
+ <BIC>BYLADEM1WOR</BIC>
+ </FinInstnId>
+ </DbtrAgt>
+ </RltdAgts>
+ <RmtInf>
+ <Ustrd>Taler FJDQ7W6G7NWX4H9M1MKA12090FRC9K7DA6N0FANDZZFXTR6QHX5G Test.,-</Ustrd>
+ </RmtInf>
+ </TxDtls>
+ </NtryDtls>
+ <AddtlNtryInf>Überweisungsgutschr.</AddtlNtryInf>
+ </Ntry>
+ <Ntry>
+ <Amt Ccy="EUR">1.10</Amt>
+ <CdtDbtInd>CRDT</CdtDbtInd>
+ <Sts>BOOK</Sts>
+ <BookgDt>
+ <Dt>2024-04-12</Dt>
+ </BookgDt>
+ <AcctSvcrRef>2024041210041357000</AcctSvcrRef>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>ICDT</Cd>
+ <SubFmlyCd>RRTN</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ </BkTxCd>
+ <NtryDtls>
+ <TxDtls>
+ <Refs>
+ <EndToEndId>G27KNKZAR5DV7HRB085YMA9GB4</EndToEndId>
+ <TxId>2024042288942205090200000010000001</TxId>
+ </Refs>
+ <AmtDtls>
+ <TxAmt>
+ <Amt Ccy="EUR">1.10</Amt>
+ </TxAmt>
+ </AmtDtls>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>ICDT</Cd>
+ <SubFmlyCd>RRTN</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ </BkTxCd>
+ <RltdPties>
+ <Dbtr>
+ <Nm>John Smith</Nm>
+ </Dbtr>
+ <DbtrAcct>
+ <Id>
+ <IBAN>DE84500105177118117964</IBAN>
+ </Id>
+ </DbtrAcct>
+ <Cdtr>
+ <Nm>Mr Test</Nm>
+ </Cdtr>
+ <CdtrAcct>
+ <Id>
+ <IBAN>DE20500105172419259181</IBAN>
+ </Id>
+ </CdtrAcct>
+ </RltdPties>
+ <RtrInf>
+ <OrgnlBkTxCd>
+ <Prtry>
+ <Cd>116</Cd>
+ <Issr>DK</Issr>
+ </Prtry>
+ </OrgnlBkTxCd>
+ <Orgtr>
+ <Id>
+ <OrgId>
+ <BICOrBEI>GENODEM1GLS</BICOrBEI>
+ </OrgId>
+ </Id>
+ </Orgtr>
+ <Rsn>
+ <Cd>AC01</Cd>
+ </Rsn>
+ <AddtlInf>IBAN ...</AddtlInf>
+ </RtrInf>
+ </TxDtls>
+ </NtryDtls>
+ <AddtlNtryInf>Überweisungsgutschr.</AddtlNtryInf>
+ </Ntry>
+ </Rpt>
+ </BkToCstmrAcctRpt>
+</Document>
+
+\ No newline at end of file
diff --git a/nexus/sample/platform/gls.xml b/nexus/sample/platform/gls_camt053.xml
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
@@ -156,7 +156,7 @@ private suspend fun ingestDocument(
whichDocument: SupportedDocument
) {
when (whichDocument) {
- SupportedDocument.CAMT_053, SupportedDocument.CAMT_054 -> {
+ SupportedDocument.CAMT_052, SupportedDocument.CAMT_053, SupportedDocument.CAMT_054 -> {
try {
parseTx(xml, cfg.currency, cfg.dialect).forEach {
if (cfg.fetch.ignoreBefore != null && it.executionTime < cfg.fetch.ignoreBefore) {
@@ -212,10 +212,6 @@ private suspend fun ingestDocument(
db.initiated.bankMessage(status.msgId, msg)
}
}
- SupportedDocument.CAMT_052 -> {
- // TODO parsing
- // TODO ingesting
- }
}
}
@@ -307,15 +303,18 @@ enum class EbicsDocument {
acknowledgement,
/// Payment status - CustomerPaymentStatusReport pain.002
status,
- /// Debit & credit notifications - BankToCustomerDebitCreditNotification camt.054
- notification,
+ /// Account intraday reports - BankToCustomerAccountReport camt.052
+ report,
/// Account statements - BankToCustomerStatement camt.053
statement,
+ /// Debit & credit notifications - BankToCustomerDebitCreditNotification camt.054
+ notification,
;
fun shortDescription(): String = when (this) {
acknowledgement -> "EBICS acknowledgement"
status -> "Payment status"
+ report -> "Account intraday reports"
statement -> "Account statements"
notification -> "Debit & credit notifications"
}
@@ -323,6 +322,7 @@ enum class EbicsDocument {
fun fullDescription(): String = when (this) {
acknowledgement -> "EBICS acknowledgement - CustomerAcknowledgement HAC pain.002"
status -> "Payment status - CustomerPaymentStatusReport pain.002"
+ report -> "Account intraday reports - BankToCustomerAccountReport camt.052"
statement -> "Account statements - BankToCustomerStatement camt.053"
notification -> "Debit & credit notifications - BankToCustomerDebitCreditNotification camt.054"
}
@@ -330,6 +330,7 @@ enum class EbicsDocument {
fun doc(): SupportedDocument = when (this) {
acknowledgement -> SupportedDocument.PAIN_002_LOGS
status -> SupportedDocument.PAIN_002
+ report -> SupportedDocument.CAMT_052
statement -> SupportedDocument.CAMT_053
notification -> SupportedDocument.CAMT_054
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
@@ -385,7 +385,7 @@ fun parseTx(
XmlDestructor.fromStream(notifXml, "Document") { when (dialect) {
Dialect.gls -> {
- opt("BkToCstmrStmt")?.each("Stmt") { // Camt.053
+ fun XmlDestructor.parseGlsInner() {
opt("Acct") {
// Sanity check on currency and IBAN ?
}
@@ -440,6 +440,12 @@ fun parseTx(
}
}
}
+ opt("BkToCstmrAcctRpt")?.each("Rpt") { // Camt.052
+ parseGlsInner()
+ }
+ opt("BkToCstmrStmt")?.each("Stmt") { // Camt.053
+ parseGlsInner()
+ }
}
Dialect.postfinance -> {
opt("BkToCstmrStmt")?.each("Stmt") { // Camt.053
diff --git a/nexus/src/test/kotlin/Iso20022Test.kt b/nexus/src/test/kotlin/Iso20022Test.kt
@@ -91,8 +91,45 @@ class Iso20022Test {
}
@Test
- fun gls() {
- val content = Files.newInputStream(Path("sample/platform/gls.xml"))
+ fun gls_camt052() {
+ val content = Files.newInputStream(Path("sample/platform/gls_camt052.xml"))
+ val txs = parseTx(content, "EUR", Dialect.gls)
+ assertEquals(
+ listOf(
+ OutgoingPayment(
+ messageId = "G059N0SR5V0WZ0XSFY1H92QBZ0",
+ amount = TalerAmount("EUR:2"),
+ wireTransferSubject = "TestABC123",
+ executionTime = instant("2024-04-18"),
+ creditPaytoUri = "payto://iban/DE20500105172419259181?receiver-name=John+Smith"
+ ),
+ OutgoingPayment(
+ messageId = "YF5QBARGQ0MNY0VK59S477VDG4",
+ amount = TalerAmount("EUR:1.1"),
+ wireTransferSubject = "This should fail because dummy",
+ executionTime = instant("2024-04-18"),
+ creditPaytoUri = "payto://iban/DE20500105172419259181?receiver-name=John+Smith"
+ ),
+ IncomingPayment(
+ bankId = "BYLADEM1WOR-G2910276709458A2",
+ amount = TalerAmount("EUR:3"),
+ wireTransferSubject = "Taler FJDQ7W6G7NWX4H9M1MKA12090FRC9K7DA6N0FANDZZFXTR6QHX5G Test.,-",
+ executionTime = instant("2024-04-12"),
+ debitPaytoUri = "payto://iban/DE84500105177118117964?receiver-name=John+Smith"
+ ),
+ Reversal(
+ msgId = "G27KNKZAR5DV7HRB085YMA9GB4",
+ reason = "IncorrectAccountNumber 'Format of the account number specified is not correct' - 'IBAN ...'",
+ executionTime = instant("2024-04-12")
+ )
+ ),
+ txs
+ )
+ }
+
+ @Test
+ fun gls_camt053() {
+ val content = Files.newInputStream(Path("sample/platform/gls_camt053.xml"))
val txs = parseTx(content, "EUR", Dialect.gls)
assertEquals(
listOf(
diff --git a/testbench/src/main/kotlin/Main.kt b/testbench/src/main/kotlin/Main.kt
@@ -135,7 +135,7 @@ class Cli : CliktCommand("Run integration tests on banks provider") {
val payto = benchCfg.payto[currency] ?: dummyPayto
?: throw Exception("Missing test payto for $currency")
- val recoverDoc = "notification statement"
+ val recoverDoc = "report statement notification"
runBlocking {
step("Init ${kind.name}")
@@ -160,6 +160,7 @@ class Cli : CliktCommand("Run integration tests on banks provider") {
put("fetch", "Fetch all documents", "ebics-fetch $ebicsFlags")
put("ack", "Fetch CustomerAcknowledgement", "ebics-fetch $ebicsFlags acknowledgement")
put("status", "Fetch CustomerPaymentStatusReport", "ebics-fetch $ebicsFlags status")
+ put("report", "Fetch BankToCustomerAccountReport", "ebics-fetch $ebicsFlags report")
put("notification", "Fetch BankToCustomerDebitCreditNotification", "ebics-fetch $ebicsFlags notification")
put("statement", "Fetch BankToCustomerStatement", "ebics-fetch $ebicsFlags statement")
put("submit", "Submit pending transactions", "ebics-submit $ebicsFlags")