libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

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:
Mbank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt | 2+-
Mbank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt | 2++
Mbank/src/main/kotlin/tech/libeufin/bank/db/CashoutDAO.kt | 43+++++++++++++++++++++++++++++++------------
Mbank/src/main/kotlin/tech/libeufin/bank/db/WithdrawalDAO.kt | 6+++---
Mbank/src/test/kotlin/CoreBankApiTest.kt | 2++
Mdatabase-versioning/libeufin-bank-0001.sql | 4++++
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