gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated (7f84324c -> 5837035a)


From: gnunet
Subject: [libeufin] branch master updated (7f84324c -> 5837035a)
Date: Tue, 13 Feb 2024 08:18:48 +0100

This is an automated email from the git hooks/post-receive script.

antoine pushed a change to branch master
in repository libeufin.

    from 7f84324c Fix withdrawal status
     new f83a22f2 Optimize memory usage and performance by using ByteArray when 
possible and improve logging
     new 5837035a Management of failures in the processing of EBICS download 
transactions

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 ebics/src/main/kotlin/Ebics.kt                     |  10 +-
 ebics/src/main/kotlin/EbicsOrderUtil.kt            |   4 +-
 ebics/src/main/kotlin/XMLUtil.kt                   |  56 ++++------
 ebics/src/main/kotlin/ebics_h004/EbicsRequest.kt   |   9 +-
 ebics/src/main/kotlin/ebics_h005/Ebics3Request.kt  |   7 +-
 ebics/src/test/kotlin/EbicsMessagesTest.kt         |  50 ++++-----
 ebics/src/test/kotlin/EbicsOrderUtilTest.kt        |   4 +-
 ebics/src/test/kotlin/XmlUtilTest.kt               |  22 ++--
 .../main/kotlin/tech/libeufin/nexus/EbicsFetch.kt  |  53 ++++-----
 .../main/kotlin/tech/libeufin/nexus/EbicsSetup.kt  |   7 +-
 .../kotlin/tech/libeufin/nexus/ebics/Ebics2.kt     |  89 ++++-----------
 .../kotlin/tech/libeufin/nexus/ebics/Ebics3.kt     |  31 +++---
 .../tech/libeufin/nexus/ebics/EbicsCommon.kt       | 120 +++++++++------------
 nexus/src/test/kotlin/Ebics.kt                     |  22 ++--
 testbench/src/main/kotlin/Main.kt                  |  18 ++--
 15 files changed, 209 insertions(+), 293 deletions(-)

diff --git a/ebics/src/main/kotlin/Ebics.kt b/ebics/src/main/kotlin/Ebics.kt
index 406da44c..91a41022 100644
--- a/ebics/src/main/kotlin/Ebics.kt
+++ b/ebics/src/main/kotlin/Ebics.kt
@@ -310,7 +310,7 @@ class HpbResponseData(
 
 fun parseEbicsHpbOrder(orderDataRaw: ByteArray): HpbResponseData {
     val resp = try {
-        
XMLUtil.convertStringToJaxb<HPBResponseOrderData>(orderDataRaw.toString(Charsets.UTF_8))
+        XMLUtil.convertBytesToJaxb<HPBResponseOrderData>(orderDataRaw)
     } catch (e: Exception) {
         throw EbicsProtocolError(HttpStatusCode.InternalServerError, "Invalid 
XML (as HPB response) received from bank")
     }
@@ -331,10 +331,10 @@ fun parseEbicsHpbOrder(orderDataRaw: ByteArray): 
HpbResponseData {
     )
 }
 
-fun ebics3toInternalRepr(response: String): EbicsResponseContent {
+fun ebics3toInternalRepr(response: ByteArray): EbicsResponseContent {
     // logger.debug("Converting bank resp to internal repr.: $response")
     val resp: JAXBElement<Ebics3Response> = try {
-        XMLUtil.convertStringToJaxb(response)
+        XMLUtil.convertBytesToJaxb(response)
     } catch (e: Exception) {
         throw EbicsProtocolError(
             HttpStatusCode.InternalServerError,
@@ -368,9 +368,9 @@ fun ebics3toInternalRepr(response: String): 
EbicsResponseContent {
     )
 }
 
-fun ebics25toInternalRepr(response: String): EbicsResponseContent {
+fun ebics25toInternalRepr(response: ByteArray): EbicsResponseContent {
     val resp: JAXBElement<EbicsResponse> = try {
-        XMLUtil.convertStringToJaxb(response)
+        XMLUtil.convertBytesToJaxb(response)
     } catch (e: Exception) {
         throw EbicsProtocolError(
             HttpStatusCode.InternalServerError,
diff --git a/ebics/src/main/kotlin/EbicsOrderUtil.kt 
b/ebics/src/main/kotlin/EbicsOrderUtil.kt
index ef7ed1f7..c056ddde 100644
--- a/ebics/src/main/kotlin/EbicsOrderUtil.kt
+++ b/ebics/src/main/kotlin/EbicsOrderUtil.kt
@@ -39,12 +39,12 @@ object EbicsOrderUtil {
     inline fun <reified T> decodeOrderDataXml(encodedOrderData: ByteArray): T {
         return InflaterInputStream(encodedOrderData.inputStream()).use {
             val bytes = it.readAllBytes()
-            
XMLUtil.convertStringToJaxb<T>(bytes.toString(Charsets.UTF_8)).value
+            XMLUtil.convertBytesToJaxb<T>(bytes).value
         }
     }
 
     inline fun <reified T> encodeOrderDataXml(obj: T): ByteArray {
-        val bytes = XMLUtil.convertJaxbToString(obj).toByteArray()
+        val bytes = XMLUtil.convertJaxbToBytes(obj)
         return DeflaterInputStream(bytes.inputStream()).use {
             it.readAllBytes()
         }
diff --git a/ebics/src/main/kotlin/XMLUtil.kt b/ebics/src/main/kotlin/XMLUtil.kt
index 63dbf35b..5947341a 100644
--- a/ebics/src/main/kotlin/XMLUtil.kt
+++ b/ebics/src/main/kotlin/XMLUtil.kt
@@ -287,17 +287,17 @@ class XMLUtil private constructor() {
          * @param xmlString XML body, as read from the POST body.
          * @return InputStream object, as wanted by the validator.
          */
-        fun validateFromString(xmlString: String): Boolean {
-            val xmlInputStream: InputStream = 
ByteArrayInputStream(xmlString.toByteArray())
+        fun validateFromBytes(xml: ByteArray): Boolean {
+            val xmlInputStream: InputStream = ByteArrayInputStream(xml)
             val xmlSource = StreamSource(xmlInputStream)
             return validate(xmlSource)
         }
 
-        inline fun <reified T> convertJaxbToString(
+        inline fun <reified T> convertJaxbToBytes(
             obj: T,
             withSchemaLocation: String? = null
-            ): String {
-            val sw = StringWriter()
+            ): ByteArray {
+            val w = ByteArrayOutputStream()
             val jc = JAXBContext.newInstance(T::class.java)
             val m = jc.createMarshaller()
             m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true)
@@ -305,8 +305,8 @@ class XMLUtil private constructor() {
                 m.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, 
withSchemaLocation)
             }
             m.setProperty("com.sun.xml.bind.namespacePrefixMapper", 
DefaultNamespaces())
-            m.marshal(obj, sw)
-            return sw.toString()
+            m.marshal(obj, w)
+            return w.toByteArray()
         }
 
         inline fun <reified T> convertJaxbToDocument(
@@ -328,37 +328,31 @@ class XMLUtil private constructor() {
         }
 
         /**
-         * Convert a XML string to the JAXB representation.
+         * Convert XML bytes to the JAXB representation.
          *
-         * @param documentString the string to convert into JAXB.
+         * @param documentBytes the bytes to convert into JAXB.
          * @return the JAXB object reflecting the original XML document.
          */
-        inline fun <reified T> convertStringToJaxb(documentString: String): 
JAXBElement<T> {
+        inline fun <reified T> convertBytesToJaxb(documentBytes: ByteArray): 
JAXBElement<T> {
             val jc = JAXBContext.newInstance(T::class.java)
             val u = jc.createUnmarshaller()
             return u.unmarshal(            /* Marshalling the object into the 
document.  */
-                StreamSource(StringReader(documentString)),
+                StreamSource(ByteArrayInputStream(documentBytes)),
                 T::class.java
             )
         }
 
-        /**
-         * Extract String from DOM.
-         *
-         * @param document the DOM to extract the string from.
-         * @return the final String, or null if errors occur.
-         */
-        fun convertDomToString(document: Document): String {
+        fun convertDomToBytes(document: Document): ByteArray {
             /* Make Transformer.  */
             val tf = TransformerFactory.newInstance()
             val t = tf.newTransformer()
 
-            /* Make string writer.  */
-            val sw = StringWriter()
+            /* Make bytes writer.  */
+            val w = ByteArrayOutputStream()
 
             /* Extract string.  */
-            t.transform(DOMSource(document), StreamResult(sw))
-            return sw.toString()
+            t.transform(DOMSource(document), StreamResult(w))
+            return w.toByteArray()
         }
 
         /**
@@ -391,20 +385,6 @@ class XMLUtil private constructor() {
             return m.unmarshal(document, finalType) // document "went" into 
Jaxb
         }
 
-        /**
-         * Parse string into XML DOM.
-         * @param xmlString the string to parse.
-         * @return the DOM representing @a xmlString
-         */
-        fun parseStringIntoDom(xmlString: String): Document {
-            val factory = DocumentBuilderFactory.newInstance().apply {
-                isNamespaceAware = true
-            }
-            val xmlInputStream = ByteArrayInputStream(xmlString.toByteArray())
-            val builder = factory.newDocumentBuilder()
-            return builder.parse(InputSource(xmlInputStream))
-        }
-
         /** Parse [xml] into a XML DOM */
         fun parseBytesIntoDom(xml: ByteArray): Document {
             val factory = DocumentBuilderFactory.newInstance().apply {
@@ -415,10 +395,10 @@ class XMLUtil private constructor() {
             return builder.parse(InputSource(xmlInputStream))
         }
 
-        fun signEbicsResponse(ebicsResponse: EbicsResponse, privateKey: 
RSAPrivateCrtKey): String {
+        fun signEbicsResponse(ebicsResponse: EbicsResponse, privateKey: 
RSAPrivateCrtKey): ByteArray {
             val doc = convertJaxbToDocument(ebicsResponse)
             signEbicsDocument(doc, privateKey)
-            val signedDoc = XMLUtil.convertDomToString(doc)
+            val signedDoc = XMLUtil.convertDomToBytes(doc)
             // logger.debug("response: $signedDoc")
             return signedDoc
         }
diff --git a/ebics/src/main/kotlin/ebics_h004/EbicsRequest.kt 
b/ebics/src/main/kotlin/ebics_h004/EbicsRequest.kt
index 263fb71a..8282e8b4 100644
--- a/ebics/src/main/kotlin/ebics_h004/EbicsRequest.kt
+++ b/ebics/src/main/kotlin/ebics_h004/EbicsRequest.kt
@@ -286,11 +286,10 @@ class EbicsRequest {
     }
 
     companion object {
-
         fun createForDownloadReceiptPhase(
-            transactionId: String?,
-            hostId: String
-
+            transactionId: String,
+            hostId: String,
+            success: Boolean
         ): EbicsRequest {
             return EbicsRequest().apply {
                 header = Header().apply {
@@ -310,7 +309,7 @@ class EbicsRequest {
                 body = Body().apply {
                     transferReceipt = TransferReceipt().apply {
                         authenticate = true
-                        receiptCode = 0 // always true at this point.
+                        receiptCode = if (success) 0 else 1
                     }
                 }
             }
diff --git a/ebics/src/main/kotlin/ebics_h005/Ebics3Request.kt 
b/ebics/src/main/kotlin/ebics_h005/Ebics3Request.kt
index 54987c8b..b6ca5df3 100644
--- a/ebics/src/main/kotlin/ebics_h005/Ebics3Request.kt
+++ b/ebics/src/main/kotlin/ebics_h005/Ebics3Request.kt
@@ -368,8 +368,9 @@ class Ebics3Request {
     companion object {
 
         fun createForDownloadReceiptPhase(
-            transactionId: String?,
-            hostId: String
+            transactionId: String,
+            hostId: String,
+            success: Boolean
         ): Ebics3Request {
             return Ebics3Request().apply {
                 header = Header().apply {
@@ -389,7 +390,7 @@ class Ebics3Request {
                 body = Body().apply {
                     transferReceipt = TransferReceipt().apply {
                         authenticate = true
-                        receiptCode = 0 // always true at this point.
+                        receiptCode = if (success) 0 else 1
                     }
                 }
             }
diff --git a/ebics/src/test/kotlin/EbicsMessagesTest.kt 
b/ebics/src/test/kotlin/EbicsMessagesTest.kt
index 5d0f8f5d..2b8d63ca 100644
--- a/ebics/src/test/kotlin/EbicsMessagesTest.kt
+++ b/ebics/src/test/kotlin/EbicsMessagesTest.kt
@@ -17,8 +17,6 @@
  * <http://www.gnu.org/licenses/>
  */
 
-package tech.libeufin.sandbox
-
 import junit.framework.TestCase.assertEquals
 import org.apache.xml.security.binding.xmldsig.SignatureType
 import org.junit.Test
@@ -43,7 +41,7 @@ class EbicsMessagesTest {
     fun testImportNonRoot() {
         val classLoader = ClassLoader.getSystemClassLoader()
         val ini = classLoader.getResource("ebics_ini_inner_key.xml")
-        val jaxb = 
XMLUtil.convertStringToJaxb<SignatureTypes.SignaturePubKeyOrderData>(ini.readText())
+        val jaxb = 
XMLUtil.convertBytesToJaxb<SignatureTypes.SignaturePubKeyOrderData>(ini.readBytes())
         assertEquals("A006", jaxb.value.signaturePubKeyInfo.signatureVersion)
     }
 
@@ -54,7 +52,7 @@ class EbicsMessagesTest {
     fun testStringToJaxb() {
         val classLoader = ClassLoader.getSystemClassLoader()
         val ini = classLoader.getResource("ebics_ini_request_sample.xml")
-        val jaxb = 
XMLUtil.convertStringToJaxb<EbicsUnsecuredRequest>(ini.readText())
+        val jaxb = 
XMLUtil.convertBytesToJaxb<EbicsUnsecuredRequest>(ini.readBytes())
         println("jaxb loaded")
         assertEquals(
             "INI",
@@ -74,7 +72,7 @@ class EbicsMessagesTest {
             }
             this.versionNumber = 
listOf(HEVResponse.VersionNumber.create("H004", "02.50"))
         }
-        XMLUtil.convertJaxbToString(hevResponseJaxb)
+        XMLUtil.convertJaxbToBytes(hevResponseJaxb)
     }
 
     /**
@@ -84,7 +82,7 @@ class EbicsMessagesTest {
     fun testDomToJaxb() {
         val classLoader = ClassLoader.getSystemClassLoader()
         val ini = classLoader.getResource("ebics_ini_request_sample.xml")!!
-        val iniDom = XMLUtil.parseStringIntoDom(ini.readText())
+        val iniDom = XMLUtil.parseBytesIntoDom(ini.readBytes())
         XMLUtil.convertDomToJaxb<EbicsUnsecuredRequest>(
             EbicsUnsecuredRequest::class.java,
             iniDom
@@ -109,22 +107,22 @@ class EbicsMessagesTest {
                 }
             }
         }
-        val text = XMLUtil.convertJaxbToString(responseXml)
-        assertTrue(text.isNotEmpty())
+        val bytes = XMLUtil.convertJaxbToBytes(responseXml)
+        assertTrue(bytes.isNotEmpty())
     }
 
     @Test
     fun testParseHiaRequestOrderData() {
         val classLoader = ClassLoader.getSystemClassLoader()
-        val hia = 
classLoader.getResource("hia_request_order_data.xml")!!.readText()
-        XMLUtil.convertStringToJaxb<HIARequestOrderData>(hia)
+        val hia = 
classLoader.getResource("hia_request_order_data.xml")!!.readBytes()
+        XMLUtil.convertBytesToJaxb<HIARequestOrderData>(hia)
     }
 
     @Test
     fun testHiaLoad() {
         val classLoader = ClassLoader.getSystemClassLoader()
         val hia = classLoader.getResource("hia_request.xml")!!
-        val hiaDom = XMLUtil.parseStringIntoDom(hia.readText())
+        val hiaDom = XMLUtil.parseBytesIntoDom(hia.readBytes())
         val x: Element = hiaDom.getElementsByTagNameNS(
             "urn:org:ebics:H004",
             "OrderDetails"
@@ -150,7 +148,7 @@ class EbicsMessagesTest {
                 "ebics_ini_inner_key.xml"
             )
             assertNotNull(file)
-            
XMLUtil.convertStringToJaxb<SignatureTypes.SignaturePubKeyOrderData>(file.readText())
+            
XMLUtil.convertBytesToJaxb<SignatureTypes.SignaturePubKeyOrderData>(file.readBytes())
         }
 
         val modulus = 
jaxbKey.value.signaturePubKeyInfo.pubKeyValue.rsaKeyValue.modulus
@@ -161,8 +159,8 @@ class EbicsMessagesTest {
     @Test
     fun testLoadIniMessage() {
         val classLoader = ClassLoader.getSystemClassLoader()
-        val text = 
classLoader.getResource("ebics_ini_request_sample.xml")!!.readText()
-        XMLUtil.convertStringToJaxb<EbicsUnsecuredRequest>(text)
+        val text = 
classLoader.getResource("ebics_ini_request_sample.xml")!!.readBytes()
+        XMLUtil.convertBytesToJaxb<EbicsUnsecuredRequest>(text)
     }
 
     @Test
@@ -185,14 +183,14 @@ class EbicsMessagesTest {
                 }
             }
         }
-        print(XMLUtil.convertJaxbToString(response))
+        print(XMLUtil.convertJaxbToBytes(response).toString())
     }
 
     @Test
     fun testLoadHpb() {
         val classLoader = ClassLoader.getSystemClassLoader()
-        val text = classLoader.getResource("hpb_request.xml")!!.readText()
-        XMLUtil.convertStringToJaxb<EbicsNpkdRequest>(text)
+        val text = classLoader.getResource("hpb_request.xml")!!.readBytes()
+        XMLUtil.convertBytesToJaxb<EbicsNpkdRequest>(text)
     }
 
     @Test
@@ -248,9 +246,8 @@ class EbicsMessagesTest {
             }
         }
 
-        val str = XMLUtil.convertJaxbToString(htd)
-        println(str)
-        assert(XMLUtil.validateFromString(str))
+        val bytes = XMLUtil.convertJaxbToBytes(htd)
+        assert(XMLUtil.validateFromBytes(bytes))
     }
 
 
@@ -308,9 +305,8 @@ class EbicsMessagesTest {
                 })
         }
 
-        val str = XMLUtil.convertJaxbToString(hkd)
-        println(str)
-        assert(XMLUtil.validateFromString(str))
+        val bytes = XMLUtil.convertJaxbToBytes(hkd)
+        assert(XMLUtil.validateFromBytes(bytes))
     }
 
     @Test
@@ -361,11 +357,11 @@ class EbicsMessagesTest {
             }
         }
 
-        val str = XMLUtil.convertJaxbToString(ebicsRequestObj)
-        val doc = XMLUtil.parseStringIntoDom(str)
+        val str = XMLUtil.convertJaxbToBytes(ebicsRequestObj)
+        val doc = XMLUtil.parseBytesIntoDom(str)
         val pair = CryptoUtil.generateRsaKeyPair(1024)
         XMLUtil.signEbicsDocument(doc, pair.private)
-        val finalStr = XMLUtil.convertDomToString(doc)
-        assert(XMLUtil.validateFromString(finalStr))
+        val bytes = XMLUtil.convertDomToBytes(doc)
+        assert(XMLUtil.validateFromBytes(bytes))
     }
 }
\ No newline at end of file
diff --git a/ebics/src/test/kotlin/EbicsOrderUtilTest.kt 
b/ebics/src/test/kotlin/EbicsOrderUtilTest.kt
index 05f7f72d..c78da738 100644
--- a/ebics/src/test/kotlin/EbicsOrderUtilTest.kt
+++ b/ebics/src/test/kotlin/EbicsOrderUtilTest.kt
@@ -302,7 +302,7 @@ class EbicsOrderUtilTest {
                     </Permission>
                 </UserInfo>
             </HTDResponseOrderData>
-        """.trimIndent()
-        XMLUtil.convertStringToJaxb<HTDResponseOrderData>(orderDataXml);
+        """.trimIndent().toByteArray()
+        XMLUtil.convertBytesToJaxb<HTDResponseOrderData>(orderDataXml);
     }
 }
\ No newline at end of file
diff --git a/ebics/src/test/kotlin/XmlUtilTest.kt 
b/ebics/src/test/kotlin/XmlUtilTest.kt
index b8639d7a..93ca8bf2 100644
--- a/ebics/src/test/kotlin/XmlUtilTest.kt
+++ b/ebics/src/test/kotlin/XmlUtilTest.kt
@@ -37,8 +37,7 @@ class XmlUtilTest {
 
     @Test
     fun deserializeConsecutiveLists() {
-
-        val tmp = XMLUtil.convertStringToJaxb<HTDResponseOrderData>("""
+        val tmp = XMLUtil.convertBytesToJaxb<HTDResponseOrderData>("""
             <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
             <HTDResponseOrderData xmlns="urn:org:ebics:H004">
               <PartnerInfo>
@@ -81,7 +80,7 @@ class XmlUtilTest {
                   <OrderTypes>C54 C53 C52 CCC</OrderTypes>
                 </Permission>
               </UserInfo>
-            </HTDResponseOrderData>""".trimIndent()
+            </HTDResponseOrderData>""".trimIndent().toByteArray()
         )
 
         println(tmp.value.partnerInfo.orderInfoList[0].description)
@@ -90,7 +89,7 @@ class XmlUtilTest {
     @Test
     fun exceptionOnConversion() {
         try {
-            
XMLUtil.convertStringToJaxb<EbicsKeyManagementResponse>("<malformed xml>")
+            XMLUtil.convertBytesToJaxb<EbicsKeyManagementResponse>("<malformed 
xml>".toByteArray())
         } catch (e: javax.xml.bind.UnmarshalException) {
             // just ensuring this is the exception
             println("caught")
@@ -115,12 +114,12 @@ class XmlUtilTest {
 
     @Test
     fun basicSigningTest() {
-        val doc = XMLUtil.parseStringIntoDom("""
+        val doc = XMLUtil.parseBytesIntoDom("""
             <myMessage xmlns:ebics="urn:org:ebics:H004">
                 <ebics:AuthSignature />
                 <foo authenticate="true">Hello World</foo>
             </myMessage>
-        """.trimIndent())
+        """.trimIndent().toByteArray())
         val kpg = KeyPairGenerator.getInstance("RSA")
         kpg.initialize(2048)
         val pair = kpg.genKeyPair()
@@ -155,10 +154,9 @@ class XmlUtilTest {
         }
 
         val signature = signEbicsResponse(response, pair.private)
-        val signatureJaxb = 
XMLUtil.convertStringToJaxb<EbicsResponse>(signature)
+        val signatureJaxb = 
XMLUtil.convertBytesToJaxb<EbicsResponse>(signature)
 
         assertTrue(
-
             XMLUtil.verifyEbicsDocument(
                 XMLUtil.convertJaxbToDocument(signatureJaxb.value),
                 pair.public
@@ -168,13 +166,13 @@ class XmlUtilTest {
 
     @Test
     fun multiAuthSigningTest() {
-        val doc = XMLUtil.parseStringIntoDom("""
+        val doc = XMLUtil.parseBytesIntoDom("""
             <myMessage xmlns:ebics="urn:org:ebics:H004">
                 <ebics:AuthSignature />
                 <foo authenticate="true">Hello World</foo>
                 <bar authenticate="true">Another one!</bar>
             </myMessage>
-        """.trimIndent())
+        """.trimIndent().toByteArray())
         val kpg = KeyPairGenerator.getInstance("RSA")
         kpg.initialize(2048)
         val pair = kpg.genKeyPair()
@@ -185,8 +183,8 @@ class XmlUtilTest {
     @Test
     fun testRefSignature() {
         val classLoader = ClassLoader.getSystemClassLoader()
-        val docText = 
classLoader.getResourceAsStream("signature1/doc.xml")!!.readAllBytes().toString(Charsets.UTF_8)
-        val doc = XMLUtil.parseStringIntoDom(docText)
+        val docText = 
classLoader.getResourceAsStream("signature1/doc.xml")!!.readAllBytes()
+        val doc = XMLUtil.parseBytesIntoDom(docText)
         val keyText = 
classLoader.getResourceAsStream("signature1/public_key.txt")!!.readAllBytes()
         val keyBytes = Base64.getDecoder().decode(keyText)
         val key = CryptoUtil.loadRsaPublicKey(keyBytes)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
index 407e2818..03d37e76 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
@@ -78,11 +78,12 @@ data class FetchContext(
  *         length is zero.  It returns null, if the bank assigned an
  *         error to the EBICS transaction.
  */
-private suspend inline fun downloadHelper(
+private suspend fun <T> downloadHelper(
     ctx: FetchContext,
     lastExecutionTime: Instant? = null,
-    doc: SupportedDocument
-): ByteArray {
+    doc: SupportedDocument,
+    processing: (ByteArray) -> T
+): T? {
     val isEbics3 = doc != SupportedDocument.PAIN_002_LOGS
     val initXml = if (isEbics3) {
         createEbics3DownloadInitialization(
@@ -101,26 +102,15 @@ private suspend inline fun downloadHelper(
             ebics2Req.orderParams
         )
     }
-    logger.trace(initXml)
-    try {
-        return doEbicsDownload(
-            ctx.httpClient,
-            ctx.cfg,
-            ctx.clientKeys,
-            ctx.bankKeys,
-            initXml,
-            isEbics3,
-            tolerateEmptyResult = true
-        )
-    } catch (e: EbicsSideException) {
-        logger.error(e.message)
-        /**
-         * Failing regardless of the error being at the client or at the
-         * bank side.  A client with an unreliable bank is not useful, hence
-         * failing here.
-         */
-        throw e
-    }
+    return ebicsDownload(
+        ctx.httpClient,
+        ctx.cfg,
+        ctx.clientKeys,
+        ctx.bankKeys,
+        initXml,
+        isEbics3,
+        processing
+    )
 }
 
 /**
@@ -282,7 +272,7 @@ private fun ingestDocument(
             val acks = parseCustomerAck(xml)
             for (ack in acks) {
                 when (ack.actionType) {
-                    HacAction.FILE_DOWNLOAD -> logger.trace("$ack")
+                    HacAction.FILE_DOWNLOAD -> logger.debug("$ack")
                     HacAction.ORDER_HAC_FINAL_POS -> {
                         // TODO update pending transaction status
                         logger.debug("$ack")
@@ -374,13 +364,14 @@ private suspend fun fetchDocuments(
             }
             val doc = doc.doc()
             // downloading the content
-            val content = downloadHelper(ctx, lastExecutionTime, doc)
-            if (!content.isEmpty()) {
-                ctx.fileLogger.logFetch(
-                    content,
-                    doc == SupportedDocument.PAIN_002_LOGS
-                )
-                ingestDocuments(db, ctx.cfg.currency, content, doc)
+            downloadHelper(ctx, lastExecutionTime, doc) { content ->
+                if (!content.isEmpty()) {
+                    ctx.fileLogger.logFetch(
+                        content,
+                        doc == SupportedDocument.PAIN_002_LOGS
+                    )
+                    ingestDocuments(db, ctx.cfg.currency, content, doc)
+                }
             }
             true
         } catch (e: Exception) {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
index 75b70c42..bd95543b 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
@@ -156,9 +156,10 @@ suspend fun doKeysRequestAndUpdateState(
         KeysOrderType.HIA -> generateHiaMessage(cfg, privs)
         KeysOrderType.HPB -> generateHpbMessage(cfg, privs)
     }
-    val xml = client.postToBank(cfg.hostBaseUrl, req)
-    if (xml == null) {
-        throw Exception("Could not POST the ${orderType.name} message to the 
bank")
+    val xml = try {
+        client.postToBank(cfg.hostBaseUrl, req)
+    } catch (e: Exception) {
+        throw Exception("Could not POST the ${orderType.name} message to the 
bank at '${cfg.hostBaseUrl}'", e)
     }
     val ebics = parseKeysMgmtResponse(privs.encryption_private_key, xml)
     if (ebics == null) {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
index cc9da79f..9f8a5d9f 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
@@ -40,60 +40,6 @@ import javax.xml.datatype.DatatypeFactory
 
 private val logger: Logger = LoggerFactory.getLogger("libeufin-nexus-ebics2")
 
-/**
- * Convenience function to download via EBICS with a
- * customer message type.
- *
- * @param messageType EBICS 2.x message type.  Defaults
- *        to HTD, to get general information about the EBICS
- *        subscriber.
- * @param cfg configuration handle
- * @param clientKeys client EBICS keys.
- * @param bankKeys bank EBICS keys.
- * @param client HTTP client handle.
- * @return raw XML response, or null upon errors.
- */
-suspend fun doEbicsCustomDownload(
-    messageType: String = "HTD",
-    cfg: EbicsSetupConfig,
-    clientKeys: ClientPrivateKeysFile,
-    bankKeys: BankPublicKeysFile,
-    client: HttpClient
-): ByteArray {
-    val xmlReq = createEbics25DownloadInit(cfg, clientKeys, bankKeys, 
messageType)
-    return doEbicsDownload(client, cfg, clientKeys, bankKeys, xmlReq, false)
-}
-
-/**
- * Request EBICS (2.x) HTD to the bank.  This message type
- * gets the list of bank accounts that are owned by the EBICS
- * client.
- *
- * @param cfg configuration handle
- * @param client client EBICS keys.
- * @param bankKeys bank EBICS keys.
- * @param client HTTP client handle.
- * @return internal representation of the HTD response, or
- *         null in case of errors.
- */
-suspend fun fetchBankAccounts(
-    cfg: EbicsSetupConfig,
-    clientKeys: ClientPrivateKeysFile,
-    bankKeys: BankPublicKeysFile,
-    client: HttpClient
-): HTDResponseOrderData? {
-    val xmlReq = createEbics25DownloadInit(cfg, clientKeys, bankKeys, "HTD")
-    val bytesResp = doEbicsDownload(client, cfg, clientKeys, bankKeys, xmlReq, 
false)
-    val xmlResp = bytesResp.toString(Charsets.UTF_8)
-    return try {
-        logger.debug("Fetched accounts: $bytesResp")
-        XMLUtil.convertStringToJaxb<HTDResponseOrderData>(xmlResp).value
-    } catch (e: Exception) {
-        logger.error("Could not parse the HTD payload, detail: ${e.message}")
-        return null
-    }
-}
-
 /**
  * Creates a EBICS 2.5 download init. message.  So far only used
  * to fetch the PostFinance bank accounts.
@@ -104,7 +50,7 @@ fun createEbics25DownloadInit(
     bankKeys: BankPublicKeysFile,
     orderType: String,
     orderParams: EbicsOrderParams = EbicsStandardOrderParams()
-): String {
+): ByteArray {
     val nonce = getNonce(128)
     val req = EbicsRequest.createForDownloadInitializationPhase(
         cfg.ebicsUserId,
@@ -127,7 +73,7 @@ fun createEbics25DownloadInit(
         clientKeys.authentication_private_key,
         withEbics3 = false
     )
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
@@ -137,16 +83,19 @@ fun createEbics25DownloadInit(
  * @param clientKeys user EBICS private keys.
  * @param transactionId transaction ID of the EBICS communication that
  *        should receive this receipt.
+ * @param success was the download sucessfully processed
  * @return receipt request in XML.
  */
 fun createEbics25DownloadReceiptPhase(
     cfg: EbicsSetupConfig,
     clientKeys: ClientPrivateKeysFile,
-    transactionId: String
-): String {
+    transactionId: String,
+    success: Boolean
+): ByteArray {
     val req = EbicsRequest.createForDownloadReceiptPhase(
         transactionId,
-        cfg.ebicsHostId
+        cfg.ebicsHostId,
+        success
     )
     val doc = XMLUtil.convertJaxbToDocument(req)
     XMLUtil.signEbicsDocument(
@@ -154,7 +103,7 @@ fun createEbics25DownloadReceiptPhase(
         clientKeys.authentication_private_key,
         withEbics3 = false
     )
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
@@ -173,7 +122,7 @@ fun createEbics25DownloadTransferPhase(
     segNumber: Int,
     totalSegments: Int,
     transactionId: String
-): String {
+): ByteArray {
     val req = EbicsRequest.createForDownloadTransferPhase(
         hostID = cfg.ebicsHostId,
         segmentNumber = segNumber,
@@ -186,7 +135,7 @@ fun createEbics25DownloadTransferPhase(
         clientKeys.authentication_private_key,
         withEbics3 = false
     )
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
@@ -202,10 +151,10 @@ fun createEbics25DownloadTransferPhase(
  */
 fun parseKeysMgmtResponse(
     clientEncryptionKey: RSAPrivateCrtKey,
-    xml: String
+    xml: ByteArray
 ): EbicsKeyManagementResponseContent? {
     val jaxb = try {
-        XMLUtil.convertStringToJaxb<EbicsKeyManagementResponse>(xml)
+        XMLUtil.convertBytesToJaxb<EbicsKeyManagementResponse>(xml)
     } catch (e: Exception) {
         tech.libeufin.nexus.logger.error("Could not parse the raw response 
from bank into JAXB.")
         return null
@@ -238,7 +187,7 @@ fun parseKeysMgmtResponse(
  * @param clientKeys set of all the client keys.
  * @return the raw EBICS INI message.
  */
-fun generateIniMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile): String {
+fun generateIniMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile): ByteArray {
     val iniRequest = EbicsUnsecuredRequest.createIni(
         cfg.ebicsHostId,
         cfg.ebicsUserId,
@@ -246,7 +195,7 @@ fun generateIniMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile)
         clientKeys.signature_private_key
     )
     val doc = XMLUtil.convertJaxbToDocument(iniRequest)
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
@@ -257,7 +206,7 @@ fun generateIniMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile)
  * @param clientKeys set of all the client keys.
  * @return the raw EBICS HIA message.
  */
-fun generateHiaMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile): String {
+fun generateHiaMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile): ByteArray {
     val hiaRequest = EbicsUnsecuredRequest.createHia(
         cfg.ebicsHostId,
         cfg.ebicsUserId,
@@ -266,7 +215,7 @@ fun generateHiaMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile)
         clientKeys.encryption_private_key
     )
     val doc = XMLUtil.convertJaxbToDocument(hiaRequest)
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
@@ -276,7 +225,7 @@ fun generateHiaMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile)
  * @param clientKeys set of all the client keys.
  * @return the raw EBICS HPB message.
  */
-fun generateHpbMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile): String {
+fun generateHpbMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile): ByteArray {
     val hpbRequest = EbicsNpkdRequest.createRequest(
         cfg.ebicsHostId,
         cfg.ebicsPartnerId,
@@ -286,7 +235,7 @@ fun generateHpbMessage(cfg: EbicsSetupConfig, clientKeys: 
ClientPrivateKeysFile)
     )
     val doc = XMLUtil.convertJaxbToDocument(hpbRequest)
     XMLUtil.signEbicsDocument(doc, clientKeys.authentication_private_key)
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
index 16e5daba..796e7809 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
@@ -41,16 +41,19 @@ import javax.xml.datatype.DatatypeFactory
  * @param clientKeys subscriber private keys.
  * @param transactionId EBICS transaction ID as assigned by the
  *        bank to any successful transaction.
+ * @param success was the download sucessfully processed
  * @return the raw XML of the EBICS request.
  */
 fun createEbics3DownloadReceiptPhase(
     cfg: EbicsSetupConfig,
     clientKeys: ClientPrivateKeysFile,
-    transactionId: String
-): String {
+    transactionId: String,
+    success: Boolean
+): ByteArray {
     val req = Ebics3Request.createForDownloadReceiptPhase(
         transactionId,
-        cfg.ebicsHostId
+        cfg.ebicsHostId,
+        success
     )
     val doc = XMLUtil.convertJaxbToDocument(req)
     XMLUtil.signEbicsDocument(
@@ -58,7 +61,7 @@ fun createEbics3DownloadReceiptPhase(
         clientKeys.authentication_private_key,
         withEbics3 = true
     )
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
@@ -78,7 +81,7 @@ fun createEbics3DownloadTransferPhase(
     howManySegments: Int,
     segmentNumber: Int,
     transactionId: String
-): String {
+): ByteArray {
     val req = Ebics3Request.createForDownloadTransferPhase(
         cfg.ebicsHostId,
         transactionId,
@@ -91,7 +94,7 @@ fun createEbics3DownloadTransferPhase(
         clientKeys.authentication_private_key,
         withEbics3 = true
     )
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
@@ -108,7 +111,7 @@ fun createEbics3DownloadInitialization(
     bankkeys: BankPublicKeysFile,
     clientKeys: ClientPrivateKeysFile,
     orderParams: Ebics3Request.OrderDetails.BTDOrderParams
-): String {
+): ByteArray {
     val nonce = getNonce(128)
     val req = Ebics3Request.createForDownloadInitializationPhase(
         cfg.ebicsUserId,
@@ -129,7 +132,7 @@ fun createEbics3DownloadInitialization(
         clientKeys.authentication_private_key,
         withEbics3 = true
     )
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
@@ -149,7 +152,7 @@ fun createEbics3RequestForUploadInitialization(
     bankkeys: BankPublicKeysFile,
     clientKeys: ClientPrivateKeysFile,
     orderService: Ebics3Request.OrderDetails.Service
-): String {
+): ByteArray {
     val nonce = getNonce(128)
     val req = Ebics3Request.createForUploadInitializationPhase(
         preparedUploadData.transactionKey,
@@ -174,7 +177,7 @@ fun createEbics3RequestForUploadInitialization(
         clientKeys.authentication_private_key,
         withEbics3 = true
     )
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
@@ -193,7 +196,7 @@ fun createEbics3RequestForUploadTransferPhase(
     clientKeys: ClientPrivateKeysFile,
     transactionId: String,
     uploadData: PreparedUploadData
-): String {
+): ByteArray {
     val chunkIndex = 1 // only 1-chunk communication currently supported.
     val req = Ebics3Request.createForUploadTransferPhase(
         cfg.ebicsHostId,
@@ -207,7 +210,7 @@ fun createEbics3RequestForUploadTransferPhase(
         clientKeys.authentication_private_key,
         withEbics3 = true
     )
-    return XMLUtil.convertDomToString(doc)
+    return XMLUtil.convertDomToBytes(doc)
 }
 
 /**
@@ -230,8 +233,7 @@ suspend fun submitPain001(
     cfg: EbicsSetupConfig,
     clientKeys: ClientPrivateKeysFile,
     bankkeys: BankPublicKeysFile,
-    httpClient: HttpClient,
-    ebicsExtraLog: Boolean = false
+    httpClient: HttpClient
 ) {
     val orderService: Ebics3Request.OrderDetails.Service = 
Ebics3Request.OrderDetails.Service().apply {
         serviceName = "MCT"
@@ -248,7 +250,6 @@ suspend fun submitPain001(
         bankkeys,
         orderService,
         pain001xml.toByteArray(Charsets.UTF_8),
-        ebicsExtraLog
     )
     logger.debug("Payment submitted, report text is: 
${maybeUploaded.reportText}," +
             " EBICS technical code is: ${maybeUploaded.technicalReturnCode}," +
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
index 6e7eff28..58718824 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
@@ -126,33 +126,19 @@ fun decryptAndDecompressPayload(
  * POSTs the EBICS message to the bank.
  *
  * @param URL where the bank serves EBICS requests.
- * @param msg EBICS message as raw string.
- * @return the raw bank response, if the request made it to the
- *         EBICS handler, or null otherwise.
+ * @param msg EBICS message as raw bytes.
+ * @return the raw bank response.
  */
-suspend fun HttpClient.postToBank(bankUrl: String, msg: String): String? {
-    logger.debug("POSTing EBICS to: $bankUrl")
-    val resp: HttpResponse = try {
-        this.post(urlString = bankUrl) {
-            expectSuccess = false // avoids exceptions on non-2xx statuses.
-            contentType(ContentType.Text.Xml)
-            setBody(msg)
-        }
-    }
-    catch (e: Exception) {
-        // hard error (network issue, invalid URL, ..)
-        logger.error("Could not POST to bank at: $bankUrl, detail: 
${e.message}")
-        return null
+suspend fun HttpClient.postToBank(bankUrl: String, msg: ByteArray): ByteArray {
+    logger.debug("POSTing EBICS to '$bankUrl'")
+    val res = post(urlString = bankUrl) {
+        contentType(ContentType.Text.Xml)
+        setBody(msg)
     }
-    // Bank was found, but the EBICS request wasn't served.
-    // Note: EBICS errors get _still_ 200 OK, so here the error
-    // _should_ not be related to EBICS.  404 for a wrong URL
-    // is one example.
-    if (resp.status != HttpStatusCode.OK) {
-        logger.error("Bank was found at $bankUrl, but EBICS wasn't served.  
Response status: ${resp.status}, body: ${resp.bodyAsText()}")
-        return null
+    if (res.status != HttpStatusCode.OK) {
+        throw Exception("Invalid response status: ${res.status}")
     }
-    return resp.bodyAsText()
+    return res.readBytes() // TODO input stream
 }
 
 /**
@@ -244,28 +230,26 @@ fun generateKeysPdf(
  *
  * @param clientKeys client keys, used to sign the request.
  * @param bankKeys bank keys, used to decrypt and validate the response.
- * @param xmlBody raw EBICS request in XML.
- * @param withEbics3 true in case the communication is EBICS 3, false 
otherwise.
- * @param tolerateEbicsReturnCode EBICS technical return code that may be 
accepted
- *                                instead of EBICS_OK.  That is the case of 
EBICS_DOWNLOAD_POSTPROCESS_DONE
- *                                along download receipt phases.
- * @param tolerateBankReturnCode Business return code that may be accepted 
instead of
- *                               EBICS_OK.  Typically, 
EBICS_NO_DOWNLOAD_DATA_AVAILABLE is tolerated
- *                               when asking for new incoming payments.
+ * @param xmlReq raw EBICS request in XML.
+ * @param isEbics3 true in case the communication is EBICS 3, false 
  * @return [EbicsResponseContent] or throws [EbicsSideException]
  */
 suspend fun postEbics(
     client: HttpClient,
     cfg: EbicsSetupConfig,
     bankKeys: BankPublicKeysFile,
-    xmlReq: String,
+    xmlReq: ByteArray,
     isEbics3: Boolean
 ): EbicsResponseContent {
-    val respXml = client.postToBank(cfg.hostBaseUrl, xmlReq)
-        ?: throw EbicsSideException(
+    val respXml = try {
+        client.postToBank(cfg.hostBaseUrl, xmlReq)
+    } catch (e: Exception) {
+        throw EbicsSideException(
             "POSTing to ${cfg.hostBaseUrl} failed",
-            sideEc = EbicsSideError.HTTP_POST_FAILED
+            sideEc = EbicsSideError.HTTP_POST_FAILED,
+            e
         )
+    }
     return parseAndValidateEbicsResponse(
         bankKeys,
         respXml,
@@ -284,8 +268,9 @@ private fun areCodesOk(ebicsResponseContent: 
EbicsResponseContent) =
             ebicsResponseContent.bankReturnCode == EbicsReturnCode.EBICS_OK
 
 /**
- * Collects all the steps of an EBICS download transaction.  Namely,
- * it conducts: init -> transfer -> receipt phases.
+ * Perform an EBICS download transaction.
+ * 
+ * It conducts init -> transfer -> processing -> receipt phases.
  *
  * @param client HTTP client for POSTing to the bank.
  * @param cfg configuration handle.
@@ -293,32 +278,27 @@ private fun areCodesOk(ebicsResponseContent: 
EbicsResponseContent) =
  * @param bankKeys bank EBICS public keys.
  * @param reqXml raw EBICS XML request of the init phase.
  * @param isEbics3 true for EBICS 3, false otherwise.
- * @param tolerateEmptyResult true if the EC EBICS_NO_DOWNLOAD_DATA_AVAILABLE
- *        should be tolerated as the bank-technical error, false otherwise.
- * @return the bank response as an uncompressed [ByteArray], or null if one
- *         error took place.  Definition of error: any EBICS- or bank-technical
- *         EC pairs where at least one is not EBICS_OK, or if 
tolerateEmptyResult
- *         is true, the bank-technical EC EBICS_NO_DOWNLOAD_DATA_AVAILABLE is 
allowed
- *         other than EBICS_OK.  If the request tolerates an empty download 
content,
- *         then the empty array is returned.  The function may throw 
[EbicsAdditionalErrors].
+ * @param processing processing lambda receiving EBICS files as bytes or empty 
bytes if nothing to download.
+ * @return T if the transaction was successful and null if the transaction was 
empty. If the failure is at the EBICS 
+ *         level EbicsSideException is thrown else ités the expection of the 
processing lambda.
  */
-suspend fun doEbicsDownload(
+suspend fun <T> ebicsDownload(
     client: HttpClient,
     cfg: EbicsSetupConfig,
     clientKeys: ClientPrivateKeysFile,
     bankKeys: BankPublicKeysFile,
-    reqXml: String,
+    reqXml: ByteArray,
     isEbics3: Boolean,
-    tolerateEmptyResult: Boolean = false
-): ByteArray {
+    processing: (ByteArray) -> T
+): T {
     val initResp = postEbics(client, cfg, bankKeys, reqXml, isEbics3)
     logger.debug("Download init phase done.  EBICS- and bank-technical codes 
are: ${initResp.technicalReturnCode}, ${initResp.bankReturnCode}")
     if (initResp.technicalReturnCode != EbicsReturnCode.EBICS_OK) {
         throw Exception("Download init phase has EBICS-technical error: 
${initResp.technicalReturnCode}")
     }
-    if (initResp.bankReturnCode == 
EbicsReturnCode.EBICS_NO_DOWNLOAD_DATA_AVAILABLE && tolerateEmptyResult) {
+    if (initResp.bankReturnCode == 
EbicsReturnCode.EBICS_NO_DOWNLOAD_DATA_AVAILABLE) {
         logger.debug("Download content is empty")
-        return ByteArray(0)
+        return processing(ByteArray(0))
     }
     if (initResp.bankReturnCode != EbicsReturnCode.EBICS_OK) {
         throw Exception("Download init phase has bank-technical error: 
${initResp.bankReturnCode}")
@@ -373,10 +353,15 @@ suspend fun doEbicsDownload(
         dataEncryptionInfo,
         ebicsChunks
     )
+    // Process payload
+    val res = runCatching {
+        processing(payloadBytes)
+    }
     // payload reconstructed, receipt to the bank.
+    val success = res.isSuccess
     val receiptXml = if (isEbics3)
-        createEbics3DownloadReceiptPhase(cfg, clientKeys, tId)
-    else createEbics25DownloadReceiptPhase(cfg, clientKeys, tId)
+        createEbics3DownloadReceiptPhase(cfg, clientKeys, tId, success)
+    else createEbics25DownloadReceiptPhase(cfg, clientKeys, tId, success)
 
     // Sending the receipt to the bank.
     postEbics(
@@ -387,7 +372,7 @@ suspend fun doEbicsDownload(
         isEbics3
     )
     // Receipt didn't throw, can now return the payload.
-    return payloadBytes
+    return res.getOrThrow()
 }
 
 /**
@@ -419,8 +404,9 @@ enum class EbicsSideError {
  */
 class EbicsSideException(
     msg: String,
-    val sideEc: EbicsSideError
-) : Exception(msg)
+    val sideEc: EbicsSideError,
+    cause: Exception? = null
+) : Exception(msg, cause)
 
 /**
  * Parses the bank response from the raw XML and verifies
@@ -433,11 +419,11 @@ class EbicsSideException(
  */
 fun parseAndValidateEbicsResponse(
     bankKeys: BankPublicKeysFile,
-    responseStr: String,
+    resp: ByteArray,
     withEbics3: Boolean
 ): EbicsResponseContent {
     val responseDocument = try {
-        XMLUtil.parseStringIntoDom(responseStr)
+        XMLUtil.parseBytesIntoDom(resp)
     } catch (e: Exception) {
         throw EbicsSideException(
             "Bank response apparently invalid",
@@ -455,8 +441,8 @@ fun parseAndValidateEbicsResponse(
         )
     }
     if (withEbics3)
-        return ebics3toInternalRepr(responseStr)
-    return ebics25toInternalRepr(responseStr)
+        return ebics3toInternalRepr(resp)
+    return ebics25toInternalRepr(resp)
 }
 
 /**
@@ -567,7 +553,6 @@ suspend fun doEbicsUpload(
     bankKeys: BankPublicKeysFile,
     orderService: Ebics3Request.OrderDetails.Service,
     payload: ByteArray,
-    extraLog: Boolean = false
 ): EbicsResponseContent {
     val preparedPayload = prepareUploadPayload(cfg, clientKeys, bankKeys, 
payload, isEbics3 = true)
     val initXml = createEbics3RequestForUploadInitialization(
@@ -577,13 +562,12 @@ suspend fun doEbicsUpload(
         clientKeys,
         orderService
     )
-    if (extraLog) logger.debug(initXml)
     val initResp = postEbics( // may throw EbicsEarlyException
-            client,
-            cfg,
-            bankKeys,
-            initXml,
-            isEbics3 = true
+        client,
+        cfg,
+        bankKeys,
+        initXml,
+        isEbics3 = true
     )
     if (!areCodesOk(initResp)) throw EbicsUploadException(
         "EBICS upload init failed",
diff --git a/nexus/src/test/kotlin/Ebics.kt b/nexus/src/test/kotlin/Ebics.kt
index 9c95b118..507820ce 100644
--- a/nexus/src/test/kotlin/Ebics.kt
+++ b/nexus/src/test/kotlin/Ebics.kt
@@ -35,7 +35,7 @@ class Ebics {
     @Test
     fun iniMessage() = conf { config -> 
         val msg = generateIniMessage(config, clientKeys)
-        val ini = XMLUtil.convertStringToJaxb<EbicsUnsecuredRequest>(msg) // 
ensures is valid
+        val ini = XMLUtil.convertBytesToJaxb<EbicsUnsecuredRequest>(msg) // 
ensures is valid
         assertEquals(ini.value.header.static.orderDetails.orderType, "INI") // 
ensures is INI
     }
 
@@ -43,7 +43,7 @@ class Ebics {
     @Test
     fun hiaMessage() = conf { config -> 
         val msg = generateHiaMessage(config, clientKeys)
-        val ini = XMLUtil.convertStringToJaxb<EbicsUnsecuredRequest>(msg) // 
ensures is valid
+        val ini = XMLUtil.convertBytesToJaxb<EbicsUnsecuredRequest>(msg) // 
ensures is valid
         assertEquals(ini.value.header.static.orderDetails.orderType, "HIA") // 
ensures is HIA
     }
 
@@ -51,7 +51,7 @@ class Ebics {
     @Test
     fun hpbMessage() = conf { config -> 
         val msg = generateHpbMessage(config, clientKeys)
-        val ini = XMLUtil.convertStringToJaxb<EbicsUnsecuredRequest>(msg) // 
ensures is valid
+        val ini = XMLUtil.convertBytesToJaxb<EbicsUnsecuredRequest>(msg) // 
ensures is valid
         assertEquals(ini.value.header.static.orderDetails.orderType, "HPB") // 
ensures is HPB
     }
     // POSTs an EBICS message to the mock bank.  Tests
@@ -68,9 +68,19 @@ class Ebics {
         val clientOk = getMockedClient {
             respondOk("Not EBICS anyway.")
         }
-        assertNull(client404.postToBank("http://ignored.example.com/";, 
"ignored"))
-        assertNull(clientNoResponse.postToBank("http://ignored.example.com/";, 
"ignored"))
-        assertNotNull(clientOk.postToBank("http://ignored.example.com/";, 
"ignored"))
+        runCatching {
+            client404.postToBank("http://ignored.example.com/";, 
"ignored".toByteArray())
+        }.run {
+            val exp = exceptionOrNull()!!
+            assertEquals(exp.message, "Invalid response status: 404 Not Found")
+        }
+        runCatching {
+            clientNoResponse.postToBank("http://ignored.example.com/";, 
"ignored".toByteArray())
+        }.run {
+            val exp = exceptionOrNull()!!
+            assertEquals(exp.message, "Network issue.")
+        }
+        clientOk.postToBank("http://ignored.example.com/";, 
"ignored".toByteArray())
     }
 
     // Tests that internal repr. of keys lead to valid PDF.
diff --git a/testbench/src/main/kotlin/Main.kt 
b/testbench/src/main/kotlin/Main.kt
index 73af7dca..4495bde8 100644
--- a/testbench/src/main/kotlin/Main.kt
+++ b/testbench/src/main/kotlin/Main.kt
@@ -53,6 +53,12 @@ fun ask(question: String): String? {
     return readlnOrNull()
 }
 
+fun CliktCommandTestResult.result() {
+    if (statusCode != 0) {
+        print("\u001b[;31mERROR:\n$output\u001b[0m")
+    }
+}
+
 fun CliktCommandTestResult.assertOk(msg: String? = null) {
     println("$output")
     assertEquals(0, statusCode, msg)
@@ -130,27 +136,27 @@ class Cli : CliktCommand("Run integration tests on banks 
provider") {
                 })
                 put("recover", suspend {
                     step("Recover old transactions")
-                    nexusCmd.test("ebics-fetch $ebicsFlags --pinned-start 
2022-01-01 notification").assertOk()
+                    nexusCmd.test("ebics-fetch $ebicsFlags --pinned-start 
2022-01-01 notification").result()
                 })
                 put("fetch", suspend {
                     step("Fetch all documents")
-                    nexusCmd.test("ebics-fetch $ebicsFlags").assertOk()
+                    nexusCmd.test("ebics-fetch $ebicsFlags").result()
                 })
                 put("ack", suspend {
                     step("Fetch CustomerAcknowledgement")
-                    nexusCmd.test("ebics-fetch $ebicsFlags 
acknowledgement").assertOk()
+                    nexusCmd.test("ebics-fetch $ebicsFlags 
acknowledgement").result()
                 })
                 put("status", suspend {
                     step("Fetch CustomerPaymentStatusReport")
-                    nexusCmd.test("ebics-fetch $ebicsFlags status").assertOk()
+                    nexusCmd.test("ebics-fetch $ebicsFlags status").result()
                 })
                 put("notification", suspend {
                     step("Fetch BankToCustomerDebitCreditNotification")
-                    nexusCmd.test("ebics-fetch $ebicsFlags 
notification").assertOk()
+                    nexusCmd.test("ebics-fetch $ebicsFlags 
notification").result()
                 })
                 put("submit", suspend {
                     step("Submit pending transactions")
-                    nexusCmd.test("ebics-submit $ebicsFlags").assertOk()
+                    nexusCmd.test("ebics-submit $ebicsFlags").result()
                 })
                 if (kind.test) {
                     put("reset-keys", suspend {

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]