libeufin

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

commit b37353d25cf0e4a8c08fea5ecd6437ccdec550e3
parent 107a5bc855579b588fe3c22210f810296075b5b0
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date:   Thu,  9 Apr 2020 17:49:05 +0200

Completing admin add-incoming.

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/DB.kt | 9+++++++--
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 3++-
Mnexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 45++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 49 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 @@ -14,7 +14,7 @@ object TalerIncomingPayments: LongIdTable() { val payment = reference("payment", EbicsRawBankTransactionsTable) val valid = bool("valid") // avoid refunding twice! - val processed = bool("refunded").default(false) + val refunded = bool("refunded").default(false) } class TalerIncomingPaymentEntry(id: EntityID<Long>) : LongEntity(id) { @@ -31,9 +31,14 @@ class TalerIncomingPaymentEntry(id: EntityID<Long>) : LongEntity(id) { } var payment by EbicsRawBankTransactionEntry referencedOn TalerIncomingPayments.payment var valid by TalerIncomingPayments.valid - var processed by TalerIncomingPayments.processed + var refunded by TalerIncomingPayments.refunded } +/** + * This table _assumes_ that all the entries have a BOOK status. The + * current code however does only enforces this trusting the C53 response, + * but never actually checking the appropriate "Sts" field. + */ object EbicsRawBankTransactionsTable : LongIdTable() { val nexusSubscriber = reference("subscriber", EbicsSubscribersTable) // How did we learn about this transaction? C52 / C53 / C54 diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -142,7 +142,8 @@ fun getSubscriberDetailsFromBankAccount(bankAccountId: String): EbicsClientSubsc /** * Given a subscriber id, returns the _list_ of bank accounts associated to it. * @param id the subscriber id - * @return the query set containing the subscriber's bank accounts + * @return the query set containing the subscriber's bank accounts. The result + * is guaranteed to be non empty. */ fun getBankAccountsInfoFromId(id: String): SizedIterable<EbicsAccountInfoEntity> { val list = transaction { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt @@ -16,6 +16,7 @@ import org.joda.time.DateTime import org.joda.time.format.DateTimeFormat import tech.libeufin.util.Amount import tech.libeufin.util.CryptoUtil +import tech.libeufin.util.toZonedString import java.util.* import kotlin.math.abs @@ -64,6 +65,12 @@ class Taler(app: Route) { private data class TalerAdminAddIncoming( val amount: String, val reserve_pub: String, + /** + * This account is the one giving money to the exchange. It doesn't + * have to be 'created' as it might (and normally is) simply be a payto:// + * address pointing to a bank account hosted in a different financial + * institution. + */ val debit_account: String ) @@ -152,11 +159,39 @@ class Taler(app: Route) { /** attaches Taler endpoints to the main Web server */ init { app.post("/taler/admin/add-incoming") { + val exchangeId = authenticateRequest(call.request.headers["Authorization"]) val addIncomingData = call.receive<TalerAdminAddIncoming>() - /** Decompose amount and payto fields. */ - + val debtor = parsePayto(addIncomingData.debit_account) + val amount = parseAmount(addIncomingData.amount) - call.respond(HttpStatusCode.OK, NexusErrorJson("Not implemented")) + /** Decompose amount and payto fields. */ + val (bookingDate, opaque_row_id) = transaction { + val exchangeBankAccount = getBankAccountsInfoFromId(exchangeId).first() + val rawPayment = EbicsRawBankTransactionEntry.new { + sourceFileName = "test" + sourceType = "C53" + unstructuredRemittanceInformation = addIncomingData.reserve_pub + transactionType = "CRDT" + currency = amount.currency + this.amount = amount.amount.toPlainString() + creditorIban = exchangeBankAccount.iban + creditorName = "Exchange's company name" + debitorIban = debtor.iban + debitorName = if (debtor.name.isNotEmpty()) { debtor.name } else "DEBITORNAMENOTGIVEN" + counterpartBic = if (debtor.bic.isNotEmpty()) { debtor.bic } else "DEBITORBICNOTGIVEN" + bookingDate = DateTime.now().toZonedString() + } + /** This payment is "valid by default" and will be returned + * as soon as the exchange will ask for new payments. */ + val row = TalerIncomingPaymentEntry.new { + payment = rawPayment + } + Pair(rawPayment.bookingDate, row.id.value) + } + call.respond(HttpStatusCode.OK, TalerAddIncomingResponse( + timestamp = parseDate(bookingDate).millis / 1000, + row_id = opaque_row_id + )) return@post } app.post("/ebics/taler/{id}/accounts/{acctid}/refund-invalid-payments") { @@ -170,7 +205,7 @@ class Taler(app: Route) { ) } TalerIncomingPaymentEntry.find { - TalerIncomingPayments.processed eq false and (TalerIncomingPayments.valid eq false) + TalerIncomingPayments.refunded eq false and (TalerIncomingPayments.valid eq false) }.forEach { createPain001entry( Pain001Data( @@ -182,7 +217,7 @@ class Taler(app: Route) { ), acctid.id.value ) - it.processed = true + it.refunded = true } } return@post