summaryrefslogtreecommitdiff
path: root/nexus/src/main
diff options
context:
space:
mode:
authorMarcello Stanisci <ms@taler.net>2020-04-29 20:39:14 +0200
committerMarcello Stanisci <ms@taler.net>2020-04-29 20:39:14 +0200
commit13bfc9f8d8eca261e515b4004ab6f24a8b50be1e (patch)
treea67d5149a5d695c0519c9140f4c50345d504afb2 /nexus/src/main
parent793875b7099a3ef8284a4d03cec485bc970333ed (diff)
downloadlibeufin-13bfc9f8d8eca261e515b4004ab6f24a8b50be1e.tar.gz
libeufin-13bfc9f8d8eca261e515b4004ab6f24a8b50be1e.tar.bz2
libeufin-13bfc9f8d8eca261e515b4004ab6f24a8b50be1e.zip
Abstracting on "bank account".
Tend to use the triple <iban, bic, holder name>, instead of the mnemonic label given to bank accounts.
Diffstat (limited to 'nexus/src/main')
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt26
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt30
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt3
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt57
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt27
5 files changed, 64 insertions, 79 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
index c850ea46..342b6d93 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 50f558ec..6da439f5 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 390745a2..63acf4bb 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 1f83c065..5094faba 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 db8ad63d..640fce14 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
}