gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated (c3de34f3 -> 04c5aa01)


From: gnunet
Subject: [libeufin] branch master updated (c3de34f3 -> 04c5aa01)
Date: Sat, 01 Apr 2023 22:07:07 +0200

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

ms pushed a change to branch master
in repository libeufin.

    from c3de34f3 Closing PG connection in more places.
     new cf0c4ebf cli tests helpers
     new 04c5aa01 x-libeufin-bank connection.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 cli/bin/libeufin-cli                               | 39 ++++++++++
 cli/tests/launch_services.sh                       |  6 +-
 cli/tests/twg-history-loop.sh                      | 21 ++++++
 cli/tests/wire-transfer.sh                         |  8 +-
 .../tech/libeufin/nexus/BankConnectionProtocol.kt  |  5 +-
 nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt    |  5 +-
 nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt |  1 -
 .../tech/libeufin/nexus/bankaccount/BankAccount.kt |  8 +-
 .../kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt | 11 +--
 .../nexus/xlibeufinbank/XLibeufinBankNexus.kt      | 88 ++++++++++++++++++----
 .../kotlin/{DownloadAndSubmit.kt => EbicsTest.kt}  | 29 +++----
 nexus/src/test/kotlin/TalerTest.kt                 | 54 +++++++------
 nexus/src/test/kotlin/XLibeufinBankTest.kt         | 41 ++++++++--
 .../src/main/kotlin/tech/libeufin/sandbox/JSON.kt  | 11 ---
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  |  5 +-
 util/src/main/kotlin/JSON.kt                       | 24 +++++-
 util/src/main/kotlin/Payto.kt                      | 15 ++--
 17 files changed, 268 insertions(+), 103 deletions(-)
 create mode 100755 cli/tests/twg-history-loop.sh
 rename nexus/src/test/kotlin/{DownloadAndSubmit.kt => EbicsTest.kt} (96%)

diff --git a/cli/bin/libeufin-cli b/cli/bin/libeufin-cli
index ebd16ebf..9bbef229 100755
--- a/cli/bin/libeufin-cli
+++ b/cli/bin/libeufin-cli
@@ -498,6 +498,45 @@ def restore_backup(obj, backup_file, passphrase, 
connection_name):
     check_response_status(resp)
 
 
+@connections.command(help="make a new x-libeufin-bank connection")
+@click.option(
+    "--bank-url",
+    help="Bank base URL, typically ending with 
'../demobanks/default/access-api'",
+    required=True
+)
+@click.option(
+    "--username",
+    help="Username at the bank, that this connection impersonates.",
+    required=True
+)
+@click.password_option()
+@click.argument("connection-name")
+@click.pass_obj
+def new_xlibeufinbank_connection(obj, bank_url, username, password, 
connection_name):
+    url = urljoin_nodrop(obj.nexus_base_url, "/bank-connections")
+    body = dict(
+        name=connection_name,
+        source="new",
+        type="x-libeufin-bank",
+        data=dict(
+            baseUrl=bank_url,
+            username=username,
+            password=password
+        ),
+    )
+    try:
+        resp = post(
+            url,
+            json=body,
+            auth=auth.HTTPBasicAuth(obj.username, obj.password)
+        )
+    except Exception as e:
+        print(e)
+        print(f"Could not reach nexus at {url}")
+        exit(1)
+
+    check_response_status(resp)
+
 @connections.command(help="make new EBICS bank connection")
 @click.option("--ebics-url", help="EBICS URL", required=True)
 @click.option("--host-id", help="Host ID", required=True)
diff --git a/cli/tests/launch_services.sh b/cli/tests/launch_services.sh
index 2bee7df7..17cd81d6 100755
--- a/cli/tests/launch_services.sh
+++ b/cli/tests/launch_services.sh
@@ -4,8 +4,8 @@
 # EBICS pair, in order to try CLI commands.
 set -eu
 
-# WITH_TASKS=1
-WITH_TASKS=0
+WITH_TASKS=1
+# WITH_TASKS=0
 function exit_cleanup()
 {
   echo "Running exit-cleanup"
@@ -141,7 +141,5 @@ if test 1 = $WITH_TASKS; then
 else
   echo NOT creating background tasks!
 fi
-echo "Requesting Taler history with 90 seconds timeout..."
-curl -u test-user:x 
"http://localhost:5001/facades/test-facade/taler-wire-gateway/history/incoming?delta=5&long_poll_ms=90000";
 
 read -p "Press Enter to terminate..."
diff --git a/cli/tests/twg-history-loop.sh b/cli/tests/twg-history-loop.sh
new file mode 100755
index 00000000..eafff9e1
--- /dev/null
+++ b/cli/tests/twg-history-loop.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -exu
+
+HOWMANY_SECONDS=30
+NO_LONG_POLL=0
+
+if test $NO_LONG_POLL = 1; then
+  echo "Requesting Taler history WITHOUT any long-poll in infinite loop..."
+  while true; do
+    curl -u test-user:x \
+      
"http://localhost:5001/facades/test-facade/taler-wire-gateway/history/incoming?delta=5";
+  done
+  exit
+fi
+
+echo "Requesting Taler history with $HOWMANY_SECONDS second(s) timeout in 
infinite loop..."
+while true; do
+  curl -v -u test-user:x \
+    
"http://localhost:5001/facades/test-facade/taler-wire-gateway/history/incoming?delta=5&long_poll_ms=${HOWMANY_SECONDS}000";
+done
diff --git a/cli/tests/wire-transfer.sh b/cli/tests/wire-transfer.sh
index c29aae30..afc6fec5 100755
--- a/cli/tests/wire-transfer.sh
+++ b/cli/tests/wire-transfer.sh
@@ -1,13 +1,13 @@
 #!/bin/bash
 
-set -eu
+set -eux
+
 # Pays the www Sandbox user, usually owned by the Exchange.
 RESERVE_PUB=$(gnunet-ecc -g1 /tmp/www &> /dev/null && gnunet-ecc -p /tmp/www)
 # Must match the one from launch_services.sh
-export 
LIBEUFIN_SANDBOX_DB_CONNECTION=jdbc:postgresql://localhost:5432/taler?user=$(whoami)
+export 
LIBEUFIN_SANDBOX_DB_CONNECTION="jdbc:postgresql://localhost:5432/libeufincheck?user=$(whoami)"
 libeufin-sandbox \
   make-transaction \
     --credit-account=www \
     --debit-account=admin MANA:2 \
-   $RESERVE_PUB 
-echo Now paid reserve $RESERVE_PUB
+   $RESERVE_PUB
diff --git 
a/nexus/src/main/kotlin/tech/libeufin/nexus/BankConnectionProtocol.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/BankConnectionProtocol.kt
index ac52095c..e87e9444 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/BankConnectionProtocol.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/BankConnectionProtocol.kt
@@ -25,10 +25,13 @@ import io.ktor.http.HttpStatusCode
 import tech.libeufin.nexus.ebics.*
 import tech.libeufin.nexus.server.BankConnectionType
 import tech.libeufin.nexus.server.FetchSpecJson
+import tech.libeufin.nexus.server.XLibeufinBankTransport
+import tech.libeufin.nexus.xlibeufinbank.XlibeufinBankConnectionProtocol
 
 // 'const' allows only primitive types.
 val bankConnectionRegistry: Map<BankConnectionType, BankConnectionProtocol> = 
mapOf(
-    BankConnectionType.EBICS to EbicsBankConnectionProtocol()
+    BankConnectionType.EBICS to EbicsBankConnectionProtocol(),
+    BankConnectionType.X_LIBEUFIN_BANK to XlibeufinBankConnectionProtocol()
 )
 
 interface BankConnectionProtocol {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
index 38fb7bd3..5679b8ca 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -237,9 +237,7 @@ class NexusBankTransactionEntity(id: EntityID<Long>) : 
LongEntity(id) {
     }
 }
 
-/**
- * Represents a prepared payment.
- */
+// Represents a prepared payment.
 object PaymentInitiationsTable : LongIdTable() {
     /**
      * Bank account that wants to initiate the payment.
@@ -259,7 +257,6 @@ object PaymentInitiationsTable : LongIdTable() {
     val submitted = bool("submitted").default(false)
     var invalid = bool("invalid").nullable()
     val messageId = text("messageId")
-
     /**
      * Points at the raw transaction witnessing that this
      * initiated payment was successfully performed.
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
index 365a4ea5..d630b06b 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
@@ -247,7 +247,6 @@ fun talerFilter(
     payment: NexusBankTransactionEntity,
     txDtls: TransactionDetails
 ) {
-    val channelsToNotify = mutableListOf<String>()
     var isInvalid = false // True when pub is invalid or duplicate.
     val subject = txDtls.unstructuredRemittanceInformation
     val debtorName = txDtls.debtor?.name
diff --git 
a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
index 7843345f..145556ed 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
@@ -125,17 +125,15 @@ suspend fun submitAllPaymentInitiations(
                         "(pointed by bank account 
'${it.bankAccount.bankAccountName}')" +
                         " not found in the database."
             )
-            // Filter out non EBICS.
-            if (bankConnection.type != "ebics") {
+            try { 
BankConnectionType.parseBankConnectionType(bankConnection.type) }
+            catch (e: Exception) {
                 logger.info("Skipping non-implemented bank connection 
'${bankConnection.type}'")
                 return@forEach
             }
             workQueue.add(Submission(it.id.value))
         }
     }
-    workQueue.forEach {
-        submitPaymentInitiation(httpClient, it.id)
-    }
+    workQueue.forEach { submitPaymentInitiation(httpClient, it.id) }
 }
 
 /**
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
index f41a3729..ce644ddf 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
@@ -125,10 +125,8 @@ private suspend fun fetchEbicsC5x(
     }
 
     when (historyType) {
-        "C52" -> {
-        }
-        "C53" -> {
-        }
+        "C52" -> {}
+        "C53" -> {}
         else -> {
             throw NexusError(HttpStatusCode.BadRequest, "history type 
'$historyType' not supported")
         }
@@ -526,7 +524,10 @@ class EbicsBankConnectionProtocol: BankConnectionProtocol {
     override suspend fun submitPaymentInitiation(httpClient: HttpClient, 
paymentInitiationId: Long) {
         val dbData = transaction {
             val preparedPayment = getPaymentInitiation(paymentInitiationId)
-            val conn = preparedPayment.bankAccount.defaultBankConnection ?: 
throw NexusError(HttpStatusCode.NotFound, "no default bank connection available 
for submission")
+            val conn = preparedPayment.bankAccount.defaultBankConnection ?: 
throw NexusError(
+                HttpStatusCode.NotFound,
+                "no default bank connection available for submission"
+            )
             val subscriberDetails = 
getEbicsSubscriberDetails(conn.connectionId)
             val painMessage = createPain001document(
                 NexusPaymentInitiationData(
diff --git 
a/nexus/src/main/kotlin/tech/libeufin/nexus/xlibeufinbank/XLibeufinBankNexus.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/xlibeufinbank/XLibeufinBankNexus.kt
index b819163a..f83046c3 100644
--- 
a/nexus/src/main/kotlin/tech/libeufin/nexus/xlibeufinbank/XLibeufinBankNexus.kt
+++ 
b/nexus/src/main/kotlin/tech/libeufin/nexus/xlibeufinbank/XLibeufinBankNexus.kt
@@ -15,10 +15,7 @@ import tech.libeufin.nexus.*
 import tech.libeufin.nexus.bankaccount.*
 import tech.libeufin.nexus.iso20022.*
 import tech.libeufin.nexus.server.*
-import tech.libeufin.util.XLibeufinBankDirection
-import tech.libeufin.util.XLibeufinBankTransaction
-import tech.libeufin.util.badRequest
-import tech.libeufin.util.internalServerError
+import tech.libeufin.util.*
 import java.net.MalformedURLException
 import java.net.URL
 
@@ -46,7 +43,16 @@ fun getXLibeufinBankCredentials(connId: String): 
XLibeufinBankTransport {
 
 class XlibeufinBankConnectionProtocol : BankConnectionProtocol {
     override suspend fun connect(client: HttpClient, connId: String) {
-        TODO("Not yet implemented")
+        // Only checking that the credentials + bank URL are correct.
+        val conn = getBankConnection(connId)
+        val credentials = getXLibeufinBankCredentials(conn)
+        // Defining the URL to request the bank account balance.
+        val url = credentials.baseUrl + "/accounts/${credentials.username}"
+        // Error handling expected by the caller.
+        client.get(url) {
+            expectSuccess = true
+            basicAuth(credentials.username, credentials.password)
+        }
     }
 
     override suspend fun fetchAccounts(client: HttpClient, connId: String) {
@@ -66,7 +72,6 @@ class XlibeufinBankConnectionProtocol : 
BankConnectionProtocol {
         connId: String,
         user: NexusUserEntity,
         data: JsonNode) {
-
         val bankConn = transaction {
             NexusBankConnectionEntity.new {
                 this.connectionId = connId
@@ -105,8 +110,67 @@ class XlibeufinBankConnectionProtocol : 
BankConnectionProtocol {
         throw NotImplementedError("x-libeufin-bank does not need analog 
details")
     }
 
-    override suspend fun submitPaymentInitiation(httpClient: HttpClient, 
paymentInitiationId: Long) {
-        TODO("Not yet implemented")
+    override suspend fun submitPaymentInitiation(
+        httpClient: HttpClient,
+        paymentInitiationId: Long
+    ) {
+        /**
+         * Main steps.
+         *
+         * 1) Get prep from the DB.
+         * 2) Collect credentials.
+         * 3) Create the format to POST.
+         * 4) POST the transaction.
+         * 5) Mark the prep as submitted.
+         * */
+        // 1
+        val preparedPayment = getPaymentInitiation(paymentInitiationId)
+        // 2
+        val conn = transaction { 
preparedPayment.bankAccount.defaultBankConnection } ?: throw
+                internalServerError("Default connection not found for bank 
account: ${preparedPayment.bankAccount.bankAccountName}")
+        val credentials: XLibeufinBankTransport = 
getXLibeufinBankCredentials(conn)
+        // 3
+        val paytoUri = buildIbanPaytoUri(
+            iban = preparedPayment.creditorIban,
+            bic = preparedPayment.creditorBic ?: "SANDBOXX",
+            receiverName = preparedPayment.creditorName,
+            message = preparedPayment.subject
+        )
+        val req = 
jacksonObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(
+            XLibeufinBankPaytoReq(
+                paytoUri = paytoUri,
+                amount = "${preparedPayment.currency}:${preparedPayment.sum}",
+                pmtInfId = preparedPayment.paymentInformationId
+            )
+        )
+        // 4
+        val url = credentials.baseUrl + 
"/accounts/${credentials.username}/transactions"
+        logger.debug("POSTing transactions to x-libeufin-bank at: $url")
+        val r = httpClient.post(url) {
+            expectSuccess = false
+            contentType(ContentType.Application.Json)
+            basicAuth(credentials.username, credentials.password)
+            setBody(req)
+        }
+        if (r.status.value.toString().startsWith("5")) {
+            throw NexusError(
+                HttpStatusCode.BadGateway,
+                "The bank failed: ${r.bodyAsText()}"
+            )
+        }
+        if (!r.status.value.toString().startsWith("2")) {
+            throw NexusError(
+                /**
+                 * Echoing whichever status code the bank gave.  That
+                 * however masks client errors where - for example - a
+                 * request detail causes 404 where Nexus has no power.
+                 */
+                HttpStatusCode(r.status.value, r.status.description),
+                r.bodyAsText()
+            )
+        }
+        // 5
+        transaction { preparedPayment.submitted = true }
     }
 
     override suspend fun fetchTransactions(
@@ -132,7 +196,7 @@ class XlibeufinBankConnectionProtocol : 
BankConnectionProtocol {
         /**
          * Now builds the URL to ask the transactions, according to the
          * FetchSpec gotten in the args.  Level 'statement' and time range
-         * 'previous-dayes' are NOT implemented.
+         * 'previous-days' are NOT implemented.
          */
         val baseUrl = URL(credentials.baseUrl)
         val fetchUrl = url {
@@ -237,9 +301,7 @@ fun processXLibeufinBankMessage(
         }
         // Searching for duplicates.
         if (findDuplicate(bankAccountId, it.uid) != null) {
-            logger.debug(
-                "x-libeufin-bank ingestion: transaction ${it.uid} is a 
duplicate, skipping."
-            )
+            logger.debug("x-libeufin-bank ingestion: transaction ${it.uid} is 
a duplicate, skipping.")
             return@forEach
         }
         val direction = if (it.debtorIban == bankAccount.iban)
@@ -268,7 +330,7 @@ fun processXLibeufinBankMessage(
              * (outgoing) payment with the one being iterated over.
              */
             if (direction == XLibeufinBankDirection.DEBIT) {
-                val maybePrepared = getPaymentInitiation(pmtInfId = it.uid)
+                val maybePrepared = it.pmtInfId?.let { it1 -> 
getPaymentInitiation(pmtInfId = it1) }
                 if (maybePrepared != null) 
maybePrepared.confirmationTransaction = localTx
             }
             // x-libeufin-bank transactions are ALWAYS modeled as reports
diff --git a/nexus/src/test/kotlin/DownloadAndSubmit.kt 
b/nexus/src/test/kotlin/EbicsTest.kt
similarity index 96%
rename from nexus/src/test/kotlin/DownloadAndSubmit.kt
rename to nexus/src/test/kotlin/EbicsTest.kt
index 622ff928..8de0d5a5 100644
--- a/nexus/src/test/kotlin/DownloadAndSubmit.kt
+++ b/nexus/src/test/kotlin/EbicsTest.kt
@@ -1,7 +1,4 @@
-import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
 import io.ktor.server.application.*
-import io.ktor.client.*
-import io.ktor.client.request.*
 import io.ktor.http.*
 import io.ktor.server.plugins.contentnegotiation.*
 import io.ktor.server.request.*
@@ -23,14 +20,17 @@ import 
tech.libeufin.nexus.iso20022.NexusPaymentInitiationData
 import tech.libeufin.nexus.iso20022.createPain001document
 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
-import kotlin.reflect.full.isSubclassOf
+
+/**
+ * These test cases run EBICS CCT and C52, mixing ordinary operations
+ * and some error cases.
+ */
 
 /**
  * Data to make the test server return for EBICS
@@ -87,9 +87,7 @@ fun getCustomEbicsServer(r: EbicsResponses, endpoint: String 
= "/ebicsweb"): App
 }
 
 class DownloadAndSubmit {
-    /**
-     * Download a C52 report from the bank.
-     */
+    // Downloads a C52 report from the bank.
     @Test
     fun download() {
         withNexusAndSandboxUser {
@@ -129,9 +127,8 @@ class DownloadAndSubmit {
             }
         }
     }
-    /**
-     * Upload one payment instruction to the bank.
-     */
+
+    // Uploads one payment instruction to the bank.
     @Test
     fun upload() {
         withNexusAndSandboxUser {
@@ -223,8 +220,8 @@ class DownloadAndSubmit {
     }
 
     /**
-     * Submit one payment instruction with an invalid Pain.001
-     * document, and check that it was marked as invalid.  Hence,
+     * Submits one payment instruction with an invalid Pain.001
+     * document, and checks that it was marked as invalid.  Hence,
      * the error is expected only by the first submission, since
      * the second won't pick the invalid payment.
      */
@@ -264,6 +261,10 @@ class DownloadAndSubmit {
         }
     }
 
+    /**
+     * Submits one pain.001 document with the wrong currency and checks
+     * that the bank responded with EBICS_PROCESSING_ERROR.
+     */
     @Test
     fun unsupportedCurrency() {
         withNexusAndSandboxUser {
@@ -278,7 +279,7 @@ class DownloadAndSubmit {
                             creditorName = "Tester",
                             subject = "test payment",
                             sum = "1",
-                            currency = "EUR"
+                            currency = "EUR" // EUR not supported.
                         ),
                         transaction {
                             NexusBankAccountEntity.findByName("foo") ?: throw 
Exception("Test failed")
diff --git a/nexus/src/test/kotlin/TalerTest.kt 
b/nexus/src/test/kotlin/TalerTest.kt
index f877203b..6565c197 100644
--- a/nexus/src/test/kotlin/TalerTest.kt
+++ b/nexus/src/test/kotlin/TalerTest.kt
@@ -18,53 +18,61 @@ import tech.libeufin.nexus.talerFilter
 import tech.libeufin.sandbox.sandboxApp
 import tech.libeufin.sandbox.wireTransfer
 import tech.libeufin.util.NotificationsChannelDomains
+import tech.libeufin.util.getIban
 
 // This class tests the features related to the Taler facade.
 class TalerTest {
-    val mapper = ObjectMapper()
+    private val mapper = ObjectMapper()
+
+    @Test
+    fun historyOutgoingTestEbics() {
+        historyOutgoingTest("foo")
+    }
+    @Test
+    fun historyOutgoingTestXLibeufinBank() {
+        historyOutgoingTest("bar")
+    }
 
     // Checking that a call to POST /transfer results in
     // an outgoing payment in GET /history/outgoing.
-    @Test
-    fun historyOutgoingTest() {
+    fun historyOutgoingTest(testedAccount: String) {
         withNexusAndSandboxUser {
             testApplication {
                 application(nexusApp)
-                client.post("/facades/foo-facade/taler-wire-gateway/transfer") 
{
+                
client.post("/facades/$testedAccount-facade/taler-wire-gateway/transfer") {
                     contentType(ContentType.Application.Json)
-                    basicAuth("foo", "foo") // exchange's credentials
+                    basicAuth(testedAccount, testedAccount) // exchange's 
credentials
                     expectSuccess = true
                     setBody("""
                         { "request_uid": "twg_transfer_0",
                           "amount": "TESTKUDOS:3",
                           "exchange_base_url": "http://exchange.example.com/";,
                           "wtid": "T0",
-                          "credit_account": 
"payto://iban/${BAR_USER_IBAN}?receiver-name=Bar"
-                        
+                          "credit_account": 
"payto://iban/${BANK_IBAN}?receiver-name=Not-Used"
                         }
                     """.trimIndent())
                 }
             }
-            /* The EBICS layer sends the payment instruction to the bank here.
-             *  and the reconciliation mechanism in Nexus should detect that 
one
-             *  outgoing payment was indeed the one instructed via the TWG.  
The
-             *  reconciliation will make the outgoing payment visible via 
/history/outgoing.
-             *  The following block achieve this by starting Sandbox and 
sending all
-             *  the prepared payments to it.
+            /* The bank connection sends the payment instruction to the bank 
here.
+             * and the reconciliation mechanism in Nexus should detect that one
+             * outgoing payment was indeed the one instructed via the TWG.  The
+             * reconciliation will make the outgoing payment visible via 
/history/outgoing.
+             * The following block achieve this by starting Sandbox and 
sending all
+             * the prepared payments to it.
              */
             testApplication {
                 application(sandboxApp)
-                submitAllPaymentInitiations(client, "foo")
+                submitAllPaymentInitiations(client, testedAccount)
                 /* Now downloads transactions from the bank, where the payment
                    submitted in the previous block is expected to appear as 
outgoing.
                  */
                 fetchBankAccountTransactions(
                     client,
                     fetchSpec = FetchSpecAllJson(
-                        level = FetchLevel.REPORT,
-                        "foo"
+                        level = if (testedAccount == "bar") 
FetchLevel.STATEMENT else FetchLevel.REPORT,
+                        bankConnection = testedAccount
                     ),
-                    "foo"
+                    accountId = testedAccount
                 )
             }
             /**
@@ -73,10 +81,10 @@ class TalerTest {
              */
             testApplication {
                 application(nexusApp)
-                val r = 
client.get("/facades/foo-facade/taler-wire-gateway/history/outgoing?delta=5") {
+                val r = 
client.get("/facades/$testedAccount-facade/taler-wire-gateway/history/outgoing?delta=5")
 {
                     expectSuccess = true
                     contentType(ContentType.Application.Json)
-                    basicAuth("foo", "foo")
+                    basicAuth(testedAccount, testedAccount)
                 }
                 val j = mapper.readTree(r.readBytes())
                 val wtidFromTwg = 
j.get("outgoing_transactions").get(0).get("wtid").asText()
@@ -103,7 +111,7 @@ class TalerTest {
         )
     }
 
-    // Tests that even if one call is long-polling, other calls
+    // Tests that even if one call is long-polling, other calls respond.
     @Test
     fun servingTest() {
         withTestDatabase {
@@ -135,7 +143,7 @@ class TalerTest {
 
     // Downloads Taler txs via the default connection of 'testedAccount'.
     // This allows to test the Taler logic on different connection types.
-    fun historyIncomingTest(testedAccount: String, connType: 
BankConnectionType) {
+    private fun historyIncomingTest(testedAccount: String, connType: 
BankConnectionType) {
         val reservePub = "GX5H5RME193FDRCM1HZKERXXQ2K21KH7788CKQM8X6MYKYRBP8F0"
         withNexusAndSandboxUser {
             testApplication {
@@ -163,10 +171,6 @@ class TalerTest {
                     }
                     launch {
                         delay(500)
-                        /**
-                         * FIXME: this test never gets the server to wait 
notifications from the DBMS.
-                         * Somehow, the wire transfer arrives always before 
the blocking await on the DBMS.
-                         */
                         newNexusBankTransaction(
                             currency = "KUDOS",
                             value = "10",
diff --git a/nexus/src/test/kotlin/XLibeufinBankTest.kt 
b/nexus/src/test/kotlin/XLibeufinBankTest.kt
index 9af9133c..852caff4 100644
--- a/nexus/src/test/kotlin/XLibeufinBankTest.kt
+++ b/nexus/src/test/kotlin/XLibeufinBankTest.kt
@@ -3,17 +3,18 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
 import io.ktor.server.testing.*
 import org.jetbrains.exposed.sql.transactions.transaction
 import org.junit.Test
-import tech.libeufin.nexus.BankConnectionProtocol
-import tech.libeufin.nexus.NexusBankTransactionEntity
-import tech.libeufin.nexus.NexusBankTransactionsTable
+import tech.libeufin.nexus.*
+import tech.libeufin.nexus.bankaccount.addPaymentInitiation
 import tech.libeufin.nexus.bankaccount.ingestBankMessagesIntoAccount
-import tech.libeufin.nexus.getNexusUser
 import tech.libeufin.nexus.iso20022.CamtBankAccountEntry
 import tech.libeufin.nexus.server.*
 import tech.libeufin.nexus.xlibeufinbank.XlibeufinBankConnectionProtocol
+import tech.libeufin.sandbox.BankAccountTransactionEntity
+import tech.libeufin.sandbox.BankAccountTransactionsTable
 import tech.libeufin.sandbox.sandboxApp
 import tech.libeufin.sandbox.wireTransfer
 import tech.libeufin.util.XLibeufinBankTransaction
+import tech.libeufin.util.getIban
 import java.net.URL
 
 // Testing the x-libeufin-bank communication
@@ -34,9 +35,37 @@ class XLibeufinBankTest {
      */
     @Test
     fun submitTransaction() {
-
+        withTestDatabase {
+            prepSandboxDb()
+            prepNexusDb()
+            testApplication {
+                application(sandboxApp)
+                val pId = addPaymentInitiation(
+                    Pain001Data(
+                        creditorIban = FOO_USER_IBAN,
+                        creditorBic = "SANDBOXX",
+                        creditorName = "Tester",
+                        subject = "test payment",
+                        sum = "1",
+                        currency = "TESTKUDOS"
+                    ),
+                    transaction {
+                        NexusBankAccountEntity.findByName("bar") ?:
+                        throw Exception("Test failed, env didn't provide Nexus 
bank account 'bar'")
+                    }
+                )
+                val conn = XlibeufinBankConnectionProtocol()
+                conn.submitPaymentInitiation(this.client, pId.id.value)
+                val maybeArrivedPayment = transaction {
+                    BankAccountTransactionEntity.find {
+                        BankAccountTransactionsTable.pmtInfId eq 
pId.paymentInformationId
+                    }.firstOrNull()
+                }
+                // Now look for the payment in the database.
+                assert(maybeArrivedPayment != null)
+            }
+        }
     }
-
     /**
      * Testing that Nexus downloads one transaction from
      * Sandbox via the x-libeufin-bank protocol supplier
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
index 87021146..dac660da 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
@@ -19,7 +19,6 @@
 
 package tech.libeufin.sandbox
 
-import com.fasterxml.jackson.annotation.JsonProperty
 import tech.libeufin.util.PaymentInfo
 
 data class WithdrawalRequest(
@@ -148,16 +147,6 @@ data class TalerWithdrawalSelection(
     val selected_exchange: String?
 )
 
-data class NewTransactionReq(
-    /**
-     * This Payto address must contain the wire transfer
-     * subject among its query parameters -- 'message' parameter.
-     */
-    val paytoUri: String,
-    // $currency:X.Y format
-    val amount: String?
-)
-
 data class SandboxConfig(
     val currency: String,
     val version: String,
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index a1a4d70b..9cc331d2 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -1259,7 +1259,7 @@ val sandboxApp: Application.() -> Unit = {
                     val authGranted: Boolean = !WITH_AUTH
                     if (!authGranted && username != bankAccount.label)
                         throw unauthorized("Username '$username' has no rights 
over bank account ${bankAccount.label}")
-                    val req = call.receive<NewTransactionReq>()
+                    val req = call.receive<XLibeufinBankPaytoReq>()
                     val payto = parsePayto(req.paytoUri)
                     val amount: String? = payto.amount ?: req.amount
                     if (amount == null) throw badRequest("Amount is missing")
@@ -1274,7 +1274,8 @@ val sandboxApp: Application.() -> Unit = {
                             subject = payto.message ?: throw badRequest(
                                 "'message' query parameter missing in Payto 
address"
                             ),
-                            amount = amount
+                            amount = amount,
+                            pmtInfId = req.pmtInfId
                         )
                     }
                     call.respond(object {})
diff --git a/util/src/main/kotlin/JSON.kt b/util/src/main/kotlin/JSON.kt
index 96ee1e05..71274ccd 100644
--- a/util/src/main/kotlin/JSON.kt
+++ b/util/src/main/kotlin/JSON.kt
@@ -52,6 +52,22 @@ enum class XLibeufinBankDirection(val direction: String) {
         }
     }
 }
+
+data class XLibeufinBankPaytoReq(
+    /**
+     * This Payto address MUST contain the wire transfer
+     * subject among its query parameters -- 'message' parameter.
+     */
+    val paytoUri: String,
+    // $currency:X.Y format
+    val amount: String?,
+    /**
+     * This value MAY be specified by the payment submitter to
+     * help reconcile the payment when they later download new
+     * transactions.  The name is only borrowed from CaMt terminology.
+     */
+    val pmtInfId: String?
+)
 data class XLibeufinBankTransaction(
     val creditorIban: String,
     val creditorBic: String?,
@@ -66,9 +82,11 @@ data class XLibeufinBankTransaction(
     val date: String,
     val uid: String,
     val direction: XLibeufinBankDirection,
-    // The following two values are rather CAMT/PAIN
-    // specific, therefore do not need to be returned
-    // along every API call using this object.
+    /**
+     * The following two values are rather CAMT/PAIN
+     * specific, therefore do not need to be returned
+     * along every API call using this object.
+     */
     val pmtInfId: String? = null,
     val msgId: String? = null
 )
diff --git a/util/src/main/kotlin/Payto.kt b/util/src/main/kotlin/Payto.kt
index f7466787..9d1f401d 100644
--- a/util/src/main/kotlin/Payto.kt
+++ b/util/src/main/kotlin/Payto.kt
@@ -5,10 +5,9 @@ import io.ktor.http.*
 import java.net.URI
 import java.net.URLDecoder
 import java.net.URLEncoder
+import javax.security.auth.Subject
 
-/**
- * Payto information.
- */
+// Payto information.
 data class Payto(
     // represent query param "sender-name" or "receiver-name".
     val receiverName: String?,
@@ -78,9 +77,15 @@ fun parsePayto(payto: String): Payto {
 
 fun buildIbanPaytoUri(
     iban: String,
-    bic: String?,
+    bic: String,
     receiverName: String,
+    message: String? = null
 ): String {
     val nameUrlEnc = URLEncoder.encode(receiverName, "utf-8")
-    return "payto://iban/${if (bic != null) "$bic/" else 
""}$iban?receiver-name=$nameUrlEnc"
+    val ret = "payto://iban/$bic/$iban?receiver-name=$nameUrlEnc"
+    if (message != null) {
+        val messageUrlEnc = URLEncoder.encode(receiverName, "utf-8")
+        return "$ret&message=$messageUrlEnc"
+    }
+    return ret
 }
\ No newline at end of file

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