1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
package tech.libeufin.nexus
import io.ktor.client.*
import io.ktor.http.*
import org.jetbrains.exposed.sql.transactions.transaction
import tech.libeufin.nexus.iso20022.TransactionDetails
import tech.libeufin.nexus.server.PermissionQuery
import tech.libeufin.nexus.server.expectNonNull
import tech.libeufin.nexus.server.expectUrlParameter
import tech.libeufin.util.EbicsProtocolError
import kotlin.math.abs
import kotlin.math.min
import io.ktor.content.TextContent
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import tech.libeufin.util.buildIbanPaytoUri
data class AnastasisIncomingBankTransaction(
val row_id: Long,
val date: GnunetTimestamp, // timestamp
val amount: String,
val credit_account: String, // payto form,
val debit_account: String,
val subject: String
)
fun anastasisFilter(payment: NexusBankTransactionEntity, txDtls: TransactionDetails) {
val debtorName = txDtls.debtor?.name
if (debtorName == null) {
logger.warn("empty debtor name")
return
}
val debtorAcct = txDtls.debtorAccount
if (debtorAcct == null) {
// FIXME: Report payment, we can't even send it back
logger.warn("empty debtor account")
return
}
val debtorIban = debtorAcct.iban
if (debtorIban == null) {
// FIXME: Report payment, we can't even send it back
logger.warn("non-iban debtor account")
return
}
val debtorAgent = txDtls.debtorAgent
if (debtorAgent == null) {
// FIXME: Report payment, we can't even send it back
logger.warn("missing debtor agent")
return
}
if (debtorAgent.bic == null) {
logger.warn("Not allowing transactions missing the BIC. IBAN and name: ${debtorIban}, $debtorName")
return
}
AnastasisIncomingPaymentEntity.new {
this.payment = payment
subject = txDtls.unstructuredRemittanceInformation
timestampMs = System.currentTimeMillis()
debtorPaytoUri = buildIbanPaytoUri(
debtorIban, debtorAgent.bic, debtorName,
)
}
}
// Handle a /taler-wire-gateway/history/incoming request.
private suspend fun historyIncoming(call: ApplicationCall) {
val facadeId = expectNonNull(call.parameters["fcid"])
call.request.requirePermission(
PermissionQuery(
"facade",
facadeId,
"facade.anastasis.history"
)
)
val param = call.expectUrlParameter("delta")
val delta: Int = try { param.toInt() } catch (e: Exception) {
throw EbicsProtocolError(HttpStatusCode.BadRequest, "'${param}' is not Int")
}
val start: Long = handleStartArgument(call.request.queryParameters["start"], delta)
val history = object {
val incoming_transactions: MutableList<AnastasisIncomingBankTransaction> = mutableListOf()
}
val startCmpOp = getComparisonOperator(delta, start, AnastasisIncomingPaymentsTable)
transaction {
val orderedPayments = AnastasisIncomingPaymentEntity.find {
startCmpOp
}.orderTaler(delta) // Taler and Anastasis have same ordering policy. Fixme: find better function's name?
if (orderedPayments.isNotEmpty()) {
orderedPayments.subList(0, min(abs(delta), orderedPayments.size)).forEach {
history.incoming_transactions.add(
AnastasisIncomingBankTransaction(
// Rounded timestamp
date = GnunetTimestamp(it.timestampMs / 1000L),
row_id = it.id.value,
amount = "${it.payment.currency}:${it.payment.amount}",
subject = it.subject,
credit_account = buildIbanPaytoUri(
it.payment.bankAccount.iban,
it.payment.bankAccount.bankCode,
it.payment.bankAccount.accountHolder,
),
debit_account = it.debtorPaytoUri
)
)
}
}
}
return call.respond(TextContent(customConverter(history), ContentType.Application.Json))
}
fun anastasisFacadeRoutes(route: Route) {
route.get("/history/incoming") {
historyIncoming(call)
return@get
}
}
|