commit df3a7c52244a7febee27b4c1254c1643683d8bba
parent aecf4112ab5d8c56a9be3a0458f227f2b61a4d2b
Author: Antoine A <>
Date: Tue, 24 Oct 2023 15:26:45 +0000
Prepare core bank cashout API
Diffstat:
4 files changed, 103 insertions(+), 8 deletions(-)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
@@ -31,6 +31,7 @@ fun Routing.coreBankApi(db: Database, ctx: BankApplicationContext) {
coreBankAccountsMgmtApi(db, ctx)
coreBankTransactionsApi(db, ctx)
coreBankWithdrawalApi(db, ctx)
+ coreBankCashoutApi(db, ctx)
}
private fun Routing.coreBankTokenApi(db: Database) {
@@ -384,7 +385,7 @@ private fun Routing.coreBankTransactionsApi(db: Database, ctx: BankApplicationCo
val subject = tx.payto_uri.message ?: throw badRequest("Wire transfer lacks subject")
val amount = tx.payto_uri.amount ?: tx.amount ?: throw badRequest("Wire transfer lacks amount")
- checkInternalCurrency(ctx, amount)
+ ctx.checkInternalCurrency(amount)
val result = db.bankTransaction(
creditAccountPayto = tx.payto_uri,
debitAccountUsername = login,
@@ -419,7 +420,7 @@ fun Routing.coreBankWithdrawalApi(db: Database, ctx: BankApplicationContext) {
val (login, _) = call.authCheck(db, TokenScope.readwrite)
val req = call.receive<BankAccountCreateWithdrawalRequest>() // Checking that the user has enough funds.
- checkInternalCurrency(ctx, req.amount)
+ ctx.checkInternalCurrency(req.amount)
val opId = UUID.randomUUID()
when (db.talerWithdrawalCreate(login, opId, req.amount)) {
@@ -511,4 +512,39 @@ fun Routing.coreBankWithdrawalApi(db: Database, ctx: BankApplicationContext) {
WithdrawalConfirmationResult.SUCCESS -> call.respond(HttpStatusCode.NoContent)
}
}
+}
+
+fun Routing.coreBankCashoutApi(db: Database, ctx: BankApplicationContext) {
+ post("/accounts/{USERNAME}/cashouts") {
+ val (login, _) = call.authCheck(db, TokenScope.readwrite)
+ val req = call.receive<CashoutRequest>() // Checking that the user has enough funds.
+
+ ctx.checkInternalCurrency(req.amount_debit)
+ ctx.checkCashoutCurrency(req.amount_credit)
+
+ // TODO
+ }
+ post("/accounts/{USERNAME}/cashouts/{CASHOUT_ID}/abort") {
+ val (login, _) = call.authCheck(db, TokenScope.readwrite)
+ // TODO
+ }
+ post("/accounts/{USERNAME}/cashouts/{CASHOUT_ID}/confirm") {
+ val (login, _) = call.authCheck(db, TokenScope.readwrite)
+ // TODO
+ }
+ get("/accounts/{USERNAME}/cashouts") {
+ val (login, _) = call.authCheck(db, TokenScope.readonly)
+ // TODO
+ }
+ get("/accounts/{USERNAME}/cashouts/{CASHOUT_ID}") {
+ val (login, _) = call.authCheck(db, TokenScope.readonly)
+ // TODO
+ }
+ get("/cashouts") {
+ call.authAdmin(db, TokenScope.readonly)
+ // TODO
+ }
+ get("/cashout-rate") {
+ // TODO
+ }
}
\ No newline at end of file
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
@@ -40,13 +40,17 @@ enum class FracDigits {
TWO, EIGHT
}
-
// Allowed values for bank transactions directions.
enum class TransactionDirection {
credit,
debit
}
+enum class CashoutStatus {
+ pending,
+ confirmed
+}
+
/**
* HTTP response type of successful token refresh.
* access_token is the Crockford encoding of the 32 byte
@@ -506,6 +510,54 @@ data class BankWithdrawalOperationPostResponse(
val confirm_transfer_url: String? = null
)
+@Serializable
+data class CashoutRequest(
+ val subject: String?,
+ val amount_debit: TalerAmount,
+ val amount_credit: TalerAmount,
+ val tan_channel: TanChannel?
+)
+
+@Serializable
+data class Cashouts(
+ val cashouts: List<CashoutInfo>,
+)
+
+@Serializable
+data class CashoutInfo(
+ val cashout_id: String,
+ val status: CashoutStatus,
+)
+
+
+@Serializable
+data class GlobalCashouts(
+ val cashouts: List<GlobalCashoutInfo>,
+)
+
+@Serializable
+data class GlobalCashoutInfo(
+ val cashout_id: String,
+ val username: String,
+ val status: CashoutStatus,
+)
+
+@Serializable
+data class CashoutStatusResponse(
+ val status: CashoutStatus,
+ val amount_debit: TalerAmount,
+ val amount_credit: TalerAmount,
+ val subject: String,
+ val credit_payto_uri: IbanPayTo,
+ val creation_time: TalerProtocolTimestamp,
+ val confirmation_time: TalerProtocolTimestamp?,
+)
+
+@Serializable
+data class CashoutConfirm(
+ val tan: String
+)
+
/**
* Request to an /admin/add-incoming request from
* the Taler Wire Gateway API.
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApi.kt
@@ -45,7 +45,7 @@ fun Routing.wireGatewayApi(db: Database, ctx: BankApplicationContext) {
post("/accounts/{USERNAME}/taler-wire-gateway/transfer") {
val (login, _) = call.authCheck(db, TokenScope.readwrite)
val req = call.receive<TransferRequest>()
- checkInternalCurrency(ctx, req.amount)
+ ctx.checkInternalCurrency(req.amount)
val dbRes = db.talerTransferCreate(
req = req,
username = login,
@@ -124,7 +124,7 @@ fun Routing.wireGatewayApi(db: Database, ctx: BankApplicationContext) {
post("/accounts/{USERNAME}/taler-wire-gateway/admin/add-incoming") {
val (login, _) = call.authCheck(db, TokenScope.readwrite) // TODO authAdmin ?
val req = call.receive<AddIncomingRequest>()
- checkInternalCurrency(ctx, req.amount)
+ ctx.checkInternalCurrency(req.amount)
val timestamp = Instant.now()
val dbRes = db.talerAddIncomingCreate(
req = req,
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
@@ -116,9 +116,16 @@ fun badRequest(
)
)
-fun checkInternalCurrency(ctx: BankApplicationContext, amount: TalerAmount) {
- if (amount.currency != ctx.currency) throw badRequest(
- "Wrong currency: expected internal currency ${ctx.currency} got ${amount.currency}",
+fun BankApplicationContext.checkInternalCurrency(amount: TalerAmount) {
+ if (amount.currency != currency) throw badRequest(
+ "Wrong currency: expected internal currency $currency got ${amount.currency}",
+ talerErrorCode = TalerErrorCode.TALER_EC_GENERIC_CURRENCY_MISMATCH
+ )
+}
+
+fun BankApplicationContext.checkCashoutCurrency(amount: TalerAmount) {
+ if (amount.currency != cashoutCurrency) throw badRequest(
+ "Wrong currency: expected cashout currency $cashoutCurrency got ${amount.currency}",
talerErrorCode = TalerErrorCode.TALER_EC_GENERIC_CURRENCY_MISMATCH
)
}