gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated (107a5bc -> f54438f)


From: gnunet
Subject: [libeufin] branch master updated (107a5bc -> f54438f)
Date: Fri, 10 Apr 2020 00:17:27 +0200

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

marcello pushed a change to branch master
in repository libeufin.

    from 107a5bc  testing payto/amount parsers
     new b37353d  Completing admin add-incoming.
     new 95994e9  DB definitions for payments ordered by the exchange.
     new 0169677  Create main/rough logic for Taler-started transfers.
     new 96950a3  Store bank-provided IDs, and transaction statuses.
     new f54438f  propagate last change

The 5 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/python/libeufin-cli                            |   4 +-
 nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt    |  82 +++++++---
 nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt  |   1 +
 nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt  |  29 ++--
 nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 165 +++++++++++++++++----
 util/src/main/kotlin/XMLUtil.kt                    |   9 ++
 6 files changed, 229 insertions(+), 61 deletions(-)

diff --git a/cli/python/libeufin-cli b/cli/python/libeufin-cli
index 10e8b32..221d0d5 100755
--- a/cli/python/libeufin-cli
+++ b/cli/python/libeufin-cli
@@ -465,8 +465,8 @@ def refund(ctx, account_id, bank_account_id, 
nexus_base_url):
 @click.argument(
   "nexus-base-url"
 )
-def digest_transactions(obj, account_id, nexus_base_url):
-    url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/digest-incoming-transactions".format(account_id))
+def crunch_transactions(obj, account_id, nexus_base_url):
+    url = urljoin(nexus_base_url, 
"/ebics/subscribers/{}/crunch-incoming-transactions".format(account_id))
     resp = post(url, json=dict())
     print(resp.content.decode("utf-8"))
 
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
index c0f8ec9..26c20cf 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -10,17 +10,52 @@ import java.sql.Connection
 
 const val ID_MAX_LENGTH = 50
 
+/**
+ * This table holds the values that exchange gave to issue a payment,
+ * plus a reference to the prepared pain.001 version of.  Note that
+ * whether a pain.001 document was sent or not to the bank is indicated
+ * in the PAIN-table.
+ */
+object TalerRequestedPayments: LongIdTable() {
+    val preparedPayment = TalerIncomingPayments.reference("payment", 
Pain001Table)
+    val requestUId = text("request_uid")
+    val amount = text("amount")
+    val exchangeBaseUrl = text("exchange_base_url")
+    val wtid = text("wtid")
+    val creditAccount = text("credit_account")
+}
+
+class TalerRequestedPaymentEntity(id: EntityID<Long>) : LongEntity(id) {
+    companion object : 
LongEntityClass<TalerRequestedPaymentEntity>(TalerIncomingPayments)
+    var preparedPayment by Pain001Entity referencedOn 
TalerRequestedPayments.preparedPayment
+    var requestUId by TalerRequestedPayments.requestUId
+    var amount by TalerRequestedPayments.amount
+    var exchangeBaseUrl by TalerRequestedPayments.exchangeBaseUrl
+    var wtid by TalerRequestedPayments.wtid
+    var creditAccount by TalerRequestedPayments.creditAccount
+}
+
+/**
+ * This table "augments" the information given in the raw payments table, with 
Taler-related
+ * ones.  It tells if a payment is valid and/or it was refunded already.  And 
moreover, it is
+ * the table whose ("clean") IDs the exchange will base its history requests 
on.
+ */
 object TalerIncomingPayments: LongIdTable() {
     val payment = reference("payment", EbicsRawBankTransactionsTable)
     val valid = bool("valid")
     // avoid refunding twice!
-    val processed = bool("refunded").default(false)
+    val refunded = bool("refunded").default(false)
 }
 
-class TalerIncomingPaymentEntry(id: EntityID<Long>) : LongEntity(id) {
-    companion object : 
LongEntityClass<TalerIncomingPaymentEntry>(TalerIncomingPayments) {
-        override fun new(init: TalerIncomingPaymentEntry.() -> Unit): 
TalerIncomingPaymentEntry {
+class TalerIncomingPaymentEntity(id: EntityID<Long>) : LongEntity(id) {
+    companion object : 
LongEntityClass<TalerIncomingPaymentEntity>(TalerIncomingPayments) {
+        override fun new(init: TalerIncomingPaymentEntity.() -> Unit): 
TalerIncomingPaymentEntity {
             val newRow = super.new(init)
+            /**
+             * In case the exchange asks for all the values strictly lesser 
than MAX_VALUE,
+             * it would lose the row whose id == MAX_VALUE.  So the check 
below makes this
+             * situation impossible by disallowing MAX_VALUE as a id value.
+             */
             if (newRow.id.value == Long.MAX_VALUE) {
                 throw NexusError(
                     HttpStatusCode.InsufficientStorage, "Cannot store rows 
anymore"
@@ -29,21 +64,21 @@ class TalerIncomingPaymentEntry(id: EntityID<Long>) : 
LongEntity(id) {
             return newRow
         }
     }
-    var payment by EbicsRawBankTransactionEntry referencedOn 
TalerIncomingPayments.payment
+    var payment by EbicsRawBankTransactionEntity referencedOn 
TalerIncomingPayments.payment
     var valid by TalerIncomingPayments.valid
-    var processed by TalerIncomingPayments.processed
+    var refunded by TalerIncomingPayments.refunded
 }
 
+/**
+ * This table _assumes_ that all the entries have a BOOK status.  The
+ * current code however does only enforces this trusting the C53 response,
+ * but never actually checking the appropriate "Sts" field.
+ */
 object EbicsRawBankTransactionsTable : LongIdTable() {
     val nexusSubscriber = reference("subscriber", EbicsSubscribersTable)
-    // How did we learn about this transaction?  C52 / C53 / C54
-    val sourceType = text("sourceType")
-    // Name of the ZIP entry
-    val sourceFileName = text("sourceFileName")
-    // "Subject" of the SEPA transaction
+    val sourceFileName = text("sourceFileName") /* ZIP entry's name */
     val unstructuredRemittanceInformation = 
text("unstructuredRemittanceInformation")
-    // Debit or credit
-    val transactionType = text("transactionType")
+    val transactionType = text("transactionType") /* DBIT or CRDT */
     val currency = text("currency")
     val amount = text("amount")
     val creditorIban = text("creditorIban")
@@ -52,11 +87,13 @@ object EbicsRawBankTransactionsTable : LongIdTable() {
     val debitorName = text("debitorName")
     val counterpartBic = text("counterpartBic")
     val bookingDate = text("bookingDate")
+    val status = text("status") // BOOK, ..
+    val servicerCode = text("servicerCode").nullable() /* "internal" code 
given by the bank */
+    val proprietaryCode = text("proprietaryCode") /* code given by the DK */
 }
 
-class EbicsRawBankTransactionEntry(id: EntityID<Long>) : LongEntity(id) {
-    companion object : 
LongEntityClass<EbicsRawBankTransactionEntry>(EbicsRawBankTransactionsTable)
-    var sourceType by EbicsRawBankTransactionsTable.sourceType // C52 or C53 
or C54?
+class EbicsRawBankTransactionEntity(id: EntityID<Long>) : LongEntity(id) {
+    companion object : 
LongEntityClass<EbicsRawBankTransactionEntity>(EbicsRawBankTransactionsTable)
     var sourceFileName by EbicsRawBankTransactionsTable.sourceFileName
     var unstructuredRemittanceInformation by 
EbicsRawBankTransactionsTable.unstructuredRemittanceInformation
     var transactionType by EbicsRawBankTransactionsTable.transactionType
@@ -69,13 +106,23 @@ class EbicsRawBankTransactionEntry(id: EntityID<Long>) : 
LongEntity(id) {
     var counterpartBic by EbicsRawBankTransactionsTable.counterpartBic
     var bookingDate by EbicsRawBankTransactionsTable.bookingDate
     var nexusSubscriber by EbicsSubscriberEntity referencedOn 
EbicsRawBankTransactionsTable.nexusSubscriber
+    var status by EbicsRawBankTransactionsTable.status
+    var servicerCode by EbicsRawBankTransactionsTable.servicerCode
+    var proprietaryCode by EbicsRawBankTransactionsTable.proprietaryCode
 }
 
+/**
+ * NOTE: every column in this table corresponds to a particular
+ * value described in the pain.001 official documentation; therefore
+ * this table is not really suitable to hold custom data (like Taler-related,
+ * for example)
+ */
 object Pain001Table : IntIdTableWithAmount() {
     val msgId = long("msgId").uniqueIndex().autoIncrement()
     val paymentId = long("paymentId")
     val fileDate = long("fileDate")
     val sum = amount("sum")
+    val currency = varchar("currency", length = 3).default("EUR")
     val debtorAccount = text("debtorAccount")
     val endToEndId = long("EndToEndId")
     val subject = text("subject")
@@ -88,7 +135,7 @@ object Pain001Table : IntIdTableWithAmount() {
 
     /* Indicates whether the bank didn't perform the payment: note that
     * this state can be reached when the payment gets listed in a CRZ
-    * response OR when the payment doesn's show up in a C52/C53 response
+    * response OR when the payment doesn't show up in a C52/C53 response
     */
     val invalid = bool("invalid").default(false)
 }
@@ -99,6 +146,7 @@ class Pain001Entity(id: EntityID<Int>) : IntEntity(id) {
     var paymentId by Pain001Table.paymentId
     var date by Pain001Table.fileDate
     var sum by Pain001Table.sum
+    var currency by Pain001Table.currency
     var debtorAccount by Pain001Table.debtorAccount
     var endToEndId by Pain001Table.endToEndId
     var subject by Pain001Table.subject
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt
index a4774a9..7d7eb28 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt
@@ -91,6 +91,7 @@ data class Pain001Data(
     val creditorBic: String,
     val creditorName: String,
     val sum: Amount,
+    val currency: String = "EUR",
     val subject: String
 )
 
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index ae80bef..9d334ec 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -142,7 +142,8 @@ fun getSubscriberDetailsFromBankAccount(bankAccountId: 
String): EbicsClientSubsc
 /**
  * Given a subscriber id, returns the _list_ of bank accounts associated to it.
  * @param id the subscriber id
- * @return the query set containing the subscriber's bank accounts
+ * @return the query set containing the subscriber's bank accounts.  The result
+ * is guaranteed to be non empty.
  */
 fun getBankAccountsInfoFromId(id: String): 
SizedIterable<EbicsAccountInfoEntity> {
     val list = transaction {
@@ -287,7 +288,7 @@ fun createPain001document(pain001Entity: Pain001Entity): 
String {
                             }
                         }
                         element("Amt/InstdAmt") {
-                            attribute("Ccy", "EUR")
+                            attribute("Ccy", pain001Entity.currency)
                             text(pain001Entity.sum.toString())
                         }
                         element("CdtrAgt/FinInstnId/BIC") {
@@ -312,10 +313,14 @@ fun createPain001document(pain001Entity: Pain001Entity): 
String {
 
 /**
  * Insert one row in the database, and leaves it marked as non-submitted.
+ * @param debtorAccountId the mnemonic id assigned by the bank to one bank
+ * account of the subscriber that is creating the pain entity.  In this case,
+ * it will be the account whose money will pay the wire transfer being defined
+ * by this pain document.
  */
-fun createPain001entry(entry: Pain001Data, debtorAccountId: String) {
+fun createPain001entity(entry: Pain001Data, debtorAccountId: String): 
Pain001Entity {
     val randomId = Random().nextLong()
-    transaction {
+    return transaction {
         Pain001Entity.new {
             subject = entry.subject
             sum = entry.sum
@@ -482,7 +487,7 @@ fun main() {
                     accountInfo.id.value
                 }
                 val pain001data = call.receive<Pain001Data>()
-                createPain001entry(pain001data, acctid)
+                createPain001entity(pain001data, acctid)
                 call.respondText(
                     "Payment instructions persisted in DB",
                     ContentType.Text.Plain, HttpStatusCode.OK
@@ -629,9 +634,8 @@ fun main() {
                 var ret = ""
                 transaction {
                     val subscriber: EbicsSubscriberEntity = 
getSubscriberEntityFromId(id)
-                    EbicsRawBankTransactionEntry.find {
-                        (EbicsRawBankTransactionsTable.nexusSubscriber eq 
subscriber.id.value) and
-                                (EbicsRawBankTransactionsTable.sourceType eq 
"C53")
+                    EbicsRawBankTransactionEntity.find {
+                        EbicsRawBankTransactionsTable.nexusSubscriber eq 
subscriber.id.value
                     }.forEach {
                         ret += "###\nDebitor: ${it.debitorIban}\nCreditor: 
${it.creditorIban}\nAmount: ${it.currency}:${it.amount}\nDate: 
${it.bookingDate}\n"
                     }
@@ -653,8 +657,7 @@ fun main() {
                 val paramsJson = call.receive<EbicsStandardOrderParamsJson>()
                 val orderParams = paramsJson.toOrderParams()
                 val subscriberData = getSubscriberDetailsFromId(id)
-                val response = doEbicsDownloadTransaction(client, 
subscriberData, "C53", orderParams)
-                when (response) {
+                when (val response = doEbicsDownloadTransaction(client, 
subscriberData, "C53", orderParams)) {
                     is EbicsDownloadSuccessResult -> {
                         /**
                          * The current code is _heavily_ dependent on the way 
GLS returns
@@ -667,13 +670,15 @@ fun main() {
                             val fileName = it.first
                             val camt53doc = 
XMLUtil.parseStringIntoDom(it.second)
                             transaction {
-                                EbicsRawBankTransactionEntry.new {
-                                    sourceType = "C53"
+                                EbicsRawBankTransactionEntity.new {
                                     sourceFileName = fileName
                                     unstructuredRemittanceInformation = 
camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']/@Ccy")
                                     transactionType = 
camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='CdtDbtInd']")
                                     currency = 
camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']/@Ccy")
                                     amount = 
camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']")
+                                    status = 
camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Sts']")
+                                    servicerCode = 
camt53doc.pickStringNullable("//*[local-name()='Ntry']//*[local-name()='AcctSvcrRef']")
+                                    proprietaryCode = 
camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='BkTxCd']/*[local-name()='Prtry']/*[local-name()='Cd']")
                                     bookingDate = 
camt53doc.pickString("//*[local-name()='BookgDt']//*[local-name()='Dt']")
                                     nexusSubscriber = 
getSubscriberEntityFromId(id)
                                     creditorName =
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
index 2d9f338..ab448ec 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
@@ -16,7 +16,7 @@ import org.joda.time.DateTime
 import org.joda.time.format.DateTimeFormat
 import tech.libeufin.util.Amount
 import tech.libeufin.util.CryptoUtil
-import java.util.*
+import tech.libeufin.util.toZonedString
 import kotlin.math.abs
 
 class Taler(app: Route) {
@@ -64,6 +64,12 @@ class Taler(app: Route) {
     private data class TalerAdminAddIncoming(
         val amount: String,
         val reserve_pub: String,
+        /**
+         * This account is the one giving money to the exchange.  It doesn't
+         * have to be 'created' as it might (and normally is) simply be a 
payto://
+         * address pointing to a bank account hosted in a different financial
+         * institution.
+         */
         val debit_account: String
     )
 
@@ -72,18 +78,22 @@ class Taler(app: Route) {
         val row_id: Long
     )
 
-    /** Helper data structures. */
+    /**
+     * Helper data structures.
+     */
     data class Payto(
-        val name: String,
+        val name: String = "NOTGIVEN",
         val iban: String,
-        val bic: String // empty string in case no BIC was given
+        val bic: String = "NOTGIVEN"
     )
     data class AmountWithCurrency(
         val currency: String,
         val amount: Amount
     )
 
-    /** Helper functions */
+    /**
+     * Helper functions
+     */
 
     fun parsePayto(paytoUri: String): Payto {
         // payto://iban/BIC?/IBAN?name=<name>
@@ -99,7 +109,7 @@ class Taler(app: Route) {
         val (currency, number) = match.destructured
         return AmountWithCurrency(currency, Amount(number))
     }
-
+    /** Sort query results in descending order for negative deltas, and 
ascending otherwise.  */
     private fun <T : Entity<Long>> SizedIterable<T>.orderTaler(delta: Int): 
List<T> {
         return if (delta < 0) {
             this.sortedByDescending { it.id }
@@ -113,10 +123,8 @@ class Taler(app: Route) {
     private fun parseDate(date: String): DateTime {
         return DateTime.parse(date, DateTimeFormat.forPattern("YYYY-MM-DD"))
     }
-    /**
-     * Builds the comparison operator for history entries based on the
-     * sign of 'delta'
-     */
+
+    /** Builds the comparison operator for history entries based on the sign 
of 'delta'  */
     private fun getComparisonOperator(delta: Int, start: Long): Op<Boolean> {
         return if (delta < 0) {
             Expression.build {
@@ -128,9 +136,7 @@ class Taler(app: Route) {
             }
         }
     }
-    /**
-     * Helper handling 'start' being optional and its dependence on 'delta'.
-     */
+    /** Helper handling 'start' being optional and its dependence on 'delta'.  
*/
     private fun handleStartArgument(start: String?, delta: Int): Long {
         return expectLong(start) ?: if (delta >= 0) {
             /**
@@ -151,14 +157,105 @@ class Taler(app: Route) {
 
     /** attaches Taler endpoints to the main Web server */
     init {
+        app.post("/taler/transfer") {
+            val exchangeId = 
authenticateRequest(call.request.headers["Authorization"])
+            val transferRequest = call.receive<TalerTransferRequest>()
+
+            /**
+             * FIXME: check the UID before putting new data into the database.
+             */
+            val opaque_row_id = transaction {
+                val creditorData = parsePayto(transferRequest.credit_account)
+                val exchangeBankAccount = getBankAccountsInfoFromId(exchangeId)
+                val pain001 = createPain001entity(
+                    Pain001Data(
+                        creditorIban = creditorData.iban,
+                        creditorBic = creditorData.bic,
+                        creditorName = creditorData.name,
+                        subject = transferRequest.wtid,
+                        sum = parseAmount(transferRequest.amount).amount
+                    ),
+                    exchangeBankAccount.first().id.value
+                )
+                TalerRequestedPaymentEntity.find {
+                    TalerRequestedPayments.requestUId eq 
transferRequest.request_uid
+                }.forEach {
+                    if (
+                        (it.amount != transferRequest.amount) or
+                        (it.creditAccount != 
transferRequest.exchange_base_url) or
+                        (it.wtid != transferRequest.wtid)
+                    ) {
+                        throw NexusError(
+                            HttpStatusCode.Conflict,
+                            "This uid (${transferRequest.request_uid}) belong 
to a different payment altrady"
+                        )
+                    }
+                }
+                val row = TalerRequestedPaymentEntity.new {
+                    preparedPayment = pain001
+                    exchangeBaseUrl = transferRequest.exchange_base_url
+                    requestUId = transferRequest.request_uid
+                    amount = transferRequest.amount
+                    wtid = transferRequest.wtid
+                    creditAccount = transferRequest.credit_account
+                }
+                row.id.value
+            }
+            call.respond(
+                HttpStatusCode.OK,
+                TalerTransferResponse(
+                    /**
+                     * Normally should point to the next round where the 
background
+                     * routing will sent new PAIN.001 data to the bank; work 
in progress..
+                     */
+                    timestamp = DateTime.now().millis / 1000,
+                    row_id = opaque_row_id
+                )
+            )
+            return@post
+        }
+        /** Test-API that creates one new payment addressed to the exchange.  
*/
         app.post("/taler/admin/add-incoming") {
+            val exchangeId = 
authenticateRequest(call.request.headers["Authorization"])
             val addIncomingData = call.receive<TalerAdminAddIncoming>()
-            /** Decompose amount and payto fields.  */
-
-
-            call.respond(HttpStatusCode.OK, NexusErrorJson("Not implemented"))
+            val debtor = parsePayto(addIncomingData.debit_account)
+            val amount = parseAmount(addIncomingData.amount)
+            val (bookingDate, opaque_row_id) = transaction {
+                val exchangeBankAccount = 
getBankAccountsInfoFromId(exchangeId).first()
+                val rawPayment = EbicsRawBankTransactionEntity.new {
+                    sourceFileName = "test"
+                    unstructuredRemittanceInformation = 
addIncomingData.reserve_pub
+                    transactionType = "CRDT"
+                    currency = amount.currency
+                    this.amount = amount.amount.toPlainString()
+                    creditorIban = exchangeBankAccount.iban
+                    creditorName = "Exchange's company name"
+                    debitorIban = debtor.iban
+                    debitorName = debtor.name
+                    counterpartBic = debtor.bic
+                    bookingDate = DateTime.now().toZonedString()
+                    status = "BOOK"
+                    servicerCode = "test-0"
+                    proprietaryCode = "test-0"
+                }
+                /** This payment is "valid by default" and will be returned
+                 * as soon as the exchange will ask for new payments.  */
+                val row = TalerIncomingPaymentEntity.new {
+                    payment = rawPayment
+                }
+                Pair(rawPayment.bookingDate, row.id.value)
+            }
+            call.respond(HttpStatusCode.OK, TalerAddIncomingResponse(
+                timestamp = parseDate(bookingDate).millis / 1000,
+                row_id = opaque_row_id
+            ))
             return@post
         }
+
+        /** This endpoint triggers the refunding of invalid payments.  
'Refunding'
+         * in this context means that nexus _prepares_ the payment instruction 
and
+         * places it into a further table.  Eventually, another routine will 
perform
+         * all the prepared payments.  */
         
app.post("/ebics/taler/{id}/accounts/{acctid}/refund-invalid-payments") {
             transaction {
                 val subscriber = expectIdTransaction(call.parameters["id"])
@@ -169,10 +266,10 @@ class Taler(app: Route) {
                         "Such subscriber (${subscriber.id}) can't drive such 
account (${acctid.id})"
                     )
                 }
-                TalerIncomingPaymentEntry.find {
-                    TalerIncomingPayments.processed eq false and 
(TalerIncomingPayments.valid eq false)
+                TalerIncomingPaymentEntity.find {
+                    TalerIncomingPayments.refunded eq false and 
(TalerIncomingPayments.valid eq false)
                 }.forEach {
-                    createPain001entry(
+                    createPain001entity(
                         Pain001Data(
                             creditorName = it.payment.debitorName,
                             creditorIban = it.payment.debitorIban,
@@ -182,12 +279,17 @@ class Taler(app: Route) {
                         ),
                         acctid.id.value
                     )
-                    it.processed = true
+                    it.refunded = true
                 }
             }
             return@post
         }
-        app.post("/ebics/taler/{id}/digest-incoming-transactions") {
+
+        /** This endpoint triggers the examination of raw incoming payments 
aimed
+         * at separating the good payments (those that will lead to a new 
reserve
+         * being created), from the invalid payments (those with a invalid 
subject
+         * that will soon be refunded.) */
+        app.post("/ebics/taler/{id}/crunch-incoming-transactions") {
             val id = expectId(call.parameters["id"])
             // first find highest ID value of already processed rows.
             transaction {
@@ -197,25 +299,25 @@ class Taler(app: Route) {
                  * information.
                  *
                  * This latestId value points at the latest id in the _raw 
transactions table_
-                 * that was last processed.  On the other hand, the "row_id" 
value that the exchange
-                 * will get along each history element will be the id in the 
_digested entries table_.
+                 * that was last processed here.  Note, the "row_id" value 
that the exchange
+                 * will get along each history element will be the id in the 
_crunched entries table_.
                  */
-                val latestId: Long = 
TalerIncomingPaymentEntry.all().sortedByDescending {
+                val latestId: Long = 
TalerIncomingPaymentEntity.all().sortedByDescending {
                     it.payment.id
                 }.firstOrNull()?.payment?.id?.value ?: -1
                 val subscriberAccount = getBankAccountsInfoFromId(id).first()
                 /* search for fresh transactions having the exchange IBAN in 
the creditor field.  */
-                EbicsRawBankTransactionEntry.find {
+                EbicsRawBankTransactionEntity.find {
                     EbicsRawBankTransactionsTable.creditorIban eq 
subscriberAccount.iban and
                             
(EbicsRawBankTransactionsTable.id.greater(latestId))
                 }.forEach {
                     if 
(CryptoUtil.checkValidEddsaPublicKey(it.unstructuredRemittanceInformation)) {
-                        TalerIncomingPaymentEntry.new {
+                        TalerIncomingPaymentEntity.new {
                             payment = it
                             valid = true
                         }
                     } else {
-                        TalerIncomingPaymentEntry.new {
+                        TalerIncomingPaymentEntity.new {
                             payment = it
                             valid = false
                         }
@@ -229,6 +331,8 @@ class Taler(app: Route) {
             )
             return@post
         }
+        /** Responds only with the payments that the EXCHANGE made.  Typically 
to
+         * merchants but possibly to refund invalid incoming payments. */
         app.get("/taler/history/outgoing") {
             /* sanitize URL arguments */
             val subscriberId = 
authenticateRequest(call.request.headers["Authorization"])
@@ -240,7 +344,7 @@ class Taler(app: Route) {
             transaction {
                 /** Retrieve all the outgoing payments from the _raw 
transactions table_ */
                 val subscriberBankAccount = 
getBankAccountsInfoFromId(subscriberId)
-                EbicsRawBankTransactionEntry.find {
+                EbicsRawBankTransactionEntity.find {
                     EbicsRawBankTransactionsTable.debitorIban eq 
subscriberBankAccount.first().iban and startCmpOp
                 }.orderTaler(delta).subList(0, abs(delta)).forEach {
                     history.outgoing_transactions.add(
@@ -262,6 +366,7 @@ class Taler(app: Route) {
             )
             return@get
         }
+        /** Responds only with the valid incoming payments */
         app.get("/taler/history/incoming") {
             val subscriberId = 
authenticateRequest(call.request.headers["Authorization"])
             val delta: Int = expectInt(call.expectUrlParameter("delta"))
@@ -270,7 +375,7 @@ class Taler(app: Route) {
             val startCmpOp = getComparisonOperator(delta, start)
             transaction {
                 val subscriberBankAccount = 
getBankAccountsInfoFromId(subscriberId)
-                TalerIncomingPaymentEntry.find {
+                TalerIncomingPaymentEntity.find {
                     TalerIncomingPayments.valid eq true and startCmpOp
                 }.orderTaler(delta).subList(0, abs(delta)).forEach {
                     history.incoming_transactions.add(
diff --git a/util/src/main/kotlin/XMLUtil.kt b/util/src/main/kotlin/XMLUtil.kt
index 2889a80..eb1f6f7 100644
--- a/util/src/main/kotlin/XMLUtil.kt
+++ b/util/src/main/kotlin/XMLUtil.kt
@@ -427,3 +427,12 @@ class XMLUtil private constructor() {
 fun Document.pickString(xpath: String): String {
     return XMLUtil.getStringFromXpath(this, xpath)
 }
+
+fun Document.pickStringNullable(xpath: String): String? {
+    return try {
+        XMLUtil.getStringFromXpath(this, xpath)
+    } catch (e: UtilError) {
+        logger.info("Tolerating missing value: $xpath")
+        null
+    }
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]