libeufin

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

commit 96950a3989fa9013b91d773e1cdae3f071a76959
parent 01696776ed983a8985928640e59b0ab960b1ea84
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date:   Thu,  9 Apr 2020 23:31:37 +0200

Store bank-provided IDs, and transaction statuses.

Diffstat:
Mcli/python/libeufin-cli | 4++--
Mnexus/src/main/kotlin/tech/libeufin/nexus/DB.kt | 16++++++++--------
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 7++++---
Mnexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 6+++---
Mutil/src/main/kotlin/XMLUtil.kt | 10++++++++++
5 files changed, 27 insertions(+), 16 deletions(-)

diff --git a/cli/python/libeufin-cli b/cli/python/libeufin-cli @@ -465,8 +465,8 @@ def refund(ctx, account_id, bank_account_id, nexus_base_url): @click.argument( "nexus-base-url" ) -def digest_transactions(obj, account_id, nexus_base_url): - url = urljoin(nexus_base_url, "/ebics/subscribers/{}/digest-incoming-transactions".format(account_id)) +def crunch_transactions(obj, account_id, nexus_base_url): + url = urljoin(nexus_base_url, "/ebics/subscribers/{}/crunch-incoming-transactions".format(account_id)) resp = post(url, json=dict()) print(resp.content.decode("utf-8")) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt @@ -76,14 +76,9 @@ class TalerIncomingPaymentEntity(id: EntityID<Long>) : LongEntity(id) { */ object EbicsRawBankTransactionsTable : LongIdTable() { val nexusSubscriber = reference("subscriber", EbicsSubscribersTable) - // How did we learn about this transaction? C52 / C53 / C54 - val sourceType = text("sourceType") - // Name of the ZIP entry - val sourceFileName = text("sourceFileName") - // "Subject" of the SEPA transaction + val sourceFileName = text("sourceFileName") /* ZIP entry's name */ val unstructuredRemittanceInformation = text("unstructuredRemittanceInformation") - // Debit or credit - val transactionType = text("transactionType") + val transactionType = text("transactionType") /* DBIT or CRDT */ val currency = text("currency") val amount = text("amount") val creditorIban = text("creditorIban") @@ -92,11 +87,13 @@ object EbicsRawBankTransactionsTable : LongIdTable() { val debitorName = text("debitorName") val counterpartBic = text("counterpartBic") val bookingDate = text("bookingDate") + val status = text("status") // BOOK, .. + val servicerCode = text("servicerCode").nullable() /* "internal" code given by the bank */ + val proprietaryCode = text("proprietaryCode") /* code given by the DK */ } class EbicsRawBankTransactionEntity(id: EntityID<Long>) : LongEntity(id) { companion object : LongEntityClass<EbicsRawBankTransactionEntity>(EbicsRawBankTransactionsTable) - var sourceType by EbicsRawBankTransactionsTable.sourceType // C52 or C53 or C54? var sourceFileName by EbicsRawBankTransactionsTable.sourceFileName var unstructuredRemittanceInformation by EbicsRawBankTransactionsTable.unstructuredRemittanceInformation var transactionType by EbicsRawBankTransactionsTable.transactionType @@ -109,6 +106,9 @@ class EbicsRawBankTransactionEntity(id: EntityID<Long>) : LongEntity(id) { var counterpartBic by EbicsRawBankTransactionsTable.counterpartBic var bookingDate by EbicsRawBankTransactionsTable.bookingDate var nexusSubscriber by EbicsSubscriberEntity referencedOn EbicsRawBankTransactionsTable.nexusSubscriber + var status by EbicsRawBankTransactionsTable.status + var servicerCode by EbicsRawBankTransactionsTable.servicerCode + var proprietaryCode by EbicsRawBankTransactionsTable.proprietaryCode } /** diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -658,8 +658,7 @@ fun main() { val paramsJson = call.receive<EbicsStandardOrderParamsJson>() val orderParams = paramsJson.toOrderParams() val subscriberData = getSubscriberDetailsFromId(id) - val response = doEbicsDownloadTransaction(client, subscriberData, "C53", orderParams) - when (response) { + when (val response = doEbicsDownloadTransaction(client, subscriberData, "C53", orderParams)) { is EbicsDownloadSuccessResult -> { /** * The current code is _heavily_ dependent on the way GLS returns @@ -673,12 +672,14 @@ fun main() { val camt53doc = XMLUtil.parseStringIntoDom(it.second) transaction { EbicsRawBankTransactionEntity.new { - sourceType = "C53" sourceFileName = fileName unstructuredRemittanceInformation = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']/@Ccy") transactionType = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='CdtDbtInd']") currency = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']/@Ccy") amount = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']") + status = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Sts']") + servicerCode = camt53doc.pickStringNullable("//*[local-name()='Ntry']//*[local-name()='AcctSvcrRef']") + proprietaryCode = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='BkTxCd']/*[local-name()='Prtry']/*[local-name()='Cd']") bookingDate = camt53doc.pickString("//*[local-name()='BookgDt']//*[local-name()='Dt']") nexusSubscriber = getSubscriberEntityFromId(id) creditorName = diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt @@ -287,7 +287,7 @@ class Taler(app: Route) { * at separating the good payments (those that will lead to a new reserve * being created), from the invalid payments (those with a invalid subject * that will soon be refunded.) */ - app.post("/ebics/taler/{id}/digest-incoming-transactions") { + app.post("/ebics/taler/{id}/crunch-incoming-transactions") { val id = expectId(call.parameters["id"]) // first find highest ID value of already processed rows. transaction { @@ -297,8 +297,8 @@ class Taler(app: Route) { * information. * * This latestId value points at the latest id in the _raw transactions table_ - * that was last processed. On the other hand, the "row_id" value that the exchange - * will get along each history element will be the id in the _digested entries table_. + * that was last processed here. Note, the "row_id" value that the exchange + * will get along each history element will be the id in the _crunched entries table_. */ val latestId: Long = TalerIncomingPaymentEntity.all().sortedByDescending { it.payment.id diff --git a/util/src/main/kotlin/XMLUtil.kt b/util/src/main/kotlin/XMLUtil.kt @@ -427,3 +427,12 @@ class XMLUtil private constructor() { fun Document.pickString(xpath: String): String { return XMLUtil.getStringFromXpath(this, xpath) } + +fun Document.pickStringNullable(xpath: String): String? { + return try { + XMLUtil.getStringFromXpath(this, xpath) + } catch (e: UtilError) { + logger.info("Tolerating missing value: $xpath") + null + } +} +\ No newline at end of file