summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcello Stanisci <stanisci.m@gmail.com>2020-03-31 18:24:58 +0200
committerMarcello Stanisci <stanisci.m@gmail.com>2020-03-31 18:24:58 +0200
commit8723a7107d0916ded72cdcf311ddc59f6343ecee (patch)
tree27c5455f4d4e7c9c99383d5c7e7d7ad8d98c06c1
parent4c196b7c81db58b379c04071ecf48625e53e439a (diff)
downloadlibeufin-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.kt8
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt16
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt127
-rw-r--r--util/src/main/kotlin/XMLUtil.kt4
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)
+}