gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated: nexus submit: adjusting pain.001 after


From: gnunet
Subject: [libeufin] branch master updated: nexus submit: adjusting pain.001 after PoFi
Date: Sun, 19 Nov 2023 10:43:52 +0100

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

ms pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new 0c219102 nexus submit: adjusting pain.001 after PoFi
0c219102 is described below

commit 0c21910217848dd34423951b2ce18cc8a5c29777
Author: MS <ms@taler.net>
AuthorDate: Sun Nov 19 10:43:24 2023 +0100

    nexus submit: adjusting pain.001 after PoFi
---
 .../main/kotlin/tech/libeufin/nexus/Database.kt    |  5 +-
 .../main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt | 79 +++++++++++++++-------
 .../main/kotlin/tech/libeufin/nexus/Iso20022.kt    | 46 +++++--------
 nexus/src/test/kotlin/Common.kt                    |  7 +-
 nexus/src/test/kotlin/DatabaseTest.kt              | 46 +++++++++++--
 5 files changed, 120 insertions(+), 63 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
index 64e2c9f7..10ec9f8c 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
@@ -519,7 +519,7 @@ class Database(dbConfig: String): java.io.Closeable {
      * @param currency in which currency should the payment be submitted to 
the bank.
      * @return [Map] of the initiated payment row ID and [InitiatedPayment]
      */
-    suspend fun initiatedPaymentsUnsubmittedGet(currency: String): Map<Long, 
InitiatedPayment> = runConn { conn ->
+    suspend fun initiatedPaymentsSubmittableGet(currency: String): Map<Long, 
InitiatedPayment> = runConn { conn ->
         val stmt = conn.prepareStatement("""
             SELECT
               initiated_outgoing_transaction_id
@@ -530,7 +530,8 @@ class Database(dbConfig: String): java.io.Closeable {
              ,initiation_time
              ,request_uid
              FROM initiated_outgoing_transactions
-             WHERE submitted='unsubmitted';
+             WHERE submitted='unsubmitted'
+               OR submitted='transient_failure';
         """)
         val maybeMap = mutableMapOf<Long, InitiatedPayment>()
         stmt.executeQuery().use {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
index c1dc2c4b..e11e39c7 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
@@ -92,10 +92,49 @@ class NexusSubmitException(
     val stage: NexusSubmissionStage
 ) : Exception(msg, cause)
 
+
+/**
+ * Optionally logs the pain.001 in the log directory, if the
+ * configuration had this latter.
+ *
+ * @param maybeLogDir log directory.  Null if the configuration
+ *        lacks it.
+ * @param xml the pain.001 document to log.
+ * @param requestUid UID of the payment request (normally equals
+ *                   the pain.001 MsgId element), will be part of
+ *                   the filename.
+ */
+fun maybeLog(
+    maybeLogDir: String?,
+    xml: String,
+    requestUid: String
+) {
+    if (maybeLogDir == null) {
+        logger.info("Logging pain.001 to files is disabled")
+        return
+    }
+    logger.debug("Logging to $maybeLogDir")
+    val now = Instant.now()
+    val asUtcDate = LocalDate.ofInstant(now, ZoneId.of("UTC"))
+    val subDir = 
"${asUtcDate.year}-${asUtcDate.monthValue}-${asUtcDate.dayOfMonth}"
+    val dirs = Path.of(maybeLogDir, subDir)
+    doOrFail { dirs.createDirectories() }
+    val f = File(
+        dirs.toString(),
+        "${now.toDbMicros()}_requestUid_${requestUid}_pain.001.xml"
+    )
+    // Very rare: same pain.001 should not be submitted twice in the same 
microsecond.
+    if (f.exists()) {
+        logger.error("pain.001 log file exists already at: $f")
+        exitProcess(1)
+    }
+    doOrFail { f.writeText(xml) }
+}
+
 /**
- * Takes the initiated payment data, as it was returned from the
- * database, sanity-checks it, makes the pain.001 document and finally
- * submits it via EBICS to the bank.
+ * Takes the initiated payment data as it was returned from the
+ * database, sanity-checks it, gets the pain.001 from the helper
+ * function and finally submits it via EBICS to the bank.
  *
  * @param ctx [SubmissionContext]
  * @return true on success, false otherwise.
@@ -118,6 +157,16 @@ private suspend fun submitInitiatedPayment(
         debitAccount = ctx.cfg.myIbanAccount,
         wireTransferSubject = initiatedPayment.wireTransferSubject
     )
+    // Logging first!
+    val maybeLogDir: String? = ctx.cfg.config.lookupString(
+        "nexus-submit",
+        "SUBMISSIONS_LOG_DIRECTORY"
+    )
+    maybeLog(
+        maybeLogDir,
+        xml,
+        initiatedPayment.requestUid
+    )
     try {
         submitPain001(
             xml,
@@ -150,28 +199,6 @@ private suspend fun submitInitiatedPayment(
             cause = permanent
         )
     }
-    // Submission succeeded, storing the pain.001 to file.
-    val logDir: String? = ctx.cfg.config.lookupString(
-        "neuxs-submit",
-        "SUBMISSIONS_LOG_DIRECTORY"
-    )
-    if (logDir != null) {
-        val now = Instant.now()
-        val asUtcDate = LocalDate.ofInstant(now, ZoneId.of("UTC"))
-        val subDir = 
"${asUtcDate.year}-${asUtcDate.monthValue}-${asUtcDate.dayOfMonth}"
-        val dirs = Path.of(logDir, subDir)
-        doOrFail { dirs.createDirectories() }
-        val f = File(
-            dirs.toString(),
-            
"${now.toDbMicros()}_requestUid_${initiatedPayment.requestUid}_pain.001.xml"
-            )
-        // Very rare: same pain.001 should not be submitted twice in the same 
microsecond.
-        if (f.exists()) {
-            logger.error("pain.001 log file exists already at: $f")
-            exitProcess(1)
-        }
-        doOrFail { f.writeText(xml) }
-    }
 }
 
 /**
@@ -190,7 +217,7 @@ private fun submitBatch(
 ) {
     logger.debug("Running submit at: ${Instant.now()}")
     runBlocking {
-        db.initiatedPaymentsUnsubmittedGet(ctx.cfg.currency).forEach {
+        db.initiatedPaymentsSubmittableGet(ctx.cfg.currency).forEach {
             logger.debug("Submitting payment initiation with row ID: 
${it.key}")
             val submissionState = try {
                 submitInitiatedPayment(ctx, initiatedPayment = it.value)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
index 3c291b99..1ad6c50b 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
@@ -8,6 +8,10 @@ import java.time.ZonedDateTime
 import java.time.format.DateTimeFormatter
 
 
+/**
+ * Collects details to define the pain.001 namespace
+ * XML attributes.
+ */
 data class Pain001Namespaces(
     val fullNamespace: String,
     val xsdFilename: String
@@ -73,8 +77,14 @@ fun createPain001(
         )
     return constructXml(indent = true) {
         root("Document") {
-            attribute("xmlns", namespace.fullNamespace)
-            attribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance";)
+            attribute(
+                "xmlns",
+                namespace.fullNamespace
+            )
+            attribute(
+                "xmlns:xsi",
+                "http://www.w3.org/2001/XMLSchema-instance";
+            )
             attribute(
                 "xsi:schemaLocation",
                 "${namespace.fullNamespace} ${namespace.xsdFilename}"
@@ -100,7 +110,7 @@ fun createPain001(
                 }
                 element("PmtInf") {
                     element("PmtInfId") {
-                        text("NOT GIVEN")
+                        text("NOTPROVIDED")
                     }
                     element("PmtMtd") {
                         text("TRF")
@@ -108,15 +118,6 @@ fun createPain001(
                     element("BtchBookg") {
                         text("true")
                     }
-                    element("NbOfTxs") {
-                        text("1")
-                    }
-                    element("CtrlSum") {
-                        text(amountWithoutCurrency)
-                    }
-                    element("PmtTpInf/SvcLvl/Cd") {
-                        text("SDVA")
-                    }
                     element("ReqdExctnDt") {
                         element("Dt") {
                             
text(DateTimeFormatter.ISO_DATE.format(zonedTimestamp))
@@ -128,31 +129,18 @@ fun createPain001(
                     element("DbtrAcct/Id/IBAN") {
                         text(debitAccount.iban)
                     }
-                    element("DbtrAgt/FinInstnId") {
-                        element("BICFI") {
-                            text(debitAccount.bic)
-                        }
-                    }
-                    element("ChrgBr") {
-                        text("SLEV")
+                    element("DbtrAgt/FinInstnId/BICFI") {
+                        text(debitAccount.bic)
                     }
                     element("CdtTrfTxInf") {
                         element("PmtId") {
-                            element("InstrId") { text("NOT PROVIDED") }
-                            element("EndToEndId") { text("NOT PROVIDED") }
+                            element("InstrId") { text("NOTPROVIDED") }
+                            element("EndToEndId") { text("NOTPROVIDED") }
                         }
                         element("Amt/InstdAmt") {
                             attribute("Ccy", amount.currency)
                             text(amountWithoutCurrency)
                         }
-                        creditAccount.bic.apply {
-                            if (this != null)
-                                element("CdtrAgt/FinInstnId") {
-                                    element("BICFI") {
-                                        text(this@apply)
-                                    }
-                                }
-                        }
                         element("Cdtr/Nm") {
                             text(creditorName)
                         }
diff --git a/nexus/src/test/kotlin/Common.kt b/nexus/src/test/kotlin/Common.kt
index ecfa8c6e..ea9e67fd 100644
--- a/nexus/src/test/kotlin/Common.kt
+++ b/nexus/src/test/kotlin/Common.kt
@@ -77,13 +77,16 @@ fun getPofiConfig(
 """.trimIndent()
 
 // Generates a payment initiation, given its subject.
-fun genInitPay(subject: String = "init payment", rowUid: String = "unique") =
+fun genInitPay(
+    subject: String = "init payment",
+    requestUid: String = "unique"
+) =
     InitiatedPayment(
         amount = TalerAmount(44, 0, "KUDOS"),
         creditPaytoUri = "payto://iban/TEST-IBAN?receiver-name=Test",
         wireTransferSubject = subject,
         initiationTime = Instant.now(),
-        requestUid = rowUid
+        requestUid = requestUid
     )
 
 // Generates an incoming payment, given its subject.
diff --git a/nexus/src/test/kotlin/DatabaseTest.kt 
b/nexus/src/test/kotlin/DatabaseTest.kt
index 9017f950..0f2cc4fa 100644
--- a/nexus/src/test/kotlin/DatabaseTest.kt
+++ b/nexus/src/test/kotlin/DatabaseTest.kt
@@ -161,7 +161,7 @@ class PaymentInitiationsTest {
     fun paymentInitiation() {
         val db = prepDb(TalerConfig(NEXUS_CONFIG_SOURCE))
         runBlocking {
-            val beEmpty = db.initiatedPaymentsUnsubmittedGet("KUDOS")// expect 
no records.
+            val beEmpty = db.initiatedPaymentsSubmittableGet("KUDOS") // 
expect no records.
             assertEquals(beEmpty.size, 0)
         }
         val initPay = InitiatedPayment(
@@ -175,17 +175,55 @@ class PaymentInitiationsTest {
             assertNull(db.initiatedPaymentGetFromUid("unique"))
             assertEquals(db.initiatedPaymentCreate(initPay), 
PaymentInitiationOutcome.SUCCESS)
             assertEquals(db.initiatedPaymentCreate(initPay), 
PaymentInitiationOutcome.UNIQUE_CONSTRAINT_VIOLATION)
-            val haveOne = db.initiatedPaymentsUnsubmittedGet("KUDOS")
+            val haveOne = db.initiatedPaymentsSubmittableGet("KUDOS")
             assertTrue {
                 haveOne.size == 1
                         && haveOne.containsKey(1)
                         && haveOne[1]?.requestUid == "unique"
             }
-            db.initiatedPaymentSetSubmittedState(1, 
DatabaseSubmissionState.success)
+            assertTrue(db.initiatedPaymentSetSubmittedState(1, 
DatabaseSubmissionState.success))
             assertNotNull(db.initiatedPaymentGetFromUid("unique"))
         }
     }
 
+    /**
+     * The SQL that gets submittable payments checks multiple
+     * statuses from them.  Checking it here.
+     */
+    @Test
+    fun submittablePayments() {
+        val db = prepDb(TalerConfig(NEXUS_CONFIG_SOURCE))
+        runBlocking {
+            val beEmpty = db.initiatedPaymentsSubmittableGet("KUDOS")
+            assertEquals(0, beEmpty.size)
+            assertEquals(
+                db.initiatedPaymentCreate(genInitPay(requestUid = "first")),
+                PaymentInitiationOutcome.SUCCESS
+            )
+            assertEquals(
+                db.initiatedPaymentCreate(genInitPay(requestUid = "second")),
+                PaymentInitiationOutcome.SUCCESS
+            )
+            assertEquals(
+                db.initiatedPaymentCreate(genInitPay(requestUid = "third")),
+                PaymentInitiationOutcome.SUCCESS
+            )
+
+            // Setting the first as "transient_failure", must be found.
+            assertTrue(db.initiatedPaymentSetSubmittedState(
+                1, DatabaseSubmissionState.transient_failure
+            ))
+            // Setting the second as "success", must not be found.
+            assertTrue(db.initiatedPaymentSetSubmittedState(
+                2, DatabaseSubmissionState.success
+            ))
+            val expectTwo = db.initiatedPaymentsSubmittableGet("KUDOS")
+            // the third initiation keeps the default "unsubmitted"
+            // state, must be found.  Total 2.
+            assertEquals(2, expectTwo.size)
+        }
+    }
+
     // Tests how the fetch method gets the list of
     // multiple unsubmitted payment initiations.
     @Test
@@ -207,7 +245,7 @@ class PaymentInitiationsTest {
             }
 
             // Expecting all the payments BUT the #3 in the result.
-            db.initiatedPaymentsUnsubmittedGet("KUDOS").apply {
+            db.initiatedPaymentsSubmittableGet("KUDOS").apply {
                 assertEquals(3, this.size)
                 assertEquals("#1", this[1]?.wireTransferSubject)
                 assertEquals("#2", this[2]?.wireTransferSubject)

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