commit 4e64f78f9111eead565aca9c69c7014c41a20195
parent ea3dbf1e782edb2fda4b13b9e53ef568d3ee2389
Author: Antoine A <>
Date: Fri, 24 Nov 2023 18:22:01 +0000
Fix withdrawal API and add tan transmission informations to cashout details
Diffstat:
6 files changed, 43 insertions(+), 16 deletions(-)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
@@ -556,7 +556,7 @@ private fun Routing.coreBankCashoutApi(db: Database, ctx: BankConfig) = conditio
TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED
)
}
- db.cashout.markSent(res.id, Instant.now(), TAN_RETRANSMISSION_PERIOD)
+ db.cashout.markSent(res.id, Instant.now(), TAN_RETRANSMISSION_PERIOD, tanChannel, res.tanInfo)
}
call.respond(CashoutPending(res.id))
}
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
@@ -448,6 +448,8 @@ data class CashoutStatusResponse(
val subject: String,
val creation_time: TalerProtocolTimestamp,
val confirmation_time: TalerProtocolTimestamp? = null,
+ val tan_channel: TanChannel? = null,
+ val tan_info: String? = null
)
@Serializable
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/CashoutDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/CashoutDAO.kt
@@ -99,17 +99,32 @@ class CashoutDAO(private val db: Database) {
suspend fun markSent(
id: Long,
now: Instant,
- retransmissionPeriod: Duration
- ) = db.serializable { conn ->
- val stmt = conn.prepareStatement("""
- SELECT challenge_mark_sent(challenge, ?, ?)
- FROM cashout_operations
- WHERE cashout_id=?
- """)
- stmt.setLong(1, now.toDbMicros() ?: throw faultyTimestampByBank())
- stmt.setLong(2, TimeUnit.MICROSECONDS.convert(retransmissionPeriod))
- stmt.setLong(3, id)
- stmt.executeQueryCheck()
+ retransmissionPeriod: Duration,
+ tanChannel: TanChannel,
+ tanInfo: String
+ ) = db.serializable {
+ it.transaction { conn ->
+ conn.prepareStatement("""
+ SELECT challenge_mark_sent(challenge, ?, ?)
+ FROM cashout_operations
+ WHERE cashout_id=?
+ """).run {
+ setLong(1, now.toDbMicros() ?: throw faultyTimestampByBank())
+ setLong(2, TimeUnit.MICROSECONDS.convert(retransmissionPeriod))
+ setLong(3, id)
+ executeQueryCheck()
+ }
+ conn.prepareStatement("""
+ UPDATE cashout_operations
+ SET tan_channel = ?, tan_info = ?
+ WHERE cashout_id=?
+ """).run {
+ setString(1, tanChannel.name)
+ setString(2, tanInfo)
+ setLong(3, id)
+ executeUpdateCheck()
+ }
+ }
}
/** Abort cashout operation [id] owned by [login] */
@@ -196,6 +211,8 @@ class CashoutDAO(private val db: Database) {
,cashout_operations.subject
,creation_time
,transaction_date as confirmation_date
+ ,tan_channel
+ ,tan_info
FROM cashout_operations
JOIN bank_accounts ON bank_account=bank_account_id
JOIN customers ON owning_customer_id=customer_id
@@ -214,7 +231,9 @@ class CashoutDAO(private val db: Database) {
confirmation_time = when (val timestamp = it.getLong("confirmation_date")) {
0L -> null
else -> TalerProtocolTimestamp(timestamp.microsToJavaInstant() ?: throw faultyTimestampByBank())
- }
+ },
+ tan_channel = it.getString("tan_channel")?.run { TanChannel.valueOf(this) },
+ tan_info = it.getString("tan_info"),
)
}
}
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/WithdrawalDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/WithdrawalDAO.kt
@@ -178,7 +178,7 @@ class WithdrawalDAO(private val db: Database) {
/** Get withdrawal operation [uuid] linked account username */
suspend fun getUsername(uuid: UUID): String? = db.conn { conn ->
val stmt = conn.prepareStatement("""
- SELECT username
+ SELECT login
FROM taler_withdrawal_operations
JOIN bank_accounts ON wallet_bank_account=bank_account_id
JOIN customers ON customer_id=owning_customer_id
@@ -199,7 +199,7 @@ class WithdrawalDAO(private val db: Database) {
,confirmation_done
,reserve_pub
,selected_exchange_payto
- ,username
+ ,login
FROM taler_withdrawal_operations
JOIN bank_accounts ON wallet_bank_account=bank_account_id
JOIN customers ON customer_id=owning_customer_id
@@ -214,7 +214,7 @@ class WithdrawalDAO(private val db: Database) {
aborted = it.getBoolean("aborted"),
selected_exchange_account = it.getString("selected_exchange_payto"),
selected_reserve_pub = it.getBytes("reserve_pub")?.run(::EddsaPublicKey),
- username = it.getString("username")
+ username = it.getString("login")
)
}
}
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -1166,6 +1166,8 @@ class CoreBankCashoutApiTest {
assertEquals(CashoutStatus.pending, it.status)
assertEquals(amountDebit, it.amount_debit)
assertEquals(amountCredit, it.amount_credit)
+ assertEquals(TanChannel.sms, it.tan_channel)
+ assertEquals("+99", it.tan_info)
}
client.postA("/accounts/customer/cashouts/$id/confirm") {
diff --git a/database-versioning/libeufin-bank-0001.sql b/database-versioning/libeufin-bank-0001.sql
@@ -198,6 +198,8 @@ CREATE TABLE IF NOT EXISTS cashout_operations
REFERENCES challenges(challenge_id)
ON DELETE CASCADE
ON UPDATE RESTRICT
+ ,tan_channel TEXT NULL DEFAULT NULL
+ ,tan_info TEXT NULL DEFAULT NULL
,aborted BOOLEAN NOT NULL DEFAULT FALSE
,local_transaction BIGINT UNIQUE DEFAULT NULL-- FIXME: Comment that the transaction only gets created after the TAN confirmation
REFERENCES bank_account_transactions(bank_transaction_id)
@@ -207,6 +209,8 @@ CREATE TABLE IF NOT EXISTS cashout_operations
COMMENT ON COLUMN cashout_operations.bank_account IS 'Bank amount to debit during confirmation';
COMMENT ON COLUMN cashout_operations.challenge IS 'TAN challenge used to confirm the operation';
COMMENT ON COLUMN cashout_operations.local_transaction IS 'Transaction generated during confirmation';
+COMMENT ON COLUMN cashout_operations.tan_channel IS 'Channel of the last successful transmission of the TAN challenge';
+COMMENT ON COLUMN cashout_operations.tan_info IS 'Info of the last successful transmission of the TAN challenge';
-- end of: cashout management