libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 4c7ac921c6cc83dab60a349c14a0a2d93906e57b
parent d221d36099e9033790e4a9ada7a91cee738991ea
Author: Antoine A <>
Date:   Fri, 23 Aug 2024 20:35:20 +0200

nexus: fix transfer status

Diffstat:
Adatabase-versioning/libeufin-nexus-0007.sql | 22++++++++++++++++++++++
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt | 4++--
Mnexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt | 3++-
Mnexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt | 35++++++++++++++++++++---------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt | 26++++++--------------------
Mnexus/src/test/kotlin/DatabaseTest.kt | 8++++----
Mnexus/src/test/kotlin/WireGatewayApiTest.kt | 5+----
7 files changed, 57 insertions(+), 46 deletions(-)

diff --git a/database-versioning/libeufin-nexus-0007.sql b/database-versioning/libeufin-nexus-0007.sql @@ -0,0 +1,22 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2024 Taler Systems SA +-- +-- TALER is free software; you can redistribute it and/or modify it under the +-- terms of the GNU General Public License as published by the Free Software +-- Foundation; either version 3, or (at your option) any later version. +-- +-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + +BEGIN; + +SELECT _v.register_patch('libeufin-nexus-0007', NULL, NULL); + +ALTER TYPE submission_state RENAME VALUE 'never_heard_back' TO 'pending'; + +COMMIT; diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt @@ -132,7 +132,7 @@ private suspend fun ingestPayload( is OutgoingPayment -> ingestOutgoingPayment(db, it) is TxNotification.Reversal -> { logger.error("BOUNCE '${it.msgId}': ${it.reason}") - db.initiated.reversal(it.msgId, "Payment bounced: ${it.reason}") + db.initiated.failure(it.msgId, "Payment bounced: ${it.reason}") } } } @@ -171,7 +171,7 @@ private suspend fun ingestPayload( val msg = status.msg() logger.debug("{}", status) if (status.paymentCode == ExternalPaymentGroupStatusCode.RJCT) { - db.initiated.bankFailure(status.msgId, msg) + db.initiated.failure(status.msgId, msg) logger.error("Transaction '${status.msgId}' was rejected : $msg") } else { db.initiated.bankMessage(status.msgId, msg) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt @@ -44,7 +44,8 @@ enum class SubmissionState { unsubmitted, transient_failure, permanent_failure, - success + success, + pending } /** diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt @@ -160,7 +160,7 @@ class ExchangeDAO(private val db: Database) { oneOrNull { TransferStatus( status = when (it.getEnum<SubmissionState>("submitted")) { - SubmissionState.unsubmitted -> TransferStatusState.pending + SubmissionState.unsubmitted, SubmissionState.pending -> TransferStatusState.pending SubmissionState.transient_failure -> TransferStatusState.transient_failure SubmissionState.permanent_failure -> TransferStatusState.permanent_failure SubmissionState.success -> TransferStatusState.success @@ -191,28 +191,33 @@ class ExchangeDAO(private val db: Database) { ,initiation_time FROM transfer_operations JOIN initiated_outgoing_transactions USING (initiated_outgoing_transaction_id) - WHERE ${if (params.status != null) "submitted=?::submission_state AND" else ""} + WHERE ${ + when (params.status) { + null -> "" + TransferStatusState.pending -> "(submitted=?::submission_state OR submitted=?::submission_state) AND" + else -> "submitted=?::submission_state AND" + } + } """, { - val state = when (params.status) { - TransferStatusState.pending -> SubmissionState.unsubmitted - TransferStatusState.transient_failure -> SubmissionState.transient_failure - TransferStatusState.permanent_failure -> SubmissionState.permanent_failure - TransferStatusState.success -> SubmissionState.success - null -> null - } - if (state != null) { - setString(1, state.name) - 1 - } else { - 0 + when (params.status) { + null -> 0 + TransferStatusState.pending -> { + setString(1, SubmissionState.pending.name) + setString(2, SubmissionState.unsubmitted.name) + 2 + } + else -> { + setString(1, params.status?.name) + 1 + } } } ) { TransferListStatus( row_id = it.getLong("initiated_outgoing_transaction_id"), status = when (it.getEnum<SubmissionState>("submitted")) { - SubmissionState.unsubmitted -> TransferStatusState.pending + SubmissionState.unsubmitted, SubmissionState.pending -> TransferStatusState.pending SubmissionState.transient_failure -> TransferStatusState.transient_failure SubmissionState.permanent_failure -> TransferStatusState.permanent_failure SubmissionState.success -> TransferStatusState.success diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt @@ -69,7 +69,7 @@ class InitiatedDAO(private val db: Database) { ) = db.serializable( """ UPDATE initiated_outgoing_transactions SET - submitted = 'success'::submission_state + submitted = 'pending'::submission_state ,last_submission_time = ? ,failure_message = NULL ,order_id = ? @@ -91,7 +91,7 @@ class InitiatedDAO(private val db: Database) { ) = db.serializable( """ UPDATE initiated_outgoing_transactions SET - submitted = 'transient_failure'::submission_state + submitted = 'transient_failure'::submission_state ,last_submission_time = ? ,failure_message = ? ,submission_counter = submission_counter + 1 @@ -143,8 +143,8 @@ class InitiatedDAO(private val db: Database) { /** Register bank status message */ suspend fun bankMessage(requestUID: String, msg: String) = db.serializable( """ - UPDATE initiated_outgoing_transactions - SET failure_message = ? + UPDATE initiated_outgoing_transactions SET + failure_message = ? WHERE request_uid = ? """ ) { @@ -153,8 +153,8 @@ class InitiatedDAO(private val db: Database) { execute() } - /** Register bank failure */ - suspend fun bankFailure(requestUID: String, msg: String) = db.serializable( + /** Register transaction failure */ + suspend fun failure(requestUID: String, msg: String) = db.serializable( """ UPDATE initiated_outgoing_transactions SET submitted = 'permanent_failure'::submission_state @@ -167,20 +167,6 @@ class InitiatedDAO(private val db: Database) { execute() } - /** Register reversal */ - suspend fun reversal(requestUID: String, msg: String) = db.serializable( - """ - UPDATE initiated_outgoing_transactions SET - submitted = 'permanent_failure'::submission_state - ,failure_message = ? - WHERE request_uid = ? - """ - ) { - setString(1, msg) - setString(2, requestUID) - execute() - } - /** List every initiated payment pending submission in the order they should be submitted */ suspend fun submittable(currency: String): List<InitiatedPayment> { val selectPart = """ diff --git a/nexus/src/test/kotlin/DatabaseTest.kt b/nexus/src/test/kotlin/DatabaseTest.kt @@ -273,13 +273,13 @@ class PaymentInitiationsTest { db.initiated.create(genInitPay(requestUid = "PAY4")) ) db.initiated.bankMessage("PAY4", "status progress") - db.initiated.bankFailure("PAY4", "status failure") + db.initiated.failure("PAY4", "status failure") assertIs<PaymentInitiationResult.Success>( db.initiated.create(genInitPay(requestUid = "PAY5")) ) db.initiated.bankMessage("PAY5", "status progress") - db.initiated.reversal("PAY5", "status reversal") + db.initiated.failure("PAY5", "status reversal") } @Test @@ -309,12 +309,12 @@ class PaymentInitiationsTest { ) // Check persistent failure not submitable - db.initiated.bankFailure("PAY3", "status failure") + db.initiated.failure("PAY3", "status failure") assertEquals( listOf("PAY2", "PAY4", "PAY5", "PAY1"), db.initiated.submittable("KUDOS").map { it.requestUid } ) - db.initiated.reversal("PAY4", "status reversal") + db.initiated.failure("PAY4", "status reversal") assertEquals( listOf("PAY2", "PAY5", "PAY1"), db.initiated.submittable("KUDOS").map { it.requestUid } diff --git a/nexus/src/test/kotlin/WireGatewayApiTest.kt b/nexus/src/test/kotlin/WireGatewayApiTest.kt @@ -173,14 +173,11 @@ class WireGatewayApiTest { db.initiated.submissionSuccess(1, Instant.now(), "ORDER1") db.initiated.submissionFailure(2, Instant.now(), "Failure") db.initiated.submissionFailure(3, Instant.now(), "Failure") - client.getA("/taler-wire-gateway/transfers?status=success").assertOkJson<TransferList> { - assertEquals(1, it.transfers.size) - } client.getA("/taler-wire-gateway/transfers?status=transient_failure").assertOkJson<TransferList> { assertEquals(2, it.transfers.size) } client.getA("/taler-wire-gateway/transfers?status=pending").assertOkJson<TransferList> { - assertEquals(3, it.transfers.size) + assertEquals(4, it.transfers.size) } }