libeufin

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

commit 01696776ed983a8985928640e59b0ab960b1ea84
parent 95994e9af317eb115a64341e179c76772afc3055
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date:   Thu,  9 Apr 2020 19:44:20 +0200

Create main/rough logic for Taler-started transfers.

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/DB.kt | 11++++++++---
Mnexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt | 1+
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 10+++++++---
Mnexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
4 files changed, 75 insertions(+), 8 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt @@ -12,10 +12,12 @@ const val ID_MAX_LENGTH = 50 /** * This table holds the values that exchange gave to issue a payment, - * plus a reference to the prepared pain.001 version of. + * plus a reference to the prepared pain.001 version of. Note that + * whether a pain.001 document was sent or not to the bank is indicated + * in the PAIN-table. */ object TalerRequestedPayments: LongIdTable() { - val payment = TalerIncomingPayments.reference("payment", Pain001Table) + val preparedPayment = TalerIncomingPayments.reference("payment", Pain001Table) val requestUId = text("request_uid") val amount = text("amount") val exchangeBaseUrl = text("exchange_base_url") @@ -24,7 +26,8 @@ object TalerRequestedPayments: LongIdTable() { } class TalerRequestedPaymentEntity(id: EntityID<Long>) : LongEntity(id) { - var payment by Pain001Entity referencedOn TalerRequestedPayments.payment + companion object : LongEntityClass<TalerRequestedPaymentEntity>(TalerIncomingPayments) + var preparedPayment by Pain001Entity referencedOn TalerRequestedPayments.preparedPayment var requestUId by TalerRequestedPayments.requestUId var amount by TalerRequestedPayments.amount var exchangeBaseUrl by TalerRequestedPayments.exchangeBaseUrl @@ -119,6 +122,7 @@ object Pain001Table : IntIdTableWithAmount() { val paymentId = long("paymentId") val fileDate = long("fileDate") val sum = amount("sum") + val currency = varchar("currency", length = 3).default("EUR") val debtorAccount = text("debtorAccount") val endToEndId = long("EndToEndId") val subject = text("subject") @@ -142,6 +146,7 @@ class Pain001Entity(id: EntityID<Int>) : IntEntity(id) { var paymentId by Pain001Table.paymentId var date by Pain001Table.fileDate var sum by Pain001Table.sum + var currency by Pain001Table.currency var debtorAccount by Pain001Table.debtorAccount var endToEndId by Pain001Table.endToEndId var subject by Pain001Table.subject diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt @@ -91,6 +91,7 @@ data class Pain001Data( val creditorBic: String, val creditorName: String, val sum: Amount, + val currency: String = "EUR", val subject: String ) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -288,7 +288,7 @@ fun createPain001document(pain001Entity: Pain001Entity): String { } } element("Amt/InstdAmt") { - attribute("Ccy", "EUR") + attribute("Ccy", pain001Entity.currency) text(pain001Entity.sum.toString()) } element("CdtrAgt/FinInstnId/BIC") { @@ -313,10 +313,14 @@ fun createPain001document(pain001Entity: Pain001Entity): String { /** * Insert one row in the database, and leaves it marked as non-submitted. + * @param debtorAccountId the mnemonic id assigned by the bank to one bank + * account of the subscriber that is creating the pain entity. In this case, + * it will be the account whose money will pay the wire transfer being defined + * by this pain document. */ -fun createPain001entity(entry: Pain001Data, debtorAccountId: String) { +fun createPain001entity(entry: Pain001Data, debtorAccountId: String): Pain001Entity { val randomId = Random().nextLong() - transaction { + return transaction { Pain001Entity.new { subject = entry.subject sum = entry.sum diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt @@ -82,9 +82,9 @@ class Taler(app: Route) { * Helper data structures. */ data class Payto( - val name: String, + val name: String = "NOTGIVEN", val iban: String, - val bic: String // empty string in case no BIC was given + val bic: String = "NOTGIVEN" ) data class AmountWithCurrency( val currency: String, @@ -157,6 +157,63 @@ class Taler(app: Route) { /** attaches Taler endpoints to the main Web server */ init { + app.post("/taler/transfer") { + val exchangeId = authenticateRequest(call.request.headers["Authorization"]) + val transferRequest = call.receive<TalerTransferRequest>() + + /** + * FIXME: check the UID before putting new data into the database. + */ + val opaque_row_id = transaction { + val creditorData = parsePayto(transferRequest.credit_account) + val exchangeBankAccount = getBankAccountsInfoFromId(exchangeId) + val pain001 = createPain001entity( + Pain001Data( + creditorIban = creditorData.iban, + creditorBic = creditorData.bic, + creditorName = creditorData.name, + subject = transferRequest.wtid, + sum = parseAmount(transferRequest.amount).amount + ), + exchangeBankAccount.first().id.value + ) + TalerRequestedPaymentEntity.find { + TalerRequestedPayments.requestUId eq transferRequest.request_uid + }.forEach { + if ( + (it.amount != transferRequest.amount) or + (it.creditAccount != transferRequest.exchange_base_url) or + (it.wtid != transferRequest.wtid) + ) { + throw NexusError( + HttpStatusCode.Conflict, + "This uid (${transferRequest.request_uid}) belong to a different payment altrady" + ) + } + } + val row = TalerRequestedPaymentEntity.new { + preparedPayment = pain001 + exchangeBaseUrl = transferRequest.exchange_base_url + requestUId = transferRequest.request_uid + amount = transferRequest.amount + wtid = transferRequest.wtid + creditAccount = transferRequest.credit_account + } + row.id.value + } + call.respond( + HttpStatusCode.OK, + TalerTransferResponse( + /** + * Normally should point to the next round where the background + * routing will sent new PAIN.001 data to the bank; work in progress.. + */ + timestamp = DateTime.now().millis / 1000, + row_id = opaque_row_id + ) + ) + return@post + } /** Test-API that creates one new payment addressed to the exchange. */ app.post("/taler/admin/add-incoming") { val exchangeId = authenticateRequest(call.request.headers["Authorization"])