libeufin

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

WireTransferApi.kt (4322B)


      1 /*
      2  * This file is part of LibEuFin.
      3  * Copyright (C) 2026 Taler Systems S.A.
      4 
      5  * LibEuFin is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU Affero General Public License as
      7  * published by the Free Software Foundation; either version 3, or
      8  * (at your option) any later version.
      9 
     10  * LibEuFin is distributed in the hope that it will be useful, but
     11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
     13  * Public License for more details.
     14 
     15  * You should have received a copy of the GNU Affero General Public
     16  * License along with LibEuFin; see the file COPYING.  If not, see
     17  * <http://www.gnu.org/licenses/>
     18  */
     19 
     20 package tech.libeufin.bank.api
     21 
     22 import io.ktor.http.*
     23 import io.ktor.server.application.*
     24 import io.ktor.server.request.*
     25 import io.ktor.server.response.*
     26 import io.ktor.server.routing.*
     27 import io.ktor.util.pipeline.*
     28 import tech.libeufin.bank.*
     29 import tech.libeufin.bank.auth.pathUsername
     30 import tech.libeufin.bank.db.Database
     31 import tech.libeufin.bank.db.TransferDAO
     32 import tech.libeufin.bank.db.TransferDAO.RegistrationResult
     33 import tech.libeufin.common.*
     34 import tech.libeufin.common.crypto.CryptoUtil
     35 import java.time.Instant
     36 import java.time.Duration
     37 
     38 fun Routing.wireTransferApi(db: Database, cfg: BankConfig) {
     39     get("/accounts/{USERNAME}/taler-wire-transfer-gateway/config") {
     40         call.respond(
     41             WireTransferConfig(
     42                 currency = cfg.regionalCurrency,
     43                 supported_formats = listOf(SubjectFormat.SIMPLE)
     44             )
     45         )
     46     }
     47     post("/accounts/{USERNAME}/taler-wire-transfer-gateway/registration") {
     48         val username = call.pathUsername
     49         val req = call.receive<SubjectRequest>();
     50         cfg.checkRegionalCurrency(req.credit_amount)
     51 
     52         if (!CryptoUtil.checkEdssaSignature(req.account_pub.raw, req.authorization_sig, req.authorization_pub))
     53             throw conflict(
     54                 "invalid signature",
     55                 TalerErrorCode.BANK_BAD_SIGNATURE
     56             )
     57 
     58         when (val result = db.transfer.register(
     59             username,
     60             req.type,
     61             req.account_pub,
     62             req.authorization_pub,
     63             req.authorization_sig,
     64             req.recurrent,
     65             req.credit_amount,
     66             Instant.now()
     67         )) {
     68             RegistrationResult.UnknownAccount -> throw unknownAccount(username)
     69             RegistrationResult.NotExchange -> throw notExchange(username)
     70             RegistrationResult.ReservePubReuse -> throw conflict(
     71                 "reserve_pub used already",
     72                 TalerErrorCode.BANK_DUPLICATE_RESERVE_PUB_SUBJECT
     73             )
     74             is RegistrationResult.Success -> {
     75                 val subjects = mutableListOf<TransferSubject>()
     76                 if (result.uuid != null)
     77                     subjects.add(TransferSubject.Uri(cfg.talerWithdrawUri(result.uuid), req.credit_amount))
     78                 subjects.add(TransferSubject.Simple(fmtIncomingSubject(IncomingType.map, req.authorization_pub), req.credit_amount))
     79                 call.respond(
     80                     SubjectResult(
     81                         subjects,
     82                         TalerTimestamp.never()
     83                     )
     84                 )
     85             }
     86         }
     87     }
     88     delete("/accounts/{USERNAME}/taler-wire-transfer-gateway/registration") {
     89         val req = call.receive<Unregistration>();
     90 
     91         val timestamp = Instant.parse(req.timestamp)
     92 
     93         if (timestamp.isBefore(Instant.now().minus(Duration.ofMinutes(15))))
     94             throw conflict(
     95                 "timestamp too old",
     96                 TalerErrorCode.BANK_OLD_TIMESTAMP
     97             )
     98 
     99         if (!CryptoUtil.checkEdssaSignature(req.timestamp.toByteArray(), req.authorization_sig, req.authorization_pub))
    100             throw conflict(
    101                 "invalid signature",
    102                 TalerErrorCode.BANK_BAD_SIGNATURE
    103             )
    104             
    105         if (db.transfer.unregister(req.authorization_pub, Instant.now())) {
    106             call.respond(HttpStatusCode.NoContent)
    107         } else {
    108             throw notFound(
    109                 "Prepared transfer '${req.authorization_pub}' not found",
    110                 TalerErrorCode.BANK_TRANSACTION_NOT_FOUND
    111             )
    112         }
    113     }
    114 }