commit 70763ba07ef2e76031626010a87c1b9bc9e5e57b
parent f54438f79232c106899bdb53e27f8084ef6d21bb
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date: Mon, 13 Apr 2020 18:32:58 +0200
Wire gateway API.
Outgoing payments are counted as those with a non-null
reference to the raw EBICS table. Link between camt.053
entries and Taler-requested payments is missing.
Diffstat:
3 files changed, 50 insertions(+), 33 deletions(-)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -23,16 +23,22 @@ object TalerRequestedPayments: LongIdTable() {
val exchangeBaseUrl = text("exchange_base_url")
val wtid = text("wtid")
val creditAccount = text("credit_account")
+ /**
+ * This column gets a value only after the bank acknowledges the payment via
+ * a camt.05x entry. The "crunch" logic is responsible for assigning such value.
+ */
+ val rawConfirmed = reference("raw_confirmed", EbicsRawBankTransactionsTable).nullable()
}
class TalerRequestedPaymentEntity(id: EntityID<Long>) : LongEntity(id) {
- companion object : LongEntityClass<TalerRequestedPaymentEntity>(TalerIncomingPayments)
+ companion object : LongEntityClass<TalerRequestedPaymentEntity>(TalerRequestedPayments)
var preparedPayment by Pain001Entity referencedOn TalerRequestedPayments.preparedPayment
var requestUId by TalerRequestedPayments.requestUId
var amount by TalerRequestedPayments.amount
var exchangeBaseUrl by TalerRequestedPayments.exchangeBaseUrl
var wtid by TalerRequestedPayments.wtid
var creditAccount by TalerRequestedPayments.creditAccount
+ var rawConfirmed by EbicsRawBankTransactionEntity optionalReferencedOn TalerRequestedPayments.rawConfirmed
}
/**
@@ -47,6 +53,10 @@ object TalerIncomingPayments: LongIdTable() {
val refunded = bool("refunded").default(false)
}
+fun LongEntityClass<*>.getLast(): Long {
+ return this.all().maxBy { it.id }?.id?.value ?: -1
+}
+
class TalerIncomingPaymentEntity(id: EntityID<Long>) : LongEntity(id) {
companion object : LongEntityClass<TalerIncomingPaymentEntity>(TalerIncomingPayments) {
override fun new(init: TalerIncomingPaymentEntity.() -> Unit): TalerIncomingPaymentEntity {
@@ -88,8 +98,6 @@ object EbicsRawBankTransactionsTable : LongIdTable() {
val counterpartBic = text("counterpartBic")
val bookingDate = text("bookingDate")
val status = text("status") // BOOK, ..
- val servicerCode = text("servicerCode").nullable() /* "internal" code given by the bank */
- val proprietaryCode = text("proprietaryCode") /* code given by the DK */
}
class EbicsRawBankTransactionEntity(id: EntityID<Long>) : LongEntity(id) {
@@ -107,8 +115,6 @@ class EbicsRawBankTransactionEntity(id: EntityID<Long>) : LongEntity(id) {
var bookingDate by EbicsRawBankTransactionsTable.bookingDate
var nexusSubscriber by EbicsSubscriberEntity referencedOn EbicsRawBankTransactionsTable.nexusSubscriber
var status by EbicsRawBankTransactionsTable.status
- var servicerCode by EbicsRawBankTransactionsTable.servicerCode
- var proprietaryCode by EbicsRawBankTransactionsTable.proprietaryCode
}
/**
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -677,8 +677,6 @@ 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']")
- servicerCode = camt53doc.pickStringNullable("//*[local-name()='Ntry']//*[local-name()='AcctSvcrRef']")
- proprietaryCode = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='BkTxCd']/*[local-name()='Prtry']/*[local-name()='Cd']")
bookingDate = camt53doc.pickString("//*[local-name()='BookgDt']//*[local-name()='Dt']")
nexusSubscriber = getSubscriberEntityFromId(id)
creditorName =
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
@@ -118,7 +118,10 @@ class Taler(app: Route) {
}
}
private fun getPaytoUri(name: String, iban: String, bic: String): String {
- return "payto://$iban/$bic?receiver-name=$name"
+ return "payto://iban/$iban/$bic?receiver-name=$name"
+ }
+ private fun getPaytoUri(iban: String, bic: String): String {
+ return "payto://iban/$iban/$bic"
}
private fun parseDate(date: String): DateTime {
return DateTime.parse(date, DateTimeFormat.forPattern("YYYY-MM-DD"))
@@ -235,8 +238,6 @@ class Taler(app: Route) {
counterpartBic = debtor.bic
bookingDate = DateTime.now().toZonedString()
status = "BOOK"
- servicerCode = "test-0"
- proprietaryCode = "test-0"
}
/** This payment is "valid by default" and will be returned
* as soon as the exchange will ask for new payments. */
@@ -293,23 +294,18 @@ class Taler(app: Route) {
val id = expectId(call.parameters["id"])
// first find highest ID value of already processed rows.
transaction {
+ val subscriberAccount = getBankAccountsInfoFromId(id).first()
+
/**
- * The following query avoids to put a "taler processed" flag-column into
- * the raw ebics transactions table. Such table should not contain taler-related
- * information.
- *
- * This latestId value points at the latest id in the _raw transactions table_
- * that was last processed here. Note, the "row_id" value that the exchange
- * will get along each history element will be the id in the _crunched entries table_.
+ * Search for fresh INCOMING transactions having a BOOK status. Cancellations and
+ * other status changes will (1) be _appended_ to the payment history, and (2) be
+ * handled _independently_ another dedicated routine.
*/
- val latestId: Long = TalerIncomingPaymentEntity.all().sortedByDescending {
- it.payment.id
- }.firstOrNull()?.payment?.id?.value ?: -1
- val subscriberAccount = getBankAccountsInfoFromId(id).first()
- /* search for fresh transactions having the exchange IBAN in the creditor field. */
+ val latestIncomingPaymentId: Long = TalerIncomingPaymentEntity.getLast()
EbicsRawBankTransactionEntity.find {
EbicsRawBankTransactionsTable.creditorIban eq subscriberAccount.iban and
- (EbicsRawBankTransactionsTable.id.greater(latestId))
+ (EbicsRawBankTransactionsTable.status eq "BOOK") and
+ (EbicsRawBankTransactionsTable.id.greater(latestIncomingPaymentId))
}.forEach {
if (CryptoUtil.checkValidEddsaPublicKey(it.unstructuredRemittanceInformation)) {
TalerIncomingPaymentEntity.new {
@@ -323,6 +319,19 @@ class Taler(app: Route) {
}
}
}
+
+ /**
+ * Search for fresh OUTGOING transactions acknowledged by the bank. As well
+ * searching only for BOOKed transactions, even though status changes should
+ * be really unexpected here.
+ */
+ val latestOutgoingPaymentId = TalerRequestedPaymentEntity.getLast()
+ EbicsRawBankTransactionEntity.find {
+ EbicsRawBankTransactionsTable.id greater latestOutgoingPaymentId and
+ (EbicsRawBankTransactionsTable.status eq "BOOK")
+ }.forEach {
+
+ }
}
call.respondText (
"New raw payments Taler-processed",
@@ -332,7 +341,9 @@ class Taler(app: Route) {
return@post
}
/** Responds only with the payments that the EXCHANGE made. Typically to
- * merchants but possibly to refund invalid incoming payments. */
+ * merchants but possibly to refund invalid incoming payments. A payment is
+ * counted only if was once confirmed by the bank.
+ */
app.get("/taler/history/outgoing") {
/* sanitize URL arguments */
val subscriberId = authenticateRequest(call.request.headers["Authorization"])
@@ -342,19 +353,21 @@ class Taler(app: Route) {
/* retrieve database elements */
val history = TalerOutgoingHistory()
transaction {
- /** Retrieve all the outgoing payments from the _raw transactions table_ */
- val subscriberBankAccount = getBankAccountsInfoFromId(subscriberId)
- EbicsRawBankTransactionEntity.find {
- EbicsRawBankTransactionsTable.debitorIban eq subscriberBankAccount.first().iban and startCmpOp
+ /** Retrieve all the outgoing payments from the _clean Taler outgoing table_ */
+ val subscriberBankAccount = getBankAccountsInfoFromId(subscriberId).first()
+ TalerRequestedPaymentEntity.find {
+ TalerRequestedPayments.rawConfirmed.isNotNull() and startCmpOp
}.orderTaler(delta).subList(0, abs(delta)).forEach {
history.outgoing_transactions.add(
TalerOutgoingBankTransaction(
row_id = it.id.value,
- amount = "${it.currency}:${it.amount}",
- wtid = it.unstructuredRemittanceInformation,
- date = parseDate(it.bookingDate).millis / 1000,
- credit_account = it.creditorIban,
- debit_account = it.debitorIban,
+ amount = it.amount,
+ wtid = it.wtid,
+ date = parseDate(it.rawConfirmed?.bookingDate ?: throw NexusError(
+ HttpStatusCode.InternalServerError, "Null value met after check, VERY strange.")
+ ).millis / 1000,
+ credit_account = it.creditAccount,
+ debit_account = getPaytoUri(subscriberBankAccount.iban, subscriberBankAccount.bankCode),
exchange_base_url = "FIXME-to-request-along-subscriber-registration"
)
)