[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] 06/06: Abstracting on "bank account".
From: |
gnunet |
Subject: |
[libeufin] 06/06: Abstracting on "bank account". |
Date: |
Wed, 29 Apr 2020 21:44:45 +0200 |
This is an automated email from the git hooks/post-receive script.
marcello pushed a commit to branch master
in repository libeufin.
commit 13bfc9f8d8eca261e515b4004ab6f24a8b50be1e
Author: Marcello Stanisci <address@hidden>
AuthorDate: Wed Apr 29 20:39:14 2020 +0200
Abstracting on "bank account".
Tend to use the triple <iban, bic, holder name>,
instead of the mnemonic label given to bank accounts.
---
integration-tests/test-ebics.py | 10 +++-
nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt | 26 +++++-----
.../src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 30 +++++++-----
nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt | 3 ++
nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 57 ++++++----------------
nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 27 +++++-----
nexus/src/test/kotlin/DbTest.kt | 53 --------------------
nexus/src/test/kotlin/PainGeneration.kt | 24 +++------
nexus/src/test/kotlin/XPathTest.kt | 4 +-
.../src/main/kotlin/tech/libeufin/sandbox/DB.kt | 3 +-
10 files changed, 84 insertions(+), 153 deletions(-)
diff --git a/integration-tests/test-ebics.py b/integration-tests/test-ebics.py
index 5c5fbbb..695f581 100755
--- a/integration-tests/test-ebics.py
+++ b/integration-tests/test-ebics.py
@@ -109,7 +109,6 @@ resp = post(
assert(resp.status_code == 200)
-# FIXME: assert that history is EMPTY at this point!
resp = get(
"http://localhost:5001/users/{}/history".format(USERNAME)
)
@@ -120,5 +119,14 @@ assert(
)
#6 Prepare a payment (via pure Nexus service)
+resp = post(
+ "http://localhost:5001/users/{}/prepare-payment".format(USERNAME),
+ json=dict()
+)
+
+assert(resp.status_code == 200)
+
+
+
#7 Execute such payment via EBICS
#8 Request history again via EBICS
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
index c850ea4..342b6d9 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -114,12 +114,8 @@ class RawBankTransactionEntity(id: EntityID<Long>) :
LongEntity(id) {
var nexusUser by NexusUserEntity referencedOn
RawBankTransactionsTable.nexusUser
var status by RawBankTransactionsTable.status
}
-
/**
- * NOTE: every column in this table corresponds to a particular
- * value described in the pain.001 official documentation; therefore
- * this table is not really suitable to hold custom data (like Taler-related,
- * for example)
+ * Represent a prepare payment.
*/
object Pain001Table : IntIdTableWithAmount() {
val msgId = long("msgId").uniqueIndex().autoIncrement()
@@ -127,23 +123,22 @@ object Pain001Table : IntIdTableWithAmount() {
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")
val creditorIban = text("creditorIban")
val creditorBic = text("creditorBic")
val creditorName = text("creditorName")
-
+ val debitorIban = text("debitorIban")
+ val debitorBic = text("debitorBic")
+ val debitorName = text("debitorName").nullable()
/* Indicates whether the PAIN message was sent to the bank. */
val submitted = bool("submitted").default(false)
-
/* Indicates whether the bank didn't perform the payment: note that
- * this state can be reached when the payment gets listed in a CRZ
- * response OR when the payment doesn't show up in a C52/C53 response
- */
+ * this state can be reached when the payment gets listed in a CRZ
+ * response OR when the payment doesn't show up in a C52/C53 response */
val invalid = bool("invalid").default(false)
+ val nexusUser = reference("nexusUser", NexusUsersTable)
}
-
class Pain001Entity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Pain001Entity>(Pain001Table)
var msgId by Pain001Table.msgId
@@ -151,7 +146,9 @@ class Pain001Entity(id: EntityID<Int>) : IntEntity(id) {
var date by Pain001Table.fileDate
var sum by Pain001Table.sum
var currency by Pain001Table.currency
- var debtorAccount by Pain001Table.debtorAccount
+ var debitorIban by Pain001Table.debitorIban
+ var debitorBic by Pain001Table.debitorBic
+ var debitorName by Pain001Table.debitorName
var endToEndId by Pain001Table.endToEndId
var subject by Pain001Table.subject
var creditorIban by Pain001Table.creditorIban
@@ -159,6 +156,7 @@ class Pain001Entity(id: EntityID<Int>) : IntEntity(id) {
var creditorName by Pain001Table.creditorName
var submitted by Pain001Table.submitted
var invalid by Pain001Table.invalid
+ var nexusUser by NexusUserEntity referencedOn Pain001Table.nexusUser
}
/**
@@ -206,7 +204,7 @@ class EbicsSubscriberEntity(id: EntityID<Int>) :
Entity<Int>(id) {
}
object NexusUsersTable : IdTable<String>() {
- override val id = varchar("id", ID_MAX_LENGTH).entityId().primaryKey()
+ override val id = varchar("id", ID_MAX_LENGTH).entityId()
val ebicsSubscriber = reference("ebicsSubscriber",
EbicsSubscribersTable).nullable()
val testSubscriber = reference("testSubscriber",
EbicsSubscribersTable).nullable()
val password = blob("password").nullable()
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
index 50f558e..6da439f 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
@@ -164,6 +164,16 @@ fun createPain001document(pain001Entity: Pain001Entity):
String {
* we'll assign the SAME id (= the row id) to all the three aforementioned
* PAIN id types.
*/
+ val debitorBankAccountLabel = transaction {
+ val debitorBankAcount = BankAccountEntity.find {
+ BankAccountsTable.iban eq pain001Entity.debitorIban and
+ (BankAccountsTable.bankCode eq pain001Entity.debitorBic)
+ }.firstOrNull() ?: throw NexusError(
+ HttpStatusCode.NotFound,
+ "Please download bank accounts details first (HTD)"
+ )
+ debitorBankAcount.id.value
+ }
val s = constructXml(indent = true) {
root("Document") {
@@ -191,7 +201,7 @@ fun createPain001document(pain001Entity: Pain001Entity):
String {
text(pain001Entity.sum.toString())
}
element("InitgPty/Nm") {
- text(pain001Entity.debtorAccount)
+ text(debitorBankAccountLabel)
}
}
element("PmtInf") {
@@ -220,18 +230,13 @@ fun createPain001document(pain001Entity: Pain001Entity):
String {
text(DateTime(dateMillis).toString("Y-MM-dd"))
}
element("Dbtr/Nm") {
- text(pain001Entity.debtorAccount)
+ text(debitorBankAccountLabel)
}
element("DbtrAcct/Id/IBAN") {
- text(transaction {
-
BankAccountEntity.findById(pain001Entity.debtorAccount)?.iban ?: throw
NexusError(HttpStatusCode.NotFound,"Debtor IBAN not found in database")
- })
+ text(pain001Entity.debitorIban)
}
element("DbtrAgt/FinInstnId/BIC") {
-
- text(transaction {
-
BankAccountEntity.findById(pain001Entity.debtorAccount)?.bankCode ?: throw
NexusError(HttpStatusCode.NotFound,"Debtor BIC not found in database")
- })
+ text(pain001Entity.debitorBic)
}
element("ChrgBr") {
text("SLEV")
@@ -274,13 +279,15 @@ fun createPain001document(pain001Entity: Pain001Entity):
String {
* it will be the account whose money will pay the wire transfer being defined
* by this pain document.
*/
-fun createPain001entity(entry: Pain001Data, debtorAccountId: String):
Pain001Entity {
+fun createPain001entity(entry: Pain001Data, nexusUser: NexusUserEntity):
Pain001Entity {
val randomId = Random().nextLong()
return transaction {
Pain001Entity.new {
subject = entry.subject
sum = entry.sum
- debtorAccount = debtorAccountId
+ debitorIban = entry.debitorIban
+ debitorBic = entry.debitorBic
+ debitorName = entry.debitorName
creditorName = entry.creditorName
creditorBic = entry.creditorBic
creditorIban = entry.creditorIban
@@ -288,6 +295,7 @@ fun createPain001entity(entry: Pain001Data,
debtorAccountId: String): Pain001Ent
paymentId = randomId
msgId = randomId
endToEndId = randomId
+ this.nexusUser = nexusUser
}
}
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt
index 390745a..63acf4b 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt
@@ -139,6 +139,9 @@ data class Pain001Data(
val creditorIban: String,
val creditorBic: String,
val creditorName: String,
+ val debitorIban: String,
+ val debitorBic: String,
+ val debitorName: 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
index 1f83c06..5094fab 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -268,7 +268,7 @@ fun main() {
}
bankAccountsMap.forEach {
Pain001Entity.find {
- Pain001Table.debtorAccount eq it.bankAccount.iban
+ Pain001Table.debitorIban eq it.bankAccount.iban
}.forEach {
ret.payments.add(
RawPayment(
@@ -285,20 +285,20 @@ fun main() {
call.respond(ret)
return@get
}
- post("/users/{id}/accounts/{acctid}/prepare-payment") {
- val acctid = transaction {
+ post("/users/{id}/accounts/prepare-payment") {
+ val nexusUser = extractNexusUser(call.parameters["id"])
+ transaction {
val accountInfo =
expectAcctidTransaction(call.parameters["acctid"])
- val nexusUser = extractNexusUser(call.parameters["id"])
if (!userHasRights(nexusUser, accountInfo)) {
throw NexusError(
HttpStatusCode.BadRequest,
"Claimed bank account '${accountInfo.id}' doesn't
belong to user '${nexusUser.id.value}'!"
)
}
- accountInfo.id.value
+
}
val pain001data = call.receive<Pain001Data>()
- createPain001entity(pain001data, acctid)
+ createPain001entity(pain001data, nexusUser)
call.respondText(
"Payment instructions persisted in DB",
ContentType.Text.Plain, HttpStatusCode.OK
@@ -329,7 +329,6 @@ fun main() {
)
return@get
}
-
/** Associate a EBICS subscriber to the existing user */
post("/ebics/subscribers/{id}") {
val nexusUser = extractNexusUser(call.parameters["id"])
@@ -571,17 +570,22 @@ fun main() {
/** STATE CHANGES VIA EBICS */
post("/ebics/admin/execute-payments") {
- val (paymentRowId, painDoc: String, debtorAccount) =
transaction {
+ val (paymentRowId, painDoc, subscriber) = transaction {
val entity = Pain001Entity.find {
(Pain001Table.submitted eq false) and
(Pain001Table.invalid eq false)
}.firstOrNull() ?: throw
NexusError(HttpStatusCode.Accepted, reason = "No ready payments found")
- Triple(entity.id, createPain001document(entity),
entity.debtorAccount)
+ Triple(entity.id, createPain001document(entity),
entity.nexusUser.ebicsSubscriber)
+ }
+ if (subscriber == null) {
+ throw NexusError(
+ HttpStatusCode.NotFound,
+ "Ebics subscriber wasn't found for this prepared
payment."
+ )
}
logger.debug("Uploading PAIN.001: ${painDoc}")
- val subscriberDetails =
getSubscriberDetailsFromBankAccount(debtorAccount)
doEbicsUploadTransaction(
client,
- subscriberDetails,
+ getSubscriberDetailsInternal(subscriber),
"CCT",
painDoc.toByteArray(Charsets.UTF_8),
EbicsStandardOrderParams()
@@ -601,37 +605,6 @@ fun main() {
)
return@post
}
- post("/ebics/admin/execute-payments-ccc") {
- val (paymentRowId, painDoc: String, debtorAccount) =
transaction {
- val entity = Pain001Entity.find {
- (Pain001Table.submitted eq false) and
(Pain001Table.invalid eq false)
- }.firstOrNull() ?: throw
NexusError(HttpStatusCode.Accepted, reason = "No ready payments found")
- Triple(entity.id, createPain001document(entity),
entity.debtorAccount)
- }
- logger.debug("Uploading PAIN.001 via CCC: ${painDoc}")
- val subscriberDetails =
getSubscriberDetailsFromBankAccount(debtorAccount)
- doEbicsUploadTransaction(
- client,
- subscriberDetails,
- "CCC",
- listOf(painDoc.toByteArray(Charsets.UTF_8)).zip(),
- EbicsStandardOrderParams()
- )
- /* flow here == no errors occurred */
- transaction {
- val payment = Pain001Entity.findById(paymentRowId) ?:
throw NexusError(
- HttpStatusCode.InternalServerError,
- "Severe internal error: could not find payment in DB
after having submitted it to the bank"
- )
- payment.submitted = true
- }
- call.respondText(
- "CCC message submitted to the bank",
- ContentType.Text.Plain,
- HttpStatusCode.OK
- )
- return@post
- }
/** exports keys backup copy */
post("/ebics/subscribers/{id}/backup") {
val body = call.receive<EbicsBackupRequestJson>()
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
index db8ad63..640fce1 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
@@ -236,14 +236,11 @@ class Taler(app: Route) {
val transferRequest = call.receive<TalerTransferRequest>()
val amountObj = parseAmount(transferRequest.amount)
val creditorObj = parsePayto(transferRequest.credit_account)
-
val opaque_row_id = transaction {
val creditorData = parsePayto(transferRequest.credit_account)
val exchangeBankAccount =
getBankAccountFromNexusUserId(exchangeId)
val nexusUser = extractNexusUser(exchangeId)
- /**
- * Checking the UID has the desired characteristics.
- */
+ /** Checking the UID has the desired characteristics */
TalerRequestedPaymentEntity.find {
TalerRequestedPayments.requestUId eq
transferRequest.request_uid
}.forEach {
@@ -264,11 +261,13 @@ class Taler(app: Route) {
creditorBic = creditorData.bic,
creditorName = creditorData.name,
subject = transferRequest.wtid,
- sum = parseAmount(transferRequest.amount).amount
+ sum = parseAmount(transferRequest.amount).amount,
+ debitorName = exchangeBankAccount.accountHolder,
+ debitorBic = exchangeBankAccount.bankCode,
+ debitorIban = exchangeBankAccount.iban
),
- exchangeBankAccount.id.value
+ nexusUser
)
-
val rawEbics = if (!isProduction()) {
RawBankTransactionEntity.new {
sourceFileName = "test"
@@ -368,14 +367,15 @@ class Taler(app: Route) {
* all the prepared payments. */
app.post("/ebics/taler/{id}/accounts/{acctid}/refund-invalid-payments") {
transaction {
- val subscriber =
getSubscriberEntityFromNexusUserId(call.parameters["id"])
+ val nexusUser = extractNexusUser(call.parameters["id"])
val acctid = expectAcctidTransaction(call.parameters["acctid"])
- if (!subscriberHasRights(subscriber, acctid)) {
+ if
(!subscriberHasRights(getEbicsSubscriberFromUser(nexusUser), acctid)) {
throw NexusError(
HttpStatusCode.Forbidden,
- "Such subscriber (${subscriber.id}) can't drive such
account (${acctid.id})"
+ "The requester can't drive such account (${acctid.id})"
)
}
+ val requesterBankAccount =
getBankAccountFromNexusUserId(nexusUser.id.value)
TalerIncomingPaymentEntity.find {
TalerIncomingPayments.refunded eq false and
(TalerIncomingPayments.valid eq false)
}.forEach {
@@ -385,9 +385,12 @@ class Taler(app: Route) {
creditorIban = it.payment.debitorIban,
creditorBic = it.payment.counterpartBic,
sum = calculateRefund(it.payment.amount),
- subject = "Taler refund"
+ subject = "Taler refund",
+ debitorIban = requesterBankAccount.iban,
+ debitorBic = requesterBankAccount.bankCode,
+ debitorName = requesterBankAccount.accountHolder
),
- acctid.id.value
+ nexusUser
)
it.refunded = true
}
diff --git a/nexus/src/test/kotlin/DbTest.kt b/nexus/src/test/kotlin/DbTest.kt
deleted file mode 100644
index cf4e7c6..0000000
--- a/nexus/src/test/kotlin/DbTest.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package tech.libeufin.nexus
-
-import org.junit.Before
-import org.junit.Test
-
-import org.jetbrains.exposed.sql.Database
-import org.jetbrains.exposed.sql.transactions.transaction
-import org.jetbrains.exposed.sql.SchemaUtils
-import tech.libeufin.util.Amount
-import javax.sql.rowset.serial.SerialBlob
-
-
-class DbTest {
-
- @Before
- fun connectAndMakeTables() {
- Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", driver =
"org.h2.Driver")
- transaction {
- SchemaUtils.create(EbicsSubscribersTable)
- SchemaUtils.create(Pain001Table)
- }
- }
-
- @Test
- fun makeEbicsSubscriber() {
- transaction {
- EbicsSubscriberEntity.new {
- ebicsURL = "ebics url"
- hostID = "host"
- partnerID = "partner"
- userID = "user"
- systemID = "system"
- signaturePrivateKey =
SerialBlob("signturePrivateKey".toByteArray())
- authenticationPrivateKey =
SerialBlob("authenticationPrivateKey".toByteArray())
- encryptionPrivateKey =
SerialBlob("encryptionPrivateKey".toByteArray())
- }
- }
- }
-
- @Test
- fun testPain001() {
- createPain001entity(
- Pain001Data(
- creditorBic = "cb",
- creditorIban = "ci",
- creditorName = "cn",
- sum = Amount(2),
- subject = "s"
- ),
- "debtor acctid"
- )
- }
-}
\ No newline at end of file
diff --git a/nexus/src/test/kotlin/PainGeneration.kt
b/nexus/src/test/kotlin/PainGeneration.kt
index 8f29f45..f412db6 100644
--- a/nexus/src/test/kotlin/PainGeneration.kt
+++ b/nexus/src/test/kotlin/PainGeneration.kt
@@ -12,28 +12,17 @@ import javax.sql.rowset.serial.SerialBlob
class PainTest {
-
@Before
fun prepare() {
Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", driver =
"org.h2.Driver")
transaction {
- SchemaUtils.create(EbicsSubscribersTable)
SchemaUtils.create(BankAccountsTable)
SchemaUtils.create(Pain001Table)
- EbicsSubscriberEntity.new {
- ebicsURL = "ebics url"
- hostID = "host"
- partnerID = "partner"
- userID = "user"
- systemID = "system"
- signaturePrivateKey =
SerialBlob("signturePrivateKey".toByteArray())
- authenticationPrivateKey =
SerialBlob("authenticationPrivateKey".toByteArray())
- encryptionPrivateKey =
SerialBlob("encryptionPrivateKey".toByteArray())
- }
+ SchemaUtils.create(NexusUsersTable)
BankAccountEntity.new(id = "acctid") {
accountHolder = "Account Holder"
- iban = "IBAN"
- bankCode = "BIC"
+ iban = "DEBIT IBAN"
+ bankCode = "DEBIT BIC"
}
}
}
@@ -41,9 +30,12 @@ class PainTest {
@Test
fun testPain001document() {
transaction {
+ val nu = NexusUserEntity.new(id = "mock") { }
val pain001Entity = Pain001Entity.new {
sum = Amount(1)
- debtorAccount = "acctid"
+ debitorIban = "DEBIT IBAN"
+ debitorBic = "DEBIT BIC"
+ debitorName = "DEBIT NAME"
subject = "subject line"
creditorIban = "CREDIT IBAN"
creditorBic = "CREDIT BIC"
@@ -52,7 +44,7 @@ class PainTest {
msgId = 1
endToEndId = 1
date = DateTime.now().millis
-
+ nexusUser = nu
}
val s = createPain001document(pain001Entity)
println(s)
diff --git a/nexus/src/test/kotlin/XPathTest.kt
b/nexus/src/test/kotlin/XPathTest.kt
index f381175..bb1b93b 100644
--- a/nexus/src/test/kotlin/XPathTest.kt
+++ b/nexus/src/test/kotlin/XPathTest.kt
@@ -13,10 +13,8 @@ class XPathTest {
<node>lorem ipsum</node>
</root>""".trimIndent()
val doc: Document = XMLUtil.parseStringIntoDom(xml)
- val node = XMLUtil.getNodeFromXpath(doc, "/*[local-name()='root']")
- assert(node != null)
+ XMLUtil.getNodeFromXpath(doc, "/*[local-name()='root']")
val text = XMLUtil.getStringFromXpath(doc, "//*[local-name()='node']")
- assert(text != null)
println(text)
}
}
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 6f49a7d..7ce01a1 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -256,7 +256,8 @@ fun dbCreateTables() {
EbicsDownloadTransactionsTable,
EbicsUploadTransactionsTable,
EbicsUploadTransactionChunksTable,
- EbicsOrderSignaturesTable
+ EbicsOrderSignaturesTable,
+ PaymentsTable
)
}
}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
address@hidden.
- [libeufin] branch master updated (b6e9340 -> 13bfc9f), gnunet, 2020/04/29
- [libeufin] 02/06: remove empty lines, gnunet, 2020/04/29
- [libeufin] 03/06: Integration test., gnunet, 2020/04/29
- [libeufin] 06/06: Abstracting on "bank account".,
gnunet <=
- [libeufin] 04/06: Integration test., gnunet, 2020/04/29
- [libeufin] 01/06: Remove notion of "bank customer" from Sandbox., gnunet, 2020/04/29
- [libeufin] 05/06: More "history logic" to the Sandbox., gnunet, 2020/04/29