gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated (9cd5454 -> 96b5026)


From: gnunet
Subject: [libeufin] branch master updated (9cd5454 -> 96b5026)
Date: Thu, 30 Apr 2020 21:46:43 +0200

This is an automated email from the git hooks/post-receive script.

marcello pushed a change to branch master
in repository libeufin.

    from 9cd5454  fixme
     new 4f5778e  Sandbox admin.
     new dd9869d  HTD response content comes from DB now.
     new bcc5d2f  fix db problem in test
     new 271dd2d  Integration test.
     new 9eb0e75  Reducing code.
     new d9547e5  Integration test.
     new d5ea867  Fix CCT handling.
     new 5005d9e  Integration test.
     new 2109595  fix time parser
     new a205b08  Move more generic helpers to util package.
     new 96b5026  Fix amount format.

The 11 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 integration-tests/test-ebics.py                    | 123 +++++----
 nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt    |  27 +-
 .../src/main/kotlin/tech/libeufin/nexus/Helpers.kt |  98 +-------
 nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt  |  26 +-
 nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt |   6 +-
 nexus/src/test/kotlin/DateTest.kt                  |  16 ++
 nexus/src/test/kotlin/LetterFormatTest.kt          |   3 +-
 nexus/src/test/kotlin/PainGeneration.kt            |  21 +-
 .../src/main/kotlin/tech/libeufin/sandbox/DB.kt    |  28 ++-
 .../tech/libeufin/sandbox/EbicsProtocolBackend.kt  | 276 ++++++++-------------
 .../main/kotlin/tech/libeufin/sandbox/Helpers.kt   |  41 +++
 .../src/main/kotlin/tech/libeufin/sandbox/JSON.kt  |  14 +-
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  |  25 +-
 util/src/main/kotlin/ParametersChecks.kt           |  33 +++
 util/src/main/kotlin/strings.kt                    |  24 +-
 util/src/main/kotlin/time.kt                       |  10 +-
 util/src/test/kotlin/CryptoUtilTest.kt             |   1 -
 17 files changed, 411 insertions(+), 361 deletions(-)
 create mode 100644 nexus/src/test/kotlin/DateTest.kt
 create mode 100644 sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
 create mode 100644 util/src/main/kotlin/ParametersChecks.kt

diff --git a/integration-tests/test-ebics.py b/integration-tests/test-ebics.py
index a1ff821..a0adfea 100755
--- a/integration-tests/test-ebics.py
+++ b/integration-tests/test-ebics.py
@@ -4,22 +4,27 @@ from requests import post, get
 
 # Steps implemented in this test.
 #
-# 1 Prepare the Sandbox to run the test.
-#  -> Make a EBICS host, and make a EBICS subscriber
-#     for the test runner.
+# 0 Prepare sandbox.
+#  -> (a) Make a EBICS host, (b) make a EBICS subscriber
+#     for the test runner, and (c) assign a IBAN to such
+#     subscriber.
 #
-# 2 Prepare the Nexus to run the test.
-#  -> Make a Nexus user, and make a EBICS transport
-#     entity associated with that user.
+# 1 Prepare nexus.
+#  -> (a) Make a Nexus user, (b) make a EBICS subscriber
+#     associated to that user
 #
-# 3 Upload keys from Nexus to the Bank (INI & HIA)
-# 4 Download key from the Bank (HPB) to the Nexus
-#
-# 5 Request history from the Nexus to the Bank (C53).
-# 6 Verify that history is empty.
-# 7 Issue a payment from Nexus (Prepare & trigger CCT)
-# 8 Request history again, from Nexus to Bank.
-# 9 Verify that previous payment shows up.
+# 2 Prepare the Ebics transport for the nexus user.
+#  -> (a) Upload keys from Nexus to the Bank (INI & HIA),
+#     (b) Download key from the Bank (HPB) to the Nexus,
+#     and (c) Fetch the bank account owned by that subscriber
+#     at the bank.
+
+# 3 Request history from the Nexus to the Bank (C53).
+# 4 Verify that history is empty.
+# 5 Issue a payment from Nexus
+#  -> (a) Prepare & (b) trigger CCT.
+# 6 Request history again, from Nexus to Bank.
+# 7 Verify that previous payment shows up.
 
 
 # Nexus user details
@@ -32,7 +37,13 @@ PARTNER_ID="PARTNER1"
 USER_ID="USER1"
 EBICS_VERSION = "H004"
 
-#0 Prepare Sandbox (make Ebics host & one subscriber)
+# Subscriber's bank account
+SUBSCRIBER_IBAN="GB33BUKB20201555555555"
+SUBSCRIBER_BIC="BUKBGB22"
+SUBSCRIBER_NAME="Oliver Smith"
+BANK_ACCOUNT_LABEL="savings"
+
+#0.a
 resp = post(
     "http://localhost:5000/admin/ebics-host";,
     json=dict(
@@ -43,6 +54,7 @@ resp = post(
 
 assert(resp.status_code == 200)
 
+#0.b
 resp = post(
     "http://localhost:5000/admin/ebics-subscriber";,
     json=dict(
@@ -54,8 +66,24 @@ resp = post(
 
 assert(resp.status_code == 200)
 
-#1 Create a Nexus user
+#0.c
+resp = post(
+    "http://localhost:5000/admin/ebics-subscriber/bank-account";,
+    json=dict(
+        subscriber=dict(
+            hostID=HOST_ID,
+            partnerID=PARTNER_ID,
+            userID=USER_ID
+       ),
+        iban=SUBSCRIBER_IBAN,
+        bic=SUBSCRIBER_BIC,
+        name=SUBSCRIBER_NAME,
+       label=BANK_ACCOUNT_LABEL
+    )
+)
+assert(resp.status_code == 200)
 
+#1.a
 resp = post(
     "http://localhost:5001/users/{}".format(USERNAME),
     json=dict(
@@ -65,7 +93,7 @@ resp = post(
 
 assert(resp.status_code == 200)
 
-#2 Create a EBICS user
+#1.b
 resp = post(
     "http://localhost:5001/ebics/subscribers/{}".format(USERNAME),
     json=dict(
@@ -78,65 +106,76 @@ resp = post(
 
 assert(resp.status_code == 200)
 
-#3 Upload keys to the bank INI & HIA
+#2.a
 resp = post(
     "http://localhost:5001/ebics/subscribers/{}/sendINI".format(USERNAME),
     json=dict()
 )
-
 assert(resp.status_code == 200)
 
 resp = post(
     "http://localhost:5001/ebics/subscribers/{}/sendHIA".format(USERNAME),
     json=dict()
 )
-
 assert(resp.status_code == 200)
 
-#4 Download keys from the bank HPB
+#2.b
 resp = post(
     "http://localhost:5001/ebics/subscribers/{}/sync".format(USERNAME),
     json=dict()
 )
+assert(resp.status_code == 200)
 
+#2.c
+resp = post(
+    
"http://localhost:5001/ebics/subscribers/{}/fetch-accounts".format(USERNAME),
+    json=dict()
+)
 assert(resp.status_code == 200)
 
-#5 Request history via EBICS
+#3
 resp = post(
     
"http://localhost:5001/ebics/subscribers/{}/collect-transactions-c53".format(USERNAME),
     json=dict()
 )
-
 assert(resp.status_code == 200)
 
+#4
 resp = get(
     "http://localhost:5001/users/{}/history".format(USERNAME)
 )
+assert(resp.status_code == 200)
+assert(len(resp.json().get("payments")) == 0)
 
-assert(
-    resp.status_code == 200 and \
-    len(resp.json().get("payments")) == 0
-)
-
-#6 Prepare a payment (via pure Nexus service)
-
-# FIXME: this currently fails, because nexus has a
-# empty table w.r.t. bank accounts.  Thus, the sandbox
-# must provide such information via the usual HTD message.
-
+#5.a
 resp = post(
     "http://localhost:5001/users/{}/prepare-payment".format(USERNAME),
     json=dict(
-        creditorIban="GB33BUKB20201555555555",
-        creditorBic="BUKBGB22",
-        creditorName="Oliver Smith",
-        debitorIban="FR7630006000011234567890189",
-        debitorBic="AGRIFRPP",
-        debitorName="Jacques LaFayette"
+        creditorIban="FR7630006000011234567890189",
+        creditorBic="AGRIFRPP",
+        creditorName="Jacques La Fayette",
+        debitorIban=SUBSCRIBER_IBAN,
+        debitorBic=SUBSCRIBER_BIC,
+        debitorName=SUBSCRIBER_NAME,
+       subject="integration test",
+       sum=1
     )
 )
+assert(resp.status_code == 200)
 
+#5.b
+resp = post("http://localhost:5001/ebics/execute-payments";)
 assert(resp.status_code == 200)
 
-#7 Execute such payment via EBICS
-#8 Request history again via EBICS
+#6
+resp = post(
+    
"http://localhost:5001/ebics/subscribers/{}/collect-transactions-c53".format(USERNAME),
+    json=dict()
+)
+assert(resp.status_code == 200)
+
+resp = get(
+    "http://localhost:5001/users/{}/history".format(USERNAME)
+)
+assert(resp.status_code == 200)
+assert(len(resp.json().get("payments")) == 1)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
index 342b6d9..ba22010 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -217,26 +217,16 @@ class NexusUserEntity(id: EntityID<String>) : 
Entity<String>(id) {
     var password by NexusUsersTable.password
 }
 
-object UserToBankAccountsTable : IntIdTable() {
-    val nexusUser = reference("nexusUser", NexusUsersTable)
-    val bankAccount = reference("bankAccount", BankAccountsTable)
-}
-
-class UserToBankAccountEntity(id: EntityID<Int>): IntEntity(id) {
-    companion object : 
IntEntityClass<UserToBankAccountEntity>(UserToBankAccountsTable)
-    var nexusUser by NexusUserEntity referencedOn 
UserToBankAccountsTable.nexusUser
-    var bankAccount by BankAccountEntity referencedOn 
EbicsToBankAccountsTable.bankAccount
-}
-
-object EbicsToBankAccountsTable : IntIdTable() {
+object BankAccountMapsTable : IntIdTable() {
     val ebicsSubscriber = reference("ebicsSubscriber", EbicsSubscribersTable)
     val bankAccount = reference("bankAccount", BankAccountsTable)
+    val nexusUser = reference("nexusUser", NexusUsersTable)
 }
-
-class EbicsToBankAccountEntity(id: EntityID<Int>): IntEntity(id) {
-    companion object : 
IntEntityClass<EbicsToBankAccountEntity>(EbicsToBankAccountsTable)
-    var ebicsSubscriber by EbicsSubscriberEntity referencedOn 
EbicsToBankAccountsTable.ebicsSubscriber
-    var bankAccount by BankAccountEntity referencedOn 
EbicsToBankAccountsTable.bankAccount
+class BankAccountMapEntity(id: EntityID<Int>): IntEntity(id) {
+    companion object : 
IntEntityClass<BankAccountMapEntity>(BankAccountMapsTable)
+    var ebicsSubscriber by EbicsSubscriberEntity referencedOn 
BankAccountMapsTable.ebicsSubscriber
+    var bankAccount by BankAccountEntity referencedOn 
BankAccountMapsTable.bankAccount
+    var nexusUser by NexusUserEntity referencedOn 
BankAccountMapsTable.nexusUser
 }
 
 fun dbCreateTables() {
@@ -251,7 +241,8 @@ fun dbCreateTables() {
             RawBankTransactionsTable,
             TalerIncomingPayments,
             TalerRequestedPayments,
-            NexusUsersTable
+            NexusUsersTable,
+            BankAccountMapsTable
          )
     }
 }
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
index 78fb2f9..e7663eb 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
@@ -20,13 +20,6 @@ import java.time.ZonedDateTime
 import java.time.Instant
 import java.time.ZoneId
 
-fun getSubscriberEntityFromNexusUserId(nexusUserId: String?): 
EbicsSubscriberEntity {
-    return transaction {
-        val nexusUser = extractNexusUser(expectId(nexusUserId))
-        getEbicsSubscriberFromUser(nexusUser)
-    }
-}
-
 fun calculateRefund(amount: String): Amount {
     // fixme: must apply refund fees!
     return Amount(amount)
@@ -64,22 +57,6 @@ fun extractFirstBic(bankCodes: 
List<EbicsTypes.AbstractBankCode>?): String? {
     return null
 }
 
-/**
- * Get EBICS subscriber details from bank account id.
- * bank account id => ... => ebics details
- */
-fun getSubscriberDetailsFromBankAccount(bankAccountId: String): 
EbicsClientSubscriberDetails {
-    return transaction {
-        val map = EbicsToBankAccountEntity.find {
-            EbicsToBankAccountsTable.bankAccount eq bankAccountId
-        }.firstOrNull() ?: throw NexusError(
-            HttpStatusCode.NotFound,
-            "Such bank account '$bankAccountId' has no EBICS subscriber 
associated"
-        )
-        getSubscriberDetailsInternal(map.ebicsSubscriber)
-    }
-}
-
 /**
  * Given a nexus user id, returns the _list_ of bank accounts associated to it.
  *
@@ -90,8 +67,8 @@ fun getSubscriberDetailsFromBankAccount(bankAccountId: 
String): EbicsClientSubsc
 fun getBankAccountFromNexusUserId(id: String): BankAccountEntity {
     logger.debug("Looking up bank account of user '$id'")
     val map = transaction {
-        UserToBankAccountEntity.find {
-            UserToBankAccountsTable.nexusUser eq id
+        BankAccountMapEntity.find {
+            BankAccountMapsTable.nexusUser eq id
         }
     }.firstOrNull() ?: throw NexusError(
         HttpStatusCode.NotFound,
@@ -300,28 +277,6 @@ fun createPain001entity(entry: Pain001Data, nexusUser: 
NexusUserEntity): Pain001
     }
 }
 
-/**
- * Inserts spaces every 2 characters, and a newline after 8 pairs.
- */
-fun chunkString(input: String): String {
-    val ret = StringBuilder()
-    var columns = 0
-    for (i in input.indices) {
-        if ((i + 1).rem(2) == 0) {
-            if (columns == 15) {
-                ret.append(input[i] + "\n")
-                columns = 0
-                continue
-            }
-            ret.append(input[i] + " ")
-            columns++
-            continue
-        }
-        ret.append(input[i])
-    }
-    return ret.toString().toUpperCase()
-}
-
 fun expectId(param: String?): String {
     return param ?: throw NexusError(HttpStatusCode.BadRequest, "Bad ID given")
 }
@@ -339,34 +294,6 @@ fun extractNexusUser(param: String?): NexusUserEntity {
     }
 }
 
-fun ApplicationCall.expectUrlParameter(name: String): String {
-    return this.request.queryParameters[name]
-        ?: throw NexusError(HttpStatusCode.BadRequest, "Parameter '$name' not 
provided in URI")
-}
-
-fun expectInt(param: String): Int {
-    return try {
-        param.toInt()
-    } catch (e: Exception) {
-        throw NexusError(HttpStatusCode.BadRequest,"'$param' is not Int")
-    }
-}
-
-fun expectLong(param: String): Long {
-    return try {
-        param.toLong()
-    } catch (e: Exception) {
-        throw NexusError(HttpStatusCode.BadRequest,"'$param' is not Long")
-    }
-}
-
-fun expectLong(param: String?): Long? {
-    if (param != null) {
-        return expectLong(param)
-    }
-    return null
-}
-
 /* Needs a transaction{} block to be called */
 fun expectAcctidTransaction(param: String?): BankAccountEntity {
     if (param == null) {
@@ -394,7 +321,8 @@ fun extractUserAndHashedPassword(authorizationHeader: 
String): Pair<String, Byte
 }
 
 /**
- * Test HTTP basic auth.  Throws error if password is wrong
+ * Test HTTP basic auth.  Throws error if password is wrong,
+ * and makes sure that the user exists in the system.
  *
  * @param authorization the Authorization:-header line.
  * @return subscriber id
@@ -420,9 +348,9 @@ fun authenticateRequest(authorization: String?): String {
  */
 fun subscriberHasRights(subscriber: EbicsSubscriberEntity, bankAccount: 
BankAccountEntity): Boolean {
     val row = transaction {
-        EbicsToBankAccountEntity.find {
-            EbicsToBankAccountsTable.bankAccount eq bankAccount.id and
-                    (EbicsToBankAccountsTable.ebicsSubscriber eq subscriber.id)
+        BankAccountMapEntity.find {
+            BankAccountMapsTable.bankAccount eq bankAccount.id and
+                    (BankAccountMapsTable.ebicsSubscriber eq subscriber.id)
         }.firstOrNull()
     }
     return row != null
@@ -443,14 +371,10 @@ fun getBankAccountFromIban(iban: String): 
BankAccountEntity {
 fun userHasRights(nexusUser: NexusUserEntity, iban: String): Boolean {
     val row = transaction {
         val bankAccount = getBankAccountFromIban(iban)
-        UserToBankAccountEntity.find {
-            UserToBankAccountsTable.bankAccount eq bankAccount.id and
-                    (UserToBankAccountsTable.nexusUser eq nexusUser.id)
+        BankAccountMapEntity.find {
+            BankAccountMapsTable.bankAccount eq bankAccount.id and
+                    (BankAccountMapsTable.nexusUser eq nexusUser.id)
         }.firstOrNull()
     }
     return row != null
-}
-
-fun parseDate(date: String): DateTime {
-    return DateTime.parse(date, DateTimeFormat.forPattern("YYYY-MM-DD"))
-}
+}
\ 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 14bd1ea..e0ae928 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -238,15 +238,15 @@ fun main() {
                 val id = expectId(call.parameters["id"])
                 val ret = BankAccountsInfoResponse()
                 transaction {
-                    BankAccountEntity.find {
-                        UserToBankAccountsTable.nexusUser eq id
+                    BankAccountMapEntity.find {
+                        BankAccountMapsTable.nexusUser eq id
                     }.forEach {
                         ret.accounts.add(
                             BankAccountInfoElement(
-                                accountHolderName = it.accountHolder,
-                                iban = it.iban,
-                                bankCode = it.bankCode,
-                                accountId = it.id.value
+                                accountHolderName = 
it.bankAccount.accountHolder,
+                                iban = it.bankAccount.iban,
+                                bankCode = it.bankAccount.bankCode,
+                                accountId = it.bankAccount.id.value
                             )
                         )
                     }
@@ -263,8 +263,8 @@ fun main() {
                 val ret = RawPayments()
                 transaction {
                     val nexusUser = extractNexusUser(nexusUserId)
-                    val bankAccountsMap = UserToBankAccountEntity.find {
-                        UserToBankAccountsTable.nexusUser eq nexusUser.id
+                    val bankAccountsMap = BankAccountMapEntity.find {
+                        BankAccountMapsTable.nexusUser eq nexusUser.id
                     }
                     bankAccountsMap.forEach {
                         Pain001Entity.find {
@@ -273,7 +273,7 @@ fun main() {
                             ret.payments.add(
                                 RawPayment(
                                     creditorIban = it.creditorIban,
-                                    debitorIban = "FIXME",
+                                    debitorIban = it.debitorIban,
                                     subject = it.subject,
                                     amount = "${it.currency}:${it.sum}",
                                     date = DateTime(it.date).toDashedDate()
@@ -567,7 +567,7 @@ fun main() {
 
             /** STATE CHANGES VIA EBICS */
 
-            post("/ebics/admin/execute-payments") {
+            post("/ebics/execute-payments") {
                 val (paymentRowId, painDoc, subscriber) = transaction {
                     val entity = Pain001Entity.find {
                         (Pain001Table.submitted eq false) and 
(Pain001Table.invalid eq false)
@@ -702,6 +702,7 @@ fun main() {
                          * at all.
                          */
                         response.orderData.unzipWithLambda {
+                            logger.debug("C53 entry: ${it.second}")
                             val fileName = it.first
                             val camt53doc = 
XMLUtil.parseStringIntoDom(it.second)
                             transaction {
@@ -712,7 +713,7 @@ fun main() {
                                     currency = 
camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']/@Ccy")
                                     amount = 
camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']")
                                     status = 
camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Sts']")
-                                    bookingDate = 
parseDate(camt53doc.pickString("//*[local-name()='BookgDt']//*[local-name()='Dt']")).millis
+                                    bookingDate = 
parseDashedDate(camt53doc.pickString("//*[local-name()='BookgDt']//*[local-name()='Dt']")).millis
                                     nexusUser = extractNexusUser(id)
                                     creditorName = 
camt53doc.pickString("//*[local-name()='RltdPties']//*[local-name()='Dbtr']//*[local-name()='Nm']")
                                     creditorIban = 
camt53doc.pickString("//*[local-name()='CdtrAcct']//*[local-name()='IBAN']")
@@ -760,8 +761,9 @@ fun main() {
                                     iban = 
extractFirstIban(it.accountNumberList) ?: throw 
NexusError(HttpStatusCode.NotFound, reason = "bank gave no IBAN")
                                     bankCode = 
extractFirstBic(it.bankCodeList) ?: throw NexusError(HttpStatusCode.NotFound, 
reason = "bank gave no BIC")
                                 }
-                                EbicsToBankAccountEntity.new {
+                                BankAccountMapEntity.new {
                                     ebicsSubscriber = 
getEbicsSubscriberFromUser(nexusUser)
+                                    this.nexusUser = nexusUser
                                     this.bankAccount = bankAccount
                                 }
                             }
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
index 640fce1..bb16050 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
@@ -16,8 +16,7 @@ import org.jetbrains.exposed.dao.IdTable
 import org.jetbrains.exposed.sql.*
 import org.jetbrains.exposed.sql.transactions.transaction
 import org.joda.time.DateTime
-import tech.libeufin.util.Amount
-import tech.libeufin.util.CryptoUtil
+import tech.libeufin.util.*
 import kotlin.math.abs
 import kotlin.math.min
 
@@ -547,9 +546,10 @@ class Taler(app: Route) {
                             bankCode = "localhost"
                         }
                         val nexusUser = extractNexusUser(exchangeId)
-                        EbicsToBankAccountEntity.new {
+                        BankAccountMapEntity.new {
                             bankAccount = newBankAccount
                             ebicsSubscriber = 
getEbicsSubscriberFromUser(nexusUser)
+                            this.nexusUser = nexusUser
                         }
                     }
                 }
diff --git a/nexus/src/test/kotlin/DateTest.kt 
b/nexus/src/test/kotlin/DateTest.kt
new file mode 100644
index 0000000..1c9285b
--- /dev/null
+++ b/nexus/src/test/kotlin/DateTest.kt
@@ -0,0 +1,16 @@
+package tech.libeufin.nexus
+
+import org.joda.time.DateTime
+import org.junit.Test
+import tech.libeufin.util.toDashedDate
+import tech.libeufin.util.parseDashedDate
+
+class DateTest {
+    @Test
+    fun dashedDateParsing() {
+        val parseddate = parseDashedDate("2020-04-30")
+        println("Parsed value: " + parseddate.toLocalDate())
+        println("To dashed value: " + parseddate.toDashedDate())
+        println("System now(): " + DateTime.now().toLocalDate())
+    }
+}
\ No newline at end of file
diff --git a/nexus/src/test/kotlin/LetterFormatTest.kt 
b/nexus/src/test/kotlin/LetterFormatTest.kt
index 7c28b46..a268492 100644
--- a/nexus/src/test/kotlin/LetterFormatTest.kt
+++ b/nexus/src/test/kotlin/LetterFormatTest.kt
@@ -1,8 +1,7 @@
 package tech.libeufin.nexus
 
 import org.junit.Test
-import tech.libeufin.nexus.chunkString
-import tech.libeufin.nexus.getNonce
+import tech.libeufin.util.chunkString
 import tech.libeufin.util.toHexString
 import java.security.SecureRandom
 
diff --git a/nexus/src/test/kotlin/PainGeneration.kt 
b/nexus/src/test/kotlin/PainGeneration.kt
index 0a6e3e1..4eef6d8 100644
--- a/nexus/src/test/kotlin/PainGeneration.kt
+++ b/nexus/src/test/kotlin/PainGeneration.kt
@@ -8,8 +8,6 @@ import org.jetbrains.exposed.sql.SchemaUtils
 import org.joda.time.DateTime
 import tech.libeufin.util.Amount
 
-
-
 class PainTest {
     @Before
     fun prepare() {
@@ -23,14 +21,7 @@ class PainTest {
                 iban = "DEBIT IBAN"
                 bankCode = "DEBIT BIC"
             }
-        }
-    }
-
-    @Test
-    fun testPain001document() {
-        transaction {
-            val nu = NexusUserEntity.new(id = "mock") { }
-            val pain001Entity = Pain001Entity.new {
+            Pain001Entity.new {
                 sum = Amount(1)
                 debitorIban = "DEBIT IBAN"
                 debitorBic = "DEBIT BIC"
@@ -43,9 +34,15 @@ class PainTest {
                 msgId = 1
                 endToEndId = 1
                 date = DateTime.now().millis
-                nexusUser = nu
+                nexusUser = NexusUserEntity.new(id = "mock") { }
             }
-            val s = createPain001document(pain001Entity)
+        }
+    }
+
+    @Test
+    fun testPain001document() {
+        transaction {
+            val s = createPain001document(Pain001Entity.all().first())
             println(s)
         }
     }
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 7ce01a1..a870c09 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -225,7 +225,7 @@ class EbicsUploadTransactionChunkEntity(id: 
EntityID<String>) : Entity<String>(i
 }
 
 /**
- * Table that keeps all the payments initiated by pain.
+ * Table that keeps all the payments initiated by PAIN.001.
  */
 object PaymentsTable : IntIdTable() {
     val creditorIban = text("creditorIban")
@@ -241,10 +241,31 @@ class PaymentEntity(id: EntityID<Int>) : IntEntity(id) {
     var debitorIban by PaymentsTable.debitorIban
     var subject by PaymentsTable.subject
     var amount by PaymentsTable.amount
-    var date by PaymentsTable.date
+    var date by PaymentsTable.date /** Date when the payment was persisted in 
this system.  */
+    /* Subscirber involved in the payment */
     var ebicsSubscriber by EbicsSubscriberEntity referencedOn 
PaymentsTable.ebicsSubscriber
 }
 
+/**
+ * Table that keeps information about which bank accounts (iban+bic+name)
+ * are active in the system.
+ */
+object BankAccountsTable : IntIdTable() {
+    val iban = text("iban")
+    val bic = text("bic")
+    val name = text("name")
+    val label = text("label")
+    val subscriber = reference("subscriber", EbicsSubscribersTable)
+}
+class BankAccountEntity(id: EntityID<Int>) : IntEntity(id) {
+    companion object : IntEntityClass<BankAccountEntity>(BankAccountsTable)
+    var iban by BankAccountsTable.iban
+    var bic by BankAccountsTable.bic
+    var name by BankAccountsTable.name
+    var label by BankAccountsTable.label
+    var subscriber by EbicsSubscriberEntity referencedOn 
BankAccountsTable.subscriber
+}
+
 fun dbCreateTables() {
     Database.connect("jdbc:sqlite:libeufin-sandbox.sqlite3", "org.sqlite.JDBC")
     TransactionManager.manager.defaultIsolationLevel = 
Connection.TRANSACTION_SERIALIZABLE
@@ -257,7 +278,8 @@ fun dbCreateTables() {
             EbicsUploadTransactionsTable,
             EbicsUploadTransactionChunksTable,
             EbicsOrderSignaturesTable,
-            PaymentsTable
+            PaymentsTable,
+            BankAccountsTable
         )
     }
 }
\ No newline at end of file
diff --git 
a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
index 5788064..b0348a7 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -50,6 +50,7 @@ import java.util.zip.InflaterInputStream
 import javax.sql.rowset.serial.SerialBlob
 import javax.xml.datatype.DatatypeFactory
 import org.apache.commons.compress.utils.IOUtils
+import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
 import org.joda.time.DateTime
 import org.joda.time.Instant
 import java.io.BufferedInputStream
@@ -159,9 +160,11 @@ fun buildCamtString(type: Int, history: 
MutableList<RawPayment>): MutableList<St
      * - Proprietary code of the bank transaction
      * - Id of the servicer (Issuer and Code)
      */
-    val now = DateTime.now()
     val ret = mutableListOf<String>()
     history.forEach {
+        val dashedDate = DateTime.parse(it.date).toDashedDate()
+        val zonedDateTime = DateTime.now().toZonedString()
+        logger.debug("Dashed date for CAMT: $dashedDate")
         ret.add(
             constructXml(indent = true) {
                 root("Document") {
@@ -174,7 +177,7 @@ fun buildCamtString(type: Int, history: 
MutableList<RawPayment>): MutableList<St
                                 text("0")
                             }
                             element("CreDtTm") {
-                                text(now.toZonedString())
+                                text(zonedDateTime)
                             }
                             element("MsgPgntn") {
                                 element("PgNb") {
@@ -196,23 +199,23 @@ fun buildCamtString(type: Int, history: 
MutableList<RawPayment>): MutableList<St
                                 text("0")
                             }
                             element("CreDtTm") {
-                                text(now.toZonedString())
+                                text(zonedDateTime)
                             }
 
                             element("Acct") {
                                 // mandatory account identifier
                                 element("Id/IBAN") {
-                                    text("GB33BUKB20201555555555")
+                                    text("Owner IBAN")
                                 }
                                 element("Ccy") {
                                     text("EUR")
                                 }
                                 element("Ownr/Nm") {
-                                    text("Max Mustermann")
+                                    text("Debitor/Owner Name")
                                 }
                                 element("Svcr/FinInstnId") {
                                     element("BIC") {
-                                        text("GENODEM1GLS")
+                                        text("Owner Bic")
                                     }
                                     element("Nm") {
                                         text("Libeufin Bank")
@@ -238,15 +241,14 @@ fun buildCamtString(type: Int, history: 
MutableList<RawPayment>): MutableList<St
                                 }
                                 element("Amt") {
                                     attribute("Ccy", "EUR")
-                                    text(Amount(1).toPlainString())
+                                    text(Amount(0).toPlainString())
                                 }
                                 element("CdtDbtInd") {
                                     text("DBIT")
-                                    // CRDT or DBIT here
                                 }
                                 element("Dt/Dt") {
                                     // date of this balance
-                                    text(now.toDashedDate())
+                                    text(dashedDate)
                                 }
                             }
                             element("Bal") {
@@ -258,14 +260,14 @@ fun buildCamtString(type: Int, history: 
MutableList<RawPayment>): MutableList<St
                                 }
                                 element("Amt") {
                                     attribute("Ccy", "EUR")
-                                    text(Amount(1).toPlainString())
+                                    text(Amount(0).toPlainString())
                                 }
                                 element("CdtDbtInd") {
                                     // CRDT or DBIT here
                                     text("DBIT")
                                 }
                                 element("Dt/Dt") {
-                                    text(now.toDashedDate())
+                                    text(dashedDate)
                                 }
                             }
                             /**
@@ -275,7 +277,7 @@ fun buildCamtString(type: Int, history: 
MutableList<RawPayment>): MutableList<St
                                 element("Ntry") {
                                     element("Amt") {
                                         attribute("Ccy", "EUR")
-                                        text(Amount(1).toPlainString())
+                                        text(it.amount)
                                     }
                                     element("CdtDbtInd") {
                                         text("DBIT")
@@ -287,10 +289,10 @@ fun buildCamtString(type: Int, history: 
MutableList<RawPayment>): MutableList<St
                                         text("BOOK")
                                     }
                                     element("BookgDt/Dt") {
-                                        text(now.toDashedDate())
+                                        text(dashedDate)
                                     } // date of the booking
                                     element("ValDt/Dt") {
-                                        text(now.toDashedDate())
+                                        text(dashedDate)
                                     } // date of assets' actual 
(un)availability
                                     element("AcctSvcrRef") {
                                         text("0")
@@ -334,7 +336,7 @@ fun buildCamtString(type: Int, history: 
MutableList<RawPayment>): MutableList<St
                                         }
                                         element("AmtDtls/TxAmt/Amt") {
                                             attribute("Ccy", "EUR")
-                                            text(Amount(1).toPlainString())
+                                            text(it.amount)
                                         }
                                         element("BkTxCd") {
                                             element("Domn") {
@@ -361,25 +363,25 @@ fun buildCamtString(type: Int, history: 
MutableList<RawPayment>): MutableList<St
                                         }
                                         element("RltdPties") {
                                             element("Dbtr/Nm") {
-                                                text("Max Mustermann")
+                                                text("Debitor Name")
                                             }
                                             element("DbtrAcct/Id/IBAN") {
-                                                text("GB33BUKB20201555555555")
+                                                text(it.debitorIban)
                                             }
                                             element("Cdtr/Nm") {
-                                                text("Lina Musterfrau")
+                                                text("Creditor Name")
                                             }
                                             element("CdtrAcct/Id/IBAN") {
-                                                text("DE75512108001245126199")
+                                                text(it.creditorIban)
                                             }
                                         }
                                         element("RltdAgts") {
                                             element("CdtrAgt/FinInstnId/BIC") {
-                                                text("GENODEM1GLS")
+                                                text("Creditor Bic")
                                             }
                                         }
                                         element("RmtInf/Ustrd") {
-                                            text("made up subject")
+                                            text(it.subject)
                                         }
                                     }
                                     element("AddtlNtryInf") {
@@ -450,7 +452,6 @@ private fun handleCct(paymentRequest: String, 
ebicsSubscriber: EbicsSubscriberEn
     val creditorIban = 
painDoc.pickString("//*[local-name()='CdtrAcct']//*[local-name()='IBAN']")
     val debitorIban = 
painDoc.pickString("//*[local-name()='DbtrAcct']//*[local-name()='IBAN']")
     val subject = painDoc.pickString("//*[local-name()='Ustrd']")
-    val currency = painDoc.pickString("//*[local-name()='InstdAmt']/@ccy")
     val amount = painDoc.pickString("//*[local-name()='InstdAmt']")
 
     transaction {
@@ -458,8 +459,9 @@ private fun handleCct(paymentRequest: String, 
ebicsSubscriber: EbicsSubscriberEn
             this.creditorIban = creditorIban
             this.debitorIban = debitorIban
             this.subject = subject
-            this.amount = "${currency}:${amount}"
+            this.amount = amount
             this.ebicsSubscriber = ebicsSubscriber
+            this.date = DateTime.now().millis
         }
     }
 }
@@ -634,86 +636,79 @@ private suspend fun ApplicationCall.receiveEbicsXml(): 
Document {
     return requestDocument
 }
 
-fun handleEbicsHtd(): ByteArray {
-    val htd = HTDResponseOrderData().apply {
-        this.partnerInfo = EbicsTypes.PartnerInfo().apply {
-            this.accountInfoList = listOf(
-                EbicsTypes.AccountInfo().apply {
-                    this.id = "acctid1"
-                    this.accountHolder = "Mina Musterfrau"
-                    this.accountNumberList = listOf(
-                        EbicsTypes.GeneralAccountNumber().apply {
-                            this.international = true
-                            this.value = "DE21500105174751659277"
-                        }
-                    )
-                    this.currency = "EUR"
-                    this.description = "ACCT"
-                    this.bankCodeList = listOf(
-                        EbicsTypes.GeneralBankCode().apply {
-                            this.international = true
-                            this.value = "INGDDEFFXXX"
-                        }
-                    )
-                },
-                EbicsTypes.AccountInfo().apply {
-                    this.id = "glsdemo"
-                    this.accountHolder = "Mina Musterfrau"
-                    this.accountNumberList = listOf(
-                        EbicsTypes.GeneralAccountNumber().apply {
-                            this.international = true
-                            this.value = "DE91430609670123123123"
-                        }
-                    )
-                    this.currency = "EUR"
-                    this.description = "glsdemoacct"
-                    this.bankCodeList = listOf(
-                        EbicsTypes.GeneralBankCode().apply {
-                            this.international = true
-                            this.value = "GENODEM1GLS"
-                        }
-                    )
-                }
-            )
-            this.addressInfo = EbicsTypes.AddressInfo().apply {
-                this.name = "Foo"
-            }
-            this.bankInfo = EbicsTypes.BankInfo().apply {
-                this.hostID = "host01"
+private fun makePartnerInfo(subscriber: EbicsSubscriberEntity): 
EbicsTypes.PartnerInfo {
+    val bankAccount = getBankAccountFromSubscriber(subscriber)
+    return EbicsTypes.PartnerInfo().apply {
+        this.accountInfoList = listOf(
+            EbicsTypes.AccountInfo().apply {
+                this.id = bankAccount.label
+                this.accountHolder = bankAccount.name
+                this.accountNumberList = listOf(
+                    EbicsTypes.GeneralAccountNumber().apply {
+                        this.international = true
+                        this.value = bankAccount.iban
+                    }
+                )
+                this.currency = "EUR"
+                this.description = "Ordinary Bank Account"
+                this.bankCodeList = listOf(
+                    EbicsTypes.GeneralBankCode().apply {
+                        this.international = true
+                        this.value = bankAccount.bic
+                    }
+                )
             }
-            this.orderInfoList = listOf(
-                EbicsTypes.AuthOrderInfoType().apply {
-                    this.description = "foo1"
-                    this.orderType = "C53"
-                    this.transferType = "Download"
-                },
-                EbicsTypes.AuthOrderInfoType().apply {
-                    this.description = "foo2"
-                    this.orderType = "C52"
-                    this.transferType = "Download"
-                },
-                EbicsTypes.AuthOrderInfoType().apply {
-                    this.description = "foo3"
-                    this.orderType = "CCC"
-                    this.transferType = "Upload"
-                },
-                EbicsTypes.AuthOrderInfoType().apply {
-                    this.description = "foo4"
-                    this.orderType = "VMK"
-                    this.transferType = "Download"
-                },
-                EbicsTypes.AuthOrderInfoType().apply {
-                    this.description = "foo5"
-                    this.orderType = "STA"
-                    this.transferType = "Download"
-                }
-            )
+        )
+        this.addressInfo = EbicsTypes.AddressInfo().apply {
+            this.name = "Address Info Object"
         }
+        this.bankInfo = EbicsTypes.BankInfo().apply {
+            this.hostID = subscriber.hostId
+        }
+        this.orderInfoList = listOf(
+            EbicsTypes.AuthOrderInfoType().apply {
+                this.description = "Transactions statement"
+                this.orderType = "C53"
+                this.transferType = "Download"
+            },
+            EbicsTypes.AuthOrderInfoType().apply {
+                this.description = "Transactions report"
+                this.orderType = "C52"
+                this.transferType = "Download"
+            },
+            EbicsTypes.AuthOrderInfoType().apply {
+                this.description = "Payment initiation (ZIPped payload)"
+                this.orderType = "CCC"
+                this.transferType = "Upload"
+            },
+            EbicsTypes.AuthOrderInfoType().apply {
+                this.description = "Payment initiation (plain text payload)"
+                this.orderType = "CCT"
+                this.transferType = "Upload"
+            },
+            EbicsTypes.AuthOrderInfoType().apply {
+                this.description = "vmk"
+                this.orderType = "VMK"
+                this.transferType = "Download"
+            },
+            EbicsTypes.AuthOrderInfoType().apply {
+                this.description = "sta"
+                this.orderType = "STA"
+                this.transferType = "Download"
+            }
+        )
+    }
+}
+
+private fun handleEbicsHtd(requestContext: RequestContext): ByteArray {
+    val bankAccount = getBankAccountFromSubscriber(requestContext.subscriber)
+    val htd = HTDResponseOrderData().apply {
+        this.partnerInfo = makePartnerInfo(requestContext.subscriber)
         this.userInfo = EbicsTypes.UserInfo().apply {
             this.name = "Some User"
             this.userID = EbicsTypes.UserIDType().apply {
                 this.status = 5
-                this.value = "USER1"
+                this.value = requestContext.subscriber.userId
             }
             this.permissionList = listOf(
                 EbicsTypes.UserPermission().apply {
@@ -727,78 +722,15 @@ fun handleEbicsHtd(): ByteArray {
     return str.toByteArray()
 }
 
-
-fun handleEbicsHkd(): ByteArray {
+private fun handleEbicsHkd(requestContext: RequestContext): ByteArray {
     val hkd = HKDResponseOrderData().apply {
-        this.partnerInfo = EbicsTypes.PartnerInfo().apply {
-            this.accountInfoList = listOf(
-                EbicsTypes.AccountInfo().apply {
-                    this.id = "acctid1"
-                    this.accountHolder = "Mina Musterfrau"
-                    this.accountNumberList = listOf(
-                        EbicsTypes.GeneralAccountNumber().apply {
-                            this.international = true
-                            this.value = "DE21500105174751659277"
-                        }
-                    )
-                    this.currency = "EUR"
-                    this.description = "ACCT"
-                    this.bankCodeList = listOf(
-                        EbicsTypes.GeneralBankCode().apply {
-                            this.international = true
-                            this.value = "INGDDEFFXXX"
-                        }
-                    )
-                },
-                EbicsTypes.AccountInfo().apply {
-                    this.id = "glsdemo"
-                    this.accountHolder = "Mina Musterfrau"
-                    this.accountNumberList = listOf(
-                        EbicsTypes.GeneralAccountNumber().apply {
-                            this.international = true
-                            this.value = "DE91430609670123123123"
-                        }
-                    )
-                    this.currency = "EUR"
-                    this.description = "glsdemoacct"
-                    this.bankCodeList = listOf(
-                        EbicsTypes.GeneralBankCode().apply {
-                            this.international = true
-                            this.value = "GENODEM1GLS"
-                        }
-                    )
-                }
-            )
-            this.addressInfo = EbicsTypes.AddressInfo().apply {
-                this.name = "Foo"
-            }
-            this.bankInfo = EbicsTypes.BankInfo().apply {
-                this.hostID = "host01"
-            }
-            this.orderInfoList = listOf(
-                EbicsTypes.AuthOrderInfoType().apply {
-                    this.description = "foo"
-                    this.orderType = "C53"
-                    this.transferType = "Download"
-                },
-                EbicsTypes.AuthOrderInfoType().apply {
-                    this.description = "foo"
-                    this.orderType = "C52"
-                    this.transferType = "Download"
-                },
-                EbicsTypes.AuthOrderInfoType().apply {
-                    this.description = "foo"
-                    this.orderType = "CCC"
-                    this.transferType = "Upload"
-                }
-            )
-        }
+        this.partnerInfo = makePartnerInfo(requestContext.subscriber)
         this.userInfoList = listOf(
             EbicsTypes.UserInfo().apply {
                 this.name = "Some User"
                 this.userID = EbicsTypes.UserIDType().apply {
                     this.status = 1
-                    this.value = "USER1"
+                    this.value = requestContext.subscriber.userId
                 }
                 this.permissionList = listOf(
                     EbicsTypes.UserPermission().apply {
@@ -832,8 +764,8 @@ private fun 
handleEbicsDownloadTransactionInitialization(requestContext: Request
         requestContext.requestObject.header.static.orderDetails?.orderType ?: 
throw EbicsInvalidRequestError()
     println("handling initialization for order type $orderType")
     val response = when (orderType) {
-        "HTD" -> handleEbicsHtd()
-        "HKD" -> handleEbicsHkd()
+        "HTD" -> handleEbicsHtd(requestContext)
+        "HKD" -> handleEbicsHkd(requestContext)
         /* Temporarily handling C52/C53 with same logic */
         "C53" -> handleEbicsC53(requestContext)
         "TSD" -> handleEbicsTSD(requestContext)
@@ -901,7 +833,6 @@ private fun 
handleEbicsUploadTransactionInitialization(requestContext: RequestCo
     val plainSigData = 
InflaterInputStream(decryptedSignatureData.inputStream()).use {
         it.readAllBytes()
     }
-
     println("creating upload transaction for transactionID $transactionID")
     EbicsUploadTransactionEntity.new(transactionID) {
         this.host = requestContext.ebicsHost
@@ -925,7 +856,6 @@ private fun 
handleEbicsUploadTransactionInitialization(requestContext: RequestCo
             this.signatureValue = SerialBlob(sig.signatureValue)
         }
     }
-
     return EbicsResponse.createForUploadInitializationPhase(transactionID, 
orderID)
 }
 
@@ -951,11 +881,9 @@ private fun 
handleEbicsUploadTransactionTransmission(requestContext: RequestCont
             (EbicsOrderSignaturesTable.orderID eq uploadTransaction.orderID) 
and
                     (EbicsOrderSignaturesTable.orderType eq 
uploadTransaction.orderType)
         }
-
         if (sigs.count() == 0) {
             throw EbicsInvalidRequestError()
         }
-
         for (sig in sigs) {
             if (sig.signatureAlgorithm == "A006") {
 
@@ -971,8 +899,8 @@ private fun 
handleEbicsUploadTransactionTransmission(requestContext: RequestCont
             }
         }
 
-        /** Handling a payment request */
-        if ("CCT" == 
requestContext.requestObject.header.static.orderDetails?.orderType) {
+        if (getOrderTypeFromTransactionId(requestTransactionID) == "CCT") {
+            logger.debug("Attempting a payment.")
             handleCct(unzippedData.toString(Charsets.UTF_8), 
requestContext.subscriber)
         }
         return EbicsResponse.createForUploadTransferPhase(
@@ -1048,7 +976,6 @@ private fun makeReqestContext(requestObject: 
EbicsRequest): RequestContext {
     )
 }
 
-
 suspend fun ApplicationCall.ebicsweb() {
     val requestDocument = receiveEbicsXml()
 
@@ -1140,7 +1067,10 @@ suspend fun ApplicationCall.ebicsweb() {
             LOGGER.info("Unknown message, just logging it!")
             respond(
                 HttpStatusCode.NotImplemented,
-                SandboxError("Not Implemented")
+                SandboxError(
+                    HttpStatusCode.NotImplemented,
+                    "Not Implemented"
+                )
             )
         }
     }
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
new file mode 100644
index 0000000..5338b89
--- /dev/null
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
@@ -0,0 +1,41 @@
+package tech.libeufin.sandbox
+
+import io.ktor.http.HttpStatusCode
+import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
+import org.jetbrains.exposed.sql.and
+import org.jetbrains.exposed.sql.transactions.transaction
+
+
+fun getOrderTypeFromTransactionId(transactionID: String): String {
+    val uploadTransaction = transaction {
+        EbicsUploadTransactionEntity.findById(transactionID)
+    } ?: throw SandboxError(
+        /**
+         * NOTE: at this point, it might even be the server's fault.
+         * For example, if it failed to store a ID earlier.
+         */
+        HttpStatusCode.NotFound,
+        "Could not retrieve order type for transaction: $transactionID"
+    )
+    return uploadTransaction.orderType
+}
+
+fun getBankAccountFromSubscriber(subscriber: EbicsSubscriberEntity): 
BankAccountEntity {
+    return transaction {
+        BankAccountEntity.find(BankAccountsTable.subscriber eq subscriber.id)
+    }.firstOrNull() ?: throw SandboxError(
+        HttpStatusCode.NotFound,
+        "Subscriber doesn't have any bank account"
+    )
+}
+fun getEbicsSubscriberFromDetails(userID: String, partnerID: String, hostID: 
String): EbicsSubscriberEntity {
+    return transaction {
+        EbicsSubscriberEntity.find {
+            (EbicsSubscribersTable.userId eq userID) and 
(EbicsSubscribersTable.partnerId eq partnerID) and
+                    (EbicsSubscribersTable.hostId eq hostID)
+        }.firstOrNull() ?: throw SandboxError(
+            HttpStatusCode.NotFound,
+            "Ebics subscriber not found"
+        )
+    }
+}
\ No newline at end of file
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
index 3d4209b..092b022 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
@@ -19,10 +19,8 @@
 
 package tech.libeufin.sandbox
 
-/** Error message */
-data class SandboxError(
-    val message: String
-)
+import io.ktor.http.HttpStatusCode
+import java.lang.Exception
 
 /**
  * Used to show the list of Ebics hosts that exist
@@ -59,3 +57,11 @@ data class EbicsSubscriberElement(
 data class AdminGetSubscribers(
     var subscribers: MutableList<EbicsSubscriberElement> = mutableListOf()
 )
+
+data class BankAccountRequest(
+    val subscriber: EbicsSubscriberElement,
+    val iban: String,
+    val bic: String,
+    val name: String,
+    val label: String
+)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 96afb72..b3c0751 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -61,6 +61,8 @@ class UnacceptableFractional(badNumber: BigDecimal) : 
Exception(
 )
 val LOGGER: Logger = LoggerFactory.getLogger("tech.libeufin.sandbox")
 
+data class SandboxError(val statusCode: HttpStatusCode, val reason: String) : 
java.lang.Exception()
+
 fun findEbicsSubscriber(partnerID: String, userID: String, systemID: String?): 
EbicsSubscriberEntity? {
     return if (systemID == null) {
         EbicsSubscriberEntity.find {
@@ -142,6 +144,27 @@ fun main() {
             }
 
             /** EBICS ADMIN ENDPOINTS */
+            post("/admin/ebics-subscriber/bank-account") {
+                val body = call.receive<BankAccountRequest>()
+                transaction {
+                    val subscriber = getEbicsSubscriberFromDetails(
+                        body.subscriber.userID,
+                        body.subscriber.partnerID,
+                        body.subscriber.hostID
+                    )
+                    BankAccountEntity.new {
+                        this.subscriber = subscriber
+                        iban = body.iban
+                        bic = body.bic
+                        name = body.name
+                        label = body.label
+                    }
+                }
+                call.respondText(
+                    "Bank account created, and associated to the subscriber"
+                )
+                return@post
+            }
 
             post("/admin/ebics-subscriber") {
                 val body = call.receive<EbicsSubscriberElement>()
@@ -189,7 +212,7 @@ fun main() {
                 }
                 if (resp == null) call.respond(
                     HttpStatusCode.NotFound,
-                    SandboxError("host not found")
+                    SandboxError(HttpStatusCode.NotFound,"host not found")
                 )
                 else call.respond(resp)
             }
diff --git a/util/src/main/kotlin/ParametersChecks.kt 
b/util/src/main/kotlin/ParametersChecks.kt
new file mode 100644
index 0000000..cc910b4
--- /dev/null
+++ b/util/src/main/kotlin/ParametersChecks.kt
@@ -0,0 +1,33 @@
+package tech.libeufin.util
+
+import io.ktor.application.ApplicationCall
+import io.ktor.http.HttpStatusCode
+
+fun expectInt(param: String): Int {
+    return try {
+        param.toInt()
+    } catch (e: Exception) {
+        throw UtilError(HttpStatusCode.BadRequest,"'$param' is not Int")
+    }
+}
+
+fun expectLong(param: String): Long {
+    return try {
+        param.toLong()
+    } catch (e: Exception) {
+        throw UtilError(HttpStatusCode.BadRequest,"'$param' is not Long")
+    }
+}
+
+fun expectLong(param: String?): Long? {
+    if (param != null) {
+        return expectLong(param)
+    }
+    return null
+}
+
+
+fun ApplicationCall.expectUrlParameter(name: String): String {
+    return this.request.queryParameters[name]
+        ?: throw UtilError(HttpStatusCode.BadRequest, "Parameter '$name' not 
provided in URI")
+}
\ No newline at end of file
diff --git a/util/src/main/kotlin/strings.kt b/util/src/main/kotlin/strings.kt
index d57b53a..68cfff3 100644
--- a/util/src/main/kotlin/strings.kt
+++ b/util/src/main/kotlin/strings.kt
@@ -1,5 +1,5 @@
 package tech.libeufin.util
-import org.apache.commons.codec.binary.Base32
+
 import java.math.BigInteger
 import java.util.*
 
@@ -47,4 +47,26 @@ fun BigInteger.toUnsignedHexString(): String {
     val start = if (signedValue[0] == 0.toByte()) { 1 } else { 0 }
     val bytes = Arrays.copyOfRange(signedValue, start, signedValue.size)
     return bytes.toHexString()
+}
+
+/**
+ * Inserts spaces every 2 characters, and a newline after 8 pairs.
+ */
+fun chunkString(input: String): String {
+    val ret = StringBuilder()
+    var columns = 0
+    for (i in input.indices) {
+        if ((i + 1).rem(2) == 0) {
+            if (columns == 15) {
+                ret.append(input[i] + "\n")
+                columns = 0
+                continue
+            }
+            ret.append(input[i] + " ")
+            columns++
+            continue
+        }
+        ret.append(input[i])
+    }
+    return ret.toString().toUpperCase()
 }
\ No newline at end of file
diff --git a/util/src/main/kotlin/time.kt b/util/src/main/kotlin/time.kt
index e2d2df9..07fe4a7 100644
--- a/util/src/main/kotlin/time.kt
+++ b/util/src/main/kotlin/time.kt
@@ -1,6 +1,7 @@
 package tech.libeufin.util
 
 import org.joda.time.DateTime
+import org.joda.time.format.DateTimeFormat
 import java.time.ZoneId
 import java.time.ZonedDateTime
 import java.time.format.DateTimeFormatter
@@ -13,5 +14,10 @@ fun DateTime.toZonedString(): String {
 }
 
 fun DateTime.toDashedDate(): String {
-    return this.toString("Y-MM-dd")
-}
\ No newline at end of file
+    return this.toString("y-MM-d")
+}
+
+fun parseDashedDate(date: String): DateTime {
+    logger.debug("Parsing date: $date")
+    return DateTime.parse(date, DateTimeFormat.forPattern("y-M-d"))
+}
diff --git a/util/src/test/kotlin/CryptoUtilTest.kt 
b/util/src/test/kotlin/CryptoUtilTest.kt
index 705f443..da545bf 100644
--- a/util/src/test/kotlin/CryptoUtilTest.kt
+++ b/util/src/test/kotlin/CryptoUtilTest.kt
@@ -158,7 +158,6 @@ class CryptoUtilTest {
     // from Crockford32 encoding to binary.
     fun base32ToBytesTest() {
         val expectedEncoding = "C9P6YRG"
-        val blob = "blob".toByteArray(Charsets.UTF_8)
         
assert(Base32Crockford.decode(expectedEncoding).toString(Charsets.UTF_8) == 
"blob")
     }
 }

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]