commit 96bbb55757be2dcc7b0c7341ffc52d14fdacec36
parent 8c26eab27768ae507a5460770ace216fd43f882c
Author: MS <ms@taler.net>
Date: Tue, 29 Nov 2022 19:44:01 +0100
Avoid retrying invalid Pain.001.
Diffstat:
6 files changed, 39 insertions(+), 17 deletions(-)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -211,6 +211,7 @@ object PaymentInitiationsTable : LongIdTable() {
val creditorBic = text("creditorBic").nullable()
val creditorName = text("creditorName")
val submitted = bool("submitted").default(false)
+ var invalid = bool("invalid").nullable()
val messageId = text("messageId")
/**
@@ -234,6 +235,7 @@ class PaymentInitiationEntity(id: EntityID<Long>) : LongEntity(id) {
var creditorBic by PaymentInitiationsTable.creditorBic
var creditorName by PaymentInitiationsTable.creditorName
var submitted by PaymentInitiationsTable.submitted
+ var invalid by PaymentInitiationsTable.invalid
var paymentInformationId by PaymentInitiationsTable.paymentInformationId
var instructionId by PaymentInitiationsTable.instructionId
var messageId by PaymentInitiationsTable.messageId
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
@@ -84,21 +84,28 @@ suspend fun submitAllPaymentInitiations(httpClient: HttpClient, accountid: Strin
HttpStatusCode.NotFound,
"account not found"
)
+ /**
+ * Skip submitted and invalid preparations.
+ */
PaymentInitiationEntity.find {
- (PaymentInitiationsTable.submitted eq false) and (
- PaymentInitiationsTable.bankAccount eq account.id)
+ // Not submitted.
+ (PaymentInitiationsTable.submitted eq false) and
+ // From the correct bank account.
+ (PaymentInitiationsTable.bankAccount eq account.id)
}.forEach {
- // Filter out non EBICS.
+ if (it.invalid == true) return@forEach
val defaultBankConnectionId = it.bankAccount.defaultBankConnection?.id ?: throw NexusError(
HttpStatusCode.NotFound,
"Default bank connection not found. Can't submit Pain document"
)
+ // Rare, but filter out bank accounts without a bank connection.
val bankConnection = NexusBankConnectionEntity.findById(defaultBankConnectionId) ?: throw NexusError(
HttpStatusCode.InternalServerError,
"Bank connection '$defaultBankConnectionId' " +
"(pointed by bank account '${it.bankAccount.bankAccountName}')" +
" not found in the database."
)
+ // Filter out non EBICS.
if (bankConnection.type != "ebics") {
logger.info("Skipping non-implemented bank connection '${bankConnection.type}'")
return@forEach
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
@@ -44,8 +44,10 @@ import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.statements.api.ExposedBlob
+import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
import tech.libeufin.nexus.*
+import tech.libeufin.nexus.bankaccount.getPaymentInitiation
import tech.libeufin.nexus.iso20022.NexusPaymentInitiationData
import tech.libeufin.nexus.iso20022.createPain001document
import tech.libeufin.nexus.logger
@@ -531,9 +533,18 @@ class EbicsBankConnectionProtocol: BankConnectionProtocol {
)
logger.debug("Sending Pain.001: ${paymentInitiation.paymentInformationId}," +
" for payment: '${paymentInitiation.subject}'")
- if (!XMLUtil.validateFromString(painMessage)) throw NexusError(
- HttpStatusCode.InternalServerError, "Pain.001 message is invalid."
- )
+ if (!XMLUtil.validateFromString(painMessage)) {
+ logger.error("Pain.001 ${paymentInitiation.paymentInformationId}" +
+ " is invalid, not submitting it and flag as invalid.")
+ val payment = getPaymentInitiation(paymentInitiationId)
+ payment.invalid = true
+ // The following commit prevents the thrown error
+ // to lose the database transaction data.
+ TransactionManager.current().commit()
+ throw NexusError(
+ HttpStatusCode.InternalServerError, "Pain.001 message is invalid."
+ )
+ }
object {
val subscriberDetails = subscriberDetails
val painMessage = painMessage
@@ -546,12 +557,10 @@ class EbicsBankConnectionProtocol: BankConnectionProtocol {
r.painMessage.toByteArray(Charsets.UTF_8),
EbicsStandardOrderParams()
)
- // Mark the payment as submitted.
transaction {
- val paymentInitiation = PaymentInitiationEntity.findById(paymentInitiationId)
- ?: throw NexusError(HttpStatusCode.NotFound, "payment initiation not found")
- paymentInitiation.submitted = true
- paymentInitiation.submissionDate = LocalDateTime.now().millis()
+ val payment = getPaymentInitiation(paymentInitiationId)
+ payment.submitted = true
+ payment.submissionDate = LocalDateTime.now().millis()
}
}
diff --git a/nexus/src/test/kotlin/DownloadAndSubmit.kt b/nexus/src/test/kotlin/DownloadAndSubmit.kt
@@ -91,7 +91,7 @@ fun getCustomEbicsServer(r: EbicsResponses, endpoint: String = "/ebicsweb"): App
* and having had access to runTask and TaskSchedule, that
* are now 'private'.
*/
-@Ignore
+// @Ignore
class DownloadAndSubmit {
/**
* Instruct the server to return invalid CAMT content.
@@ -122,7 +122,7 @@ class DownloadAndSubmit {
level = FetchLevel.REPORT,
"foo"
),
- "mock-bank-account"
+ "foo"
)
}
}
@@ -147,7 +147,7 @@ class DownloadAndSubmit {
),
transaction {
NexusBankAccountEntity.findByName(
- "mock-bank-account"
+ "foo"
) ?: throw Exception("Test failed")
}
)
diff --git a/nexus/src/test/kotlin/MakeEnv.kt b/nexus/src/test/kotlin/MakeEnv.kt
@@ -86,7 +86,7 @@ fun prepNexusDb() {
bankAuthenticationPublicKey = ExposedBlob(bankKeys.auth.public.encoded)
}
val a = NexusBankAccountEntity.new {
- bankAccountName = "mock-bank-account"
+ bankAccountName = "foo"
iban = FOO_USER_IBAN
bankCode = "SANDBOXX"
defaultBankConnection = c
@@ -94,7 +94,7 @@ fun prepNexusDb() {
accountHolder = "foo"
}
val b = NexusBankAccountEntity.new {
- bankAccountName = "bar-bank-account"
+ bankAccountName = "bar"
iban = BAR_USER_IBAN
bankCode = "SANDBOXX"
defaultBankConnection = c
diff --git a/util/src/main/kotlin/XMLUtil.kt b/util/src/main/kotlin/XMLUtil.kt
@@ -252,7 +252,11 @@ class XMLUtil private constructor() {
try {
getEbicsValidator().validate(xmlDoc)
} catch (e: Exception) {
- e.printStackTrace()
+ /**
+ * Would be convenient to return also the error
+ * message to the caller, so that it can link it
+ * to a document ID in the logs.
+ */
logger.warn("Validation failed: ${e}")
return false
}