aboutsummaryrefslogtreecommitdiff
path: root/nexus/src/main/kotlin
diff options
context:
space:
mode:
authorAntoine A <>2024-04-15 16:28:24 +0900
committerAntoine A <>2024-04-26 11:47:47 +0900
commite1e8a3b2e28321a56ddc0206b7485e754e6f8f77 (patch)
tree5cdb1fc3e84270279632ffccd3d9753ed072dc41 /nexus/src/main/kotlin
parentfbec8bebc30a4619b6b192a0fc466dc81f15da14 (diff)
downloadlibeufin-e1e8a3b2e28321a56ddc0206b7485e754e6f8f77.tar.gz
libeufin-e1e8a3b2e28321a56ddc0206b7485e754e6f8f77.tar.bz2
libeufin-e1e8a3b2e28321a56ddc0206b7485e754e6f8f77.zip
nexus: wire gateway /transfer
Diffstat (limited to 'nexus/src/main/kotlin')
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt35
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt1
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt80
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt15
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 */