commit 210f2cb5e61fcd796262d84a0dc1306605d76c70
parent a6aaf2085ae70a4a8ce6949e52d044e09d80b55d
Author: Antoine A <>
Date: Tue, 8 Apr 2025 17:58:47 +0200
nexus: complete outgoing transactions with new ids
Diffstat:
2 files changed, 72 insertions(+), 21 deletions(-)
diff --git a/database-versioning/libeufin-nexus-procedures.sql b/database-versioning/libeufin-nexus-procedures.sql
@@ -233,7 +233,6 @@ local_ref TEXT;
local_amount taler_amount;
local_subject TEXT;
local_debit_payto TEXT;
-need_completion BOOLEAN;
BEGIN
IF in_credit_fee = (0, 0)::taler_amount THEN
in_credit_fee = NULL;
@@ -248,31 +247,25 @@ out_found=FOUND;
IF out_found THEN
local_ref=COALESCE(in_uetr::text, in_tx_id, in_acct_svcr_ref);
-- Check metadata
- IF in_subject IS NOT NULL THEN
- IF local_subject IS NULL THEN
- need_completion=TRUE;
- ELSIF local_subject != in_subject THEN
- RAISE NOTICE 'incoming tx %: stored subject is ''%'' got ''%''', local_ref, local_subject, in_subject;
- END IF;
+ IF in_subject != local_subject THEN
+ RAISE NOTICE 'incoming tx %: stored subject is ''%'' got ''%''', local_ref, local_subject, in_subject;
END IF;
- IF in_debit_payto IS NOT NULL THEN
- IF local_debit_payto IS NULL THEN
- need_completion=TRUE;
- ELSIF local_debit_payto != in_debit_payto THEN
- RAISE NOTICE 'incoming tx %: stored subject debit payto is % got %', local_ref, local_debit_payto, in_debit_payto;
- END IF;
+ IF in_debit_payto != local_debit_payto THEN
+ RAISE NOTICE 'incoming tx %: stored subject debit payto is % got %', local_ref, local_debit_payto, in_debit_payto;
END IF;
IF local_amount != in_amount THEN
RAISE NOTICE 'incoming tx %: stored amount is % got %', local_ref, local_amount, in_amount;
END IF;
- IF need_completion THEN
- UPDATE incoming_transactions
- SET subject=COALESCE(subject, in_subject), debit_payto=COALESCE(debit_payto, in_debit_payto)
- WHERE incoming_transaction_id = out_tx_id;
- out_completed=COALESCE(in_subject, local_subject) IS NOT NULL AND COALESCE(in_debit_payto, local_debit_payto) IS NOT NULL;
- IF out_completed THEN
- PERFORM pg_notify('nexus_revenue_tx', out_tx_id::text);
- END IF;
+ UPDATE incoming_transactions
+ SET subject=COALESCE(subject, in_subject),
+ debit_payto=COALESCE(debit_payto, in_debit_payto),
+ uetr=COALESCE(uetr, in_uetr),
+ tx_id=COALESCE(tx_id, in_tx_id),
+ acct_svcr_ref=COALESCE(acct_svcr_ref, in_acct_svcr_ref)
+ WHERE incoming_transaction_id = out_tx_id;
+ out_completed=COALESCE(in_subject, local_subject) IS NOT NULL AND COALESCE(in_debit_payto, local_debit_payto) IS NOT NULL;
+ IF out_completed THEN
+ PERFORM pg_notify('nexus_revenue_tx', out_tx_id::text);
END IF;
ELSE
out_reserve_pub_reuse=in_type = 'reserve' AND EXISTS(SELECT FROM talerable_incoming_transactions WHERE metadata = in_metadata AND type = 'reserve');
diff --git a/nexus/src/test/kotlin/DatabaseTest.kt b/nexus/src/test/kotlin/DatabaseTest.kt
@@ -29,6 +29,7 @@ import tech.libeufin.nexus.db.*
import tech.libeufin.nexus.db.PaymentDAO.OutgoingRegistrationResult
import tech.libeufin.nexus.db.InitiatedDAO.PaymentInitiationResult
import java.time.Instant
+import java.util.UUID;
import kotlin.test.*
suspend fun Database.checkInCount(nbIncoming: Int, nbBounce: Int, nbTalerable: Int) = serializable(
@@ -280,6 +281,63 @@ class IncomingPaymentsTest {
registerIncomingPayment(db, cfg, original)
db.checkInCount(5, 2, 2)
}
+
+ @Test
+ fun recoverInfo() = setup { db, _ ->
+ val cfg = NexusIngestConfig.default(AccountType.exchange)
+
+ suspend fun Database.checkContent(payment: IncomingPayment) = serializable(
+ """
+ SELECT
+ uetr IS NOT DISTINCT FROM ? AND
+ tx_id IS NOT DISTINCT FROM ? AND
+ acct_svcr_ref IS NOT DISTINCT FROM ? AND
+ subject IS NOT DISTINCT FROM ? AND
+ debit_payto IS NOT DISTINCT FROM ?
+ FROM incoming_transactions ORDER BY incoming_transaction_id DESC LIMIT 1
+ """
+ ) {
+ setObject(1, payment.id.uetr)
+ setString(2, payment.id.txId)
+ setString(3, payment.id.acctSvcrRef)
+ setString(4, payment.subject)
+ setString(5, payment.debtor?.toString())
+ one {
+ assertTrue(it.getBoolean(1))
+ }
+ }
+
+ for ((index, partialId) in sequenceOf(
+ IncomingId(UUID.randomUUID(), null, null),
+ IncomingId(null, randEbicsId(), null),
+ IncomingId(null, null, randEbicsId()),
+ ).withIndex()) {
+ val payment = genInPay("subject")
+
+ // Register minimal
+ val partialPayment = payment.copy(id = partialId, subject = null, debtor = null)
+ registerIncomingPayment(db, cfg, partialPayment)
+ db.checkContent(partialPayment)
+ db.checkInCount(index + 1, index, 0)
+
+ // Recover ID
+ val fullId = IncomingId(
+ partialId.uetr ?: UUID.randomUUID(),
+ partialId.txId ?: randEbicsId(),
+ partialId.acctSvcrRef ?: randEbicsId()
+ )
+ val idPayment = partialPayment.copy(id = fullId)
+ registerIncomingPayment(db, cfg, idPayment)
+ db.checkContent(idPayment)
+ db.checkInCount(index + 1, index, 0)
+
+ // Recover subject & debtor
+ val fullPayment = payment.copy(id = fullId)
+ registerIncomingPayment(db, cfg, fullPayment)
+ db.checkContent(fullPayment)
+ db.checkInCount(index + 1, index + 1, 0)
+ }
+ }
}
class PaymentInitiationsTest {