diff options
author | Marcello Stanisci <ms@taler.net> | 2020-04-29 20:39:14 +0200 |
---|---|---|
committer | Marcello Stanisci <ms@taler.net> | 2020-04-29 20:39:14 +0200 |
commit | 13bfc9f8d8eca261e515b4004ab6f24a8b50be1e (patch) | |
tree | a67d5149a5d695c0519c9140f4c50345d504afb2 /nexus/src/main | |
parent | 793875b7099a3ef8284a4d03cec485bc970333ed (diff) | |
download | libeufin-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.kt | 26 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 30 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt | 3 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 57 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 27 |
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 } |