diff options
author | Marcello Stanisci <stanisci.m@gmail.com> | 2020-03-31 18:24:58 +0200 |
---|---|---|
committer | Marcello Stanisci <stanisci.m@gmail.com> | 2020-03-31 18:24:58 +0200 |
commit | 8723a7107d0916ded72cdcf311ddc59f6343ecee (patch) | |
tree | 27c5455f4d4e7c9c99383d5c7e7d7ad8d98c06c1 | |
parent | 4c196b7c81db58b379c04071ecf48625e53e439a (diff) | |
download | libeufin-8723a7107d0916ded72cdcf311ddc59f6343ecee.tar.gz libeufin-8723a7107d0916ded72cdcf311ddc59f6343ecee.tar.bz2 libeufin-8723a7107d0916ded72cdcf311ddc59f6343ecee.zip |
Implement refunds, plus helpers.
However, the function that calculates the
refund amount is still WIP as it only echoes
back the original amount.
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt | 8 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 16 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 127 | ||||
-rw-r--r-- | util/src/main/kotlin/XMLUtil.kt | 4 |
4 files changed, 85 insertions, 70 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt index e15b2958..361cf6a8 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt @@ -38,7 +38,10 @@ object EbicsRawBankTransactionsTable : LongIdTable() { val currency = text("currency") val amount = text("amount") val creditorIban = text("creditorIban") + val creditorName = text("creditorBic") val debitorIban = text("debitorIban") + val debitorName = text("debitorName") + val counterpartBic = text("counterpartBic") val bookingDate = text("bookingDate") } @@ -50,8 +53,11 @@ class EbicsRawBankTransactionEntry(id: EntityID<Long>) : LongEntity(id) { var transactionType by EbicsRawBankTransactionsTable.transactionType var currency by EbicsRawBankTransactionsTable.currency var amount by EbicsRawBankTransactionsTable.amount - var creditorIban by EbicsRawBankTransactionsTable.creditorIban var debitorIban by EbicsRawBankTransactionsTable.debitorIban + var debitorName by EbicsRawBankTransactionsTable.debitorName + var creditorName by EbicsRawBankTransactionsTable.creditorName + var creditorIban by EbicsRawBankTransactionsTable.creditorIban + var counterpartBic by EbicsRawBankTransactionsTable.counterpartBic var bookingDate by EbicsRawBankTransactionsTable.bookingDate var nexusSubscriber by EbicsSubscriberEntity referencedOn EbicsRawBankTransactionsTable.nexusSubscriber } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt index 468e8f03..e39f521f 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt @@ -28,4 +28,20 @@ fun chunkString(input: String): String { fun expectId(param: String?): String { return param ?: throw NexusError(HttpStatusCode.BadRequest, "Bad ID given") +} + +/* Needs a transaction{} block to be called */ +fun expectIdTransaction(param: String?): EbicsSubscriberEntity { + if (param == null) { + throw NexusError(HttpStatusCode.BadRequest, "Null Id given") + } + return EbicsSubscriberEntity.findById(param) ?: throw NexusError(HttpStatusCode.NotFound, "Subscriber: $param not found") +} + +/* Needs a transaction{} block to be called */ +fun expectAcctidTransaction(param: String?): EbicsAccountInfoEntity { + if (param == null) { + throw NexusError(HttpStatusCode.BadRequest, "Null Acctid given") + } + return EbicsAccountInfoEntity.findById(param) ?: throw NexusError(HttpStatusCode.NotFound, "Account: $param not found") }
\ No newline at end of file diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt index 7f3dae0a..81773a8d 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -101,18 +101,9 @@ fun getSubscriberEntityFromId(id: String): EbicsSubscriberEntity { } } -fun getBankAccountDetailsFromAcctid(id: String): EbicsAccountInfoElement { - return transaction { - val bankAccount = EbicsAccountInfoEntity.find { - EbicsAccountsInfoTable.id eq id - }.firstOrNull() ?: throw NexusError(HttpStatusCode.NotFound, "Bank account not found from account id '$id'") - EbicsAccountInfoElement( - accountId = id, - accountHolderName = bankAccount.accountHolder, - iban = bankAccount.iban, - bankCode = bankAccount.bankCode - ) - } +fun calculateRefund(amount: String): Amount { + // fixme: must apply refund fees! + return Amount(amount) } /** @@ -457,38 +448,34 @@ fun main() { ) return@get } - /** * This endpoint gathers all the data needed to create a payment and persists it * into the database. However, it does NOT perform the payment itself! */ post("/ebics/subscribers/{id}/accounts/{acctid}/prepare-payment") { - val acctid = expectId(call.parameters["acctid"]) - val subscriberId = expectId(call.parameters["id"]) - - transaction { - val accountinfo = EbicsAccountInfoEntity.findById(acctid) ?: throw NexusError( - HttpStatusCode.NotFound, "Bank account with id '$acctid' not found (trigger HTD first?)" - ) - val subscriber = EbicsSubscriberEntity.findById(subscriberId) ?: throw NexusError( - HttpStatusCode.NotFound, "Subscriber '$subscriberId' not found" - ) - if (accountinfo.subscriber != subscriber) { - throw NexusError(HttpStatusCode.BadRequest, "Claimed bank account '$acctid' doesn't belong to subscriber '$subscriberId'!") + val acctid = transaction { + val accountInfo = expectAcctidTransaction(call.parameters["acctid"]) + val subscriber = expectIdTransaction(call.parameters["subscriber"]) + if (accountInfo.subscriber != subscriber) { + throw NexusError( + HttpStatusCode.BadRequest, + "Claimed bank account '${accountInfo.id}' doesn't belong to subscriber '${subscriber.id}'!" + ) } + accountInfo.id.value } val pain001data = call.receive<Pain001Data>() createPain001entry(pain001data, acctid) - - call.respondText("Payment instructions persisted in DB", ContentType.Text.Plain, HttpStatusCode.OK) + call.respondText( + "Payment instructions persisted in DB", + ContentType.Text.Plain, HttpStatusCode.OK + ) return@post } - /** * list all the prepared payments related to customer {id} */ get("/ebics/subscribers/{id}/payments") { - val id = expectId(call.parameters["id"]) val ret = PaymentsInfo() transaction { @@ -552,7 +539,6 @@ fun main() { ) return@post } - /** * This function triggers the Nexus to perform all those un-submitted payments. * Ideally, this logic will be moved into some more automatic mechanism. @@ -643,12 +629,40 @@ fun main() { return@get } + + post("/ebics/taler/{id}/{acctid}/refund-invalid-payments") { + transaction { + val subscriber = expectIdTransaction(call.parameters["id"]) + val acctid = expectAcctidTransaction(call.parameters["acctid"]) + if (acctid.subscriber.id != subscriber.id) { + throw NexusError( + HttpStatusCode.Forbidden, + "Such subscriber (${subscriber.id}) can't drive such account (${acctid.id})" + ) + } + TalerIncomingPaymentEntry.find { + TalerIncomingPayments.processed eq false + }.forEach { + createPain001entry( + Pain001Data( + creditorName = it.payment.debitorName, + creditorIban = it.payment.debitorIban, + creditorBic = it.payment.counterpartBic, + sum = calculateRefund(it.payment.amount), + subject = "Taler refund" + ), + acctid.id.value + ) + } + } + return@post + } /** * VERY taler-related behaviour, where the Nexus differentiates good * incoming transactions (those with a valid subject, i.e. a public key), * and invalid ones (the rest). */ - post("/ebics/subscribers/{id}/digest-incoming-transactions") { + post("/ebics/taler/{id}/digest-incoming-transactions") { val id = expectId(call.parameters["id"]) // first find highest ID value of already processed rows. transaction { @@ -706,47 +720,23 @@ fun main() { response.orderData.unzipWithLoop { val fileName = it.first val camt53doc = XMLUtil.parseStringIntoDom(it.second) - - val creditorIban = XMLUtil.getStringFromXpath( - camt53doc, - "//*[local-name()='CdtrAcct']//*[local-name()='IBAN']" - ) - val debitorIban = XMLUtil.getStringFromXpath( - camt53doc, - "//*[local-name()='DbtrAcct']//*[local-name()='IBAN']" - ) - val creditOrDebit = XMLUtil.getStringFromXpath( - camt53doc, - "//*[local-name()='Ntry']//*[local-name()='CdtDbtInd']" - ) - val amount = XMLUtil.getStringFromXpath( - camt53doc, - "//*[local-name()='Ntry']//*[local-name()='Amt']" - ) - val bookingDate = XMLUtil.getStringFromXpath( - camt53doc, - "//*[local-name()='BookgDt']//*[local-name()='Dt']" - ) - val subject = XMLUtil.getStringFromXpath( - camt53doc, - "//*[local-name()='RmtInf']//*[local-name()='Ustrd']" - ) - val currency = XMLUtil.getStringFromXpath( - camt53doc, - "//*[local-name()='Ntry']//*[local-name()='Amt']/@Ccy" - ) transaction { EbicsRawBankTransactionEntry.new { sourceType = "C53" sourceFileName = fileName - unstructuredRemittanceInformation = subject - transactionType = creditOrDebit - this.currency = currency - this.amount = amount - this.creditorIban = creditorIban - this.debitorIban = debitorIban - this.bookingDate = bookingDate + 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']") + bookingDate = camt53doc.pickString("//*[local-name()='BookgDt']//*[local-name()='Dt']") nexusSubscriber = getSubscriberEntityFromId(id) + creditorName = + camt53doc.pickString("//*[local-name()='RltdPties']//*[local-name()='Dbtr']//*[local-name()='Nm']") + creditorIban = + camt53doc.pickString("//*[local-name()='CdtrAcct']//*[local-name()='IBAN']") + debitorName = camt53doc.pickString("//*[local-name()='RltdPties']//*[local-name()='Dbtr']//*[local-name()='Nm']") + debitorIban = camt53doc.pickString("//*[local-name()='DbtrAcct']//*[local-name()='IBAN']") + counterpartBic = camt53doc.pickString("//*[local-name()='RltdAgts']//*[local-name()='BIC']") } } } @@ -763,9 +753,8 @@ fun main() { ) } } - + return@post } - post("/ebics/subscribers/{id}/collect-transactions-c54") { // FIXME(florian): Download C54 and store the result in the right database table } diff --git a/util/src/main/kotlin/XMLUtil.kt b/util/src/main/kotlin/XMLUtil.kt index 273660f1..2889a808 100644 --- a/util/src/main/kotlin/XMLUtil.kt +++ b/util/src/main/kotlin/XMLUtil.kt @@ -423,3 +423,7 @@ class XMLUtil private constructor() { } } } + +fun Document.pickString(xpath: String): String { + return XMLUtil.getStringFromXpath(this, xpath) +} |