diff options
author | Antoine A <> | 2024-04-15 16:28:24 +0900 |
---|---|---|
committer | Antoine A <> | 2024-04-26 11:47:47 +0900 |
commit | e1e8a3b2e28321a56ddc0206b7485e754e6f8f77 (patch) | |
tree | 5cdb1fc3e84270279632ffccd3d9753ed072dc41 /nexus/src/main/kotlin | |
parent | fbec8bebc30a4619b6b192a0fc466dc81f15da14 (diff) | |
download | libeufin-e1e8a3b2e28321a56ddc0206b7485e754e6f8f77.tar.gz libeufin-e1e8a3b2e28321a56ddc0206b7485e754e6f8f77.tar.bz2 libeufin-e1e8a3b2e28321a56ddc0206b7485e754e6f8f77.zip |
nexus: wire gateway /transfer
Diffstat (limited to 'nexus/src/main/kotlin')
4 files changed, 103 insertions, 28 deletions
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 f7374204..877101b2 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt @@ -29,6 +29,8 @@ import tech.libeufin.common.* import tech.libeufin.nexus.* import tech.libeufin.nexus.db.* import tech.libeufin.nexus.db.PaymentDAO.* +import tech.libeufin.nexus.db.InitiatedDAO.* +import tech.libeufin.nexus.db.ExchangeDAO.* import java.time.Instant @@ -41,38 +43,28 @@ fun Routing.wireGatewayApi(db: Database, cfg: NexusConfig) { post("/taler-wire-gateway/transfer") { val req = call.receive<TransferRequest>() cfg.checkCurrency(req.amount) - // TODO - /*val res = db.exchange.transfer( - req = req, - login = username, - now = Instant.now() + val bankId = run { + val bytes = ByteArray(16) + kotlin.random.Random.nextBytes(bytes) + Base32Crockford.encode(bytes) + } + val res = db.exchange.transfer( + req, + bankId, + Instant.now() ) when (res) { - is TransferResult.UnknownExchange -> throw unknownAccount(username) - is TransferResult.NotAnExchange -> throw conflict( - "$username is not an exchange account.", - TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE - ) - is TransferResult.UnknownCreditor -> throw unknownCreditorAccount(req.credit_account.canonical) - is TransferResult.BothPartyAreExchange -> throw conflict( - "Wire transfer attempted with credit and debit party being both exchange account", - TalerErrorCode.BANK_ACCOUNT_IS_EXCHANGE - ) - is TransferResult.ReserveUidReuse -> throw conflict( + TransferResult.RequestUidReuse -> throw conflict( "request_uid used already", TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED ) - is TransferResult.BalanceInsufficient -> throw conflict( - "Insufficient balance for exchange", - TalerErrorCode.BANK_UNALLOWED_DEBIT - ) is TransferResult.Success -> call.respond( TransferResponse( timestamp = res.timestamp, row_id = res.id ) ) - }*/ + } } /*suspend fun <T> PipelineContext<Unit, ApplicationCall>.historyEndpoint( reduce: (List<T>, String) -> Any, @@ -122,7 +114,6 @@ fun Routing.wireGatewayApi(db: Database, cfg: NexusConfig) { "reserve_pub used already", TalerErrorCode.BANK_DUPLICATE_RESERVE_PUB_SUBJECT ) - // TODO timestamp when idempotent is IncomingRegistrationResult.Success -> call.respond( AddIncomingResponse( timestamp = TalerProtocolTimestamp(timestamp), diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt index b6422612..4cc70452 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt @@ -42,4 +42,5 @@ data class InitiatedPayment( class Database(dbConfig: DatabaseConfig): DbPool(dbConfig, "libeufin_nexus") { val payment = PaymentDAO(this) val initiated = InitiatedDAO(this) + val exchange = ExchangeDAO(this) }
\ No newline at end of file diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt new file mode 100644 index 00000000..d3844167 --- /dev/null +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt @@ -0,0 +1,80 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2024 Taler Systems S.A. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin 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 Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +package tech.libeufin.nexus.db + +import tech.libeufin.common.db.one +import tech.libeufin.common.db.getTalerTimestamp +import tech.libeufin.common.micros +import tech.libeufin.common.TalerProtocolTimestamp +import tech.libeufin.common.TransferRequest +import java.sql.ResultSet +import java.time.Instant + +/** Data access logic for exchange specific logic */ +class ExchangeDAO(private val db: Database) { + + /** Result of taler transfer transaction creation */ + sealed interface TransferResult { + /** Transaction [id] and wire transfer [timestamp] */ + data class Success(val id: Long, val timestamp: TalerProtocolTimestamp): TransferResult + data object RequestUidReuse: TransferResult + } + + /** Perform a Taler transfer */ + suspend fun transfer( + req: TransferRequest, + bankId: String, + now: Instant + ): TransferResult = db.serializable { conn -> + val subject = "${req.wtid} ${req.exchange_base_url.url}" + val stmt = conn.prepareStatement(""" + SELECT + out_request_uid_reuse + ,out_tx_row_id + ,out_timestamp + FROM + taler_transfer ( + ?, ?, ?, + (?,?)::taler_amount, + ?, ?, ?, ? + ); + """) + + stmt.setBytes(1, req.request_uid.raw) + stmt.setBytes(2, req.wtid.raw) + stmt.setString(3, subject) + stmt.setLong(4, req.amount.value) + stmt.setInt(5, req.amount.frac) + stmt.setString(6, req.exchange_base_url.url) + stmt.setString(7, req.credit_account.canonical) + stmt.setString(8, bankId) + stmt.setLong(9, now.micros()) + + stmt.one { + when { + it.getBoolean("out_request_uid_reuse") -> TransferResult.RequestUidReuse + else -> TransferResult.Success( + id = it.getLong("out_tx_row_id"), + timestamp = it.getTalerTimestamp("out_timestamp") + ) + } + } + } +}
\ No newline at end of file diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt index 04fd3965..052b75f9 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt @@ -22,6 +22,7 @@ package tech.libeufin.nexus.db import tech.libeufin.common.asInstant import tech.libeufin.common.db.all import tech.libeufin.common.db.executeUpdateViolation +import tech.libeufin.common.db.oneUniqueViolation import tech.libeufin.common.db.getAmount import tech.libeufin.common.db.oneOrNull import tech.libeufin.common.micros @@ -32,9 +33,9 @@ import java.time.Instant class InitiatedDAO(private val db: Database) { /** Outgoing payments initiation result */ - enum class PaymentInitiationResult { - REQUEST_UID_REUSE, - SUCCESS + sealed interface PaymentInitiationResult { + data class Success(val id: Long): PaymentInitiationResult + data object RequestUidReuse: PaymentInitiationResult } /** Register a new pending payment in the database */ @@ -47,16 +48,18 @@ class InitiatedDAO(private val db: Database) { ,initiation_time ,request_uid ) VALUES ((?,?)::taler_amount,?,?,?,?) + RETURNING initiated_outgoing_transaction_id """) + // TODO check payto uri stmt.setLong(1, paymentData.amount.value) stmt.setInt(2, paymentData.amount.frac) stmt.setString(3, paymentData.wireTransferSubject) stmt.setString(4, paymentData.creditPaytoUri.toString()) stmt.setLong(5, paymentData.initiationTime.micros()) stmt.setString(6, paymentData.requestUid) - if (stmt.executeUpdateViolation()) - return@conn PaymentInitiationResult.SUCCESS - return@conn PaymentInitiationResult.REQUEST_UID_REUSE + stmt.oneUniqueViolation(PaymentInitiationResult.RequestUidReuse) { + PaymentInitiationResult.Success(it.getLong("initiated_outgoing_transaction_id")) + } } /** Register EBICS submission success */ |