diff options
Diffstat (limited to 'nexus/src/main/kotlin/tech/libeufin/nexus/FacadeUtil.kt')
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/FacadeUtil.kt | 75 |
1 files changed, 46 insertions, 29 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/FacadeUtil.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/FacadeUtil.kt index 7fdd2c26..c908a828 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/FacadeUtil.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/FacadeUtil.kt @@ -2,47 +2,60 @@ package tech.libeufin.nexus import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.ktor.http.* +import org.jetbrains.exposed.dao.flushCache import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.transactions.transaction import tech.libeufin.nexus.iso20022.CamtBankAccountEntry import tech.libeufin.nexus.iso20022.CreditDebitIndicator -import tech.libeufin.nexus.iso20022.EntryStatus import tech.libeufin.nexus.iso20022.TransactionDetails +import tech.libeufin.nexus.server.NexusFacadeType - -/** - * Mainly used to resort the last processed transaction ID. - */ +// Mainly used to resort the last processed transaction ID. fun getFacadeState(fcid: String): FacadeStateEntity { - val facade = FacadeEntity.find { FacadesTable.facadeName eq fcid }.firstOrNull() ?: throw NexusError( - HttpStatusCode.NotFound, - "Could not find facade '${fcid}'" - ) - return FacadeStateEntity.find { - FacadeStateTable.facade eq facade.id.value - }.firstOrNull() ?: throw NexusError( - HttpStatusCode.NotFound, - "Could not find any state for facade: $fcid" - ) + return transaction { + val facade = FacadeEntity.find { + FacadesTable.facadeName eq fcid + }.firstOrNull() ?: throw NexusError( + HttpStatusCode.NotFound, + "Could not find facade '${fcid}'" + ) + FacadeStateEntity.find { + FacadeStateTable.facade eq facade.id.value + }.firstOrNull() ?: throw NexusError( + HttpStatusCode.NotFound, + "Could not find any state for facade: $fcid" + ) + } } fun getFacadeBankAccount(fcid: String): NexusBankAccountEntity { - val facadeState = getFacadeState(fcid) - return NexusBankAccountEntity.findByName(facadeState.bankAccount) ?: throw NexusError( - HttpStatusCode.NotFound, - "The facade: $fcid doesn't manage bank account: ${facadeState.bankAccount}" - ) + return transaction { + val facadeState = getFacadeState(fcid) + NexusBankAccountEntity.findByName(facadeState.bankAccount) ?: throw NexusError( + HttpStatusCode.NotFound, + "The facade: $fcid doesn't manage bank account: ${facadeState.bankAccount}" + ) + } } /** * Ingests transactions for those facades accounting for bankAccountId. + * 'incomingFilterCb' decides whether the facade accepts the payment; + * if not, refundCb prepares a refund. The 'txStatus' parameter decides + * at which state one transaction deserve to fuel Taler transactions. BOOK + * is conservative, and with some banks the delay can be significant. PNDG + * instead reacts faster, but risks that one transaction gets undone by the + * bank and never reach the BOOK state; this would mean a loss and/or admin + * burden. */ fun ingestFacadeTransactions( bankAccountId: String, - facadeType: String, + facadeType: NexusFacadeType, incomingFilterCb: ((NexusBankTransactionEntity, TransactionDetails) -> Unit)?, - refundCb: ((NexusBankAccountEntity, Long) -> Unit)? + refundCb: ((NexusBankAccountEntity, Long) -> Unit)?, + txStatus: EntryStatus = EntryStatus.BOOK ) { fun ingest(bankAccount: NexusBankAccountEntity, facade: FacadeEntity) { logger.debug( @@ -55,18 +68,21 @@ fun ingestFacadeTransactions( /** Those with "our" bank account involved */ NexusBankTransactionsTable.bankAccount eq bankAccount.id.value and /** Those that are booked */ - (NexusBankTransactionsTable.status eq EntryStatus.BOOK) and + (NexusBankTransactionsTable.status eq txStatus) and /** Those that came later than the latest processed payment */ (NexusBankTransactionsTable.id.greater(lastId)) }.orderBy(Pair(NexusBankTransactionsTable.id, SortOrder.ASC)).forEach { // Incoming payment. - logger.debug("Facade checks payment: ${it.transactionJson}") val tx = jacksonObjectMapper().readValue( - it.transactionJson, CamtBankAccountEntry::class.java + it.transactionJson, + CamtBankAccountEntry::class.java ) - val details = tx.batches?.get(0)?.batchTransactions?.get(0)?.details + /** + * Need transformer from "JSON tx" to TransactionDetails?. + */ + val details: TransactionDetails? = tx.batches?.get(0)?.batchTransactions?.get(0)?.details if (details == null) { - logger.warn("A void money movement made it through the ingestion: VERY strange") + logger.warn("A void money movement (${tx.accountServicerRef}) made it through the ingestion: VERY strange") return@forEach } when (tx.creditDebitIndicator) { @@ -90,16 +106,17 @@ fun ingestFacadeTransactions( ) } } catch (e: Exception) { - logger.warn("sending refund payment failed", e) + logger.warn("Sending refund payment failed: ${e.message}") } facadeState.highestSeenMessageSerialId = lastId } // invoke ingestion for all the facades transaction { - FacadeEntity.find { FacadesTable.type eq facadeType }.forEach { + FacadeEntity.find { FacadesTable.type eq facadeType.facadeType }.forEach { val facadeBankAccount = getFacadeBankAccount(it.facadeName) if (facadeBankAccount.bankAccountName == bankAccountId) ingest(facadeBankAccount, it) + flushCache() } } }
\ No newline at end of file |