summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt9
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt18
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt11
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt28
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt27
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt4
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/xlibeufinbank/XLibeufinBankNexus.kt4
-rw-r--r--nexus/src/test/kotlin/Iso20022Test.kt17
-rw-r--r--nexus/src/test/kotlin/MakeEnv.kt139
-rw-r--r--sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt4
-rw-r--r--util/src/main/kotlin/Ebics.kt1
-rw-r--r--util/src/main/kotlin/XMLUtil.kt4
12 files changed, 214 insertions, 52 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index a63e6503..0886e177 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -37,6 +37,7 @@ import startServer
import tech.libeufin.nexus.iso20022.NexusPaymentInitiationData
import tech.libeufin.nexus.iso20022.createPain001document
import tech.libeufin.nexus.iso20022.parseCamtMessage
+import tech.libeufin.nexus.server.EbicsDialects
import tech.libeufin.nexus.server.client
import tech.libeufin.nexus.server.nexusApp
import tech.libeufin.util.*
@@ -137,15 +138,15 @@ class ParseCamt : CliktCommand("Parse camt.05x file, outputs JSON in libEufin in
private val logLevel by option(
help = "Set the log level to: 'off', 'error', 'warn', 'info', 'debug', 'trace', 'all'"
)
- private val withC54 by option(
- help = "Treats the input as camt.054. Without this option, the" +
- " parser expects a camt.052 or camt.053 and handles them equally."
+ private val withPfDialect by option(
+ help = "Set the dialect to 'pf' (PostFinance). If not given, it defaults to GLS."
).flag(default = false)
private val filename by argument("FILENAME", "File in CAMT format")
override fun run() {
setLogLevel(logLevel)
val camtText = File(filename).readText(Charsets.UTF_8)
- val res = parseCamtMessage(XMLUtil.parseStringIntoDom(camtText))
+ val dialect = if (withPfDialect) EbicsDialects.POSTFINANCE.dialectName else null
+ val res = parseCamtMessage(XMLUtil.parseStringIntoDom(camtText), dialect)
println(jacksonObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(res))
}
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
index a1576a05..8f1f6ca5 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
@@ -29,7 +29,7 @@ import org.jetbrains.exposed.sql.transactions.transaction
import tech.libeufin.nexus.*
import tech.libeufin.nexus.iso20022.*
import tech.libeufin.nexus.server.*
-import tech.libeufin.nexus.xlibeufinbank.processXLibeufinBankMessage
+import tech.libeufin.nexus.xlibeufinbank.ingestXLibeufinBankMessage
import tech.libeufin.util.XMLUtil
import tech.libeufin.util.internalServerError
import java.time.Instant
@@ -204,9 +204,13 @@ fun ingestBankMessagesIntoAccount(
}.orderBy(
Pair(NexusBankMessagesTable.id, SortOrder.ASC)
).forEach {
- val processingResult: IngestedTransactionsCount = when(BankConnectionType.parseBankConnectionType(conn.type)) {
+ val ingestionResult: IngestedTransactionsCount = when(BankConnectionType.parseBankConnectionType(conn.type)) {
BankConnectionType.EBICS -> {
val camtString = it.message.bytes.toString(Charsets.UTF_8)
+ /**
+ * NOT validating _again_ the camt document because it was
+ * already validate before being stored into the database.
+ */
val doc = XMLUtil.parseStringIntoDom(camtString)
/**
* Calling the CaMt handler. After its return, all the Neuxs-meaningful
@@ -214,7 +218,7 @@ fun ingestBankMessagesIntoAccount(
* processed by any facade OR simply be communicated to the CLI via JSON.
*/
try {
- processCamtMessage(
+ ingestCamtMessageIntoAccount(
bankAccountId,
doc,
it.fetchLevel,
@@ -234,7 +238,7 @@ fun ingestBankMessagesIntoAccount(
" be parsed into JSON by the x-libeufin-bank ingestion.")
throw internalServerError("Could not ingest x-libeufin-bank messages.")
}
- processXLibeufinBankMessage(
+ ingestXLibeufinBankMessage(
bankAccountId,
jMessage
)
@@ -246,13 +250,13 @@ fun ingestBankMessagesIntoAccount(
* (1) flagged, (2) skipped when this function will run again, and (3)
* NEVER deleted from the database.
*/
- if (processingResult.newTransactions == -1) {
+ if (ingestionResult.newTransactions == -1) {
it.errors = true
lastId = it.id.value
return@forEach
}
- totalNew += processingResult.newTransactions
- downloadedTransactions += processingResult.downloadedTransactions
+ totalNew += ingestionResult.newTransactions
+ downloadedTransactions += ingestionResult.downloadedTransactions
/**
* Disk-space conservative check: only store if "yes" was
* explicitly set into the environment variable. Any other
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt
index 382aefc8..0d3aa67b 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt
@@ -78,7 +78,14 @@ private suspend inline fun HttpClient.postToBank(url: String, body: String): Str
sealed class EbicsDownloadResult
class EbicsDownloadSuccessResult(
- val orderData: ByteArray
+ val orderData: ByteArray,
+ /**
+ * This value points at the EBICS transaction that carried
+ * the order data contained in this structure. That makes
+ * possible to log the EBICS transaction that carried one
+ * invalid order data, for example.
+ */
+ val transactionID: String? = null
) : EbicsDownloadResult()
class EbicsDownloadEmptyResult(
@@ -244,7 +251,7 @@ suspend fun doEbicsDownloadTransaction(
}
}
logger.debug("Bank acknowledges EBICS download receipt. Transaction ID: $transactionID.")
- return EbicsDownloadSuccessResult(respPayload)
+ return EbicsDownloadSuccessResult(respPayload, transactionID)
}
// Currently only 1-segment requests.
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
index e84e9846..50578894 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
@@ -90,18 +90,31 @@ private fun getFetchLevelFromEbicsOrder(ebicsHistoryType: String): FetchLevel {
}
}
-fun storeCamt(
+// Validate and store the received document for later ingestion.
+private fun validateAndStoreCamt(
bankConnectionId: String,
camt: String,
- fetchLevel: FetchLevel
+ fetchLevel: FetchLevel,
+ transactionID: String? = null // the EBICS transaction that carried this camt.
) {
- val camt53doc = XMLUtil.parseStringIntoDom(camt)
- val msgId = camt53doc.pickStringWithRootNs("/*[1]/*[1]/root:GrpHdr/root:MsgId")
+ val camtDoc = try {
+ XMLUtil.parseStringIntoDom(camt)
+ }
+ catch (e: Exception) {
+ throw badGateway("Could not parse camt document from EBICS transaction $transactionID")
+ }
+ if (!XMLUtil.validateFromDom(camtDoc))
+ throw badGateway("Camt document from EBICS transaction $transactionID is invalid")
+
+ val msgId = camtDoc.pickStringWithRootNs("/*[1]/*[1]/root:GrpHdr/root:MsgId")
logger.info("Camt document '$msgId' received via $fetchLevel.")
transaction {
val conn = NexusBankConnectionEntity.findByName(bankConnectionId)
if (conn == null) {
- throw NexusError(HttpStatusCode.InternalServerError, "bank connection missing")
+ throw NexusError(
+ HttpStatusCode.InternalServerError,
+ "bank connection missing"
+ )
}
val oldMsg = NexusBankMessageEntity.find { NexusBankMessagesTable.messageId eq msgId }.firstOrNull()
if (oldMsg == null) {
@@ -164,10 +177,11 @@ private suspend fun fetchEbicsC5x(
is EbicsDownloadSuccessResult -> {
response.orderData.unzipWithLambda {
// logger.debug("Camt entry (filename (in the Zip archive): ${it.first}): ${it.second}")
- storeCamt(
+ validateAndStoreCamt(
bankConnectionId,
it.second,
- getFetchLevelFromEbicsOrder(historyType)
+ getFetchLevelFromEbicsOrder(historyType),
+ transactionID = response.transactionID
)
}
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt
index b11818f9..14e24485 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt
@@ -30,6 +30,7 @@ import CashAccount
import CreditDebitIndicator
import CurrencyAmount
import CurrencyExchange
+import EntryStatus
import GenericId
import OrganizationIdentification
import PartyIdentification
@@ -581,7 +582,7 @@ private fun XmlElementDestructor.extractInnerBkTxCd(creditDebitIndicator: Credit
return "XTND-NTAV-NTAV"
}
-private fun XmlElementDestructor.extractInnerTransactions(): CamtReport {
+private fun XmlElementDestructor.extractInnerTransactions(dialect: String? = null): CamtReport {
val account = requireUniqueChildNamed("Acct") { extractAccount() }
val balances = mapEachChildNamed("Bal") {
@@ -613,8 +614,16 @@ private fun XmlElementDestructor.extractInnerTransactions(): CamtReport {
// multiple money transactions *within* one Ntry element.
val entries = mapEachChildNamed("Ntry") {
val amount = extractCurrencyAmount()
- val status = requireUniqueChildNamed("Sts") { focusElement.textContent }.let {
- EntryStatus.valueOf(it)
+ val status = requireUniqueChildNamed("Sts") {
+ val textContent = if (dialect == EbicsDialects.POSTFINANCE.dialectName) {
+ requireUniqueChildNamed("Cd") {
+ focusElement.textContent
+ }
+ } else
+ focusElement.textContent
+ textContent.let {
+ EntryStatus.valueOf(it)
+ }
}
val creditDebitIndicator = requireUniqueChildNamed("CdtDbtInd") { focusElement.textContent }.let {
CreditDebitIndicator.valueOf(it)
@@ -677,7 +686,7 @@ private fun XmlElementDestructor.extractInnerTransactions(): CamtReport {
* Extract a list of transactions from
* an ISO20022 camt.052 / camt.053 message.
*/
-fun parseCamtMessage(doc: Document): CamtParseResult {
+fun parseCamtMessage(doc: Document, dialect: String? = null): CamtParseResult {
return destructXml(doc) {
requireRootElement("Document") {
// Either bank to customer statement or report
@@ -685,17 +694,17 @@ fun parseCamtMessage(doc: Document): CamtParseResult {
when (focusElement.localName) {
"BkToCstmrAcctRpt" -> {
mapEachChildNamed("Rpt") {
- extractInnerTransactions()
+ extractInnerTransactions(dialect)
}
}
"BkToCstmrStmt" -> {
mapEachChildNamed("Stmt") {
- extractInnerTransactions()
+ extractInnerTransactions(dialect)
}
}
"BkToCstmrDbtCdtNtfctn" -> {
mapEachChildNamed("Ntfctn") {
- extractInnerTransactions()
+ extractInnerTransactions(dialect)
}
}
else -> {
@@ -846,7 +855,7 @@ fun extractPaymentUidFromSingleton(
* case of DBIT transaction.
* - returns a IngestedTransactionCount object.
*/
-fun processCamtMessage(
+fun ingestCamtMessageIntoAccount(
bankAccountId: String,
camtDoc: Document,
fetchLevel: FetchLevel,
@@ -866,7 +875,7 @@ fun processCamtMessage(
if (acct == null) {
throw NexusError(HttpStatusCode.NotFound, "user not found")
}
- val res = try { parseCamtMessage(camtDoc) } catch (e: CamtParsingError) {
+ val res = try { parseCamtMessage(camtDoc, dialect) } catch (e: CamtParsingError) {
logger.warn("Invalid CAMT received from bank: ${e.message}")
newTransactions = -1
return@transaction
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
index fca81b51..21614fbe 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
@@ -44,7 +44,7 @@ import org.slf4j.event.Level
import tech.libeufin.nexus.*
import tech.libeufin.nexus.bankaccount.*
import tech.libeufin.nexus.ebics.*
-import tech.libeufin.nexus.iso20022.processCamtMessage
+import tech.libeufin.nexus.iso20022.ingestCamtMessageIntoAccount
import tech.libeufin.util.*
import java.net.URLEncoder
@@ -437,7 +437,7 @@ val nexusApp: Application.() -> Unit = {
defaultConn.dialect
} else null
val msgType = ensureNonNull(call.parameters["type"])
- processCamtMessage(
+ ingestCamtMessageIntoAccount(
ensureNonNull(accountId),
XMLUtil.parseStringIntoDom(call.receiveText()),
when(msgType) {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/xlibeufinbank/XLibeufinBankNexus.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/xlibeufinbank/XLibeufinBankNexus.kt
index bfa52208..cd37862a 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/xlibeufinbank/XLibeufinBankNexus.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/xlibeufinbank/XLibeufinBankNexus.kt
@@ -19,12 +19,10 @@ import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.util.*
import io.ktor.util.*
-import io.ktor.util.date.*
import org.jetbrains.exposed.sql.statements.api.ExposedBlob
import org.jetbrains.exposed.sql.transactions.transaction
import tech.libeufin.nexus.*
import tech.libeufin.nexus.bankaccount.*
-import tech.libeufin.nexus.iso20022.*
import tech.libeufin.nexus.server.*
import tech.libeufin.util.*
import java.net.MalformedURLException
@@ -328,7 +326,7 @@ class XlibeufinBankConnectionProtocol : BankConnectionProtocol {
* status, since Sandbox has only one (unnamed) transaction state and
* all transactions are asked as reports.
*/
-fun processXLibeufinBankMessage(
+fun ingestXLibeufinBankMessage(
bankAccountId: String,
data: JsonNode
): IngestedTransactionsCount {
diff --git a/nexus/src/test/kotlin/Iso20022Test.kt b/nexus/src/test/kotlin/Iso20022Test.kt
index 58777cba..bc5d7379 100644
--- a/nexus/src/test/kotlin/Iso20022Test.kt
+++ b/nexus/src/test/kotlin/Iso20022Test.kt
@@ -9,7 +9,7 @@ import org.junit.Ignore
import org.junit.Test
import org.w3c.dom.Document
import poFiCamt052
-import poFiCamt054
+import poFiCamt054_2019
import prepNexusDb
import tech.libeufin.nexus.bankaccount.getBankAccount
import tech.libeufin.nexus.iso20022.*
@@ -21,13 +21,6 @@ import tech.libeufin.util.DestructionError
import tech.libeufin.util.XMLUtil
import tech.libeufin.util.destructXml
import withTestDatabase
-import java.math.BigDecimal
-import java.time.LocalDateTime
-import java.time.ZoneId
-import java.time.ZoneOffset
-import java.time.ZonedDateTime
-import java.time.format.DateTimeFormatter
-import java.util.TimeZone
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -125,15 +118,15 @@ class Iso20022Test {
@Test
fun parsePoFiCamt054() {
- val doc = XMLUtil.parseStringIntoDom(poFiCamt054)
- parseCamtMessage(doc)
+ val doc = XMLUtil.parseStringIntoDom(poFiCamt054_2019)
+ parseCamtMessage(doc, dialect = "pf")
}
@Test
fun ingestPoFiCamt054() {
- val doc = XMLUtil.parseStringIntoDom(poFiCamt054)
+ val doc = XMLUtil.parseStringIntoDom(poFiCamt054_2019)
withTestDatabase { prepNexusDb()
- processCamtMessage(
+ ingestCamtMessageIntoAccount(
"foo",
doc,
FetchLevel.NOTIFICATION,
diff --git a/nexus/src/test/kotlin/MakeEnv.kt b/nexus/src/test/kotlin/MakeEnv.kt
index c64d20e8..f3c71a11 100644
--- a/nexus/src/test/kotlin/MakeEnv.kt
+++ b/nexus/src/test/kotlin/MakeEnv.kt
@@ -6,7 +6,6 @@ import org.jetbrains.exposed.sql.transactions.transaction
import tech.libeufin.nexus.*
import tech.libeufin.nexus.dbCreateTables
import tech.libeufin.nexus.dbDropTables
-import tech.libeufin.nexus.iso20022.*
import tech.libeufin.nexus.server.BankConnectionType
import tech.libeufin.nexus.server.FetchLevel
import tech.libeufin.nexus.server.FetchSpecAllJson
@@ -495,7 +494,143 @@ fun genNexusIncomingCamt(
)
)
-val poFiCamt054: String = """
+// Comes from a "mit Sammelbuchung" sample.
+// "mit Einzelbuchung" sample didn't have the "Ustrd"
+// See: https://www.postfinance.ch/de/support/services/dokumente/musterfiles-fuer-geschaeftskunden.html
+val poFiCamt054_2019: String = """
+<?xml version="1.0" encoding="UTF-8"?>
+<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.054.001.08" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:camt.054.001.08 file:///C:/Users/burkhalterl/Documents/Musterfiles%20ISOV19/Schemen/camt.054.001.08.xsd">
+ <BkToCstmrDbtCdtNtfctn>
+ <GrpHdr>
+ <MsgId>20200618375204295372463</MsgId>
+ <CreDtTm>2022-03-08T23:31:31</CreDtTm>
+ <MsgPgntn>
+ <PgNb>1</PgNb>
+ <LastPgInd>true</LastPgInd>
+ </MsgPgntn>
+ <AddtlInf>SPS/2.0/PROD</AddtlInf>
+ </GrpHdr>
+ <Ntfctn>
+ <Id>20200618375204295372465</Id>
+ <CreDtTm>2022-03-08T23:31:31</CreDtTm>
+ <FrToDt>
+ <FrDtTm>2022-03-08T00:00:00</FrDtTm>
+ <ToDtTm>2022-03-08T23:59:59</ToDtTm>
+ </FrToDt>
+ <Acct>
+ <Id>
+ <IBAN>${FOO_USER_IBAN}</IBAN>
+ </Id>
+ <Ccy>CHF</Ccy>
+ <Ownr>
+ <Nm>Robert Schneider SA Grands magasins Biel/Bienne</Nm>
+ </Ownr>
+ </Acct>
+ <Ntry>
+ <NtryRef>CH2909000000250094239</NtryRef>
+ <Amt Ccy="CHF">501.05</Amt>
+ <CdtDbtInd>CRDT</CdtDbtInd>
+ <RvslInd>false</RvslInd>
+ <Sts>
+ <Cd>BOOK</Cd>
+ </Sts>
+ <BookgDt>
+ <Dt>2022-03-08</Dt>
+ </BookgDt>
+ <ValDt>
+ <Dt>2022-03-08</Dt>
+ </ValDt>
+ <AcctSvcrRef>1000000000000000</AcctSvcrRef>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>RCDT</Cd>
+ <SubFmlyCd>AUTT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ </BkTxCd>
+ <NtryDtls>
+ <Btch>
+ <NbOfTxs>1</NbOfTxs>
+ </Btch>
+ <TxDtls>
+ <Refs>
+ <AcctSvcrRef>2000000000000000</AcctSvcrRef>
+ <InstrId>1006265-25bbb3b1a</InstrId>
+ <EndToEndId>NOTPROVIDED</EndToEndId>
+ <UETR>b009c997-97b3-4a9c-803c-d645a7276b0</UETR>
+ <Prtry>
+ <Tp>00</Tp>
+ <Ref>00000000000000000000020</Ref>
+ </Prtry>
+ </Refs>
+ <Amt Ccy="CHF">501.05</Amt>
+ <CdtDbtInd>CRDT</CdtDbtInd>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>RCDT</Cd>
+ <SubFmlyCd>AUTT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ </BkTxCd>
+ <RltdPties>
+ <Dbtr>
+ <Pty>
+ <Nm>Bernasconi Maria</Nm>
+ <PstlAdr>
+ <AdrLine>Place de la Gare 12</AdrLine>
+ <AdrLine>2502 Biel/Bienne</AdrLine>
+ </PstlAdr>
+ </Pty>
+ </Dbtr>
+ <DbtrAcct>
+ <Id>
+ <IBAN>CH5109000000250092291</IBAN>
+ </Id>
+ </DbtrAcct>
+ <CdtrAcct>
+ <Id>
+ <IBAN>CH2909000000250094239</IBAN>
+ </Id>
+ </CdtrAcct>
+ </RltdPties>
+ <RltdAgts>
+ <DbtrAgt>
+ <FinInstnId>
+ <BICFI>POFICHBEXXX</BICFI>
+ <Nm>POSTFINANCE AG</Nm>
+ <PstlAdr>
+ <AdrLine>MINGERSTRASSE , 20</AdrLine>
+ <AdrLine>3030 BERN</AdrLine>
+ </PstlAdr>
+ </FinInstnId>
+ </DbtrAgt>
+ </RltdAgts>
+ <RmtInf>
+ <Ustrd>Muster</Ustrd>
+ <Ustrd> Musterfile</Ustrd>
+ <Strd>
+ <AddtlRmtInf>?REJECT?0</AddtlRmtInf>
+ <AddtlRmtInf>?ERROR?000</AddtlRmtInf>
+ </Strd>
+ </RmtInf>
+ <RltdDts>
+ <AccptncDtTm>2022-03-08T20:00:00</AccptncDtTm>
+ </RltdDts>
+ </TxDtls>
+ </NtryDtls>
+ <AddtlNtryInf>SAMMELGUTSCHRIFT FÜR KONTO: CH2909000000250094239 VERARBEITUNG VOM 08.03.2022 PAKET ID: 200000000000XXX</AddtlNtryInf>
+ </Ntry>
+ </Ntfctn>
+ </BkToCstmrDbtCdtNtfctn>
+</Document>
+
+""".trimIndent()
+
+val poFiCamt054_2013: String = """
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.054.001.04" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:camt.054.001.04 camt.054.001.04.xsd">
<BkToCstmrDbtCdtNtfctn>
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
index d79d0ab2..fc240963 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -1017,8 +1017,8 @@ private fun ensureEbicsHost(requestHostID: String): EbicsHostPublicInfo {
}
fun receiveEbicsXmlInternal(xmlData: String): Document {
// logger.debug("Data received: $xmlData")
- val requestDocument: Document? = XMLUtil.parseStringIntoDom(xmlData)
- if (requestDocument == null || (!XMLUtil.validateFromDom(requestDocument))) {
+ val requestDocument: Document = XMLUtil.parseStringIntoDom(xmlData)
+ if (!XMLUtil.validateFromDom(requestDocument)) {
println("Problematic document was: $requestDocument")
throw EbicsInvalidXmlError()
}
diff --git a/util/src/main/kotlin/Ebics.kt b/util/src/main/kotlin/Ebics.kt
index 737039a6..da814d5d 100644
--- a/util/src/main/kotlin/Ebics.kt
+++ b/util/src/main/kotlin/Ebics.kt
@@ -264,7 +264,6 @@ fun createEbicsRequestForUploadInitialization(
return XMLUtil.convertDomToString(doc)
}
-
fun createEbicsRequestForDownloadInitialization(
subscriberDetails: EbicsClientSubscriberDetails,
orderType: String,
diff --git a/util/src/main/kotlin/XMLUtil.kt b/util/src/main/kotlin/XMLUtil.kt
index e9074cb4..aa96e00f 100644
--- a/util/src/main/kotlin/XMLUtil.kt
+++ b/util/src/main/kotlin/XMLUtil.kt
@@ -225,6 +225,8 @@ class XMLUtil private constructor() {
}
}
}
+
+ // FIXME: need here the "2019" Swiss versions of camt and pain.
val schemaInputs: Array<Source> = listOf(
"xsd/ebics_H004.xsd",
"xsd/ebics_hev.xsd",
@@ -232,7 +234,7 @@ class XMLUtil private constructor() {
"xsd/camt.053.001.02.xsd",
"xsd/camt.054.001.02.xsd",
"xsd/pain.001.001.03.xsd",
- "xsd/pain.001.001.03.ch.02.xsd"
+ "xsd/pain.001.001.03.ch.02.xsd" // Swiss 2013 version.
).map {
val stream =
classLoader.getResourceAsStream(it) ?: throw FileNotFoundException("Schema file $it not found.")