[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated: Access API: GETting withdrawal informa
From: |
gnunet |
Subject: |
[libeufin] branch master updated: Access API: GETting withdrawal information. |
Date: |
Thu, 21 Oct 2021 09:14:19 +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 4474752 Access API: GETting withdrawal information.
4474752 is described below
commit 4474752e53c0ffdec32150282bd85e88261eb020
Author: ms <ms@taler.net>
AuthorDate: Thu Oct 21 09:13:51 2021 +0200
Access API: GETting withdrawal information.
---
.../src/main/kotlin/tech/libeufin/sandbox/DB.kt | 4 +-
.../main/kotlin/tech/libeufin/sandbox/Helpers.kt | 9 +++
.../src/main/kotlin/tech/libeufin/sandbox/JSON.kt | 8 +++
.../src/main/kotlin/tech/libeufin/sandbox/Main.kt | 68 ++++++++++++++++------
util/src/main/kotlin/HTTP.kt | 8 +++
util/src/main/kotlin/amounts.kt | 7 +++
util/src/main/kotlin/strings.kt | 7 ---
7 files changed, 83 insertions(+), 28 deletions(-)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 4aaac47..47b1e34 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -430,7 +430,7 @@ object TalerWithdrawalsTable : LongIdTable() {
* gets completed _on the bank's side_. This does never guarantees that
* the payment arrived at the exchange's bank yet.
*/
- val transferDone = bool("transferDone").default(false)
+ val confirmationDone = bool("confirmationDone").default(false)
val reservePub = text("reservePub").nullable()
val selectedExchangePayto = text("selectedExchangePayto").nullable()
val walletBankAccount = reference("walletBankAccount", BankAccountsTable)
@@ -439,7 +439,7 @@ class TalerWithdrawalEntity(id: EntityID<Long>) :
LongEntity(id) {
companion object :
LongEntityClass<TalerWithdrawalEntity>(TalerWithdrawalsTable)
var wopid by TalerWithdrawalsTable.wopid
var selectionDone by TalerWithdrawalsTable.selectionDone
- var transferDone by TalerWithdrawalsTable.transferDone
+ var confirmationDone by TalerWithdrawalsTable.confirmationDone
var reservePub by TalerWithdrawalsTable.reservePub
var selectedExchangePayto by TalerWithdrawalsTable.selectedExchangePayto
var amount by TalerWithdrawalsTable.amount
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
index 75c876e..4aef3c2 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
@@ -151,6 +151,15 @@ fun wireTransfer(
return transactionRef
}
+fun getWithdrawalOperation(opId: String): TalerWithdrawalEntity {
+ return transaction {
+ TalerWithdrawalEntity.find {
+ TalerWithdrawalsTable.wopid eq java.util.UUID.fromString(opId)
+ }.firstOrNull() ?: throw SandboxError(
+ HttpStatusCode.NotFound, "Withdrawal operation $opId not found."
+ )
+ }
+}
fun getBankAccountFromPayto(paytoUri: String): BankAccountEntity {
val paytoParse = parsePayto(paytoUri)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
index 3447c4b..86758b2 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
@@ -22,6 +22,14 @@ package tech.libeufin.sandbox
import tech.libeufin.util.PaymentInfo
import tech.libeufin.util.RawPayment
+data class WithdrawalRequest(
+ /**
+ * Note: the currency is redundant, because at each point during
+ * the execution the Demobank should have a handle of the currency.
+ */
+ val amount: String // $CURRENCY:X.Y
+)
+
data class Demobank(
val currency: String,
val name: String,
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 487c71f..a5dee37 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -63,7 +63,6 @@ import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.util.*
import io.ktor.util.date.*
-import io.ktor.util.pipeline.*
import kotlinx.coroutines.newSingleThreadContext
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.api.ExposedBlob
@@ -72,6 +71,7 @@ import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.w3c.dom.Document
+import parseAmount
import startServer
import tech.libeufin.util.*
import validatePlainAmount
@@ -960,7 +960,7 @@ val sandboxApp: Application.() -> Unit = {
HttpStatusCode.NotFound, "Withdrawal operation
$wopid not found."
)
if (wo.selectionDone) {
- if (wo.transferDone) {
+ if (wo.confirmationDone) {
logger.info("Wallet performs again this
operation that was paid out earlier: idempotent")
return@newSuspendedTransaction
}
@@ -977,7 +977,7 @@ val sandboxApp: Application.() -> Unit = {
wo.reservePub = body.reserve_pub
wo.selectedExchangePayto = body.selected_exchange
wo.selectionDone = true
- wo.transferDone
+ wo.confirmationDone
}
call.respond(object {
val transfer_done = transferDone
@@ -997,7 +997,7 @@ val sandboxApp: Application.() -> Unit = {
val demobank = ensureDemobank(call)
val ret = TalerWithdrawalStatus(
selection_done = wo.selectionDone,
- transfer_done = wo.transferDone,
+ transfer_done = wo.confirmationDone,
amount = wo.amount,
suggested_exchange = demobank.suggestedExchange
)
@@ -1007,13 +1007,31 @@ val sandboxApp: Application.() -> Unit = {
}
// Talk to Web UI.
route("/access-api") {
+ // Information about one withdrawal.
+ get("/accounts/{account_name}/withdrawals/{withdrawal_id}") {
+ val op =
getWithdrawalOperation(call.getUriComponent("withdrawal_id"))
+ val demobank = ensureDemobank(call)
+ if (!op.selectionDone && op.reservePub != null) throw
internalServerError(
+ "Unselected withdrawal has a reserve public key",
+ LibeufinErrorCode.LIBEUFIN_EC_INCONSISTENT_STATE
+ )
+ call.respond(object {
+ val amount = "${demobank.currency}:${op.amount}"
+ val aborted = op.aborted
+ val confirmation_done = op.confirmationDone
+ val selection_done = op.selectionDone
+ val selected_reserve_pub = op.reservePub
+ val selected_exchange_account =
op.selectedExchangePayto
+ })
+ return@get
+ }
// Create a new withdrawal operation.
post("/accounts/{account_name}/withdrawals") {
- val username = call.request.basicAuth()
- if (username == null) throw badRequest(
- "Taler withdrawal tried with authentication disabled.
" +
- "That is impossible, because no bank account
can get this operation debited."
- )
+ var username = call.request.basicAuth()
+ if (username == null && (!WITH_AUTH)) {
+ logger.info("Authentication is disabled to facilitate
tests, defaulting to 'admin' username")
+ username = "admin"
+ }
val demobank = ensureDemobank(call)
/**
* Check here if the user has the right over the claimed
bank account. After
@@ -1024,8 +1042,14 @@ val sandboxApp: Application.() -> Unit = {
if (maybeOwnedAccount.owner != username) throw
unauthorized(
"Customer '$username' has no rights over bank account
'${maybeOwnedAccount.label}'"
)
+ val req = call.receive<WithdrawalRequest>()
+ // Check for currency consistency
+ val amount = parseAmount(req.amount)
+ if (amount.currency != demobank.currency) throw badRequest(
+ "Currency ${amount.currency} differs from Demobank's:
${demobank.currency}"
+ )
val wo: TalerWithdrawalEntity = transaction {
TalerWithdrawalEntity.new {
- amount = "${demobank.currency}:5"
+ this.amount = amount.amount.toPlainString()
walletBankAccount = maybeOwnedAccount
} }
val baseUrl = URL(call.request.getBaseUrl())
@@ -1043,15 +1067,11 @@ val sandboxApp: Application.() -> Unit = {
})
return@post
}
- // Confirm a withdrawal.
+ // Confirm a withdrawal: no basic auth, because the ID should
be unguessable.
post("/accounts/{account_name}/withdrawals/{withdrawal_id}/confirm") {
val withdrawalId = call.getUriComponent("withdrawal_id")
transaction {
- val wo = TalerWithdrawalEntity.find {
- TalerWithdrawalsTable.wopid eq
java.util.UUID.fromString(withdrawalId)
- }.firstOrNull() ?: throw SandboxError(
- HttpStatusCode.NotFound, "Withdrawal operation
$withdrawalId not found."
- )
+ val wo = getWithdrawalOperation(withdrawalId)
if (wo.aborted) throw SandboxError(
HttpStatusCode.Conflict,
"Cannot confirm an aborted withdrawal."
@@ -1066,7 +1086,7 @@ val sandboxApp: Application.() -> Unit = {
"Cannot withdraw without an exchange."
)
)
- if (!wo.transferDone) {
+ if (!wo.confirmationDone) {
// Need the exchange bank account!
wireTransfer(
debitAccount = wo.walletBankAccount,
@@ -1075,15 +1095,25 @@ val sandboxApp: Application.() -> Unit = {
subject = wo.reservePub ?: throw
internalServerError(
"Cannot transfer funds without reserve
public key."
),
+ // provide the currency.
demoBank = ensureDemobank(call)
)
- wo.transferDone = true
+ wo.confirmationDone = true
}
- wo.transferDone
+ wo.confirmationDone
}
call.respond(object {})
return@post
}
+
+
post("/accounts/{account_name}/withdrawals/{withdrawal_id}/abort") {
+ val withdrawalId = call.getUriComponent("withdrawal_id")
+ val operation = getWithdrawalOperation(withdrawalId)
+ if (operation.confirmationDone) throw conflict("Cannot
abort paid withdrawal.")
+ transaction { operation.aborted = true }
+ call.respond(object {})
+ return@post
+ }
// Bank account basic information.
get("/accounts/{account_name}") {
val username = call.request.basicAuth()
diff --git a/util/src/main/kotlin/HTTP.kt b/util/src/main/kotlin/HTTP.kt
index 9fc12f2..70f6631 100644
--- a/util/src/main/kotlin/HTTP.kt
+++ b/util/src/main/kotlin/HTTP.kt
@@ -68,6 +68,14 @@ fun badRequest(msg: String): UtilError {
)
}
+fun conflict(msg: String): UtilError {
+ return UtilError(
+ HttpStatusCode.Conflict,
+ msg,
+ ec = LibeufinErrorCode.LIBEUFIN_EC_NONE
+ )
+}
+
/**
* Get the base URL of a request; handles proxied case.
*/
diff --git a/util/src/main/kotlin/amounts.kt b/util/src/main/kotlin/amounts.kt
index 19446db..aaead15 100644
--- a/util/src/main/kotlin/amounts.kt
+++ b/util/src/main/kotlin/amounts.kt
@@ -28,3 +28,10 @@ val re = Regex("^([0-9]+(\\.[0-9]+)?)$")
fun validatePlainAmount(plainAmount: String): Boolean {
return re.matches(plainAmount)
}
+
+fun parseAmount(amount: String): AmountWithCurrency {
+ val match = Regex("([A-Z]+):([0-9]+(\\.[0-9]+)?)").find(amount) ?: throw
+ EbicsProtocolError(HttpStatusCode.BadRequest, "invalid amount: $amount")
+ val (currency, number) = match.destructured
+ return AmountWithCurrency(currency, Amount(number))
+}
diff --git a/util/src/main/kotlin/strings.kt b/util/src/main/kotlin/strings.kt
index e44b9a2..c7253f7 100644
--- a/util/src/main/kotlin/strings.kt
+++ b/util/src/main/kotlin/strings.kt
@@ -122,13 +122,6 @@ fun parseDecimal(decimalStr: String): BigDecimal {
}
}
-fun parseAmount(amount: String): AmountWithCurrency {
- val match = Regex("([A-Z]+):([0-9]+(\\.[0-9]+)?)").find(amount) ?: throw
- EbicsProtocolError(HttpStatusCode.BadRequest, "invalid amount: $amount")
- val (currency, number) = match.destructured
- return AmountWithCurrency(currency, Amount(number))
-}
-
fun getRandomString(length: Int): String {
val allowedChars = ('A' .. 'Z') + ('0' .. '9')
return (1 .. length)
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated: Access API: GETting withdrawal information.,
gnunet <=