gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated: start separating out EBICS handling fr


From: gnunet
Subject: [libeufin] branch master updated: start separating out EBICS handling from rest of nexus
Date: Mon, 15 Jun 2020 11:06:30 +0200

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

dold pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new b86b5a2  start separating out EBICS handling from rest of nexus
b86b5a2 is described below

commit b86b5a2fd7e13adeaa5d49f7ee459a584730b30e
Author: Florian Dold <florian.dold@gmail.com>
AuthorDate: Mon Jun 15 14:36:22 2020 +0530

    start separating out EBICS handling from rest of nexus
---
 integration-tests/test-ebics-highlevel.py          |   2 -
 .../src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 127 +-----
 nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt  |   2 +-
 nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt  | 383 +++-------------
 .../tech/libeufin/nexus/{ => ebics}/EbicsClient.kt |   3 +-
 .../kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt | 482 +++++++++++++++++++++
 nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt |   2 +
 .../tech/libeufin/sandbox/EbicsProtocolBackend.kt  |   1 +
 8 files changed, 543 insertions(+), 459 deletions(-)

diff --git a/integration-tests/test-ebics-highlevel.py 
b/integration-tests/test-ebics-highlevel.py
index f78f09f..bbdfe53 100755
--- a/integration-tests/test-ebics-highlevel.py
+++ b/integration-tests/test-ebics-highlevel.py
@@ -162,7 +162,6 @@ assertResponse(
 assertResponse(
     post(
         
f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/fetch-transactions";,
-        json=dict(),
         headers=dict(Authorization=USER_AUTHORIZATION_HEADER),
     )
 )
@@ -210,7 +209,6 @@ assertResponse(
 assertResponse(
     post(
         
f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/fetch-transactions";,
-        json=dict(),
         headers=dict(Authorization=USER_AUTHORIZATION_HEADER),
     )
 )
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
index b57dc26..0494225 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
@@ -100,19 +100,6 @@ fun getEbicsSubscriberDetailsInternal(subscriber: 
EbicsSubscriberEntity): EbicsC
     )
 }
 
-/**
- * Retrieve Ebics subscriber details given a bank connection.
- */
-fun getEbicsSubscriberDetails(bankConnectionId: String): 
EbicsClientSubscriberDetails {
-    val transport = NexusBankConnectionEntity.findById(bankConnectionId)
-    if (transport == null) {
-        throw NexusError(HttpStatusCode.NotFound, "transport not found")
-    }
-    val subscriber = EbicsSubscriberEntity.find { 
EbicsSubscribersTable.nexusBankConnection eq transport.id }.first()
-    // transport exists and belongs to caller.
-    return getEbicsSubscriberDetailsInternal(subscriber)
-}
-
 /**
  * Check if the transaction is already found in the database.
  */
@@ -194,93 +181,6 @@ fun ingestBankMessagesIntoAccount(
     }
 }
 
-private data class EbicsFetchSpec(
-    val orderType: String,
-    val orderParams: EbicsOrderParams
-)
-
-suspend fun fetchEbicsBySpec(fetchSpec: FetchSpecJson, client: HttpClient, 
bankConnectionId: String) {
-    val subscriberDetails = getEbicsSubscriberDetails(bankConnectionId)
-    val specs = mutableListOf<EbicsFetchSpec>()
-    when (fetchSpec) {
-        is FetchSpecLatestJson -> {
-            val p = EbicsStandardOrderParams()
-            when (fetchSpec.level) {
-                FetchLevel.ALL -> {
-                    specs.add(EbicsFetchSpec("C52", p))
-                    specs.add(EbicsFetchSpec("C53", p))
-                }
-                FetchLevel.REPORT -> {
-                    specs.add(EbicsFetchSpec("C52", p))
-                }
-                FetchLevel.STATEMENT -> {
-                    specs.add(EbicsFetchSpec("C53", p))
-                }
-            }
-        }
-    }
-    for (spec in specs) {
-        fetchEbicsC5x(spec.orderType, client, bankConnectionId, 
spec.orderParams, subscriberDetails)
-    }
-}
-
-/**
- * Fetch EBICS C5x and store it locally, but do not update bank accounts.
- */
-private suspend fun fetchEbicsC5x(
-    historyType: String,
-    client: HttpClient,
-    bankConnectionId: String,
-    orderParams: EbicsOrderParams,
-    subscriberDetails: EbicsClientSubscriberDetails
-) {
-    val response = doEbicsDownloadTransaction(
-        client,
-        subscriberDetails,
-        historyType,
-        orderParams
-    )
-    when (historyType) {
-        "C52" -> {
-        }
-        "C53" -> {
-        }
-        else -> {
-            throw NexusError(HttpStatusCode.BadRequest, "history type 
'$historyType' not supported")
-        }
-    }
-    when (response) {
-        is EbicsDownloadSuccessResult -> {
-            response.orderData.unzipWithLambda {
-                logger.debug("Camt entry: ${it.second}")
-                val camt53doc = XMLUtil.parseStringIntoDom(it.second)
-                val msgId = 
camt53doc.pickStringWithRootNs("/*[1]/*[1]/root:GrpHdr/root:MsgId")
-                logger.info("msg id $msgId")
-                transaction {
-                    val conn = 
NexusBankConnectionEntity.findById(bankConnectionId)
-                    if (conn == null) {
-                        throw NexusError(HttpStatusCode.InternalServerError, 
"bank connection missing")
-                    }
-                    val oldMsg = NexusBankMessageEntity.find { 
NexusBankMessagesTable.messageId eq msgId }.firstOrNull()
-                    if (oldMsg == null) {
-                        NexusBankMessageEntity.new {
-                            this.bankConnection = conn
-                            this.code = historyType
-                            this.messageId = msgId
-                            this.message = 
ExposedBlob(it.second.toByteArray(Charsets.UTF_8))
-                        }
-                    }
-                }
-            }
-        }
-        is EbicsDownloadBankErrorResult -> {
-            throw NexusError(
-                HttpStatusCode.BadGateway,
-                response.returnCode.errorCode
-            )
-        }
-    }
-}
 
 /**
  * Create a PAIN.001 XML document according to the input data.
@@ -472,29 +372,4 @@ fun extractUserAndPassword(authorizationHeader: String): 
Pair<String, String> {
         )
     }
     return Pair(username, password)
-}
-
-/**
- * Test HTTP basic auth.  Throws error if password is wrong,
- * and makes sure that the user exists in the system.
- *
- * @param authorization the Authorization:-header line.
- * @return user id
- */
-fun authenticateRequest(request: ApplicationRequest): NexusUserEntity {
-    val authorization = request.headers["Authorization"]
-    val headerLine = if (authorization == null) throw NexusError(
-        HttpStatusCode.BadRequest, "Authentication:-header line not found"
-    ) else authorization
-    val (username, password) = extractUserAndPassword(headerLine)
-    val user = NexusUserEntity.find {
-        NexusUsersTable.id eq username
-    }.firstOrNull()
-    if (user == null) {
-        throw NexusError(HttpStatusCode.Unauthorized, "Unknown user 
'$username'")
-    }
-    if (!CryptoUtil.checkpw(password, user.passwordHash)) {
-        throw NexusError(HttpStatusCode.Forbidden, "Wrong password")
-    }
-    return user
-}
+}
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt
index 0ec844a..c5d7c3a 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt
@@ -28,7 +28,7 @@ import tech.libeufin.util.*
 import java.time.LocalDate
 import java.time.LocalDateTime
 
-data class EbicsBackupRequestJson(
+data class BackupRequestJson(
     val passphrase: String
 )
 
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index e270e28..8ca4b9f 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -67,16 +67,14 @@ import org.jetbrains.exposed.sql.transactions.transaction
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import org.slf4j.event.Level
+import tech.libeufin.nexus.ebics.*
 import tech.libeufin.util.*
 import tech.libeufin.util.CryptoUtil.hashpw
-import tech.libeufin.util.ebics_h004.HTDResponseOrderData
 import java.io.PrintWriter
 import java.io.StringWriter
 import java.net.URLEncoder
 import java.time.Duration
-import java.util.*
 import java.util.zip.InflaterInputStream
-import javax.crypto.EncryptedPrivateKeyInfo
 
 data class NexusError(val statusCode: HttpStatusCode, val reason: String) :
     Exception("$reason (HTTP status $statusCode)")
@@ -135,65 +133,32 @@ suspend inline fun <reified T : Any> 
ApplicationCall.receiveJson(): T {
     }
 }
 
-fun createEbicsBankConnectionFromBackup(
-    bankConnectionName: String,
-    user: NexusUserEntity,
-    passphrase: String?,
-    backup: JsonNode
-) {
-    if (passphrase === null) {
-        throw NexusError(HttpStatusCode.BadRequest, "EBICS backup needs 
passphrase")
-    }
-    val bankConn = NexusBankConnectionEntity.new(bankConnectionName) {
-        owner = user
-        type = "ebics"
-    }
-    val ebicsBackup = jacksonObjectMapper().treeToValue(backup, 
EbicsKeysBackupJson::class.java)
-    val (authKey, encKey, sigKey) = try {
-        Triple(
-            CryptoUtil.decryptKey(
-                EncryptedPrivateKeyInfo(base64ToBytes(ebicsBackup.authBlob)),
-                passphrase
-            ),
-            CryptoUtil.decryptKey(
-                EncryptedPrivateKeyInfo(base64ToBytes(ebicsBackup.encBlob)),
-                passphrase
-            ),
-            CryptoUtil.decryptKey(
-                EncryptedPrivateKeyInfo(base64ToBytes(ebicsBackup.sigBlob)),
-                passphrase
-            )
-        )
-    } catch (e: Exception) {
-        e.printStackTrace()
-        logger.info("Restoring keys failed, probably due to wrong passphrase")
-        throw NexusError(
-            HttpStatusCode.BadRequest,
-            "Bad backup given"
-        )
+/**
+ * Test HTTP basic auth.  Throws error if password is wrong,
+ * and makes sure that the user exists in the system.
+ *
+ * @param authorization the Authorization:-header line.
+ * @return user id
+ */
+fun authenticateRequest(request: ApplicationRequest): NexusUserEntity {
+    val authorization = request.headers["Authorization"]
+    val headerLine = if (authorization == null) throw NexusError(
+        HttpStatusCode.BadRequest, "Authentication:-header line not found"
+    ) else authorization
+    val (username, password) = extractUserAndPassword(headerLine)
+    val user = NexusUserEntity.find {
+        NexusUsersTable.id eq username
+    }.firstOrNull()
+    if (user == null) {
+        throw NexusError(HttpStatusCode.Unauthorized, "Unknown user 
'$username'")
     }
-    try {
-        EbicsSubscriberEntity.new {
-            ebicsURL = ebicsBackup.ebicsURL
-            hostID = ebicsBackup.hostID
-            partnerID = ebicsBackup.partnerID
-            userID = ebicsBackup.userID
-            signaturePrivateKey = ExposedBlob(sigKey.encoded)
-            encryptionPrivateKey = ExposedBlob((encKey.encoded))
-            authenticationPrivateKey = ExposedBlob((authKey.encoded))
-            nexusBankConnection = bankConn
-            ebicsIniState = EbicsInitState.UNKNOWN
-            ebicsHiaState = EbicsInitState.UNKNOWN
-        }
-    } catch (e: Exception) {
-        throw NexusError(
-            HttpStatusCode.BadRequest,
-            "exception: $e"
-        )
+    if (!CryptoUtil.checkpw(password, user.passwordHash)) {
+        throw NexusError(HttpStatusCode.Forbidden, "Wrong password")
     }
-    return
+    return user
 }
 
+
 fun createLoopbackBankConnection(bankConnectionName: String, user: 
NexusUserEntity, data: JsonNode) {
     val bankConn = NexusBankConnectionEntity.new(bankConnectionName) {
         owner = user
@@ -511,12 +476,10 @@ fun serverMain(dbName: String) {
                 return@get
             }
 
-            post("/bank-connection-protocols/ebics/test-host") {
-                val r = call.receiveJson<EbicsHostTestRequest>()
-                val qr = doEbicsHostVersionQuery(client, r.ebicsBaseUrl, 
r.ebicsHostId)
-                call.respond(HttpStatusCode.OK, qr)
-                return@post
+            route("/bank-connection-protocols/ebics") {
+                ebicsBankProtocolRoutes(client)
             }
+
             /**
              * Shows the bank accounts belonging to the requesting user.
              */
@@ -570,24 +533,16 @@ fun serverMain(dbName: String) {
                     }
                     val defaultBankConnection = 
bankAccount.defaultBankConnection
                         ?: throw NexusError(HttpStatusCode.NotFound, "needs a 
default connection")
-                    val subscriberDetails = 
getEbicsSubscriberDetails(defaultBankConnection.id.value)
                     return@transaction object {
                         val pain001document = 
createPain001document(preparedPayment)
                         val bankConnectionType = defaultBankConnection.type
-                        val subscriberDetails = subscriberDetails
+                        val connId = defaultBankConnection.id.value
                     }
                 }
                 // type and name aren't null
                 when (res.bankConnectionType) {
                     "ebics" -> {
-                        logger.debug("Uploading PAIN.001: 
${res.pain001document}")
-                        doEbicsUploadTransaction(
-                            client,
-                            res.subscriberDetails,
-                            "CCT",
-                            res.pain001document.toByteArray(Charsets.UTF_8),
-                            EbicsStandardOrderParams()
-                        )
+                        submitEbicsPaymentInitiation(client, res.connId, 
res.pain001document)
                     }
                     else -> throw NexusError(
                         HttpStatusCode.NotFound,
@@ -771,61 +726,29 @@ fun serverMain(dbName: String) {
                 val resp = transaction {
                     val user = authenticateRequest(call.request)
                     val conn = requireBankConnection(call, "connid")
-                    if (conn.type != "ebics") {
-                        throw NexusError(
-                            HttpStatusCode.BadRequest,
-                            "bank connection is not of type 'ebics' (but 
'${conn.type}')"
-                        )
+                    when (conn.type) {
+                        "ebics" -> {
+                            getEbicsConnectionDetails(conn)
+                        }
+                        else -> {
+                            throw NexusError(
+                                HttpStatusCode.BadRequest,
+                                "bank connection is not of type 'ebics' (but 
'${conn.type}')"
+                            )
+                        }
                     }
-                    val ebicsSubscriber = 
getEbicsSubscriberDetails(conn.id.value)
-                    val mapper = ObjectMapper()
-                    val details = mapper.createObjectNode()
-                    details.put("ebicsUrl", ebicsSubscriber.ebicsUrl)
-                    details.put("ebicsHostId", ebicsSubscriber.hostId)
-                    details.put("partnerId", ebicsSubscriber.partnerId)
-                    details.put("userId", ebicsSubscriber.userId)
-                    val node = mapper.createObjectNode()
-                    node.put("type", conn.type)
-                    node.put("owner", conn.owner.id.value)
-                    node.set<JsonNode>("details", details)
-                    node
                 }
                 call.respond(resp)
             }
 
             post("/bank-connections/{connid}/export-backup") {
-                val body = call.receive<EbicsBackupRequestJson>()
-                val response = transaction {
-                    val user = authenticateRequest(call.request)
+                transaction { authenticateRequest(call.request) }
+                val body = call.receive<BackupRequestJson>()
+                val response = run {
                     val conn = requireBankConnection(call, "connid")
                     when (conn.type) {
                         "ebics" -> {
-                            val subscriber = 
getEbicsSubscriberDetails(conn.id.value)
-                            EbicsKeysBackupJson(
-                                type = "ebics",
-                                userID = subscriber.userId,
-                                hostID = subscriber.hostId,
-                                partnerID = subscriber.partnerId,
-                                ebicsURL = subscriber.ebicsUrl,
-                                authBlob = bytesToBase64(
-                                    CryptoUtil.encryptKey(
-                                        subscriber.customerAuthPriv.encoded,
-                                        body.passphrase
-                                    )
-                                ),
-                                encBlob = bytesToBase64(
-                                    CryptoUtil.encryptKey(
-                                        subscriber.customerEncPriv.encoded,
-                                        body.passphrase
-                                    )
-                                ),
-                                sigBlob = bytesToBase64(
-                                    CryptoUtil.encryptKey(
-                                        subscriber.customerSignPriv.encoded,
-                                        body.passphrase
-                                    )
-                                )
-                            )
+                            exportEbicsKeyBackup(conn.id.value, 
body.passphrase)
                         }
                         else -> {
                             throw NexusError(
@@ -843,62 +766,13 @@ fun serverMain(dbName: String) {
             }
 
             post("/bank-connections/{connid}/connect") {
-                val subscriber = transaction {
-                    val user = authenticateRequest(call.request)
-                    val conn = requireBankConnection(call, "connid")
-                    if (conn.type != "ebics") {
-                        throw NexusError(
-                            HttpStatusCode.BadRequest,
-                            "bank connection is not of type 'ebics' (but 
'${conn.type}')"
-                        )
-                    }
-                    getEbicsSubscriberDetails(conn.id.value)
-                }
-                if (subscriber.bankAuthPub != null && subscriber.bankEncPub != 
null) {
-                    call.respond(object {
-                        val ready = true
-                    })
-                    return@post
-                }
-
-                val iniDone = when (subscriber.ebicsIniState) {
-                    EbicsInitState.NOT_SENT, EbicsInitState.UNKNOWN -> {
-                        val iniResp = doEbicsIniRequest(client, subscriber)
-                        iniResp.bankReturnCode == EbicsReturnCode.EBICS_OK && 
iniResp.technicalReturnCode == EbicsReturnCode.EBICS_OK
-                    }
-                    else -> {
-                        false
-                    }
-                }
-                val hiaDone = when (subscriber.ebicsHiaState) {
-                    EbicsInitState.NOT_SENT, EbicsInitState.UNKNOWN -> {
-                        val hiaResp = doEbicsHiaRequest(client, subscriber)
-                        hiaResp.bankReturnCode == EbicsReturnCode.EBICS_OK && 
hiaResp.technicalReturnCode == EbicsReturnCode.EBICS_OK
-                    }
-                    else -> {
-                        false
-                    }
-                }
-                val hpbData = try {
-                    doEbicsHpbRequest(client, subscriber)
-                } catch (e: EbicsProtocolError) {
-                    logger.warn("failed hpb request", e)
-                    null
+                val conn = transaction {
+                    authenticateRequest(call.request)
+                    requireBankConnection(call, "connid")
                 }
-                transaction {
-                    val conn = requireBankConnection(call, "connid")
-                    val subscriberEntity =
-                        EbicsSubscriberEntity.find { 
EbicsSubscribersTable.nexusBankConnection eq conn.id }.first()
-                    if (iniDone) {
-                        subscriberEntity.ebicsIniState = EbicsInitState.SENT
-                    }
-                    if (hiaDone) {
-                        subscriberEntity.ebicsHiaState = EbicsInitState.SENT
-                    }
-                    if (hpbData != null) {
-                        subscriberEntity.bankAuthenticationPublicKey =
-                            ExposedBlob((hpbData.authenticationPubKey.encoded))
-                        subscriberEntity.bankEncryptionPublicKey = 
ExposedBlob((hpbData.encryptionPubKey.encoded))
+                when (conn.type) {
+                    "ebics" -> {
+                        connectEbics(client, conn.id.value)
                     }
                 }
                 call.respond(object {})
@@ -933,157 +807,6 @@ fun serverMain(dbName: String) {
                 call.respondBytes(ret.msgContent, ContentType("application", 
"xml"))
             }
 
-            post("/bank-connections/{connid}/ebics/send-ini") {
-                val subscriber = transaction {
-                    val user = authenticateRequest(call.request)
-                    val conn = requireBankConnection(call, "connid")
-                    if (conn.type != "ebics") {
-                        throw NexusError(
-                            HttpStatusCode.BadRequest,
-                            "bank connection is not of type 'ebics' (but 
'${conn.type}')"
-                        )
-                    }
-                    getEbicsSubscriberDetails(conn.id.value)
-                }
-                val resp = doEbicsIniRequest(client, subscriber)
-                call.respond(resp)
-            }
-
-            post("/bank-connections/{connid}/ebics/send-hia") {
-                val subscriber = transaction {
-                    val user = authenticateRequest(call.request)
-                    val conn = requireBankConnection(call, "connid")
-                    if (conn.type != "ebics") {
-                        throw NexusError(HttpStatusCode.BadRequest, "bank 
connection is not of type 'ebics'")
-                    }
-                    getEbicsSubscriberDetails(conn.id.value)
-                }
-                val resp = doEbicsHiaRequest(client, subscriber)
-                call.respond(resp)
-            }
-
-            post("/bank-connections/{connid}/ebics/send-hev") {
-                val subscriber = transaction {
-                    val user = authenticateRequest(call.request)
-                    val conn = requireBankConnection(call, "connid")
-                    if (conn.type != "ebics") {
-                        throw NexusError(HttpStatusCode.BadRequest, "bank 
connection is not of type 'ebics'")
-                    }
-                    getEbicsSubscriberDetails(conn.id.value)
-                }
-                val resp = doEbicsHostVersionQuery(client, 
subscriber.ebicsUrl, subscriber.hostId)
-                call.respond(resp)
-            }
-
-            post("/bank-connections/{connid}/ebics/send-hpb") {
-                val subscriberDetails = transaction {
-                    val user = authenticateRequest(call.request)
-                    val conn = requireBankConnection(call, "connid")
-                    if (conn.type != "ebics") {
-                        throw NexusError(HttpStatusCode.BadRequest, "bank 
connection is not of type 'ebics'")
-                    }
-                    getEbicsSubscriberDetails(conn.id.value)
-                }
-                val hpbData = doEbicsHpbRequest(client, subscriberDetails)
-                transaction {
-                    val conn = requireBankConnection(call, "connid")
-                    val subscriber =
-                        EbicsSubscriberEntity.find { 
EbicsSubscribersTable.nexusBankConnection eq conn.id }.first()
-                    subscriber.bankAuthenticationPublicKey = 
ExposedBlob((hpbData.authenticationPubKey.encoded))
-                    subscriber.bankEncryptionPublicKey = 
ExposedBlob((hpbData.encryptionPubKey.encoded))
-                }
-                call.respond(object {})
-            }
-
-            /**
-             * Directly import accounts.  Used for testing.
-             */
-            post("/bank-connections/{connid}/ebics/import-accounts") {
-                val subscriberDetails = transaction {
-                    val user = authenticateRequest(call.request)
-                    val conn = requireBankConnection(call, "connid")
-                    if (conn.type != "ebics") {
-                        throw NexusError(HttpStatusCode.BadRequest, "bank 
connection is not of type 'ebics'")
-                    }
-                    getEbicsSubscriberDetails(conn.id.value)
-                }
-                val response = doEbicsDownloadTransaction(
-                    client, subscriberDetails, "HTD", 
EbicsStandardOrderParams()
-                )
-                when (response) {
-                    is EbicsDownloadBankErrorResult -> {
-                        throw NexusError(
-                            HttpStatusCode.BadGateway,
-                            response.returnCode.errorCode
-                        )
-                    }
-                    is EbicsDownloadSuccessResult -> {
-                        val payload = 
XMLUtil.convertStringToJaxb<HTDResponseOrderData>(
-                            response.orderData.toString(Charsets.UTF_8)
-                        )
-                        transaction {
-                            val conn = requireBankConnection(call, "connid")
-                            payload.value.partnerInfo.accountInfoList?.forEach 
{
-                                val bankAccount = 
NexusBankAccountEntity.new(id = it.id) {
-                                    accountHolder = it.accountHolder ?: 
"NOT-GIVEN"
-                                    iban = 
extractFirstIban(it.accountNumberList)
-                                        ?: throw 
NexusError(HttpStatusCode.NotFound, reason = "bank gave no IBAN")
-                                    bankCode = 
extractFirstBic(it.bankCodeList) ?: throw NexusError(
-                                        HttpStatusCode.NotFound,
-                                        reason = "bank gave no BIC"
-                                    )
-                                    defaultBankConnection = conn
-                                    highestSeenBankMessageId = 0
-                                }
-                            }
-                        }
-                        response.orderData.toString(Charsets.UTF_8)
-                    }
-                }
-                call.respond(object {})
-            }
-
-            post("/bank-connections/{connid}/ebics/download/{msgtype}") {
-                val orderType = 
requireNotNull(call.parameters["msgtype"]).toUpperCase(Locale.ROOT)
-                if (orderType.length != 3) {
-                    throw NexusError(HttpStatusCode.BadRequest, "ebics order 
type must be three characters")
-                }
-                val paramsJson = 
call.receiveOrNull<EbicsStandardOrderParamsDateJson>()
-                val orderParams = if (paramsJson == null) {
-                    EbicsStandardOrderParams()
-                } else {
-                    paramsJson.toOrderParams()
-                }
-                val subscriberDetails = transaction {
-                    val user = authenticateRequest(call.request)
-                    val conn = requireBankConnection(call, "connid")
-                    if (conn.type != "ebics") {
-                        throw NexusError(HttpStatusCode.BadRequest, "bank 
connection is not of type 'ebics'")
-                    }
-                    getEbicsSubscriberDetails(conn.id.value)
-                }
-                val response = doEbicsDownloadTransaction(
-                    client,
-                    subscriberDetails,
-                    orderType,
-                    orderParams
-                )
-                when (response) {
-                    is EbicsDownloadSuccessResult -> {
-                        call.respondText(
-                            response.orderData.toString(Charsets.UTF_8),
-                            ContentType.Text.Plain,
-                            HttpStatusCode.OK
-                        )
-                    }
-                    is EbicsDownloadBankErrorResult -> {
-                        call.respond(
-                            HttpStatusCode.BadGateway,
-                            EbicsErrorJson(EbicsErrorDetailJson("bankError", 
response.returnCode.errorCode))
-                        )
-                    }
-                }
-            }
             post("/facades") {
                 val body = call.receive<FacadeInfo>()
                 val newFacade = transaction {
@@ -1106,10 +829,12 @@ fun serverMain(dbName: String) {
                 return@post
             }
 
-            route("/facades/{fcid}") {
-                route("taler") {
-                    talerFacadeRoutes(this)
-                }
+            route("/bank-connections/{connid}/ebics") {
+                ebicsBankConnectionRoutes(client)
+            }
+
+            route("/facades/{fcid}/taler") {
+                talerFacadeRoutes(this)
             }
             /**
              * Hello endpoint.
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt
similarity index 99%
rename from nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt
rename to nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt
index f23ad84..1cdabc9 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt
@@ -20,11 +20,12 @@
 /**
  * High-level interface for the EBICS protocol.
  */
-package tech.libeufin.nexus
+package tech.libeufin.nexus.ebics
 
 import io.ktor.client.HttpClient
 import io.ktor.client.request.post
 import io.ktor.http.HttpStatusCode
+import tech.libeufin.nexus.NexusError
 import tech.libeufin.util.*
 import java.util.*
 
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
new file mode 100644
index 0000000..5fc83f2
--- /dev/null
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
@@ -0,0 +1,482 @@
+/*
+ * This file is part of LibEuFin.
+ * Copyright (C) 2020 Taler Systems S.A.
+ *
+ * LibEuFin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3, or
+ * (at your option) any later version.
+ *
+ * LibEuFin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with LibEuFin; see the file COPYING.  If not, see
+ * <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Handlers for EBICS-related endpoints offered by the nexus for EBICS
+ * connections.
+ */
+package tech.libeufin.nexus.ebics
+
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
+import io.ktor.application.Application
+import io.ktor.application.ApplicationCall
+import io.ktor.application.call
+import io.ktor.client.HttpClient
+import io.ktor.http.ContentType
+import io.ktor.http.HttpStatusCode
+import io.ktor.request.receive
+import io.ktor.request.receiveOrNull
+import io.ktor.response.respond
+import io.ktor.response.respondText
+import io.ktor.routing.Route
+import io.ktor.routing.Routing
+import io.ktor.routing.post
+import io.ktor.util.pipeline.PipelineContext
+import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
+import org.jetbrains.exposed.sql.statements.api.ExposedBlob
+import org.jetbrains.exposed.sql.transactions.transaction
+import tech.libeufin.nexus.*
+import tech.libeufin.nexus.logger
+import tech.libeufin.util.*
+import tech.libeufin.util.ebics_h004.HTDResponseOrderData
+import java.util.*
+import javax.crypto.EncryptedPrivateKeyInfo
+
+
+private data class EbicsFetchSpec(
+    val orderType: String,
+    val orderParams: EbicsOrderParams
+)
+
+suspend fun fetchEbicsBySpec(fetchSpec: FetchSpecJson, client: HttpClient, 
bankConnectionId: String) {
+    val subscriberDetails = transaction { 
getEbicsSubscriberDetails(bankConnectionId) }
+    val specs = mutableListOf<EbicsFetchSpec>()
+    when (fetchSpec) {
+        is FetchSpecLatestJson -> {
+            val p = EbicsStandardOrderParams()
+            when (fetchSpec.level) {
+                FetchLevel.ALL -> {
+                    specs.add(EbicsFetchSpec("C52", p))
+                    specs.add(EbicsFetchSpec("C53", p))
+                }
+                FetchLevel.REPORT -> {
+                    specs.add(EbicsFetchSpec("C52", p))
+                }
+                FetchLevel.STATEMENT -> {
+                    specs.add(EbicsFetchSpec("C53", p))
+                }
+            }
+        }
+    }
+    for (spec in specs) {
+        fetchEbicsC5x(spec.orderType, client, bankConnectionId, 
spec.orderParams, subscriberDetails)
+    }
+}
+
+/**
+ * Fetch EBICS C5x and store it locally, but do not update bank accounts.
+ */
+private suspend fun fetchEbicsC5x(
+    historyType: String,
+    client: HttpClient,
+    bankConnectionId: String,
+    orderParams: EbicsOrderParams,
+    subscriberDetails: EbicsClientSubscriberDetails
+) {
+    val response = doEbicsDownloadTransaction(
+        client,
+        subscriberDetails,
+        historyType,
+        orderParams
+    )
+    when (historyType) {
+        "C52" -> {
+        }
+        "C53" -> {
+        }
+        else -> {
+            throw NexusError(HttpStatusCode.BadRequest, "history type 
'$historyType' not supported")
+        }
+    }
+    when (response) {
+        is EbicsDownloadSuccessResult -> {
+            response.orderData.unzipWithLambda {
+                logger.debug("Camt entry: ${it.second}")
+                val camt53doc = XMLUtil.parseStringIntoDom(it.second)
+                val msgId = 
camt53doc.pickStringWithRootNs("/*[1]/*[1]/root:GrpHdr/root:MsgId")
+                logger.info("msg id $msgId")
+                transaction {
+                    val conn = 
NexusBankConnectionEntity.findById(bankConnectionId)
+                    if (conn == null) {
+                        throw NexusError(HttpStatusCode.InternalServerError, 
"bank connection missing")
+                    }
+                    val oldMsg = NexusBankMessageEntity.find { 
NexusBankMessagesTable.messageId eq msgId }.firstOrNull()
+                    if (oldMsg == null) {
+                        NexusBankMessageEntity.new {
+                            this.bankConnection = conn
+                            this.code = historyType
+                            this.messageId = msgId
+                            this.message = 
ExposedBlob(it.second.toByteArray(Charsets.UTF_8))
+                        }
+                    }
+                }
+            }
+        }
+        is EbicsDownloadBankErrorResult -> {
+            throw NexusError(
+                HttpStatusCode.BadGateway,
+                response.returnCode.errorCode
+            )
+        }
+    }
+}
+
+
+fun createEbicsBankConnectionFromBackup(
+    bankConnectionName: String,
+    user: NexusUserEntity,
+    passphrase: String?,
+    backup: JsonNode
+) {
+    if (passphrase === null) {
+        throw NexusError(HttpStatusCode.BadRequest, "EBICS backup needs 
passphrase")
+    }
+    val bankConn = NexusBankConnectionEntity.new(bankConnectionName) {
+        owner = user
+        type = "ebics"
+    }
+    val ebicsBackup = jacksonObjectMapper().treeToValue(backup, 
EbicsKeysBackupJson::class.java)
+    val (authKey, encKey, sigKey) = try {
+        Triple(
+            CryptoUtil.decryptKey(
+                EncryptedPrivateKeyInfo(base64ToBytes(ebicsBackup.authBlob)),
+                passphrase
+            ),
+            CryptoUtil.decryptKey(
+                EncryptedPrivateKeyInfo(base64ToBytes(ebicsBackup.encBlob)),
+                passphrase
+            ),
+            CryptoUtil.decryptKey(
+                EncryptedPrivateKeyInfo(base64ToBytes(ebicsBackup.sigBlob)),
+                passphrase
+            )
+        )
+    } catch (e: Exception) {
+        e.printStackTrace()
+        logger.info("Restoring keys failed, probably due to wrong passphrase")
+        throw NexusError(
+            HttpStatusCode.BadRequest,
+            "Bad backup given"
+        )
+    }
+    try {
+        EbicsSubscriberEntity.new {
+            ebicsURL = ebicsBackup.ebicsURL
+            hostID = ebicsBackup.hostID
+            partnerID = ebicsBackup.partnerID
+            userID = ebicsBackup.userID
+            signaturePrivateKey = ExposedBlob(sigKey.encoded)
+            encryptionPrivateKey = ExposedBlob((encKey.encoded))
+            authenticationPrivateKey = ExposedBlob((authKey.encoded))
+            nexusBankConnection = bankConn
+            ebicsIniState = EbicsInitState.UNKNOWN
+            ebicsHiaState = EbicsInitState.UNKNOWN
+        }
+    } catch (e: Exception) {
+        throw NexusError(
+            HttpStatusCode.BadRequest,
+            "exception: $e"
+        )
+    }
+    return
+}
+
+/**
+ * Retrieve Ebics subscriber details given a bank connection.
+ */
+private fun getEbicsSubscriberDetails(bankConnectionId: String): 
EbicsClientSubscriberDetails {
+    val transport = NexusBankConnectionEntity.findById(bankConnectionId)
+    if (transport == null) {
+        throw NexusError(HttpStatusCode.NotFound, "transport not found")
+    }
+    val subscriber = EbicsSubscriberEntity.find { 
EbicsSubscribersTable.nexusBankConnection eq transport.id }.first()
+    // transport exists and belongs to caller.
+    return getEbicsSubscriberDetailsInternal(subscriber)
+}
+
+fun Route.ebicsBankProtocolRoutes(client: HttpClient) {
+    post("test-host") {
+        val r = call.receiveJson<EbicsHostTestRequest>()
+        val qr = doEbicsHostVersionQuery(client, r.ebicsBaseUrl, r.ebicsHostId)
+        call.respond(HttpStatusCode.OK, qr)
+        return@post
+    }
+}
+
+fun Route.ebicsBankConnectionRoutes(client: HttpClient) {
+    post("/send-ini") {
+        val subscriber = transaction {
+            val user = authenticateRequest(call.request)
+            val conn = requireBankConnection(call, "connid")
+            if (conn.type != "ebics") {
+                throw NexusError(
+                    HttpStatusCode.BadRequest,
+                    "bank connection is not of type 'ebics' (but 
'${conn.type}')"
+                )
+            }
+            getEbicsSubscriberDetails(conn.id.value)
+        }
+        val resp = doEbicsIniRequest(client, subscriber)
+        call.respond(resp)
+    }
+
+    post("/send-hia") {
+        val subscriber = transaction {
+            val user = authenticateRequest(call.request)
+            val conn = requireBankConnection(call, "connid")
+            if (conn.type != "ebics") {
+                throw NexusError(HttpStatusCode.BadRequest, "bank connection 
is not of type 'ebics'")
+            }
+            getEbicsSubscriberDetails(conn.id.value)
+        }
+        val resp = doEbicsHiaRequest(client, subscriber)
+        call.respond(resp)
+    }
+
+    post("/send-hev") {
+        val subscriber = transaction {
+            val user = authenticateRequest(call.request)
+            val conn = requireBankConnection(call, "connid")
+            if (conn.type != "ebics") {
+                throw NexusError(HttpStatusCode.BadRequest, "bank connection 
is not of type 'ebics'")
+            }
+            getEbicsSubscriberDetails(conn.id.value)
+        }
+        val resp = doEbicsHostVersionQuery(client, subscriber.ebicsUrl, 
subscriber.hostId)
+        call.respond(resp)
+    }
+
+    post("/send-hpb") {
+        val subscriberDetails = transaction {
+            val user = authenticateRequest(call.request)
+            val conn = requireBankConnection(call, "connid")
+            if (conn.type != "ebics") {
+                throw NexusError(HttpStatusCode.BadRequest, "bank connection 
is not of type 'ebics'")
+            }
+            getEbicsSubscriberDetails(conn.id.value)
+        }
+        val hpbData = doEbicsHpbRequest(client, subscriberDetails)
+        transaction {
+            val conn = requireBankConnection(call, "connid")
+            val subscriber =
+                EbicsSubscriberEntity.find { 
EbicsSubscribersTable.nexusBankConnection eq conn.id }.first()
+            subscriber.bankAuthenticationPublicKey = 
ExposedBlob((hpbData.authenticationPubKey.encoded))
+            subscriber.bankEncryptionPublicKey = 
ExposedBlob((hpbData.encryptionPubKey.encoded))
+        }
+        call.respond(object {})
+    }
+
+    /**
+     * Directly import accounts.  Used for testing.
+     */
+    post("/import-accounts") {
+        val subscriberDetails = transaction {
+            val user = authenticateRequest(call.request)
+            val conn = requireBankConnection(call, "connid")
+            if (conn.type != "ebics") {
+                throw NexusError(HttpStatusCode.BadRequest, "bank connection 
is not of type 'ebics'")
+            }
+            getEbicsSubscriberDetails(conn.id.value)
+        }
+        val response = doEbicsDownloadTransaction(
+            client, subscriberDetails, "HTD", EbicsStandardOrderParams()
+        )
+        when (response) {
+            is EbicsDownloadBankErrorResult -> {
+                throw NexusError(
+                    HttpStatusCode.BadGateway,
+                    response.returnCode.errorCode
+                )
+            }
+            is EbicsDownloadSuccessResult -> {
+                val payload = 
XMLUtil.convertStringToJaxb<HTDResponseOrderData>(
+                    response.orderData.toString(Charsets.UTF_8)
+                )
+                transaction {
+                    val conn = requireBankConnection(call, "connid")
+                    payload.value.partnerInfo.accountInfoList?.forEach {
+                        val bankAccount = NexusBankAccountEntity.new(id = 
it.id) {
+                            accountHolder = it.accountHolder ?: "NOT-GIVEN"
+                            iban = extractFirstIban(it.accountNumberList)
+                                ?: throw NexusError(HttpStatusCode.NotFound, 
reason = "bank gave no IBAN")
+                            bankCode = extractFirstBic(it.bankCodeList) ?: 
throw NexusError(
+                                HttpStatusCode.NotFound,
+                                reason = "bank gave no BIC"
+                            )
+                            defaultBankConnection = conn
+                            highestSeenBankMessageId = 0
+                        }
+                    }
+                }
+                response.orderData.toString(Charsets.UTF_8)
+            }
+        }
+        call.respond(object {})
+    }
+
+    post("/download/{msgtype}") {
+        val orderType = 
requireNotNull(call.parameters["msgtype"]).toUpperCase(Locale.ROOT)
+        if (orderType.length != 3) {
+            throw NexusError(HttpStatusCode.BadRequest, "ebics order type must 
be three characters")
+        }
+        val paramsJson = call.receiveOrNull<EbicsStandardOrderParamsDateJson>()
+        val orderParams = if (paramsJson == null) {
+            EbicsStandardOrderParams()
+        } else {
+            paramsJson.toOrderParams()
+        }
+        val subscriberDetails = transaction {
+            val user = authenticateRequest(call.request)
+            val conn = requireBankConnection(call, "connid")
+            if (conn.type != "ebics") {
+                throw NexusError(HttpStatusCode.BadRequest, "bank connection 
is not of type 'ebics'")
+            }
+            getEbicsSubscriberDetails(conn.id.value)
+        }
+        val response = doEbicsDownloadTransaction(
+            client,
+            subscriberDetails,
+            orderType,
+            orderParams
+        )
+        when (response) {
+            is EbicsDownloadSuccessResult -> {
+                call.respondText(
+                    response.orderData.toString(Charsets.UTF_8),
+                    ContentType.Text.Plain,
+                    HttpStatusCode.OK
+                )
+            }
+            is EbicsDownloadBankErrorResult -> {
+                call.respond(
+                    HttpStatusCode.BadGateway,
+                    EbicsErrorJson(EbicsErrorDetailJson("bankError", 
response.returnCode.errorCode))
+                )
+            }
+        }
+    }
+}
+
+fun exportEbicsKeyBackup(bankConnectionId: String, passphrase: String): Any {
+    val subscriber = transaction { getEbicsSubscriberDetails(bankConnectionId) 
}
+    return EbicsKeysBackupJson(
+        type = "ebics",
+        userID = subscriber.userId,
+        hostID = subscriber.hostId,
+        partnerID = subscriber.partnerId,
+        ebicsURL = subscriber.ebicsUrl,
+        authBlob = bytesToBase64(
+            CryptoUtil.encryptKey(
+                subscriber.customerAuthPriv.encoded,
+                passphrase
+            )
+        ),
+        encBlob = bytesToBase64(
+            CryptoUtil.encryptKey(
+                subscriber.customerEncPriv.encoded,
+                passphrase
+            )
+        ),
+        sigBlob = bytesToBase64(
+            CryptoUtil.encryptKey(
+                subscriber.customerSignPriv.encoded,
+                passphrase
+            )
+        )
+    )
+}
+
+suspend fun submitEbicsPaymentInitiation(client: HttpClient, connId: String, 
pain001Document: String) {
+    val ebicsSubscriberDetails = transaction { 
getEbicsSubscriberDetails(connId) }
+    logger.debug("Uploading PAIN.001: ${pain001Document}")
+    doEbicsUploadTransaction(
+        client,
+        ebicsSubscriberDetails,
+        "CCT",
+        pain001Document.toByteArray(Charsets.UTF_8),
+        EbicsStandardOrderParams()
+    )
+}
+
+fun getEbicsConnectionDetails(conn: NexusBankConnectionEntity): Any {
+    val ebicsSubscriber = transaction { 
getEbicsSubscriberDetails(conn.id.value) }
+    val mapper = ObjectMapper()
+    val details = mapper.createObjectNode()
+    details.put("ebicsUrl", ebicsSubscriber.ebicsUrl)
+    details.put("ebicsHostId", ebicsSubscriber.hostId)
+    details.put("partnerId", ebicsSubscriber.partnerId)
+    details.put("userId", ebicsSubscriber.userId)
+    val node = mapper.createObjectNode()
+    node.put("type", conn.type)
+    node.put("owner", conn.owner.id.value)
+    node.set<JsonNode>("details", details)
+    return node
+}
+
+suspend fun connectEbics(client: HttpClient, connId: String) {
+    val subscriber = transaction { getEbicsSubscriberDetails(connId) }
+    if (subscriber.bankAuthPub != null && subscriber.bankEncPub != null) {
+        return
+    }
+    val iniDone = when (subscriber.ebicsIniState) {
+        EbicsInitState.NOT_SENT, EbicsInitState.UNKNOWN -> {
+            val iniResp = doEbicsIniRequest(client, subscriber)
+            iniResp.bankReturnCode == EbicsReturnCode.EBICS_OK && 
iniResp.technicalReturnCode == EbicsReturnCode.EBICS_OK
+        }
+        else -> {
+            false
+        }
+    }
+    val hiaDone = when (subscriber.ebicsHiaState) {
+        EbicsInitState.NOT_SENT, EbicsInitState.UNKNOWN -> {
+            val hiaResp = doEbicsHiaRequest(client, subscriber)
+            hiaResp.bankReturnCode == EbicsReturnCode.EBICS_OK && 
hiaResp.technicalReturnCode == EbicsReturnCode.EBICS_OK
+        }
+        else -> {
+            false
+        }
+    }
+    val hpbData = try {
+        doEbicsHpbRequest(client, subscriber)
+    } catch (e: EbicsProtocolError) {
+        logger.warn("failed hpb request", e)
+        null
+    }
+    transaction {
+        val conn = NexusBankConnectionEntity.findById(connId)
+        if (conn == null) {
+            throw NexusError(HttpStatusCode.NotFound, "bank connection 
'$connId' not found")
+        }
+        val subscriberEntity =
+            EbicsSubscriberEntity.find { 
EbicsSubscribersTable.nexusBankConnection eq conn.id }.first()
+        if (iniDone) {
+            subscriberEntity.ebicsIniState = EbicsInitState.SENT
+        }
+        if (hiaDone) {
+            subscriberEntity.ebicsHiaState = EbicsInitState.SENT
+        }
+        if (hpbData != null) {
+            subscriberEntity.bankAuthenticationPublicKey =
+                ExposedBlob((hpbData.authenticationPubKey.encoded))
+            subscriberEntity.bankEncryptionPublicKey = 
ExposedBlob((hpbData.encryptionPubKey.encoded))
+        }
+    }
+}
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
index 33a7153..85e9cc8 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
@@ -38,6 +38,7 @@ import org.jetbrains.exposed.dao.Entity
 import org.jetbrains.exposed.dao.id.IdTable
 import org.jetbrains.exposed.sql.*
 import org.jetbrains.exposed.sql.transactions.transaction
+import tech.libeufin.nexus.ebics.doEbicsUploadTransaction
 import tech.libeufin.util.*
 import kotlin.math.abs
 import kotlin.math.min
@@ -378,6 +379,7 @@ private suspend fun talerAddIncoming(call: 
ApplicationCall): Unit {
 }
 
 // submits ALL the prepared payments from ALL the Taler facades.
+// FIXME(dold): This should not be done here.
 suspend fun submitPreparedPaymentsViaEbics() {
     data class EbicsSubmission(
         val subscriberDetails: EbicsClientSubscriberDetails,
diff --git 
a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
index c558309..f827ce7 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -828,6 +828,7 @@ private fun 
handleEbicsDownloadTransactionInitialization(requestContext: Request
         "HKD" -> handleEbicsHkd(requestContext)
         /* Temporarily handling C52/C53 with same logic */
         "C53" -> handleEbicsC53(requestContext)
+        "C52" -> handleEbicsC53(requestContext)
         "TSD" -> handleEbicsTSD(requestContext)
         "PTK" -> handleEbicsPTK(requestContext)
         else -> throw EbicsInvalidXmlError()

-- 
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]