libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit bfd94d8055b3b154a45744e8b9a289bce7bc38fa
parent eaeb60386b9b2d04695f8c8c84ee6079b4cd1a86
Author: MS <ms@taler.net>
Date:   Mon, 28 Nov 2022 14:15:38 +0100

logging

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt | 6++++--
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt | 18++++++++++--------
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt | 4++--
Anexus/src/test/kotlin/DownloadAndSubmit.kt | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dnexus/src/test/kotlin/SchedulingTest.kt | 143-------------------------------------------------------------------------------
Msandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt | 50++++++++++++++++++++++++++++++--------------------
Mutil/src/main/kotlin/Ebics.kt | 26++++++++++++++++++++++----
8 files changed, 220 insertions(+), 180 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -88,7 +88,7 @@ class Serve : CliktCommand("Run nexus HTTP server") { } } -class ParseCamt : CliktCommand("Parse a camt file") { +class ParseCamt : CliktCommand("Parse CAMT file, outputs JSON in libEufin internal representation.") { private val logLevel by option() private val filename by argument("FILENAME", "File in CAMT format") override fun run() { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt @@ -149,7 +149,6 @@ data class CamtTransactionsCount( fun processCamtMessage( bankAccountId: String, camtDoc: Document, code: String ): CamtTransactionsCount { - logger.info("processing CAMT message") var newTransactions = 0 var downloadedTransactions = 0 transaction { @@ -371,7 +370,10 @@ suspend fun fetchBankAccountTransactions( val connectionName = conn.connectionId } } - // abstracts over the connection type: ebics or others. + /** + * Collects transactions from the bank and stores the (camt) + * document into the database. + */ getConnectionPlugin(res.connectionType).fetchTransactions( fetchSpec, client, diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt @@ -121,9 +121,9 @@ suspend fun doEbicsDownloadTransaction( val transactionID = initResponse.transactionID ?: throw NexusError( HttpStatusCode.InternalServerError, - "initial response must contain transaction ID" + "Initial response must contain transaction ID" ) - + logger.debug("Bank acknowledges EBICS download initialization. Transaction ID: $transactionID.") val encryptionInfo = initResponse.dataEncryptionInfo ?: throw NexusError(HttpStatusCode.InternalServerError, "initial response did not contain encryption info") @@ -139,10 +139,10 @@ suspend fun doEbicsDownloadTransaction( ?: throw NexusError(HttpStatusCode.FailedDependency, "missing segment number in EBICS download init response") // Transfer phase - for (x in 2 .. numSegments) { val transferReqStr = createEbicsRequestForDownloadTransferPhase(subscriberDetails, transactionID, x, numSegments) + logger.debug("EBICS download transfer phase of ${transactionID}: sending segment $x") val transferResponseStr = client.postToBank(subscriberDetails.ebicsUrl, transferReqStr) val transferResponse = parseAndValidateEbicsResponse(subscriberDetails, transferResponseStr) when (transferResponse.technicalReturnCode) { @@ -171,6 +171,7 @@ suspend fun doEbicsDownloadTransaction( "transfer response for download transaction does not contain data transfer" ) payloadChunks.add(transferOrderDataEncChunk) + logger.debug("Download transfer phase of ${transactionID}: bank acknowledges $x") } val respPayload = decryptAndDecompressResponse(subscriberDetails, encryptionInfo, payloadChunks) @@ -193,10 +194,13 @@ suspend fun doEbicsDownloadTransaction( ) } } + logger.debug("Bank acknowledges EBICS download receipt. Transaction ID: $transactionID.") return EbicsDownloadSuccessResult(respPayload) } - +/** + * Currently only 1-segment requests. + */ suspend fun doEbicsUploadTransaction( client: HttpClient, subscriberDetails: EbicsClientSubscriberDetails, @@ -221,8 +225,7 @@ suspend fun doEbicsUploadTransaction( HttpStatusCode.InternalServerError, "init response must have transaction ID" ) - - logger.debug("INIT phase passed!") + logger.debug("Bank acknowledges EBICS upload initialization. Transaction ID: $transactionID.") /* now send actual payload */ val payload = createEbicsRequestForUploadTransferPhase( @@ -231,14 +234,12 @@ suspend fun doEbicsUploadTransaction( preparedUploadData, 0 ) - val txRespStr = client.postToBank( subscriberDetails.ebicsUrl, payload ) val txResp = parseAndValidateEbicsResponse(subscriberDetails, txRespStr) - when (txResp.technicalReturnCode) { EbicsReturnCode.EBICS_OK -> { } @@ -248,6 +249,7 @@ suspend fun doEbicsUploadTransaction( ) } } + logger.debug("Bank acknowledges EBICS upload transfer. Transaction ID: $transactionID") } suspend fun doEbicsHostVersionQuery(client: HttpClient, ebicsBaseUrl: String, ebicsHostId: String): EbicsHevDetails { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt @@ -73,7 +73,7 @@ private data class EbicsFetchSpec( fun storeCamt(bankConnectionId: String, camt: String, historyType: String) { val camt53doc = XMLUtil.parseStringIntoDom(camt) val msgId = camt53doc.pickStringWithRootNs("/*[1]/*[1]/root:GrpHdr/root:MsgId") - logger.info("msg id $msgId") + logger.info("camt document '$msgId' received.") transaction { val conn = NexusBankConnectionEntity.findByName(bankConnectionId) if (conn == null) { @@ -102,7 +102,6 @@ private suspend fun fetchEbicsC5x( orderParams: EbicsOrderParams, subscriberDetails: EbicsClientSubscriberDetails ) { - logger.debug("Requesting $historyType") val response = try { doEbicsDownloadTransaction( client, @@ -529,6 +528,7 @@ class EbicsBankConnectionProtocol: BankConnectionProtocol { messageId = paymentInitiation.messageId ) ) + logger.debug("Sending Pain.001: ${paymentInitiation.paymentInformationId}") if (!XMLUtil.validateFromString(painMessage)) throw NexusError( HttpStatusCode.InternalServerError, "Pain.001 message is invalid." ) diff --git a/nexus/src/test/kotlin/DownloadAndSubmit.kt b/nexus/src/test/kotlin/DownloadAndSubmit.kt @@ -0,0 +1,150 @@ +import io.ktor.application.* +import io.ktor.client.* +import io.ktor.client.request.* +import io.ktor.features.* +import io.ktor.http.* +import io.ktor.request.* +import io.ktor.response.* +import io.ktor.routing.* +import io.ktor.server.testing.* +import kotlinx.coroutines.runBlocking +import org.jetbrains.exposed.sql.transactions.transaction +import org.junit.Ignore +import org.junit.Test +import org.w3c.dom.Document +import tech.libeufin.nexus.* +import tech.libeufin.nexus.bankaccount.addPaymentInitiation +import tech.libeufin.nexus.bankaccount.fetchBankAccountTransactions +import tech.libeufin.nexus.ebics.EbicsBankConnectionProtocol +import tech.libeufin.nexus.server.FetchLevel +import tech.libeufin.nexus.server.FetchSpecAllJson +import tech.libeufin.nexus.server.FetchSpecJson +import tech.libeufin.nexus.server.Pain001Data +import tech.libeufin.sandbox.* +import tech.libeufin.util.* +import tech.libeufin.util.ebics_h004.EbicsRequest +import tech.libeufin.util.ebics_h004.EbicsResponse +import tech.libeufin.util.ebics_h004.EbicsTypes + +/** + * This source is NOT a test case -- as it uses no assertions -- + * but merely a tool to download and submit payments to the bank + * via Nexus. + * / + + */ +/** + * Data to make the test server return for EBICS + * phases. Currently only init is supported. + */ +data class EbicsResponses( + val init: String, + val download: String? = null, + val receipt: String? = null +) + +/** + * Minimal server responding always the 'init' field of a EbicsResponses + * object along a download EBICS message. Suitable to set arbitrary data + * in said response. Signs the response assuming the client is the one + * created a MakeEnv.kt. + */ +fun getCustomEbicsServer(r: EbicsResponses, endpoint: String = "/ebicsweb"): Application.() -> Unit { + val ret: Application.() -> Unit = { + install(ContentNegotiation) { + register(ContentType.Text.Xml, XMLEbicsConverter()) + register(ContentType.Text.Plain, XMLEbicsConverter()) + } + routing { + post(endpoint) { + val requestDocument = this.call.receive<Document>() + val req = requestDocument.toObject<EbicsRequest>() + val clientKey = CryptoUtil.loadRsaPublicKey(userKeys.enc.public.encoded) + val msgId = EbicsOrderUtil.generateTransactionId() + val resp: EbicsResponse = if ( + req.header.mutable.transactionPhase == EbicsTypes.TransactionPhaseType.INITIALISATION + ) { + val payload = prepareEbicsPayload(r.init, clientKey) + EbicsResponse.createForDownloadInitializationPhase( + msgId, + 1, + 4096, + payload.second, // for key material + payload.first // actual payload + ) + } else { + // msgId doesn't have to match the one used for the init phase. + EbicsResponse.createForDownloadReceiptPhase(msgId, true) + } + val sigEbics = XMLUtil.signEbicsResponse( + resp, + CryptoUtil.loadRsaPrivateKey(bankKeys.auth.private.encoded) + ) + call.respond(sigEbics) + } + } + } + return ret +} + +/** + * Remove @Ignore, after having put asserts along tests, + * and having had access to runTask and TaskSchedule, that + * are now 'private'. + */ +// @Ignore +class SchedulingTest { + /** + * Instruct the server to return invalid CAMT content. + */ + @Test + fun download() { + withNexusAndSandboxUser { + withTestApplication(sandboxApp) { + val conn = EbicsBankConnectionProtocol() + runBlocking { + conn.fetchTransactions( + fetchSpec = FetchSpecAllJson( + level = FetchLevel.REPORT, + "foo" + ), + client, + "foo", + "mock-bank-account" + ) + } + } + } + } + + @Test + fun upload() { + withNexusAndSandboxUser { + withTestApplication(sandboxApp) { + val conn = EbicsBankConnectionProtocol() + runBlocking { + // Create Pain.001 to be submitted. + addPaymentInitiation( + Pain001Data( + creditorIban = getIban(), + creditorBic = "SANDBOXX", + creditorName = "Tester", + subject = "test payment", + sum = Amount(1), + currency = "TESTKUDOS" + ), + transaction { + NexusBankAccountEntity.findByName( + "mock-bank-account" + ) ?: throw Exception("Test failed") + } + ) + conn.submitPaymentInitiation( + client, + 1L + ) + } + } + } + } +} +\ No newline at end of file diff --git a/nexus/src/test/kotlin/SchedulingTest.kt b/nexus/src/test/kotlin/SchedulingTest.kt @@ -1,142 +0,0 @@ -import io.ktor.application.* -import io.ktor.features.* -import io.ktor.http.* -import io.ktor.request.* -import io.ktor.response.* -import io.ktor.routing.* -import io.ktor.server.testing.* -import kotlinx.coroutines.runBlocking -import org.junit.Ignore -import org.junit.Test -import org.w3c.dom.Document -import tech.libeufin.nexus.* -import tech.libeufin.sandbox.* -import tech.libeufin.util.* -import tech.libeufin.util.ebics_h004.EbicsRequest -import tech.libeufin.util.ebics_h004.EbicsResponse -import tech.libeufin.util.ebics_h004.EbicsTypes - - -/** - * Data to make the test server return for EBICS - * phases. Currently only init is supported. - */ -data class EbicsResponses( - val init: String, - val download: String? = null, - val receipt: String? = null -) - -/** - * Minimal server responding always the 'init' field of a EbicsResponses - * object along a download EBICS message. Suitable to set arbitrary data - * in said response. Signs the response assuming the client is the one - * created a MakeEnv.kt. - */ -fun getCustomEbicsServer(r: EbicsResponses, endpoint: String = "/ebicsweb"): Application.() -> Unit { - val ret: Application.() -> Unit = { - install(ContentNegotiation) { - register(ContentType.Text.Xml, XMLEbicsConverter()) - register(ContentType.Text.Plain, XMLEbicsConverter()) - } - routing { - post(endpoint) { - val requestDocument = this.call.receive<Document>() - val req = requestDocument.toObject<EbicsRequest>() - val clientKey = CryptoUtil.loadRsaPublicKey(userKeys.enc.public.encoded) - val msgId = EbicsOrderUtil.generateTransactionId() - val resp: EbicsResponse = if ( - req.header.mutable.transactionPhase == EbicsTypes.TransactionPhaseType.INITIALISATION - ) { - val payload = prepareEbicsPayload(r.init, clientKey) - EbicsResponse.createForDownloadInitializationPhase( - msgId, - 1, - 4096, - payload.second, // for key material - payload.first // actual payload - ) - } else { - // msgId doesn't have to match the one used for the init phase. - EbicsResponse.createForDownloadReceiptPhase(msgId, true) - } - val sigEbics = XMLUtil.signEbicsResponse( - resp, - CryptoUtil.loadRsaPrivateKey(bankKeys.auth.private.encoded) - ) - call.respond(sigEbics) - } - } - } - return ret -} - -/** - * Remove @Ignore, after having put asserts along tests, - * and having had access to runTask and TaskSchedule, that - * are now 'private'. - */ -@Ignore -class SchedulingTest { - /** - * Instruct the server to return invalid CAMT content. - */ - @Test - fun inject() { - withNexusAndSandboxUser { - val payload = """ - Invalid Camt Document - """.trimIndent() - withTestApplication( - getCustomEbicsServer(EbicsResponses(payload)) - ) { - runBlocking { - runTask( - client, - TaskSchedule( - 0L, - "test-schedule", - "fetch", - "bank-account", - "mock-bank-account", - params = "{\"level\":\"report\",\"rangeType\":\"all\"}" - ) - ) - } - } - } - } - /** - * Create two payments and asks for C52. - */ - @Test - fun ordinary() { - withNexusAndSandboxUser { // DB prep - for (t in 1 .. 2) { - wireTransfer( - "bank", - "foo", - "default", - "1HJX78AH7WAGBDJTCXJ4JKX022DBCHERA051KH7D3EC48X09G4RG", - "TESTKUDOS:5", - "xxx" - ) - } - withTestApplication(sandboxApp) { - runBlocking { - runTask( - client, - TaskSchedule( - 0L, - "test-schedule", - "fetch", - "bank-account", - "mock-bank-account", - params = "{\"level\":\"report\",\"rangeType\":\"all\"}" - ) - ) - } - } - } - } -} -\ No newline at end of file diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt @@ -544,17 +544,19 @@ private fun constructCamtResponse( baseBalance = lastBalance ) + val camtData = buildCamtString( + type, + bankAccount.iban, + history, + balancePrcd = lastBalance, + balanceClbd = freshBalance + ) + logger.debug("camt.052 document '${camtData.messageId}' generated.") return listOf( - buildCamtString( - type, - bankAccount.iban, - history, - balancePrcd = lastBalance, - balanceClbd = freshBalance - ).camtMessage + camtData.camtMessage ) } - SandboxAssert(type == 53, "Didn't catch unsupported Camt type") + SandboxAssert(type == 53, "Didn't catch unsupported CAMT type") logger.debug("Finding C$type records") /** @@ -569,7 +571,7 @@ private fun constructCamtResponse( * time range given as a function's parameter. */ if (dateRange != null) { - logger.debug("Querying c$type with date range: $dateRange") + logger.debug("Querying C$type with date range: $dateRange") BankAccountStatementEntity.find { BankAccountStatementsTable.creationTime.between( dateRange.first, @@ -696,8 +698,8 @@ private fun parsePain001(paymentRequest: String): PainParseResult { * Process a payment request in the pain.001 format. */ private fun handleCct(paymentRequest: String) { - logger.debug("Handling CCT") val parseResult = parsePain001(paymentRequest) + logger.debug("Handling Pain.001: ${parseResult.pmtInfId}") transaction(Connection.TRANSACTION_SERIALIZABLE, repetitionAttempts = 10) { val maybeExist = BankAccountTransactionEntity.find { BankAccountTransactionsTable.pmtInfId eq parseResult.pmtInfId @@ -761,7 +763,6 @@ private fun handleCct(paymentRequest: String) { * to the querying subscriber. */ private fun handleEbicsC52(requestContext: RequestContext): ByteArray { - logger.debug("Handling C52 request") // Ignoring any dateRange parameter. (FIXME) val report = constructCamtResponse(52, requestContext.subscriber, dateRange = null) SandboxAssert( @@ -776,8 +777,6 @@ private fun handleEbicsC52(requestContext: RequestContext): ByteArray { } private fun handleEbicsC53(requestContext: RequestContext): ByteArray { - logger.debug("Handling C53 request") - // Fetch date range. val orderParams = requestContext.requestObject.header.static.orderDetails?.orderParams // as EbicsRequest.StandardOrderParams val dateRange = if (orderParams != null) { @@ -1094,7 +1093,6 @@ private fun handleEbicsHkd(requestContext: RequestContext): ByteArray { return str.toByteArray() } - private data class RequestContext( val ebicsHost: EbicsHostEntity, val subscriber: EbicsSubscriberEntity, @@ -1108,6 +1106,10 @@ private data class RequestContext( val downloadTransaction: EbicsDownloadTransactionEntity? ) +/** + * Get segmentation values and the EBICS transaction ID, before + * handing the response to 'createForDownloadTransferPhase()'. + */ private fun handleEbicsDownloadTransactionTransfer(requestContext: RequestContext): EbicsResponse { val segmentNumber = requestContext.requestObject.header.mutable.segmentNumber?.value ?: throw EbicsInvalidRequestError() @@ -1128,7 +1130,7 @@ private fun handleEbicsDownloadTransactionTransfer(requestContext: RequestContex private fun handleEbicsDownloadTransactionInitialization(requestContext: RequestContext): EbicsResponse { val orderType = requestContext.requestObject.header.static.orderDetails?.orderType ?: throw EbicsInvalidRequestError() - logger.debug("handling initialization for order type $orderType") + val nonce = requestContext.requestObject.header.static.nonce val response = when (orderType) { "HTD" -> handleEbicsHtd(requestContext) "HKD" -> handleEbicsHkd(requestContext) @@ -1139,10 +1141,14 @@ private fun handleEbicsDownloadTransactionInitialization(requestContext: Request else -> throw EbicsInvalidXmlError() } val transactionID = EbicsOrderUtil.generateTransactionId() + logger.debug( + "Handling download initialization for order type $orderType, " + + "nonce: ${nonce?.toHexString() ?: "not given"}, " + + "transaction ID: $transactionID" + ) val compressedResponse = DeflaterInputStream(response.inputStream()).use { it.readAllBytes() } - val enc = CryptoUtil.encryptEbicsE002(compressedResponse, requestContext.clientEncPub) val encodedResponse = Base64.getEncoder().encodeToString(enc.encryptedData) @@ -1169,11 +1175,14 @@ private fun handleEbicsDownloadTransactionInitialization(requestContext: Request ) } - private fun handleEbicsUploadTransactionInitialization(requestContext: RequestContext): EbicsResponse { val orderType = requestContext.requestObject.header.static.orderDetails?.orderType ?: throw EbicsInvalidRequestError() val transactionID = EbicsOrderUtil.generateTransactionId() + logger.debug("Handling upload initialization for order $orderType, " + + "transactionID $transactionID, nonce: " + + (requestContext.requestObject.header.static.nonce?.toHexString() ?: "not given") + ) val oidn = requestContext.subscriber.nextOrderID++ if (EbicsOrderUtil.checkOrderIDOverflow(oidn)) throw NotImplementedError() val orderID = EbicsOrderUtil.computeOrderIDFromNumber(oidn) @@ -1197,7 +1206,6 @@ private fun handleEbicsUploadTransactionInitialization(requestContext: RequestCo val plainSigData = InflaterInputStream(decryptedSignatureData.inputStream()).use { it.readAllBytes() } - logger.debug("creating upload transaction for transactionID $transactionID") EbicsUploadTransactionEntity.new(transactionID) { this.host = requestContext.ebicsHost this.subscriber = requestContext.subscriber @@ -1209,7 +1217,7 @@ private fun handleEbicsUploadTransactionInitialization(requestContext: RequestCo } val sigObj = XMLUtil.convertStringToJaxb<UserSignatureData>(plainSigData.toString(Charsets.UTF_8)) for (sig in sigObj.value.orderSignatureList ?: listOf()) { - logger.debug("inserting order signature for orderID $orderID and orderType $orderType") + logger.debug("inserting order signature for orderID $orderID, order type $orderType, transaction '$transactionID'") EbicsOrderSignatureEntity.new { this.orderID = orderID this.orderType = orderType @@ -1238,7 +1246,6 @@ private fun handleEbicsUploadTransactionTransmission(requestContext: RequestCont ) val unzippedData = InflaterInputStream(zippedData.inputStream()).use { it.readAllBytes() } - // logger.debug("got upload data: ${unzippedData.toString(Charsets.UTF_8)}") val sigs = EbicsOrderSignatureEntity.find { (EbicsOrderSignaturesTable.orderID eq uploadTransaction.orderID) and @@ -1415,9 +1422,12 @@ suspend fun ApplicationCall.ebicsweb() { requestObject.header.static.transactionID ?: throw EbicsInvalidRequestError() if (requestContext.downloadTransaction == null) throw EbicsInvalidRequestError() + logger.debug("Handling download receipt for EBICS transaction: " + + requestTransactionID) val receiptCode = requestObject.body.transferReceipt?.receiptCode ?: throw EbicsInvalidRequestError() EbicsResponse.createForDownloadReceiptPhase(requestTransactionID, receiptCode == 0) + } } signEbicsResponse(ebicsResponse, requestContext.hostAuthPriv) diff --git a/util/src/main/kotlin/Ebics.kt b/util/src/main/kotlin/Ebics.kt @@ -156,7 +156,6 @@ private fun signOrder( return userSignatureData } - fun createEbicsRequestForDownloadReceipt( subscriberDetails: EbicsClientSubscriberDetails, transactionID: String @@ -232,11 +231,12 @@ fun createEbicsRequestForUploadInitialization( orderParams: EbicsOrderParams, preparedUploadData: PreparedUploadData ): String { + val nonce = getNonce(128) val req = EbicsRequest.createForUploadInitializationPhase( preparedUploadData.transactionKey, preparedUploadData.userSignatureDataEncrypted, subscriberDetails.hostId, - getNonce(128), + nonce, subscriberDetails.partnerId, subscriberDetails.userId, DatatypeFactory.newInstance().newXMLGregorianCalendar(GregorianCalendar()), @@ -246,6 +246,15 @@ fun createEbicsRequestForUploadInitialization( orderType, makeOrderParams(orderParams) ) + /** + * FIXME: this log should be made by the caller. + * That way, all the EBICS transaction steps would be logged in only one function, + * as opposed to have them spread through the helpers here. This function + * returning a string blocks now, since the caller should parse and stringify + * again the message, only to get its nonce. + */ + logger.debug("Created EBICS $orderType document for upload initialization," + + " nonce: ${nonce.toHexString()}") val doc = XMLUtil.convertJaxbToDocument(req) XMLUtil.signEbicsDocument(doc, subscriberDetails.customerAuthPriv) return XMLUtil.convertDomToString(doc) @@ -257,11 +266,12 @@ fun createEbicsRequestForDownloadInitialization( orderType: String, orderParams: EbicsOrderParams ): String { + val nonce = getNonce(128) val req = EbicsRequest.createForDownloadInitializationPhase( subscriberDetails.userId, subscriberDetails.partnerId, subscriberDetails.hostId, - getNonce(128), + nonce, DatatypeFactory.newInstance().newXMLGregorianCalendar(GregorianCalendar()), subscriberDetails.bankEncPub ?: throw EbicsProtocolError( HttpStatusCode.BadRequest, @@ -274,6 +284,15 @@ fun createEbicsRequestForDownloadInitialization( orderType, makeOrderParams(orderParams) ) + /** + * FIXME: this log should be made by the caller. + * That way, all the EBICS transaction steps would be logged in only one function, + * as opposed to have them spread through the helpers here. This function + * returning a string blocks now, since the caller should parse and stringify + * again the message, only to get its nonce. + */ + logger.debug("Created EBICS $orderType document for download initialization," + + " nonce: ${nonce.toHexString()}") val doc = XMLUtil.convertJaxbToDocument(req) XMLUtil.signEbicsDocument(doc, subscriberDetails.customerAuthPriv) return XMLUtil.convertDomToString(doc) @@ -296,7 +315,6 @@ fun createEbicsRequestForDownloadTransferPhase( return XMLUtil.convertDomToString(doc) } - fun createEbicsRequestForUploadTransferPhase( subscriberDetails: EbicsClientSubscriberDetails, transactionID: String,