[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated: nexus db: outgoing payments logic.
From: |
gnunet |
Subject: |
[libeufin] branch master updated: nexus db: outgoing payments logic. |
Date: |
Tue, 24 Oct 2023 15:33:35 +0200 |
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 217975d6 nexus db: outgoing payments logic.
217975d6 is described below
commit 217975d6bc77d84aa391cb02bba2bf4f317576a5
Author: MS <ms@taler.net>
AuthorDate: Tue Oct 24 15:32:31 2023 +0200
nexus db: outgoing payments logic.
---
database-versioning/libeufin-nexus-procedures.sql | 53 +++++++++++++++++
.../main/kotlin/tech/libeufin/nexus/Database.kt | 69 +++++++++++++++++++++-
nexus/src/test/kotlin/Common.kt | 12 +++-
nexus/src/test/kotlin/DatabaseTest.kt | 44 ++++++++++++--
4 files changed, 171 insertions(+), 7 deletions(-)
diff --git a/database-versioning/libeufin-nexus-procedures.sql
b/database-versioning/libeufin-nexus-procedures.sql
index f807fb21..b6150cec 100644
--- a/database-versioning/libeufin-nexus-procedures.sql
+++ b/database-versioning/libeufin-nexus-procedures.sql
@@ -1,6 +1,59 @@
BEGIN;
SET search_path TO libeufin_nexus;
+CREATE OR REPLACE FUNCTION create_outgoing_tx(
+ IN in_amount taler_amount
+ ,IN in_wire_transfer_subject TEXT
+ ,IN in_execution_time BIGINT
+ ,IN in_credit_payto_uri TEXT
+ ,IN in_bank_transfer_id TEXT
+ ,IN in_initiated_id BIGINT
+ ,OUT out_nx_initiated BOOLEAN
+)
+LANGUAGE plpgsql AS $$
+DECLARE
+new_outgoing_transaction_id BIGINT;
+BEGIN
+
+IF in_initiated_id IS NULL THEN
+ out_nx_initiated = FALSE;
+ELSE
+ PERFORM 1
+ FROM initiated_outgoing_transactions
+ WHERE initiated_outgoing_transaction_id = in_initiated_id;
+ IF NOT FOUND THEN
+ out_nx_initiated = TRUE;
+ RETURN;
+ END IF;
+END IF;
+
+INSERT INTO outgoing_transactions (
+ amount
+ ,wire_transfer_subject
+ ,execution_time
+ ,credit_payto_uri
+ ,bank_transfer_id
+) VALUES (
+ in_amount
+ ,in_wire_transfer_subject
+ ,in_execution_time
+ ,in_credit_payto_uri
+ ,in_bank_transfer_id
+)
+ RETURNING outgoing_transaction_id
+ INTO new_outgoing_transaction_id;
+
+IF in_initiated_id IS NOT NULL
+THEN
+ UPDATE initiated_outgoing_transactions
+ SET outgoing_transaction_id = new_outgoing_transaction_id
+ WHERE initiated_outgoing_transaction_id = in_initiated_id;
+END IF;
+END $$;
+
+COMMENT ON FUNCTION create_outgoing_tx(taler_amount, TEXT, BIGINT, TEXT, TEXT,
BIGINT)
+ IS 'Creates a new outgoing payment and optionally reconciles the related
initiated payment with it. If the initiated payment to reconcile is not found,
it inserts NOTHING.';
+
CREATE OR REPLACE FUNCTION bounce_payment(
IN in_incoming_transaction_id BIGINT
,IN in_initiation_time BIGINT
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
index 2c1b4400..e8d8bd83 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
@@ -25,7 +25,7 @@ data class TalerAmount(
*/
data class IncomingPayment(
val amount: TalerAmount,
- val wireTransferSubject: String,
+ val wireTransferSubject: String?,
val debitPaytoUri: String,
val executionTime: Instant,
val bankTransferId: String,
@@ -56,6 +56,25 @@ enum class PaymentInitiationOutcome {
SUCCESS
}
+// OUTGOING PAYMENTS STRUCTS
+
+data class OutgoingPayment(
+ val amount: TalerAmount,
+ val wireTransferSubject: String?,
+ val executionTime: Instant,
+ val creditPaytoUri: String,
+ val bankTransferId: String
+)
+
+/**
+ * Witnesses the outcome of inserting an outgoing
+ * payment into the database.
+ */
+enum class OutgoingPaymentOutcome {
+ INITIATED_COUNTERPART_NOT_FOUND,
+ SUCCESS
+}
+
/**
* Performs a INSERT, UPDATE, or DELETE operation.
*
@@ -111,6 +130,54 @@ class Database(dbConfig: String): java.io.Closeable {
}
}
+ // OUTGOING PAYMENTS METHODS
+
+ /**
+ * Creates one outgoing payment OPTIONALLY reconciling it with its
+ * initiated payment counterpart.
+ *
+ * @param paymentData information about the outgoing payment.
+ * @param reconcileId optional row ID of the initiated payment
+ * that will reference this one. Note: if this value is
+ * not found, then NO row gets inserted in the database.
+ * @return operation outcome enum.
+ */
+ suspend fun outgoingPaymentCreate(
+ paymentData: OutgoingPayment,
+ reconcileId: Long? = null
+ ): OutgoingPaymentOutcome = runConn {
+ val stmt = it.prepareStatement("""
+ SELECT out_nx_initiated
+ FROM create_outgoing_tx(
+ (?,?)::taler_amount
+ ,?
+ ,?
+ ,?
+ ,?
+ ,?
+ )"""
+ )
+ val executionTime = paymentData.executionTime.toDbMicros()
+ ?: throw Exception("Could not convert outgoing payment
execution_time to microseconds")
+ stmt.setLong(1, paymentData.amount.value)
+ stmt.setInt(2, paymentData.amount.fraction)
+ stmt.setString(3, paymentData.wireTransferSubject)
+ stmt.setLong(4, executionTime)
+ stmt.setString(5, paymentData.creditPaytoUri)
+ stmt.setString(6, paymentData.bankTransferId)
+ if (reconcileId == null)
+ stmt.setNull(7, java.sql.Types.BIGINT)
+ else
+ stmt.setLong(7, reconcileId)
+
+ stmt.executeQuery().use {
+ if (!it.next()) throw Exception("Inserting outgoing payment gave
no outcome.")
+ if (it.getBoolean("out_nx_initiated"))
+ return@runConn
OutgoingPaymentOutcome.INITIATED_COUNTERPART_NOT_FOUND
+ }
+ return@runConn OutgoingPaymentOutcome.SUCCESS
+ }
+
// INCOMING PAYMENTS METHODS
/**
diff --git a/nexus/src/test/kotlin/Common.kt b/nexus/src/test/kotlin/Common.kt
index 3e89c854..23faab6b 100644
--- a/nexus/src/test/kotlin/Common.kt
+++ b/nexus/src/test/kotlin/Common.kt
@@ -82,7 +82,7 @@ fun genInitPay(subject: String, rowUuid: String? = null) =
)
// Generates an incoming payment, given its subject.
-fun genIncPay(subject: String, rowUuid: String? = null) =
+fun genIncPay(subject: String? = null, rowUuid: String? = null) =
IncomingPayment(
amount = TalerAmount(44, 0, "KUDOS"),
debitPaytoUri = "payto://iban/not-used",
@@ -90,4 +90,14 @@ fun genIncPay(subject: String, rowUuid: String? = null) =
executionTime = Instant.now(),
bounced = false,
bankTransferId = "entropic"
+ )
+
+// Generates an outgoing payment, given its subject.
+fun genOutPay(subject: String? = null) =
+ OutgoingPayment(
+ amount = TalerAmount(44, 0, "KUDOS"),
+ creditPaytoUri = "payto://iban/not-used",
+ wireTransferSubject = subject,
+ executionTime = Instant.now(),
+ bankTransferId = "entropic"
)
\ No newline at end of file
diff --git a/nexus/src/test/kotlin/DatabaseTest.kt
b/nexus/src/test/kotlin/DatabaseTest.kt
index 8eae1040..367aa650 100644
--- a/nexus/src/test/kotlin/DatabaseTest.kt
+++ b/nexus/src/test/kotlin/DatabaseTest.kt
@@ -1,14 +1,46 @@
import kotlinx.coroutines.runBlocking
import org.junit.Test
-import tech.libeufin.nexus.InitiatedPayment
-import tech.libeufin.nexus.NEXUS_CONFIG_SOURCE
-import tech.libeufin.nexus.PaymentInitiationOutcome
-import tech.libeufin.nexus.TalerAmount
+import tech.libeufin.nexus.*
import java.time.Instant
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
+
+class OutgoingPaymentsTest {
+
+ /**
+ * Tests the insertion of outgoing payments, including
+ * the case where we reconcile with an initiated payment.
+ */
+ @Test
+ fun outgoingPaymentCreation() {
+ val db = prepDb(TalerConfig(NEXUS_CONFIG_SOURCE))
+ runBlocking {
+ // inserting without reconciling
+ assertEquals(
+ OutgoingPaymentOutcome.SUCCESS,
+ db.outgoingPaymentCreate(genOutPay("paid by nexus"))
+ )
+ // inserting trying to reconcile with a non-existing initiated
payment.
+ assertEquals(
+ OutgoingPaymentOutcome.INITIATED_COUNTERPART_NOT_FOUND,
+ db.outgoingPaymentCreate(genOutPay("paid by nexus"), 5)
+ )
+ // initiating a payment to reconcile later. Takes row ID == 1
+ assertEquals(
+ PaymentInitiationOutcome.SUCCESS,
+ db.initiatedPaymentCreate(genInitPay("waiting for
reconciliation"))
+ )
+ // Creating an outgoing payment, reconciling it with the one above.
+ assertEquals(
+ OutgoingPaymentOutcome.SUCCESS,
+ db.outgoingPaymentCreate(genOutPay(), 1)
+ )
+ }
+ }
+}
+
class IncomingPaymentsTest {
// Tests the function that flags incoming payments as bounced.
@Test
@@ -50,13 +82,15 @@ class IncomingPaymentsTest {
assertTrue(res.next())
assertEquals(0, res.getInt("how_many"))
}
- db.incomingPaymentCreate(genIncPay("singleton"))
+ assertTrue(db.incomingPaymentCreate(genIncPay("singleton")))
// Asserting the table has one.
db.runConn {
val res = it.execSQLQuery(countRows)
assertTrue(res.next())
assertEquals(1, res.getInt("how_many"))
}
+ // Checking insertion of null (allowed) subjects.
+ assertTrue(db.incomingPaymentCreate(genIncPay()))
}
}
}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated: nexus db: outgoing payments logic.,
gnunet <=