gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] 02/05: DB definitions for payments ordered by the exchange.


From: gnunet
Subject: [libeufin] 02/05: DB definitions for payments ordered by the exchange.
Date: Fri, 10 Apr 2020 00:17:29 +0200

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

marcello pushed a commit to branch master
in repository libeufin.

commit 95994e9af317eb115a64341e179c76772afc3055
Author: Marcello Stanisci <address@hidden>
AuthorDate: Thu Apr 9 18:39:48 2020 +0200

    DB definitions for payments ordered by the exchange.
---
 nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt    | 52 +++++++++++++++++---
 nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt  |  8 +--
 nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 57 +++++++++++++---------
 3 files changed, 83 insertions(+), 34 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
index df84540..1311dca 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -10,6 +10,33 @@ 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.
+ */
+object TalerRequestedPayments: LongIdTable() {
+    val payment = 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) {
+    var payment by Pain001Entity referencedOn TalerRequestedPayments.payment
+    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")
@@ -17,10 +44,15 @@ object TalerIncomingPayments: LongIdTable() {
     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,7 +61,7 @@ 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 refunded by TalerIncomingPayments.refunded
 }
@@ -59,8 +91,8 @@ object EbicsRawBankTransactionsTable : LongIdTable() {
     val bookingDate = text("bookingDate")
 }
 
-class EbicsRawBankTransactionEntry(id: EntityID<Long>) : LongEntity(id) {
-    companion object : 
LongEntityClass<EbicsRawBankTransactionEntry>(EbicsRawBankTransactionsTable)
+class EbicsRawBankTransactionEntity(id: EntityID<Long>) : LongEntity(id) {
+    companion object : 
LongEntityClass<EbicsRawBankTransactionEntity>(EbicsRawBankTransactionsTable)
     var sourceType by EbicsRawBankTransactionsTable.sourceType // C52 or C53 
or C54?
     var sourceFileName by EbicsRawBankTransactionsTable.sourceFileName
     var unstructuredRemittanceInformation by 
EbicsRawBankTransactionsTable.unstructuredRemittanceInformation
@@ -76,6 +108,12 @@ class EbicsRawBankTransactionEntry(id: EntityID<Long>) : 
LongEntity(id) {
     var nexusSubscriber by EbicsSubscriberEntity referencedOn 
EbicsRawBankTransactionsTable.nexusSubscriber
 }
 
+/**
+ * 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")
@@ -93,7 +131,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)
 }
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index 652cc3c..3a97db5 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -314,7 +314,7 @@ fun createPain001document(pain001Entity: Pain001Entity): 
String {
 /**
  * Insert one row in the database, and leaves it marked as non-submitted.
  */
-fun createPain001entry(entry: Pain001Data, debtorAccountId: String) {
+fun createPain001entity(entry: Pain001Data, debtorAccountId: String) {
     val randomId = Random().nextLong()
     transaction {
         Pain001Entity.new {
@@ -483,7 +483,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
@@ -630,7 +630,7 @@ fun main() {
                 var ret = ""
                 transaction {
                     val subscriber: EbicsSubscriberEntity = 
getSubscriberEntityFromId(id)
-                    EbicsRawBankTransactionEntry.find {
+                    EbicsRawBankTransactionEntity.find {
                         (EbicsRawBankTransactionsTable.nexusSubscriber eq 
subscriber.id.value) and
                                 (EbicsRawBankTransactionsTable.sourceType eq 
"C53")
                     }.forEach {
@@ -668,7 +668,7 @@ fun main() {
                             val fileName = it.first
                             val camt53doc = 
XMLUtil.parseStringIntoDom(it.second)
                             transaction {
-                                EbicsRawBankTransactionEntry.new {
+                                EbicsRawBankTransactionEntity.new {
                                     sourceType = "C53"
                                     sourceFileName = fileName
                                     unstructuredRemittanceInformation = 
camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']/@Ccy")
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
index 5347c78..d44c027 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
@@ -17,7 +17,6 @@ import org.joda.time.format.DateTimeFormat
 import tech.libeufin.util.Amount
 import tech.libeufin.util.CryptoUtil
 import tech.libeufin.util.toZonedString
-import java.util.*
 import kotlin.math.abs
 
 class Taler(app: Route) {
@@ -79,7 +78,9 @@ class Taler(app: Route) {
         val row_id: Long
     )
 
-    /** Helper data structures. */
+    /**
+     * Helper data structures.
+     */
     data class Payto(
         val name: String,
         val iban: String,
@@ -90,7 +91,9 @@ class Taler(app: Route) {
         val amount: Amount
     )
 
-    /** Helper functions */
+    /**
+     * Helper functions
+     */
 
     fun parsePayto(paytoUri: String): Payto {
         // payto://iban/BIC?/IBAN?name=<name>
@@ -106,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 }
@@ -120,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 {
@@ -135,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) {
             /**
@@ -158,16 +157,15 @@ class Taler(app: Route) {
 
     /** attaches Taler endpoints to the main Web server */
     init {
+        /** 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>()
             val debtor = parsePayto(addIncomingData.debit_account)
             val amount = parseAmount(addIncomingData.amount)
-
-            /** Decompose amount and payto fields.  */
             val (bookingDate, opaque_row_id) = transaction {
                 val exchangeBankAccount = 
getBankAccountsInfoFromId(exchangeId).first()
-                val rawPayment = EbicsRawBankTransactionEntry.new {
+                val rawPayment = EbicsRawBankTransactionEntity.new {
                     sourceFileName = "test"
                     sourceType = "C53"
                     unstructuredRemittanceInformation = 
addIncomingData.reserve_pub
@@ -183,7 +181,7 @@ class Taler(app: Route) {
                 }
                 /** This payment is "valid by default" and will be returned
                  * as soon as the exchange will ask for new payments.  */
-                val row = TalerIncomingPaymentEntry.new {
+                val row = TalerIncomingPaymentEntity.new {
                     payment = rawPayment
                 }
                 Pair(rawPayment.bookingDate, row.id.value)
@@ -194,6 +192,11 @@ class Taler(app: Route) {
             ))
             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"])
@@ -204,10 +207,10 @@ class Taler(app: Route) {
                         "Such subscriber (${subscriber.id}) can't drive such 
account (${acctid.id})"
                     )
                 }
-                TalerIncomingPaymentEntry.find {
+                TalerIncomingPaymentEntity.find {
                     TalerIncomingPayments.refunded eq false and 
(TalerIncomingPayments.valid eq false)
                 }.forEach {
-                    createPain001entry(
+                    createPain001entity(
                         Pain001Data(
                             creditorName = it.payment.debitorName,
                             creditorIban = it.payment.debitorIban,
@@ -222,6 +225,11 @@ class Taler(app: Route) {
             }
             return@post
         }
+
+        /** 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}/digest-incoming-transactions") {
             val id = expectId(call.parameters["id"])
             // first find highest ID value of already processed rows.
@@ -235,22 +243,22 @@ class Taler(app: Route) {
                  * 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_.
                  */
-                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
                         }
@@ -264,6 +272,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"])
@@ -275,7 +285,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(
@@ -297,6 +307,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"))
@@ -305,7 +316,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(

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



reply via email to

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