summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine A <>2024-03-04 11:08:08 +0100
committerAntoine A <>2024-03-04 11:08:08 +0100
commit4f2f3926822a3574d1fba96c20b0c159754d9f98 (patch)
tree4fabd2024d5af73dfd9a75124e5c291d31720c15
parent769d458c8de9e38f77a599f9301290624280bb92 (diff)
downloadlibeufin-4f2f3926822a3574d1fba96c20b0c159754d9f98.tar.gz
libeufin-4f2f3926822a3574d1fba96c20b0c159754d9f98.tar.bz2
libeufin-4f2f3926822a3574d1fba96c20b0c159754d9f98.zip
Improve ack parsing and logging
-rw-r--r--ebics/src/main/kotlin/Ebics.kt3
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt3
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt13
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt7
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt12
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt3
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt65
7 files changed, 56 insertions, 50 deletions
diff --git a/ebics/src/main/kotlin/Ebics.kt b/ebics/src/main/kotlin/Ebics.kt
index 31efa07c..ab591b61 100644
--- a/ebics/src/main/kotlin/Ebics.kt
+++ b/ebics/src/main/kotlin/Ebics.kt
@@ -287,6 +287,7 @@ enum class EbicsReturnCode(val errorCode: String) {
data class EbicsResponseContent(
val transactionID: String?,
+ val orderID: String?,
val dataEncryptionInfo: DataEncryptionInfo?,
val orderDataEncChunk: String?,
val technicalReturnCode: EbicsReturnCode,
@@ -362,6 +363,7 @@ fun ebics3toInternalRepr(response: Document): EbicsResponseContent {
return EbicsResponseContent(
transactionID = resp.value.header._static.transactionID,
+ orderID = resp.value.header.mutable.orderID,
bankReturnCode = bankReturnCode,
technicalReturnCode = techReturnCode,
reportText = reportText,
@@ -398,6 +400,7 @@ fun ebics25toInternalRepr(response: Document): EbicsResponseContent {
return EbicsResponseContent(
transactionID = resp.value.header._static.transactionID,
+ orderID = resp.value.header.mutable.orderID,
bankReturnCode = bankReturnCode,
technicalReturnCode = techReturnCode,
reportText = reportText,
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
index 956ae58c..8a2b9185 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
@@ -169,6 +169,9 @@ private fun PreparedStatement.maybeUpdate(): Boolean {
*/
class Database(dbConfig: String): DbPool(dbConfig, "libeufin_nexus") {
+ // Temporary in memory database to store EBICS order status until we modify the schema to actually store it in the database
+ var mem: MutableMap<String, String> = mutableMapOf()
+
// OUTGOING PAYMENTS METHODS
/**
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
index f4298287..e8d4b049 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
@@ -230,6 +230,17 @@ private fun ingestDocument(
SupportedDocument.PAIN_002_LOGS -> {
val acks = parseCustomerAck(xml)
for (ack in acks) {
+ val msg = if (ack.orderId != null) {
+ if (ack.code != null) {
+ val msg = ack.msg()
+ db.mem[ack.orderId] = msg
+ msg
+ } else {
+ db.mem[ack.orderId]
+ }
+ } else {
+ null
+ }
when (ack.actionType) {
HacAction.FILE_DOWNLOAD -> logger.debug("$ack")
HacAction.ORDER_HAC_FINAL_POS -> {
@@ -240,7 +251,7 @@ private fun ingestDocument(
HacAction.ORDER_HAC_FINAL_NEG -> {
// TODO update pending transaction status
logger.debug("$ack")
- logger.warn("Order '${ack.orderId}' was refused at ${ack.timestamp.fmtDateTime()}")
+ logger.warn("Order '${ack.orderId}' was refused at ${ack.timestamp.fmtDateTime()}: $msg")
}
else -> {
// TODO update pending transaction status
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
index e96e3db2..ce02f865 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
@@ -91,7 +91,7 @@ class NexusSubmitException(
private suspend fun submitInitiatedPayment(
ctx: SubmissionContext,
payment: InitiatedPayment
-) {
+): String {
val creditAccount = try {
val payto = Payto.parse(payment.creditPaytoUri).expectIban()
IbanAccountMetadata(
@@ -114,7 +114,7 @@ private suspend fun submitInitiatedPayment(
)
ctx.fileLogger.logSubmit(xml)
try {
- submitPain001(
+ return submitPain001(
xml,
ctx.cfg,
ctx.clientPrivateKeysFile,
@@ -166,7 +166,8 @@ private fun submitBatch(
db.initiatedPaymentsSubmittableGet(ctx.cfg.currency).forEach {
logger.debug("Submitting payment initiation with row ID: ${it.id}")
val submissionState = try {
- submitInitiatedPayment(ctx, it)
+ val orderId = submitInitiatedPayment(ctx, it)
+ db.mem[orderId] = "Init"
DatabaseSubmissionState.success
} catch (e: NexusSubmitException) {
logger.error(e.message)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
index 53cfdf80..fb9c03ce 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
@@ -129,15 +129,19 @@ data class CustomerAck(
val code: ExternalStatusReasonCode?,
val timestamp: Instant
) {
- override fun toString(): String {
- var str = "${timestamp.fmtDateTime()}"
- if (orderId != null) str += " ${orderId}"
- str += " ${actionType}"
+ fun msg(): String {
+ var str = "${actionType}"
if (code != null) str += " ${code.isoCode}"
str += " - '${actionType.description}'"
if (code != null) str += " '${code.description}'"
return str
}
+
+ override fun toString(): String {
+ var str = "${timestamp.fmtDateTime()}"
+ if (orderId != null) str += " ${orderId}"
+ return str + " ${msg()}"
+ }
}
/**
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
index 52bdeb0b..09aac3df 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
@@ -234,7 +234,7 @@ suspend fun submitPain001(
clientKeys: ClientPrivateKeysFile,
bankkeys: BankPublicKeysFile,
httpClient: HttpClient
-) {
+): String {
val orderService: Ebics3Request.OrderDetails.Service = Ebics3Request.OrderDetails.Service().apply {
serviceName = "MCT"
scope = "CH"
@@ -255,6 +255,7 @@ suspend fun submitPain001(
" EBICS technical code is: ${maybeUploaded.technicalReturnCode}," +
" bank technical return code is: ${maybeUploaded.bankReturnCode}"
)
+ return maybeUploaded.orderID!!
}
/**
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 f497799e..7eebcea8 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
@@ -225,11 +225,30 @@ suspend fun postEbics(
e
)
}
- return parseAndValidateEbicsResponse(
- bankKeys,
- respXml,
+
+ // Parses the bank response from the raw XML and verifies
+ // the bank signature.
+ val doc = try {
+ XMLUtil.parseIntoDom(respXml)
+ } catch (e: Exception) {
+ throw EbicsSideException(
+ "Bank response apparently invalid",
+ sideEc = EbicsSideError.BANK_RESPONSE_IS_INVALID
+ )
+ }
+ if (!XMLUtil.verifyEbicsDocument(
+ doc,
+ bankKeys.bank_authentication_public_key,
isEbics3
- )
+ )) {
+ throw EbicsSideException(
+ "Bank signature did not verify",
+ sideEc = EbicsSideError.BANK_SIGNATURE_DIDNT_VERIFY
+ )
+ }
+ if (isEbics3)
+ return ebics3toInternalRepr(doc)
+ return ebics25toInternalRepr(doc)
}
/**
@@ -384,43 +403,6 @@ class EbicsSideException(
) : Exception(msg, cause)
/**
- * Parses the bank response from the raw XML and verifies
- * the bank signature.
- *
- * @param bankKeys provides the bank auth pub, to verify the signature.
- * @param responseStr raw XML response from the bank
- * @param withEbics3 true if the communication is EBICS 3, false otherwise.
- * @return [EbicsResponseContent] or throw [EbicsSideException]
- */
-fun parseAndValidateEbicsResponse(
- bankKeys: BankPublicKeysFile,
- resp: InputStream,
- withEbics3: Boolean
-): EbicsResponseContent {
- val doc = try {
- XMLUtil.parseIntoDom(resp)
- } catch (e: Exception) {
- throw EbicsSideException(
- "Bank response apparently invalid",
- sideEc = EbicsSideError.BANK_RESPONSE_IS_INVALID
- )
- }
- if (!XMLUtil.verifyEbicsDocument(
- doc,
- bankKeys.bank_authentication_public_key,
- withEbics3
- )) {
- throw EbicsSideException(
- "Bank signature did not verify",
- sideEc = EbicsSideError.BANK_SIGNATURE_DIDNT_VERIFY
- )
- }
- if (withEbics3)
- return ebics3toInternalRepr(doc)
- return ebics25toInternalRepr(doc)
-}
-
-/**
* Signs and the encrypts the data to send via EBICS.
*
* @param cfg configuration handle.
@@ -527,6 +509,7 @@ suspend fun doEbicsUpload(
orderService: Ebics3Request.OrderDetails.Service,
payload: ByteArray,
): EbicsResponseContent {
+ // TODO use a lambda and pass the order detail there for atomicity ?
val preparedPayload = prepareUploadPayload(cfg, clientKeys, bankKeys, payload, isEbics3 = true)
val initXml = createEbics3RequestForUploadInitialization(
cfg,