diff options
author | Antoine A <> | 2024-04-26 18:10:22 +0900 |
---|---|---|
committer | Antoine A <> | 2024-04-26 18:10:22 +0900 |
commit | 6aaf661c073d5ffd74f4407fd386f7df4d0702de (patch) | |
tree | 362dc9b0566838d2f6f89eb3fa9c7dcc957124c2 /nexus | |
parent | 71050ab44fbccb970ec530383cdeef42ca0cf928 (diff) | |
download | libeufin-6aaf661c073d5ffd74f4407fd386f7df4d0702de.tar.gz libeufin-6aaf661c073d5ffd74f4407fd386f7df4d0702de.tar.bz2 libeufin-6aaf661c073d5ffd74f4407fd386f7df4d0702de.zip |
nexus: wire gateway /history/outgoing
Diffstat (limited to 'nexus')
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt | 7 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt | 4 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt | 32 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/db/PaymentDAO.kt | 23 | ||||
-rw-r--r-- | nexus/src/test/kotlin/DatabaseTest.kt | 31 | ||||
-rw-r--r-- | nexus/src/test/kotlin/WireGatewayApiTest.kt | 28 | ||||
-rw-r--r-- | nexus/src/test/kotlin/helpers.kt | 25 |
7 files changed, 117 insertions, 33 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt index e5f18595..8e876f20 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt @@ -95,7 +95,10 @@ suspend fun ingestOutgoingPayment( db: Database, payment: OutgoingPayment ) { - val result = db.payment.registerOutgoing(payment) + val metadata: Pair<ShortHashCode, ExchangeUrl>? = payment.wireTransferSubject?.let { + runCatching { parseOutgoingTxMetadata(it) }.getOrNull() + } + val result = db.payment.registerOutgoing(payment, metadata?.first, metadata?.second) if (result.new) { if (result.initiated) logger.info("$payment") @@ -106,8 +109,6 @@ suspend fun ingestOutgoingPayment( } } -private val PATTERN = Regex("[a-z0-9A-Z]{52}") - /** * Ingests an incoming payment. Stores the payment into valid talerable ones * or bounces it, according to the subject. diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt index 7879522c..6073072b 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt @@ -81,9 +81,9 @@ fun Routing.wireGatewayApi(db: Database, cfg: NexusConfig) { get("/taler-wire-gateway/history/incoming") { historyEndpoint(::IncomingHistory, ExchangeDAO::incomingHistory) } - /*get("/taler-wire-gateway/history/outgoing") { + get("/taler-wire-gateway/history/outgoing") { historyEndpoint(::OutgoingHistory, ExchangeDAO::outgoingHistory) - }*/ + } post("/taler-wire-gateway/admin/add-incoming") { val req = call.receive<AddIncomingRequest>() cfg.checkCurrency(req.amount) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt index 6d65e444..e2ccd7b8 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt @@ -52,6 +52,38 @@ class ExchangeDAO(private val db: Database) { ) } + /** Query [exchangeId] history of taler outgoing transactions */ + suspend fun outgoingHistory( + params: HistoryParams + ): List<OutgoingTransaction> + // Outgoing transactions can be initiated or recovered. We take the first data to + // reach database : the initiation first else the recovered transaction. + = db.poolHistoryGlobal(params, db::listenOutgoing, """ + SELECT + talerable_outgoing_transaction_id + ,COALESCE(iot.initiation_time, ot.execution_time) AS execution_time + ,(COALESCE(iot.amount, ot.amount)).val AS amount_val + ,(COALESCE(iot.amount, ot.amount)).frac AS amount_frac + ,COALESCE(iot.credit_payto_uri, ot.credit_payto_uri) AS credit_payto_uri + ,wtid + ,exchange_base_url + FROM talerable_outgoing_transactions AS tot + LEFT OUTER JOIN outgoing_transactions AS ot + ON tot.outgoing_transaction_id=ot.outgoing_transaction_id + LEFT OUTER JOIN initiated_outgoing_transactions AS iot + ON tot.initiated_outgoing_transaction_id=iot.initiated_outgoing_transaction_id + WHERE + """, "talerable_outgoing_transaction_id") { + OutgoingTransaction( + row_id = it.getLong("talerable_outgoing_transaction_id"), + date = it.getTalerTimestamp("execution_time"), + amount = it.getAmount("amount", db.bankCurrency), + credit_account = it.getString("credit_payto_uri"), + wtid = ShortHashCode(it.getBytes("wtid")), + exchange_base_url = it.getString("exchange_base_url") + ) + } + /** Result of taler transfer transaction creation */ sealed interface TransferResult { /** Transaction [id] and wire transfer [timestamp] */ diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/PaymentDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/PaymentDAO.kt index 05548b99..4609def8 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/db/PaymentDAO.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/db/PaymentDAO.kt @@ -19,10 +19,8 @@ package tech.libeufin.nexus.db -import tech.libeufin.common.EddsaPublicKey -import tech.libeufin.common.TalerAmount +import tech.libeufin.common.* import tech.libeufin.common.db.one -import tech.libeufin.common.micros import tech.libeufin.nexus.IncomingPayment import tech.libeufin.nexus.OutgoingPayment import java.time.Instant @@ -37,10 +35,14 @@ class PaymentDAO(private val db: Database) { ) /** Register an outgoing payment reconciling it with its initiated payment counterpart if present */ - suspend fun registerOutgoing(paymentData: OutgoingPayment): OutgoingRegistrationResult = db.conn { + suspend fun registerOutgoing( + paymentData: OutgoingPayment, + wtid: ShortHashCode?, + baseUrl: ExchangeUrl?, + ): OutgoingRegistrationResult = db.conn { val stmt = it.prepareStatement(""" SELECT out_tx_id, out_initiated, out_found - FROM register_outgoing((?,?)::taler_amount,?,?,?,?) + FROM register_outgoing((?,?)::taler_amount,?,?,?,?,?,?) """) val executionTime = paymentData.executionTime.micros() stmt.setLong(1, paymentData.amount.value) @@ -49,6 +51,17 @@ class PaymentDAO(private val db: Database) { stmt.setLong(4, executionTime) stmt.setString(5, paymentData.creditPaytoUri) stmt.setString(6, paymentData.messageId) + if (wtid != null) { + stmt.setBytes(7, wtid.raw) + } else { + stmt.setNull(7, java.sql.Types.NULL) + } + if (baseUrl != null) { + stmt.setString(8, baseUrl.url) + } else { + stmt.setNull(8, java.sql.Types.NULL) + } + stmt.one { OutgoingRegistrationResult( it.getLong("out_tx_id"), diff --git a/nexus/src/test/kotlin/DatabaseTest.kt b/nexus/src/test/kotlin/DatabaseTest.kt index 1c2e82ae..29a79799 100644 --- a/nexus/src/test/kotlin/DatabaseTest.kt +++ b/nexus/src/test/kotlin/DatabaseTest.kt @@ -18,8 +18,9 @@ */ import org.junit.Test -import tech.libeufin.common.TalerAmount +import tech.libeufin.common.* import tech.libeufin.nexus.db.InitiatedDAO.PaymentInitiationResult +import tech.libeufin.nexus.* import java.time.Instant import kotlin.test.assertEquals import kotlin.test.assertIs @@ -31,31 +32,43 @@ class OutgoingPaymentsTest { @Test fun register() = setup { db, _ -> // With reconciling - genOutPay("paid by nexus", "first").run { + genOutPay("paid by nexus").run { assertIs<PaymentInitiationResult.Success>( - db.initiated.create(genInitPay("waiting for reconciliation", "first")) + db.initiated.create(genInitPay("waiting for reconciliation", messageId)) ) - db.payment.registerOutgoing(this).run { - assertTrue(new,) + db.payment.registerOutgoing(this, null, null).run { + assertTrue(new) assertTrue(initiated) } - db.payment.registerOutgoing(this).run { + db.payment.registerOutgoing(this, null, null).run { assertFalse(new) assertTrue(initiated) } } // Without reconciling - genOutPay("not paid by nexus", "second").run { - db.payment.registerOutgoing(this).run { + genOutPay("not paid by nexus").run { + db.payment.registerOutgoing(this, null, null).run { assertTrue(new) assertFalse(initiated) } - db.payment.registerOutgoing(this).run { + db.payment.registerOutgoing(this, null, null).run { assertFalse(new) assertFalse(initiated) } } } + + @Test + fun talerable() = setup { db, _ -> + val wtid = ShortHashCode.rand() + val url = "https://exchange.com" + genOutPay("$wtid $url").run { + assertIs<PaymentInitiationResult.Success>( + db.initiated.create(genInitPay("waiting for reconciliation", messageId)) + ) + ingestOutgoingPayment(db, this) + } + } } class IncomingPaymentsTest { diff --git a/nexus/src/test/kotlin/WireGatewayApiTest.kt b/nexus/src/test/kotlin/WireGatewayApiTest.kt index 2393bd0d..2b662e83 100644 --- a/nexus/src/test/kotlin/WireGatewayApiTest.kt +++ b/nexus/src/test/kotlin/WireGatewayApiTest.kt @@ -123,7 +123,7 @@ class WireGatewayApiTest { }, { // Transactions using raw bank transaction logic - ingestIncomingPayment(db, genInPay("history test with ${ShortHashCode.rand()} reserve pub")) + talerableIn(db) } ), ignored = listOf( @@ -133,45 +133,45 @@ class WireGatewayApiTest { }, { // Ignore outgoing transaction - ingestOutgoingPayment(db, genOutPay("ignored")) + talerableOut(db) } ) ) } - /* /** * Testing the /history/outgoing call from the TWG API. */ @Test - fun historyOutgoing() = serverSetup { - setMaxDebt("exchange", "KUDOS:1000000") - authRoutine(HttpMethod.Get, "/accounts/merchant/taler-wire-gateway/history/outgoing") + fun historyOutgoing() = serverSetup { db -> + //authRoutine(HttpMethod.Get, "/accounts/merchant/taler-wire-gateway/history/outgoing") historyRoutine<OutgoingHistory>( - url = "/accounts/exchange/taler-wire-gateway/history/outgoing", + url = "/taler-wire-gateway/history/outgoing", ids = { it.outgoing_transactions.map { it.row_id } }, registered = listOf( { - // Transactions using clean add incoming logic - transfer("KUDOS:10") + transfer() + }, + { + talerableOut(db) } ), ignored = listOf( { - // gnore manual incoming transaction - tx("exchange", "KUDOS:10", "merchant", "${ShortHashCode.rand()} http://exchange.example.com/") + // Ignore manual incoming transaction + talerableIn(db) }, { // Ignore malformed incoming transaction - tx("merchant", "KUDOS:10", "exchange", "ignored") + ingestIncomingPayment(db, genInPay("ignored")) }, { // Ignore malformed outgoing transaction - tx("exchange", "KUDOS:10", "merchant", "ignored") + ingestOutgoingPayment(db, genOutPay("ignored")) } ) ) - }*/ + } // Testing the /admin/add-incoming call from the TWG API. @Test diff --git a/nexus/src/test/kotlin/helpers.kt b/nexus/src/test/kotlin/helpers.kt index 3cad0692..dc5a98a8 100644 --- a/nexus/src/test/kotlin/helpers.kt +++ b/nexus/src/test/kotlin/helpers.kt @@ -125,4 +125,29 @@ fun genOutPay(subject: String, messageId: String? = null): OutgoingPayment { executionTime = Instant.now(), messageId = id ) +} + +/** Perform a taler outgoing transaction */ +suspend fun ApplicationTestBuilder.transfer() { + client.post("/taler-wire-gateway/transfer") { + json { + "request_uid" to HashCode.rand() + "amount" to "CHF:55" + "exchange_base_url" to "http://exchange.example.com/" + "wtid" to ShortHashCode.rand() + "credit_account" to grothoffPayto + } + }.assertOk() +} + +/** Ingest a talerable outgoing transaction */ +suspend fun talerableOut(db: Database) { + val wtid = ShortHashCode.rand() + ingestOutgoingPayment(db, genOutPay("$wtid http://exchange.example.com/")) +} + +/** Ingest a talerable incoming transaction */ +suspend fun talerableIn(db: Database) { + val reserve_pub = ShortHashCode.rand() + ingestIncomingPayment(db, genInPay("history test with $reserve_pub reserve pub")) }
\ No newline at end of file |