gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated: Taler integration API handlers.


From: gnunet
Subject: [libeufin] branch master updated: Taler integration API handlers.
Date: Wed, 20 Sep 2023 16:30:48 +0200

This is an automated email from the git hooks/post-receive script.

ms pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new ddef3b15 Taler integration API handlers.
ddef3b15 is described below

commit ddef3b151f6cf3a2b4884698c0bdde2b046b0314
Author: MS <ms@taler.net>
AuthorDate: Wed Sep 20 16:30:31 2023 +0200

    Taler integration API handlers.
---
 bank/src/main/kotlin/tech/libeufin/bank/Main.kt    |   3 +-
 bank/src/main/kotlin/tech/libeufin/bank/helpers.kt |  39 +++++++-
 .../tech/libeufin/bank/talerIntegrationHandlers.kt | 106 +++++++++++++++++++++
 .../kotlin/tech/libeufin/bank/talerWebHandlers.kt  |  21 ----
 bank/src/main/kotlin/tech/libeufin/bank/types.kt   |  59 +++++++++++-
 5 files changed, 204 insertions(+), 24 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
index cf5b48a9..b0e9e46c 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -226,6 +226,7 @@ val webApp: Application.() -> Unit = {
         this.tokenHandlers()
         this.transactionsHandlers()
         this.talerWebHandlers()
-        // this.walletIntegrationHandlers()
+        this.talerIntegrationHandlers()
+        // this.talerWireGatewayHandlers()
     }
 }
\ No newline at end of file
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
index 59ddb318..9487679a 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
@@ -27,6 +27,7 @@ import net.taler.wallet.crypto.Base32Crockford
 import tech.libeufin.util.*
 import java.lang.NumberFormatException
 import java.net.URL
+import java.util.*
 
 fun ApplicationCall.expectUriComponent(componentName: String) =
     this.maybeUriComponent(componentName) ?: throw badRequest(
@@ -369,4 +370,40 @@ fun getTalerWithdrawUri(baseUrl: String, woId: String) =
         }
         pathSegments.add("taler-integration/${woId}")
         this.appendPathSegments(pathSegments)
-    }
\ No newline at end of file
+    }
+
+fun getWithdrawalConfirmUrl(baseUrl: String, wopId: String) =
+    url {
+        val baseUrlObj = URL(baseUrl)
+        protocol = URLProtocol(name = baseUrlObj.protocol, defaultPort = -1)
+        host = baseUrlObj.host
+        // Removing potential double slashes:
+        baseUrlObj.path.split("/").forEach {
+            this.appendPathSegments(it)
+        }
+        // Completing the endpoint:
+        this.appendPathSegments("${wopId}/confirm")
+    }
+
+
+/**
+ * This handler factors out the checking of the query param
+ * and the retrieval of the related withdrawal database row.
+ * It throws 404 if the operation is not found, and throws 400
+ * if the query param doesn't parse into a UUID.  Currently
+ * used by the Taler Web/SPA and Integration API handlers.
+ */
+fun getWithdrawal(opIdParam: String): TalerWithdrawalOperation {
+    val opId = try {
+        UUID.fromString(opIdParam)
+    } catch (e: Exception) {
+        logger.error(e.message)
+        throw badRequest("withdrawal_id query parameter was malformed")
+    }
+    val op = db.talerWithdrawalGet(opId)
+        ?: throw notFound(
+            hint = "Withdrawal operation ${opIdParam} not found",
+            talerEc = TalerErrorCode.TALER_EC_END
+        )
+    return op
+}
diff --git 
a/bank/src/main/kotlin/tech/libeufin/bank/talerIntegrationHandlers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/talerIntegrationHandlers.kt
new file mode 100644
index 00000000..ef66e7ef
--- /dev/null
+++ b/bank/src/main/kotlin/tech/libeufin/bank/talerIntegrationHandlers.kt
@@ -0,0 +1,106 @@
+/*
+ * This file is part of LibEuFin.
+ * Copyright (C) 2019 Stanisci and Dold.
+
+ * 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/>
+ */
+
+/* This file contains the Taler Integration API endpoints,
+* that are typically requested by wallets.  */
+package tech.libeufin.bank
+
+import io.ktor.server.application.*
+import io.ktor.server.request.*
+import io.ktor.server.response.*
+import io.ktor.server.routing.*
+import net.taler.common.errorcodes.TalerErrorCode
+import tech.libeufin.util.getBaseUrl
+
+fun Routing.talerIntegrationHandlers() {
+    get("/taler-integration/config") {
+        val internalCurrency: String = db.configGet("internal_currency")
+            ?: throw internalServerError("Currency not found")
+        call.respond(TalerIntegrationConfigResponse(currency = 
internalCurrency))
+        return@get
+    }
+    // Note: wopid acts as an authentication token.
+    get("/taler-integration/withdrawal-operation/{wopid}") {
+        val wopid = call.expectUriComponent("wopid")
+        val op = getWithdrawal(wopid) // throws 404 if not found.
+        val relatedBankAccount = 
db.bankAccountGetFromOwnerId(op.walletBankAccount)
+        if (relatedBankAccount == null)
+            throw internalServerError("Bank has a withdrawal not related to 
any bank account.")
+        val suggestedExchange = db.configGet("suggested_exchange")
+            ?: throw internalServerError("Bank does not have an exchange to 
suggest.")
+        val confirmUrl = getWithdrawalConfirmUrl(
+            baseUrl = call.request.getBaseUrl() ?: throw 
internalServerError("Could not get bank own base URL."),
+            wopId = wopid
+        )
+        call.respond(BankWithdrawalOperationStatus(
+            aborted = op.aborted,
+            selection_done = op.selectionDone,
+            transfer_done = op.confirmationDone,
+            amount = op.amount.toString(),
+            sender_wire = relatedBankAccount.internalPaytoUri,
+            suggested_exchange = suggestedExchange,
+            confirm_transfer_url = confirmUrl
+        ))
+        return@get
+    }
+    post("/taler-integration/withdrawal-operation/{wopid}") {
+        val wopid = call.expectUriComponent("wopid")
+        val req = call.receive<BankWithdrawalOperationPostRequest>()
+        val op = getWithdrawal(wopid) // throws 404 if not found.
+        if (op.selectionDone) {
+            // idempotency
+            if (op.selectedExchangePayto != req.selected_exchange &&
+                op.reservePub != req.reserve_pub)
+                throw conflict(
+                    hint = "Cannot select different exchange and reserve pub. 
under the same withdrawal operation",
+                    talerEc = 
TalerErrorCode.TALER_EC_BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT
+                )
+        }
+        val dbSuccess: Boolean = if (!op.selectionDone) {
+            val exchangePayto = req.selected_exchange
+                ?: (db.configGet("suggested_exchange")
+                    ?: throw internalServerError("Suggested exchange not 
found")
+                        )
+            db.talerWithdrawalSetDetails(
+                op.withdrawalUuid,
+                exchangePayto,
+                req.reserve_pub
+            )
+        }
+        else // DB succeeded in the past.
+            true
+        if (!dbSuccess)
+            // Whatever the problem, the bank missed it: respond 500.
+            throw internalServerError("Bank failed at selecting the 
withdrawal.")
+        val resp = BankWithdrawalOperationPostResponse(
+            transfer_done = op.confirmationDone,
+            confirm_transfer_url = if (!op.confirmationDone)
+                getWithdrawalConfirmUrl(
+                    baseUrl = call.request.getBaseUrl()
+                        ?: throw internalServerError("Could not get bank own 
base URL."),
+                    wopId = wopid
+                )
+                else
+                    null
+        )
+        call.respond(resp)
+        return@post
+    }
+}
+
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/talerWebHandlers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/talerWebHandlers.kt
index 33a334ed..f449cb54 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/talerWebHandlers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/talerWebHandlers.kt
@@ -36,27 +36,6 @@ import tech.libeufin.util.getBaseUrl
 import tech.libeufin.util.getNowUs
 import java.util.*
 
-/**
- * This handler factors out the checking of the query param
- * and the retrieval of the related withdrawal database row.
- * It throws 404 if the operation is not found, and throws 400
- * if the query param doesn't parse into an UUID.
- */
-private fun getWithdrawal(opIdParam: String): TalerWithdrawalOperation {
-    val opId = try {
-        UUID.fromString(opIdParam)
-    } catch (e: Exception) {
-        logger.error(e.message)
-        throw badRequest("withdrawal_id query parameter was malformed")
-    }
-    val op = db.talerWithdrawalGet(opId)
-        ?: throw notFound(
-            hint = "Withdrawal operation ${opIdParam} not found",
-            talerEc = TalerErrorCode.TALER_EC_END
-        )
-    return op
-}
-
 fun Routing.talerWebHandlers() {
     post("/accounts/{USERNAME}/withdrawals") {
         val c = call.myAuth(TokenScope.readwrite) ?: throw unauthorized()
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/types.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/types.kt
index fe6d2488..8daaf8ce 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/types.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/types.kt
@@ -439,4 +439,61 @@ enum class WithdrawalConfirmationResult {
     OP_NOT_FOUND,
     EXCHANGE_NOT_FOUND,
     BALANCE_INSUFFICIENT
-}
\ No newline at end of file
+}
+
+// GET /config response from the Taler Integration API.
+@Serializable
+data class TalerIntegrationConfigResponse(
+    val name: String = "taler-bank-integration",
+    val version: String = "0:0:0:",
+    val currency: String
+)
+
+// Withdrawal status as spec'd in the Taler Integration API.
+@Serializable
+data class BankWithdrawalOperationStatus(
+    // Indicates whether the withdrawal was aborted.
+    val aborted: Boolean,
+
+    /* Has the wallet selected parameters for the withdrawal operation
+      (exchange and reserve public key) and successfully sent it
+      to the bank? */
+    val selection_done: Boolean,
+
+    /* The transfer has been confirmed and registered by the bank.
+       Does not guarantee that the funds have arrived at the exchange
+       already. */
+    val transfer_done: Boolean,
+
+    /* Amount that will be withdrawn with this operation
+       (raw amount without fee considerations). */
+    val amount: String,
+
+    /* Bank account of the customer that is withdrawing, as a
+      ``payto`` URI. */
+    val sender_wire: String? = null,
+
+    // Suggestion for an exchange given by the bank.
+    val suggested_exchange: String? = null,
+
+    /* URL that the user needs to navigate to in order to
+       complete some final confirmation (e.g. 2FA).
+       It may contain withdrawal operation id */
+    val confirm_transfer_url: String? = null,
+
+    // Wire transfer types supported by the bank.
+    val wire_types: MutableList<String> = mutableListOf("iban")
+)
+
+// Selection request on a Taler withdrawal.
+@Serializable
+data class BankWithdrawalOperationPostRequest(
+    val reserve_pub: String,
+    val selected_exchange: String? = null // Use suggested exchange if that's 
missing.
+)
+
+@Serializable
+data class BankWithdrawalOperationPostResponse(
+    val transfer_done: Boolean,
+    val confirm_transfer_url: String? = null
+)
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]