commit b37353d25cf0e4a8c08fea5ecd6437ccdec550e3
parent 107a5bc855579b588fe3c22210f810296075b5b0
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date: Thu, 9 Apr 2020 17:49:05 +0200
Completing admin add-incoming.
Diffstat:
3 files changed, 49 insertions(+), 8 deletions(-)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -14,7 +14,7 @@ object TalerIncomingPayments: LongIdTable() {
val payment = reference("payment", EbicsRawBankTransactionsTable)
val valid = bool("valid")
// avoid refunding twice!
- val processed = bool("refunded").default(false)
+ val refunded = bool("refunded").default(false)
}
class TalerIncomingPaymentEntry(id: EntityID<Long>) : LongEntity(id) {
@@ -31,9 +31,14 @@ class TalerIncomingPaymentEntry(id: EntityID<Long>) : LongEntity(id) {
}
var payment by EbicsRawBankTransactionEntry referencedOn TalerIncomingPayments.payment
var valid by TalerIncomingPayments.valid
- var processed by TalerIncomingPayments.processed
+ var refunded by TalerIncomingPayments.refunded
}
+/**
+ * This table _assumes_ that all the entries have a BOOK status. The
+ * current code however does only enforces this trusting the C53 response,
+ * but never actually checking the appropriate "Sts" field.
+ */
object EbicsRawBankTransactionsTable : LongIdTable() {
val nexusSubscriber = reference("subscriber", EbicsSubscribersTable)
// How did we learn about this transaction? C52 / C53 / C54
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -142,7 +142,8 @@ fun getSubscriberDetailsFromBankAccount(bankAccountId: String): EbicsClientSubsc
/**
* Given a subscriber id, returns the _list_ of bank accounts associated to it.
* @param id the subscriber id
- * @return the query set containing the subscriber's bank accounts
+ * @return the query set containing the subscriber's bank accounts. The result
+ * is guaranteed to be non empty.
*/
fun getBankAccountsInfoFromId(id: String): SizedIterable<EbicsAccountInfoEntity> {
val list = transaction {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
@@ -16,6 +16,7 @@ import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import tech.libeufin.util.Amount
import tech.libeufin.util.CryptoUtil
+import tech.libeufin.util.toZonedString
import java.util.*
import kotlin.math.abs
@@ -64,6 +65,12 @@ class Taler(app: Route) {
private data class TalerAdminAddIncoming(
val amount: String,
val reserve_pub: String,
+ /**
+ * This account is the one giving money to the exchange. It doesn't
+ * have to be 'created' as it might (and normally is) simply be a payto://
+ * address pointing to a bank account hosted in a different financial
+ * institution.
+ */
val debit_account: String
)
@@ -152,11 +159,39 @@ class Taler(app: Route) {
/** attaches Taler endpoints to the main Web server */
init {
app.post("/taler/admin/add-incoming") {
+ val exchangeId = authenticateRequest(call.request.headers["Authorization"])
val addIncomingData = call.receive<TalerAdminAddIncoming>()
- /** Decompose amount and payto fields. */
-
+ val debtor = parsePayto(addIncomingData.debit_account)
+ val amount = parseAmount(addIncomingData.amount)
- call.respond(HttpStatusCode.OK, NexusErrorJson("Not implemented"))
+ /** Decompose amount and payto fields. */
+ val (bookingDate, opaque_row_id) = transaction {
+ val exchangeBankAccount = getBankAccountsInfoFromId(exchangeId).first()
+ val rawPayment = EbicsRawBankTransactionEntry.new {
+ sourceFileName = "test"
+ sourceType = "C53"
+ unstructuredRemittanceInformation = addIncomingData.reserve_pub
+ transactionType = "CRDT"
+ currency = amount.currency
+ this.amount = amount.amount.toPlainString()
+ creditorIban = exchangeBankAccount.iban
+ creditorName = "Exchange's company name"
+ debitorIban = debtor.iban
+ debitorName = if (debtor.name.isNotEmpty()) { debtor.name } else "DEBITORNAMENOTGIVEN"
+ counterpartBic = if (debtor.bic.isNotEmpty()) { debtor.bic } else "DEBITORBICNOTGIVEN"
+ bookingDate = DateTime.now().toZonedString()
+ }
+ /** This payment is "valid by default" and will be returned
+ * as soon as the exchange will ask for new payments. */
+ val row = TalerIncomingPaymentEntry.new {
+ payment = rawPayment
+ }
+ Pair(rawPayment.bookingDate, row.id.value)
+ }
+ call.respond(HttpStatusCode.OK, TalerAddIncomingResponse(
+ timestamp = parseDate(bookingDate).millis / 1000,
+ row_id = opaque_row_id
+ ))
return@post
}
app.post("/ebics/taler/{id}/accounts/{acctid}/refund-invalid-payments") {
@@ -170,7 +205,7 @@ class Taler(app: Route) {
)
}
TalerIncomingPaymentEntry.find {
- TalerIncomingPayments.processed eq false and (TalerIncomingPayments.valid eq false)
+ TalerIncomingPayments.refunded eq false and (TalerIncomingPayments.valid eq false)
}.forEach {
createPain001entry(
Pain001Data(
@@ -182,7 +217,7 @@ class Taler(app: Route) {
),
acctid.id.value
)
- it.processed = true
+ it.refunded = true
}
}
return@post