summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine A <>2024-03-11 17:14:24 +0100
committerAntoine A <>2024-03-11 17:14:24 +0100
commit0d62875dd2287857c5da172dcb3301062880810a (patch)
tree6880751fb475a9f0db8dcedbf8ef8e78a299f729
parent861389ea98224e86f28fdf06570a260c3ae12f90 (diff)
downloadlibeufin-0d62875dd2287857c5da172dcb3301062880810a.tar.gz
libeufin-0d62875dd2287857c5da172dcb3301062880810a.tar.bz2
libeufin-0d62875dd2287857c5da172dcb3301062880810a.zip
Support both 2.5 and 3 EBICS version for download
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt5
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt2
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt9
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/XMLUtil.kt10
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt (renamed from nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt)146
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt90
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsDialect.kt49
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt (renamed from nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt)8
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsOrder.kt59
9 files changed, 160 insertions, 218 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
index c74ea2f1..ca7b9116 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
@@ -285,14 +285,13 @@ private suspend fun fetchDocuments(
}
// downloading the content
val doc = doc.doc()
- val (orderType, service) = downloadDocService(doc)
+ val order = downloadDocService(doc, doc == SupportedDocument.PAIN_002_LOGS)
ebicsDownload(
ctx.httpClient,
ctx.cfg,
ctx.clientKeys,
ctx.bankKeys,
- orderType,
- service,
+ order,
lastExecutionTime,
null
) { stream ->
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
index 230b2cc9..b0cbc299 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
@@ -116,7 +116,7 @@ suspend fun doKeysRequestAndUpdateState(
KeysOrderType.HIA -> impl.HIA()
KeysOrderType.HPB -> impl.HPB()
}
- val xml = client.postToBank(cfg.hostBaseUrl, req)
+ val xml = client.postToBank(cfg.hostBaseUrl, req, "$orderType")
val resp = Ebics3KeyMng.parseResponse(xml, privs.encryption_private_key)
when (orderType) {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
index ea20967e..82c1a459 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
@@ -25,7 +25,7 @@ import com.github.ajalt.clikt.parameters.options.*
import io.ktor.client.*
import kotlinx.coroutines.*
import tech.libeufin.common.*
-import tech.libeufin.nexus.ebics.submitPain001
+import tech.libeufin.nexus.ebics.*
import java.time.*
import java.util.*
@@ -85,12 +85,13 @@ private suspend fun submitInitiatedPayment(
wireTransferSubject = payment.wireTransferSubject
)
ctx.fileLogger.logSubmit(xml)
- return submitPain001(
- xml,
+ return doEbicsUpload(
+ ctx.httpClient,
ctx.cfg,
ctx.clientPrivateKeysFile,
ctx.bankPublicKeysFile,
- ctx.httpClient
+ uploadPaymentService(),
+ xml
)
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/XMLUtil.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/XMLUtil.kt
index bbae68ae..2893f367 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/XMLUtil.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/XMLUtil.kt
@@ -113,11 +113,10 @@ object XMLUtil {
fun signEbicsDocument(
doc: Document,
signingPriv: PrivateKey,
- withEbics3: Boolean = false
+ schema: String
) {
- val ns = if (withEbics3) "urn:org:ebics:H005" else "urn:org:ebics:H004"
val authSigNode = XPathFactory.newInstance().newXPath()
- .evaluate("/*[1]/$ns:AuthSignature", doc, XPathConstants.NODE)
+ .evaluate("/*[1]/urn:org:ebics:$schema:AuthSignature", doc, XPathConstants.NODE)
if (authSigNode !is Node)
throw java.lang.Exception("no AuthSignature")
val fac = XMLSignatureFactory.getInstance("DOM")
@@ -150,12 +149,11 @@ object XMLUtil {
fun verifyEbicsDocument(
doc: Document,
signingPub: PublicKey,
- withEbics3: Boolean = false
+ schema: String
): Boolean {
val doc2: Document = doc.cloneNode(true) as Document
- val ns = if (withEbics3) "urn:org:ebics:H005" else "urn:org:ebics:H004"
val authSigNode = XPathFactory.newInstance().newXPath()
- .evaluate("/*[1]/$ns:AuthSignature", doc2, XPathConstants.NODE)
+ .evaluate("/*[1]/urn:org:ebics:$schema:AuthSignature", doc2, XPathConstants.NODE)
if (authSigNode !is Node)
throw java.lang.Exception("no AuthSignature")
val sigEl = doc2.createElementNS("http://www.w3.org/2000/09/xmldsig#", "ds:Signature")
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt
index 5acba13b..356d4b96 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt
@@ -36,59 +36,16 @@ import java.security.interfaces.*
fun Instant.xmlDate(): String = DateTimeFormatter.ISO_DATE.withZone(ZoneId.of("UTC")).format(this)
fun Instant.xmlDateTime(): String = DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("UTC")).format(this)
-// TODO WIP
-fun iniRequest(
- cfg: EbicsSetupConfig,
- clientKeys: ClientPrivateKeysFile
-): ByteArray {
- val temp = XmlBuilder.toBytes("ns2:SignaturePubKeyOrderData") {
- attr("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
- attr("xmlns:ns2", "http://www.ebics.org/S001")
- el("ns2:SignaturePubKeyInfo") {
- el("ns2:PubKeyValue") {
- el("ds:RSAKeyValue") {
- el("ds:Modulus", clientKeys.signature_private_key.modulus.encodeBase64())
- el("ds:Exponent", clientKeys.signature_private_key.publicExponent.encodeBase64())
- }
- }
- el("ns2:SignatureVersion", "A006")
- }
- el("ns2:PartnerID", cfg.ebicsPartnerId)
- el("ns2:UserID", cfg.ebicsUserId)
- }
- // TODO in ebics:H005 we MUST use x509 certificates ...
- println(temp)
- val inner = temp.inputStream().deflate().encodeBase64()
- val doc = XmlBuilder.toDom("ebicsUnsecuredRequest", "urn:org:ebics:H005") {
- attr("http://www.w3.org/2000/xmlns/", "xmlns", "urn:org:ebics:H005")
- attr("http://www.w3.org/2000/xmlns/", "xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
- attr("Version", "H005")
- attr("Revision", "1")
- el("header") {
- attr("authenticate", "true")
- el("static") {
- el("HostID", cfg.ebicsHostId)
- el("PartnerID", cfg.ebicsPartnerId)
- el("UserID", cfg.ebicsUserId)
- el("OrderDetails/AdminOrderType", "INI")
- el("SecurityMedium", "0200")
- }
- el("mutable")
- }
- el("body/DataTransfer/OrderData", inner)
- }
- return XMLUtil.convertDomToBytes(doc)
-}
-
-/** EBICS 3 protocol for business transactions */
-class Ebics3BTS(
- private val cfg: EbicsSetupConfig,
- private val bankKeys: BankPublicKeysFile,
- private val clientKeys: ClientPrivateKeysFile
+/** EBICS protocol for business transactions */
+class EbicsBTS(
+ val cfg: EbicsSetupConfig,
+ val bankKeys: BankPublicKeysFile,
+ val clientKeys: ClientPrivateKeysFile,
+ val order: EbicsOrder
) {
/* ----- Download ----- */
- fun downloadInitialization(orderType: String, service: Ebics3Service?, startDate: Instant?, endDate: Instant?): ByteArray {
+ fun downloadInitialization(startDate: Instant?, endDate: Instant?): ByteArray {
val nonce = getNonce(128)
return signedRequest {
el("header") {
@@ -102,28 +59,42 @@ class Ebics3BTS(
// SystemID
// Product
el("OrderDetails") {
- el("AdminOrderType", orderType)
- if (orderType == "BTD") {
- el("BTDOrderParams") {
- if (service != null) {
- el("Service") {
- el("ServiceName", service.name)
- el("Scope", service.scope)
- if (service.container != null) {
- el("Container") {
- attr("containerType", service.container)
- }
- }
- el("MsgName") {
- attr("version", service.messageVersion)
- text(service.messageName)
+ when (order) {
+ is EbicsOrder.V2_5 -> {
+ el("OrderType", order.type)
+ el("OrderAttribute", order.attribute)
+ el("StandardOrderParams") {
+ if (startDate != null) {
+ el("DateRange") {
+ el("Start", startDate.xmlDate())
+ el("End", (endDate ?: Instant.now()).xmlDate())
}
}
}
- if (startDate != null) {
- el("DateRange") {
- el("Start", startDate.xmlDate())
- el("End", (endDate ?: Instant.now()).xmlDate())
+ }
+ is EbicsOrder.V3 -> {
+ el("AdminOrderType", order.type)
+ if (order.type == "BTD") {
+ el("BTDOrderParams") {
+ el("Service") {
+ el("ServiceName", order.name!!)
+ el("Scope", order.scope!!)
+ if (order.container != null) {
+ el("Container") {
+ attr("containerType", order.container)
+ }
+ }
+ el("MsgName") {
+ attr("version", order.messageVersion!!)
+ text(order.messageName!!)
+ }
+ }
+ if (startDate != null) {
+ el("DateRange") {
+ el("Start", startDate.xmlDate())
+ el("End", (endDate ?: Instant.now()).xmlDate())
+ }
+ }
}
}
}
@@ -187,7 +158,7 @@ class Ebics3BTS(
/* ----- Upload ----- */
- fun uploadInitialization(service: Ebics3Service, preparedUploadData: PreparedUploadData): ByteArray {
+ fun uploadInitialization(preparedUploadData: PreparedUploadData): ByteArray {
val nonce = getNonce(128)
return signedRequest {
el("header") {
@@ -201,17 +172,24 @@ class Ebics3BTS(
// SystemID
// Product
el("OrderDetails") {
- el("AdminOrderType", "BTU")
- el("BTUOrderParams") {
- el("Service") {
- el("ServiceName", service.name)
- el("Scope", service.scope)
- el("MsgName") {
- attr("version", service.messageVersion)
- text(service.messageName)
+ when (order) {
+ is EbicsOrder.V2_5 -> {
+ // TODO
+ }
+ is EbicsOrder.V3 -> {
+ el("AdminOrderType", order.type)
+ el("BTUOrderParams") {
+ el("Service") {
+ el("ServiceName", order.name!!)
+ el("Scope", order.scope!!)
+ el("MsgName") {
+ attr("version", order.messageVersion!!)
+ text(order.messageName!!)
+ }
+ }
+ el("SignatureFlag", "true")
}
}
- el("SignatureFlag", "true")
}
}
bankDigest()
@@ -274,19 +252,19 @@ class Ebics3BTS(
/* ----- Helpers ----- */
- /** Generate a signed H005 ebicsRequest */
+ /** Generate a signed ebicsRequest */
private fun signedRequest(lambda: XmlBuilder.() -> Unit): ByteArray {
- val doc = XmlBuilder.toDom("ebicsRequest", "urn:org:ebics:H005") {
- attr("http://www.w3.org/2000/xmlns/", "xmlns", "urn:org:ebics:H005")
+ val doc = XmlBuilder.toDom("ebicsRequest", "urn:org:ebics:${order.schema}") {
+ attr("http://www.w3.org/2000/xmlns/", "xmlns", "urn:org:ebics:${order.schema}")
attr("http://www.w3.org/2000/xmlns/", "xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
- attr("Version", "H005")
+ attr("Version", order.schema)
attr("Revision", "1")
lambda()
}
XMLUtil.signEbicsDocument(
doc,
clientKeys.authentication_private_key,
- withEbics3 = true
+ order.schema
)
return XMLUtil.convertDomToBytes(doc)
}
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 69e6da19..74c1dd32 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
@@ -109,45 +109,45 @@ sealed class EbicsError(msg: String, cause: Throwable? = null): Exception(msg, c
* @param msg EBICS message as raw bytes.
* @return the raw bank response.
*/
-suspend fun HttpClient.postToBank(bankUrl: String, msg: ByteArray): Document {
+suspend fun HttpClient.postToBank(bankUrl: String, msg: ByteArray, phase: String): Document {
val res = try {
post(urlString = bankUrl) {
contentType(ContentType.Text.Xml)
setBody(msg)
}
} catch (e: Exception) {
- throw EbicsError.Transport("failed to contact bank", e)
+ throw EbicsError.Transport("$phase: failed to contact bank", e)
}
if (res.status != HttpStatusCode.OK) {
- throw EbicsError.Transport("bank HTTP error: ${res.status}")
+ throw EbicsError.Transport("$phase: bank HTTP error: ${res.status}")
}
try {
return XMLUtil.parseIntoDom(res.bodyAsChannel().toInputStream())
} catch (e: SAXException) {
- throw EbicsError.Protocol("invalid XML bank reponse", e)
+ throw EbicsError.Protocol("$phase: invalid XML bank reponse", e)
} catch (e: Exception) {
- throw EbicsError.Transport("failed read bank response", e)
+ throw EbicsError.Transport("$phase: failed read bank response", e)
}
}
-suspend fun postBTS(
+
+suspend fun EbicsBTS.postBTS(
client: HttpClient,
- cfg: EbicsSetupConfig,
- bankKeys: BankPublicKeysFile,
- xmlReq: ByteArray
+ xmlReq: ByteArray,
+ phase: String,
): EbicsResponse<BTSResponse> {
- val doc = client.postToBank(cfg.hostBaseUrl, xmlReq)
+ val doc = client.postToBank(cfg.hostBaseUrl, xmlReq, phase)
if (!XMLUtil.verifyEbicsDocument(
doc,
bankKeys.bank_authentication_public_key,
- true
+ order.schema
)) {
- throw EbicsError.Protocol("bank signature did not verify")
+ throw EbicsError.Protocol("$phase: bank signature did not verify")
}
try {
- return Ebics3BTS.parseResponse(doc)
+ return EbicsBTS.parseResponse(doc)
} catch (e: Exception) {
- throw EbicsError.Protocol("invalid ebics response", e)
+ throw EbicsError.Protocol("$phase: invalid ebics response", e)
}
}
@@ -170,13 +170,12 @@ suspend fun ebicsDownload(
cfg: EbicsSetupConfig,
clientKeys: ClientPrivateKeysFile,
bankKeys: BankPublicKeysFile,
- orderType: String,
- service: Ebics3Service?,
+ order: EbicsOrder,
startDate: Instant?,
endDate: Instant?,
processing: (InputStream) -> Unit,
) = coroutineScope {
- val impl = Ebics3BTS(cfg, bankKeys, clientKeys)
+ val impl = EbicsBTS(cfg, bankKeys, clientKeys, order)
val parentScope = this
// We need to run the logic in a non-cancelable context because we need to send
@@ -185,8 +184,8 @@ suspend fun ebicsDownload(
// TODO find a way to cancel the pending transaction ?
withContext(NonCancellable) {
// Init phase
- val initReq = impl.downloadInitialization(orderType, service, startDate, endDate)
- val initResp = postBTS(client, cfg, bankKeys, initReq)
+ val initReq = impl.downloadInitialization(startDate, endDate)
+ val initResp = impl.postBTS(client, initReq, "Download init phase")
if (initResp.bankCode == EbicsReturnCode.EBICS_NO_DOWNLOAD_DATA_AVAILABLE) {
logger.debug("Download content is empty")
return@withContext
@@ -210,7 +209,7 @@ suspend fun ebicsDownload(
/** Send download receipt */
suspend fun receipt(success: Boolean) {
val xml = impl.downloadReceipt(tId, success)
- postBTS(client, cfg, bankKeys, xml).okOrFail("Download receipt phase")
+ impl.postBTS(client, xml, "Download receipt phase").okOrFail("Download receipt phase")
}
/** Throw if parent scope have been canceled */
suspend fun checkCancellation() {
@@ -227,7 +226,7 @@ suspend fun ebicsDownload(
for (x in 2 .. howManySegments) {
checkCancellation()
val transReq = impl.downloadTransfer(x, howManySegments, tId)
- val transResp = postBTS(client, cfg, bankKeys, transReq).okOrFail("Download transfer phase")
+ val transResp = impl.postBTS(client, transReq, "Download transfer phase").okOrFail("Download transfer phase")
val chunk = requireNotNull(transResp.payloadChunk) {
"Download transfer phase: missing encrypted chunk"
}
@@ -329,23 +328,23 @@ suspend fun doEbicsUpload(
cfg: EbicsSetupConfig,
clientKeys: ClientPrivateKeysFile,
bankKeys: BankPublicKeysFile,
- service: Ebics3Service,
+ order: EbicsOrder,
payload: ByteArray,
): String = withContext(NonCancellable) {
- val impl = Ebics3BTS(cfg, bankKeys, clientKeys)
+ val impl = EbicsBTS(cfg, bankKeys, clientKeys, order)
// TODO use a lambda and pass the order detail there for atomicity ?
val preparedPayload = prepareUploadPayload(cfg, clientKeys, bankKeys, payload)
// Init phase
- val initXml = impl.uploadInitialization(service, preparedPayload)
- val initResp = postBTS(client, cfg, bankKeys, initXml).okOrFail("Upload init phase")
+ val initXml = impl.uploadInitialization(preparedPayload)
+ val initResp = impl.postBTS(client, initXml, "Upload init phase").okOrFail("Upload init phase")
val tId = requireNotNull(initResp.transactionID) {
"Upload init phase: missing transaction ID"
}
// Transfer phase
val transferXml = impl.uploadTransfer(tId, preparedPayload)
- val transferResp = postBTS(client, cfg, bankKeys, transferXml).okOrFail("Upload transfer phase")
+ val transferResp = impl.postBTS(client, transferXml, "Upload transfer phase").okOrFail("Upload transfer phase")
val orderId = requireNotNull(transferResp.orderID) {
"Upload transfer phase: missing order ID"
}
@@ -374,45 +373,6 @@ class DataEncryptionInfo(
val bankPubDigest: ByteArray
)
-/**
- * Collects all the steps to prepare the submission of a pain.001
- * document to the bank, and finally send it. Indirectly throws
- * [EbicsSideException] or [EbicsUploadException]. The first means
- * that the bank sent an invalid response or signature, the second
- * that a proper EBICS or business error took place. The caller must
- * catch those exceptions and decide the retry policy.
- *
- * @param pain001xml pain.001 document in XML. The caller should
- * ensure its validity.
- * @param cfg configuration handle.
- * @param clientKeys client private keys.
- * @param bankkeys bank public keys.
- * @param httpClient HTTP client to connect to the bank.
- */
-suspend fun submitPain001(
- pain001xml: ByteArray,
- cfg: EbicsSetupConfig,
- clientKeys: ClientPrivateKeysFile,
- bankkeys: BankPublicKeysFile,
- httpClient: HttpClient
-): String {
- val service = Ebics3Service(
- name = "MCT",
- scope = "CH",
- messageName = "pain.001",
- messageVersion = "09",
- container = null
- )
- return doEbicsUpload(
- httpClient,
- cfg,
- clientKeys,
- bankkeys,
- service,
- pain001xml,
- )
-}
-
class EbicsResponse<T>(
val technicalCode: EbicsReturnCode,
val bankCode: EbicsReturnCode,
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsDialect.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsDialect.kt
deleted file mode 100644
index 5d672de0..00000000
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsDialect.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * This file is part of LibEuFin.
- * Copyright (C) 2024 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/>
- */
-package tech.libeufin.nexus.ebics
-
-// We will support more dialect in the future
-
-data class Ebics3Service(
- val name: String,
- val scope: String,
- val messageName: String,
- val messageVersion: String,
- val container: String?
-)
-
-fun downloadDocService(doc: SupportedDocument): Pair<String, Ebics3Service?> {
- return when (doc) {
- SupportedDocument.PAIN_002 -> Pair("BTD", Ebics3Service("PSR", "CH", "pain.002", "10", "ZIP"))
- SupportedDocument.CAMT_052 -> Pair("BTD", Ebics3Service("STM", "CH", "camt.052", "08", "ZIP"))
- SupportedDocument.CAMT_053 -> Pair("BTD", Ebics3Service("EOP", "CH", "camt.053", "08", "ZIP"))
- SupportedDocument.CAMT_054 -> Pair("BTD", Ebics3Service("REP", "CH", "camt.054", "08", "ZIP"))
- SupportedDocument.PAIN_002_LOGS -> Pair("HAC", null)
- }
-}
-
-fun uploadPaymentService(): Ebics3Service {
- return Ebics3Service(
- name = "MCT",
- scope = "CH",
- messageName = "pain.001",
- messageVersion = "09",
- container = null
- )
-}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt
index a7c2ba50..1847beec 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt
@@ -17,10 +17,6 @@
* <http://www.gnu.org/licenses/>
*/
-/**
- * This file contains helpers to construct EBICS 2.x requests.
- */
-
package tech.libeufin.nexus.ebics
import org.w3c.dom.Document
@@ -37,7 +33,7 @@ import java.util.*
import javax.xml.datatype.DatatypeFactory
import java.security.interfaces.*
-/** Ebics 3 protocol for */
+/** EBICS protocol for key management */
class Ebics3KeyMng(
private val cfg: EbicsSetupConfig,
private val clientKeys: ClientPrivateKeysFile
@@ -134,7 +130,7 @@ class Ebics3KeyMng(
el("AuthSignature")
el("body")
}
- XMLUtil.signEbicsDocument(doc, clientKeys.authentication_private_key)
+ XMLUtil.signEbicsDocument(doc, clientKeys.authentication_private_key, "H004")
return XMLUtil.convertDomToBytes(doc)
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsOrder.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsOrder.kt
new file mode 100644
index 00000000..3c73fff0
--- /dev/null
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsOrder.kt
@@ -0,0 +1,59 @@
+/*
+ * This file is part of LibEuFin.
+ * Copyright (C) 2024 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/>
+ */
+package tech.libeufin.nexus.ebics
+
+// We will support more dialect in the future
+
+sealed class EbicsOrder(val schema: String) {
+ data class V2_5(
+ val type: String,
+ val attribute: String
+ ): EbicsOrder("H004")
+ data class V3(
+ val type: String,
+ val name: String? = null,
+ val scope: String? = null,
+ val messageName: String? = null,
+ val messageVersion: String? = null,
+ val container: String? = null,
+ ): EbicsOrder("H005")
+}
+
+fun downloadDocService(doc: SupportedDocument, ebics2: Boolean): EbicsOrder {
+ return if (ebics2) {
+ when (doc) {
+ SupportedDocument.PAIN_002 -> EbicsOrder.V2_5("Z01", "DZHNN")
+ SupportedDocument.CAMT_052 -> EbicsOrder.V2_5("Z52", "DZHNN")
+ SupportedDocument.CAMT_053 -> EbicsOrder.V2_5("Z53", "DZHNN")
+ SupportedDocument.CAMT_054 -> EbicsOrder.V2_5("Z54", "DZHNN")
+ SupportedDocument.PAIN_002_LOGS -> EbicsOrder.V2_5("HAC", "DZHNN")
+ }
+ } else {
+ when (doc) {
+ SupportedDocument.PAIN_002 -> EbicsOrder.V3("BTD", "PSR", "CH", "pain.002", "10", "ZIP")
+ SupportedDocument.CAMT_052 -> EbicsOrder.V3("BTD", "STM", "CH", "camt.052", "08", "ZIP")
+ SupportedDocument.CAMT_053 -> EbicsOrder.V3("BTD", "EOP", "CH", "camt.053", "08", "ZIP")
+ SupportedDocument.CAMT_054 -> EbicsOrder.V3("BTD", "REP", "CH", "camt.054", "08", "ZIP")
+ SupportedDocument.PAIN_002_LOGS -> EbicsOrder.V3("HAC")
+ }
+ }
+}
+
+fun uploadPaymentService(): EbicsOrder =
+ EbicsOrder.V3("BTU", "MCT", "CH", "pain.001", "09")