RegistrationTest.kt (35849B)
1 /* 2 * This file is part of LibEuFin. 3 * Copyright (C) 2024-2025 Taler Systems S.A. 4 5 * LibEuFin is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation; either version 3, or 8 * (at your option) any later version. 9 10 * LibEuFin is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General 13 * Public License for more details. 14 15 * You should have received a copy of the GNU Affero General Public 16 * License along with LibEuFin; see the file COPYING. If not, see 17 * <http://www.gnu.org/licenses/> 18 */ 19 20 import org.junit.Test 21 import tech.libeufin.common.* 22 import tech.libeufin.common.db.* 23 import tech.libeufin.nexus.* 24 import tech.libeufin.nexus.cli.* 25 import tech.libeufin.nexus.db.* 26 import tech.libeufin.nexus.iso20022.* 27 import tech.libeufin.ebics.* 28 import java.nio.file.Files 29 import java.time.Instant 30 import java.util.UUID 31 import kotlin.io.path.* 32 import kotlin.test.* 33 34 /** End-to-end test for XML file registration */ 35 class RegistrationTest { 36 37 /** Register batches of initiated payments for reconcciliation */ 38 suspend fun Database.batches(batches: Map<String, List<InitiatedPayment>>): List<PaymentBatch> { 39 val tmp = mutableListOf<PaymentBatch>() 40 for ((name, txs) in batches) { 41 for (tx in txs) { 42 initiated.create(tx) 43 } 44 this.initiated.batch(Instant.now(), name, false) 45 val (batch) = this.initiated.submittable() 46 this.initiated.batchSubmissionSuccess(batch.id, Instant.now(), name.replace("BATCH", "ORDER")) 47 tmp.add(batch) 48 } 49 return tmp 50 } 51 52 /** Register an XML sample into the database */ 53 suspend fun Database.register( 54 cfg: NexusConfig, 55 path: String, 56 doc: OrderDoc 57 ) { 58 registerFile(this, cfg, Files.newInputStream(Path(path)), doc) 59 } 60 61 /** Check database content */ 62 suspend fun Database.check( 63 status: Map<String, Pair<SubmissionState, Map<String, SubmissionState>>>, 64 incoming: List<IncomingPayment>, 65 outgoing: List<OutgoingPayment>, 66 bounced: List<OutgoingPayment> 67 ) { 68 // Check batch status 69 val batch_status = this.serializable( 70 """ 71 SELECT message_id, status FROM initiated_outgoing_batches ORDER BY initiated_outgoing_batch_id 72 """ 73 ) { 74 all { 75 Pair( 76 it.getString("message_id"), 77 it.getEnum<SubmissionState>("status") 78 ) 79 } 80 } 81 assertContentEquals(status.map { Pair(it.key, it.value.first) }, batch_status) 82 83 // Check transactions status 84 val batch_tx = this.serializable( 85 """ 86 SELECT message_id, end_to_end_id, initiated_outgoing_transactions.status 87 FROM initiated_outgoing_transactions 88 JOIN initiated_outgoing_batches USING (initiated_outgoing_batch_id) 89 ORDER BY initiated_outgoing_batch_id, initiated_outgoing_transaction_id 90 """ 91 ) { 92 all { 93 Triple( 94 it.getString("message_id"), 95 it.getString("end_to_end_id"), 96 it.getEnum<SubmissionState>("status") 97 ) 98 }.groupBy( 99 keySelector = { it.first }, 100 valueTransform = { Pair(it.second, it.third) } 101 ).mapValues { it.value.toMap() } 102 } 103 assertContentEquals(status.mapValues { it.value.second }.toList(), batch_tx.toList()) 104 105 // Check incoming transactions 106 val incoming_tx = this.serializable( 107 """ 108 SELECT 109 uetr 110 ,tx_id 111 ,acct_svcr_ref 112 ,(amount).val as amount_val 113 ,(amount).frac AS amount_frac 114 ,(credit_fee).val AS credit_fee_val 115 ,(credit_fee).frac AS credit_fee_frac 116 ,subject 117 ,execution_time 118 ,debit_payto 119 FROM incoming_transactions 120 ORDER BY incoming_transaction_id 121 """ 122 ) { 123 all { 124 IncomingPayment( 125 id = IncomingId( 126 it.getObject("uetr") as UUID?, 127 it.getString("tx_id"), 128 it.getString("acct_svcr_ref"), 129 ), 130 amount = it.getAmount("amount", this@check.currency), 131 creditFee = it.getAmount("credit_fee", this@check.currency).notZeroOrNull(), 132 subject = it.getString("subject"), 133 executionTime = it.getLong("execution_time").asInstant(), 134 debtor = it.getOptIbanPayto("debit_payto"), 135 ) 136 } 137 } 138 assertContentEquals(incoming, incoming_tx) 139 140 // Check outgoing transactions 141 val outgoing_tx = this.serializable( 142 """ 143 SELECT end_to_end_id 144 ,acct_svcr_ref 145 ,(amount).val as amount_val 146 ,(amount).frac AS amount_frac 147 ,(debit_fee).val AS debit_fee_val 148 ,(debit_fee).frac AS debit_fee_frac 149 ,subject 150 ,execution_time 151 ,credit_payto 152 FROM outgoing_transactions 153 ORDER BY outgoing_transaction_id 154 """ 155 ) { 156 all { 157 OutgoingPayment( 158 id = OutgoingId(null, it.getString("end_to_end_id"), it.getString("acct_svcr_ref")), 159 amount = it.getAmount("amount", this@check.currency), 160 debitFee = it.getAmount("debit_fee", this@check.currency).notZeroOrNull(), 161 subject = it.getString("subject"), 162 executionTime = it.getLong("execution_time").asInstant(), 163 creditor = it.getOptIbanPayto("credit_payto"), 164 ) 165 } 166 } 167 assertContentEquals(outgoing, outgoing_tx) 168 169 // Check outgoing transactions 170 val bounced_tx = this.serializable( 171 """ 172 SELECT end_to_end_id 173 ,(amount).val as amount_val 174 ,(amount).frac as amount_frac 175 ,subject 176 ,credit_payto 177 FROM initiated_outgoing_transactions 178 JOIN bounced_transactions USING (initiated_outgoing_transaction_id) 179 """ 180 ) { 181 all { 182 OutgoingPayment( 183 id = OutgoingId(null, null, null), 184 amount = it.getAmount("amount", this@check.currency), 185 subject = it.getString("subject"), 186 executionTime = Instant.EPOCH, 187 creditor = it.getOptIbanPayto("credit_payto"), 188 ) 189 } 190 } 191 assertContentEquals(bounced, bounced_tx) 192 } 193 194 @Test 195 fun pain001() = setup { db, cfg -> 196 val (batch) = db.batches(mapOf( 197 "MESSAGE_ID" to listOf( 198 genInitPay( 199 endToEndId = "TX_FIRST", 200 amount = "EUR:42", 201 subject = "Test 42", 202 ), 203 genInitPay( 204 endToEndId = "TX_SECOND", 205 amount = "EUR:5.11", 206 subject = "Test 5.11", 207 ), 208 genInitPay( 209 endToEndId = "TX_THIRD", 210 amount = "EUR:0.21", 211 subject = "Test 0.21", 212 ), 213 ), 214 )) 215 val msg = batchToPain001Msg(cfg.ebics.account, batch).copy(timestamp = dateToInstant("2024-09-09"),) 216 for (dialect in Dialect.entries) { 217 println(dialect) 218 assertEquals( 219 Path("sample/platform/${dialect}_pain001.xml").readText().replace("VERSION", VERSION), 220 createPain001(msg, dialect, false).asUtf8() 221 ) 222 } 223 } 224 225 /** HAC order id test */ 226 @Test 227 fun hac() = setup { db, cfg -> 228 db.batches(mapOf( 229 "BATCH_SUCCESS" to listOf( 230 genInitPay("BATCH_SUCCESS_0"), 231 genInitPay("BATCH_SUCCESS_1"), 232 ), 233 "BATCH_FAILURE" to listOf( 234 genInitPay("BATCH_FAILURE_0"), 235 genInitPay("BATCH_FAILURE_1"), 236 ) 237 )) 238 239 // Register HAC files 240 db.register(cfg, "sample/platform/hac.xml", OrderDoc.acknowledgement) 241 242 // Check state 243 db.check( 244 status = mapOf( 245 "BATCH_SUCCESS" to Pair(SubmissionState.success, mapOf( 246 "BATCH_SUCCESS_0" to SubmissionState.pending, 247 "BATCH_SUCCESS_1" to SubmissionState.pending, 248 )), 249 "BATCH_FAILURE" to Pair(SubmissionState.permanent_failure, mapOf( 250 "BATCH_FAILURE_0" to SubmissionState.permanent_failure, 251 "BATCH_FAILURE_1" to SubmissionState.permanent_failure, 252 )) 253 ), 254 incoming = emptyList(), 255 outgoing = emptyList(), 256 bounced = emptyList() 257 ) 258 } 259 260 /** CreditSuisse dialect test */ 261 @Test 262 fun cs() = setup { db, cfg -> 263 db.batches(mapOf( 264 "05BD4C5B4A2649B5B08F6EF6A31F197A" to listOf( 265 genInitPay("AQCXNCPWD8PHW5JTN65Y5XTF7R"), 266 genInitPay("EE9SX76FC5YSC657EK3GMVZ9TC"), 267 genInitPay("V5B3MXPEWES9VQW1JDRD6VAET4"), 268 genInitPay("M9NGRCAC1FBX3ENX3XEDEPJ2JW"), 269 ), 270 )) 271 272 // Register pain files 273 db.register(cfg, "sample/platform/pain002_part.xml", OrderDoc.status) 274 275 // Check state 276 db.check( 277 status = mapOf( 278 "05BD4C5B4A2649B5B08F6EF6A31F197A" to Pair(SubmissionState.pending, mapOf( 279 "AQCXNCPWD8PHW5JTN65Y5XTF7R" to SubmissionState.permanent_failure, 280 "EE9SX76FC5YSC657EK3GMVZ9TC" to SubmissionState.permanent_failure, 281 "V5B3MXPEWES9VQW1JDRD6VAET4" to SubmissionState.permanent_failure, 282 "M9NGRCAC1FBX3ENX3XEDEPJ2JW" to SubmissionState.pending, 283 )), 284 ), 285 incoming = emptyList(), 286 outgoing = emptyList(), 287 bounced = emptyList() 288 ) 289 } 290 291 /** Valiant dialect test */ 292 @Test 293 fun valiant() = setup("valiant.conf") { db, cfg -> 294 db.batches(mapOf( 295 "MJDJO2BDDBL7YSL2P96SXHG3TQZEZQD26L" to listOf( 296 genInitPay("4UWWIDGTEIGDU6Z721QE95PYJSIEA48PYE"), 297 ), 298 "5HIS3433VVIBAANHW3GX9DR1AXRS43KZ4U" to listOf( 299 genInitPay("SKMU2891PAAYBDW22DBWX2W7KTFZ1CDFO8"), 300 genInitPay("RC9YD301NZ17YKD6WDWLNOROFHIIN29VJN"), 301 genInitPay("GKDGTHLB82X6XVHBJIJ1CK8MEGU9XJ2EL7"), 302 genInitPay("PXCH2VVVTXEXBVDWICP23HZ4NV0H2CWW28"), 303 ), 304 "X166701F6RV59LP71RVWVIW9SV2AFZYLG4" to listOf( 305 genInitPay("R48UBIIB7B4LX0DMVOSI0ZTJWMMG8FMNKX"), 306 ), 307 "OLAMDPI6YPMNRZHQ5PQ6JCVUQV2AN5NW6P" to listOf( 308 genInitPay("TU2WJ54DR9Z6HT5VE494BNH4EXUSM0DRF7"), 309 ), 310 "6OZN5T9W7MK6BIZYE01E62NHGP5JLMUD4X" to listOf( 311 genInitPay("02WDIX4J90Z1M1WNFHLNSXY59SHXQTQCMQ"), 312 genInitPay("XAP5L7HVWPLCEMECU4GZK6GKUPBL0TD13Y"), 313 genInitPay("GM8I8GIETR72LP6CFBGRBUDKNO2CEQBGOE") 314 ), 315 )) 316 317 // Register camt files 318 db.register(cfg, "sample/platform/valiant_camt052.xml", OrderDoc.report) 319 320 // Check state 321 db.check( 322 status = mapOf( 323 "MJDJO2BDDBL7YSL2P96SXHG3TQZEZQD26L" to Pair(SubmissionState.success, mapOf( 324 "4UWWIDGTEIGDU6Z721QE95PYJSIEA48PYE" to SubmissionState.success 325 )), 326 "5HIS3433VVIBAANHW3GX9DR1AXRS43KZ4U" to Pair(SubmissionState.success, mapOf( 327 "SKMU2891PAAYBDW22DBWX2W7KTFZ1CDFO8" to SubmissionState.success, 328 "RC9YD301NZ17YKD6WDWLNOROFHIIN29VJN" to SubmissionState.success, 329 "GKDGTHLB82X6XVHBJIJ1CK8MEGU9XJ2EL7" to SubmissionState.success, 330 "PXCH2VVVTXEXBVDWICP23HZ4NV0H2CWW28" to SubmissionState.success 331 )), 332 "X166701F6RV59LP71RVWVIW9SV2AFZYLG4" to Pair(SubmissionState.success, mapOf( 333 "R48UBIIB7B4LX0DMVOSI0ZTJWMMG8FMNKX" to SubmissionState.late_failure 334 )), 335 "OLAMDPI6YPMNRZHQ5PQ6JCVUQV2AN5NW6P" to Pair(SubmissionState.success, mapOf( 336 "TU2WJ54DR9Z6HT5VE494BNH4EXUSM0DRF7" to SubmissionState.success 337 )), 338 "6OZN5T9W7MK6BIZYE01E62NHGP5JLMUD4X" to Pair(SubmissionState.success, mapOf( 339 "02WDIX4J90Z1M1WNFHLNSXY59SHXQTQCMQ" to SubmissionState.success, 340 "XAP5L7HVWPLCEMECU4GZK6GKUPBL0TD13Y" to SubmissionState.late_failure, 341 "GM8I8GIETR72LP6CFBGRBUDKNO2CEQBGOE" to SubmissionState.success 342 )), 343 ), 344 incoming = listOf( 345 IncomingPayment( 346 id = IncomingId(null, "51030655601.0001", "ZV20251030/514778/1"), 347 amount = TalerAmount("CHF:0.85"), 348 subject = "fun stuff", 349 executionTime = dateToInstant("2025-10-30"), 350 debtor = ibanPayto("CH7389144832588726658", "Grothoff Hans"), 351 ), 352 IncomingPayment( 353 id = IncomingId(null, "51030655601.0002", "ZV20251030/514779/1"), 354 amount = TalerAmount("CHF:0.95"), 355 subject = "Taler PC2MKG0B7CK32K1T7DP08P6E1B7FHB6HY6R Q0PT3VTPBPRPYM1B0", 356 executionTime = dateToInstant("2025-10-30"), 357 debtor = ibanPayto("CH7389144832588726658", "Grothoff Hans"), 358 ), 359 IncomingPayment( 360 id = IncomingId("7b76d488-05d5-44ab-9d77-31d4165ec158", "00204EQY370", "ZV20251118/685062/1"), 361 amount = TalerAmount("CHF:4.55"), 362 subject = "TEST", 363 executionTime = dateToInstant("2025-11-18"), 364 debtor = null 365 ), 366 ), 367 outgoing = listOf( 368 OutgoingPayment( 369 id = OutgoingId(null, "4UWWIDGTEIGDU6Z721QE95PYJSIEA48PYE", "ZV20251030/511372/1"), 370 amount = TalerAmount("CHF:0.1"), 371 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"), 372 subject = "single 2025-10-30T09:46:04.55293090 9Z", 373 executionTime = dateToInstant("2025-10-30") 374 ), 375 OutgoingPayment( 376 id = OutgoingId(null, "SKMU2891PAAYBDW22DBWX2W7KTFZ1CDFO8", "ZV20251030/511373/1"), 377 amount = TalerAmount("CHF:0.1"), 378 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"), 379 subject = "multi 0 2025-10-30T09:46:10.3877961 30Z", 380 executionTime = dateToInstant("2025-10-30") 381 ), 382 OutgoingPayment( 383 id = OutgoingId(null, "RC9YD301NZ17YKD6WDWLNOROFHIIN29VJN", "ZV20251030/511373/2"), 384 amount = TalerAmount("CHF:0.11"), 385 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"), 386 subject = "multi 1 2025-10-30T09:46:10.3877961 30Z", 387 executionTime = dateToInstant("2025-10-30") 388 ), 389 OutgoingPayment( 390 id = OutgoingId(null, "GKDGTHLB82X6XVHBJIJ1CK8MEGU9XJ2EL7", "ZV20251030/511373/3"), 391 amount = TalerAmount("CHF:0.12"), 392 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"), 393 subject = "multi 2 2025-10-30T09:46:10.3877961 30Z", 394 executionTime = dateToInstant("2025-10-30") 395 ), 396 OutgoingPayment( 397 id = OutgoingId(null, "PXCH2VVVTXEXBVDWICP23HZ4NV0H2CWW28", "ZV20251030/511373/4"), 398 amount = TalerAmount("CHF:0.13"), 399 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"), 400 subject = "multi 3 2025-10-30T09:46:10.3877961 30Z", 401 executionTime = dateToInstant("2025-10-30") 402 ), 403 OutgoingPayment( 404 id = OutgoingId(null, "R48UBIIB7B4LX0DMVOSI0ZTJWMMG8FMNKX", "ZV20251030/524078/1"), 405 amount = TalerAmount("CHF:0.21"), 406 creditor = ibanPayto("CH6208704048981247126", "John Smith"), 407 subject = "bad name 2025-10-30T12:03:24.997478 811Z", 408 executionTime = dateToInstant("2025-10-30") 409 ), 410 OutgoingPayment( 411 id = OutgoingId(null, "02WDIX4J90Z1M1WNFHLNSXY59SHXQTQCMQ", "ZV20251030/524079/1"), 412 amount = TalerAmount("CHF:0.1"), 413 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"), 414 subject = "single 2025-10-30T12:04:00.37042083 6Z", 415 executionTime = dateToInstant("2025-10-30") 416 ), 417 OutgoingPayment( 418 id = OutgoingId(null, "XAP5L7HVWPLCEMECU4GZK6GKUPBL0TD13Y", "ZV20251030/524079/2"), 419 amount = TalerAmount("CHF:0.21"), 420 creditor = ibanPayto("CH6208704048981247126", "John Smith"), 421 subject = "bad name 2025-10-30T12:03:53.042190 686Z", 422 executionTime = dateToInstant("2025-10-30") 423 ), 424 OutgoingPayment( 425 id = OutgoingId(null, "TU2WJ54DR9Z6HT5VE494BNH4EXUSM0DRF7", "ZV20251030/524077/1"), 426 amount = TalerAmount("CHF:0.23"), 427 debitFee = TalerAmount("CHF:5"), 428 creditor = ibanPayto("DE48330605920000686018", "Christian Grothoff"), 429 subject = "foreign iban 2025-10-30T12:03:44.0972 63765Z", 430 executionTime = dateToInstant("2025-10-30") 431 ), 432 OutgoingPayment( 433 id = OutgoingId(null, "GM8I8GIETR72LP6CFBGRBUDKNO2CEQBGOE", "ZV20251030/524080/1"), 434 amount = TalerAmount("CHF:0.23"), 435 debitFee = TalerAmount("CHF:5"), 436 creditor = ibanPayto("DE48330605920000686018", "Christian Grothoff"), 437 subject = "foreign iban 2025-10-30T12:03:58.0046 73747Z", 438 executionTime = dateToInstant("2025-10-30") 439 ), 440 ), 441 bounced = listOf( 442 OutgoingPayment( 443 id = OutgoingId(null, null, null), 444 amount = TalerAmount("CHF:0.85"), 445 subject = "bounce 51030655601.0001: missing reserve public key", 446 executionTime = Instant.EPOCH, 447 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 448 ), 449 ) 450 ) 451 } 452 453 /** GLS dialect test */ 454 @Test 455 fun gls() = setup("gls.conf") { db, cfg -> 456 db.batches(mapOf( 457 "COMPAT_SUCCESS" to listOf( 458 genInitPay("COMPAT_SUCCESS") 459 ), 460 "COMPAT_FAILURE" to listOf( 461 genInitPay("COMPAT_FAILURE") 462 ), 463 "BATCH_SINGLE_SUCCESS" to listOf( 464 genInitPay("FD622SMXKT5QWSAHDY0H8NYG3G"), 465 ), 466 // JEYMR3OYZTFM7505OWWENFPAH53LNOWJHS 467 "BATCH_SINGLE_FAILURE" to listOf( 468 genInitPay("DAFC3NEE4T48WVC560T76ABA2C"), 469 ), 470 "BATCH_SINGLE_RETURN" to listOf( 471 genInitPay("KLJJ28S1LVNDK1R2HCHLN884M7EKM5XGM5"), 472 ), 473 "BATCH_MANY_SUCCESS" to listOf( 474 genInitPay("IVMIGCUIE7Q7VOF73R8GU3KGRYBZPAYC5V"), 475 genInitPay("CDFN7I4FVIZ848DGDQ35DZ2K49H9EWXGAW"), 476 genInitPay("35M1268GW5ZFHS5JCB41UKDQNPMD40T849"), 477 genInitPay("HPOMV7A4E3P1TK9UZJS1WTM94A9V3X2SR1"), 478 ), 479 "BATCH_MANY_PART" to listOf( 480 genInitPay("27SK3166EG36SJ7VP7VFYP0MW8"), 481 genInitPay("KGTDBASWTJ6JM89WXD3Q5KFQC4"), 482 genInitPay("8XK8Z7RAX224FGWK832FD40GYC"), 483 ), 484 // ZQOOPJC1DYBP52X119YGO6WMXU6NWIDPJK 485 "BATCH_MANY_FAILURE" to listOf( 486 genInitPay("4XTPKWE4A9V90PRQJCT8Z3MQZ8"), 487 genInitPay("3VZZHVYJ6XP2SNPKWF4D4YVHNG"), 488 ) 489 )) 490 491 // Register camt files 492 db.register(cfg, "sample/platform/gls_camt052.xml", OrderDoc.report) 493 db.register(cfg, "sample/platform/gls_camt053.xml", OrderDoc.statement) 494 // TODO camt054 with missing id before and after 495 496 // Check state 497 db.check( 498 status = mapOf( 499 "COMPAT_SUCCESS" to Pair(SubmissionState.success, mapOf( 500 "COMPAT_SUCCESS" to SubmissionState.success 501 )), 502 "COMPAT_FAILURE" to Pair(SubmissionState.pending, mapOf( 503 "COMPAT_FAILURE" to SubmissionState.permanent_failure 504 )), 505 "BATCH_SINGLE_SUCCESS" to Pair(SubmissionState.success, mapOf( 506 "FD622SMXKT5QWSAHDY0H8NYG3G" to SubmissionState.success 507 )), 508 "BATCH_SINGLE_FAILURE" to Pair(SubmissionState.pending, mapOf( // TODO success 509 "DAFC3NEE4T48WVC560T76ABA2C" to SubmissionState.pending, // TODO failure 510 )), 511 "BATCH_SINGLE_RETURN" to Pair(SubmissionState.success, mapOf( 512 "KLJJ28S1LVNDK1R2HCHLN884M7EKM5XGM5" to SubmissionState.late_failure, 513 )), 514 "BATCH_MANY_SUCCESS" to Pair(SubmissionState.success, mapOf( 515 "IVMIGCUIE7Q7VOF73R8GU3KGRYBZPAYC5V" to SubmissionState.success, 516 "CDFN7I4FVIZ848DGDQ35DZ2K49H9EWXGAW" to SubmissionState.success, 517 "35M1268GW5ZFHS5JCB41UKDQNPMD40T849" to SubmissionState.success, 518 "HPOMV7A4E3P1TK9UZJS1WTM94A9V3X2SR1" to SubmissionState.success, 519 )), 520 "BATCH_MANY_PART" to Pair(SubmissionState.success, mapOf( 521 "27SK3166EG36SJ7VP7VFYP0MW8" to SubmissionState.success, 522 "KGTDBASWTJ6JM89WXD3Q5KFQC4" to SubmissionState.permanent_failure, 523 "8XK8Z7RAX224FGWK832FD40GYC" to SubmissionState.permanent_failure, 524 )), 525 "BATCH_MANY_FAILURE" to Pair(SubmissionState.pending, mapOf( // TODO success 526 "4XTPKWE4A9V90PRQJCT8Z3MQZ8" to SubmissionState.pending, // TODO failure 527 "3VZZHVYJ6XP2SNPKWF4D4YVHNG" to SubmissionState.pending, // TODO failure 528 )) 529 ), 530 incoming = listOf( 531 IncomingPayment( 532 id = IncomingId(null, "BYLADEM1WOR-G2910276709458A2", "2024041210041357000"), 533 amount = TalerAmount("EUR:3"), 534 subject = "Taler FJDQ7W6G7NWX4H9M1MKA12090FRC9K7DA6N0FANDZZFXTR6QHX5G Test.,-", 535 executionTime = dateToInstant("2024-04-12"), 536 debtor = ibanPayto("DE84500105177118117964", "John Smith") 537 ), 538 ), 539 outgoing = listOf( 540 OutgoingPayment( 541 id = OutgoingId(null, "COMPAT_SUCCESS", "2024041801514102000"), 542 amount = TalerAmount("EUR:2"), 543 subject = "TestABC123", 544 executionTime = dateToInstant("2024-04-18"), 545 creditor = ibanPayto("DE20500105172419259181", "John Smith") 546 ), 547 OutgoingPayment( 548 id = OutgoingId(null, "FD622SMXKT5QWSAHDY0H8NYG3G", "2024090216552232000"), 549 amount = TalerAmount("EUR:1.1"), 550 subject = "single 2024-09-02T14:29:52.875253314Z", 551 executionTime = dateToInstant("2024-09-02"), 552 creditor = ibanPayto("DE89500105173198527518", "Grothoff Hans") 553 ), 554 OutgoingPayment( 555 id = OutgoingId(null, "YF5QBARGQ0MNY0VK59S477VDG4", "2024041810552821000"), 556 amount = TalerAmount("EUR:1.1"), 557 subject = "Simple tx", 558 executionTime = dateToInstant("2024-04-18"), 559 creditor = ibanPayto("DE20500105172419259181", "John Smith") 560 ), 561 OutgoingPayment( 562 id = OutgoingId(null, "IVMIGCUIE7Q7VOF73R8GU3KGRYBZPAYC5V", null), 563 amount = TalerAmount("EUR:44"), 564 subject = "init payment", 565 executionTime = dateToInstant("2024-09-20"), 566 creditor = ibanPayto("CH4189144589712575493", "Test") 567 ), 568 OutgoingPayment( 569 id = OutgoingId(null, "CDFN7I4FVIZ848DGDQ35DZ2K49H9EWXGAW", null), 570 amount = TalerAmount("EUR:44"), 571 subject = "init payment", 572 executionTime = dateToInstant("2024-09-20"), 573 creditor = ibanPayto("CH4189144589712575493", "Test") 574 ), 575 OutgoingPayment( 576 id = OutgoingId(null, "35M1268GW5ZFHS5JCB41UKDQNPMD40T849", null), 577 amount = TalerAmount("EUR:44"), 578 subject = "init payment", 579 executionTime = dateToInstant("2024-09-20"), 580 creditor = ibanPayto("CH4189144589712575493", "Test") 581 ), 582 OutgoingPayment( 583 id = OutgoingId(null, "HPOMV7A4E3P1TK9UZJS1WTM94A9V3X2SR1", null), 584 amount = TalerAmount("EUR:44"), 585 subject = "init payment", 586 executionTime = dateToInstant("2024-09-20"), 587 creditor = ibanPayto("CH4189144589712575493", "Test") 588 ), 589 OutgoingPayment( 590 id = OutgoingId(null, "KLJJ28S1LVNDK1R2HCHLN884M7EKM5XGM5", "2024092100252498000"), 591 amount = TalerAmount("EUR:0.42"), 592 subject = "This should fail because bad iban", 593 executionTime = dateToInstant("2024-09-23"), 594 creditor = ibanPayto("DE18500105173385245163", "John Smith") 595 ), 596 OutgoingPayment( 597 id = OutgoingId(null, "27SK3166EG36SJ7VP7VFYP0MW8", null), 598 amount = TalerAmount("EUR:44"), 599 subject = "init payment", 600 executionTime = dateToInstant("2024-09-04"), 601 creditor = ibanPayto("CH4189144589712575493", "Test") 602 ), 603 ), 604 bounced = emptyList() 605 ) 606 } 607 608 /** Maerki Baumann dialect test */ 609 @Test 610 fun maerki_baumann() = setup("maerki_baumann.conf") { db, cfg -> 611 db.batches(mapOf( 612 "BATCH_SINGLE_REPORTING" to listOf( 613 genInitPay("5IBJZOWESQGPCSOXSNNBBY49ZURI5W7Q4H"), 614 genInitPay("XZ15UR0XU52QWI7Q4XB88EDS44PLH7DYXH"), 615 genInitPay("A09R35EW0359SZ51464E7TC37A0P2CBK04"), 616 genInitPay("UYXZ78LE9KAIMBY6UNXFYT1K8KNY8VLZLT"), 617 ), 618 )) 619 620 // Register camt files 621 db.register(cfg, "sample/platform/maerki_baumann_camt053.xml", OrderDoc.statement) 622 623 // Check state 624 db.check( 625 status = mapOf( 626 "BATCH_SINGLE_REPORTING" to Pair(SubmissionState.success, mapOf( 627 "5IBJZOWESQGPCSOXSNNBBY49ZURI5W7Q4H" to SubmissionState.success, 628 "XZ15UR0XU52QWI7Q4XB88EDS44PLH7DYXH" to SubmissionState.success, 629 "A09R35EW0359SZ51464E7TC37A0P2CBK04" to SubmissionState.success, 630 "UYXZ78LE9KAIMBY6UNXFYT1K8KNY8VLZLT" to SubmissionState.success, 631 )), 632 ), 633 incoming = listOf( 634 IncomingPayment( 635 id = IncomingId("adbe4a5a-6cea-4263-b259-8ab964561a32", "41103099704.0002", "ZV20241104/765446/1"), 636 amount = TalerAmount("CHF:1"), 637 creditFee = TalerAmount("CHF:0.2"), 638 subject = "SFHP6H24C16A5J05Q3FJW2XN1PB3EK70ZPY 5SJ30ADGY68FWN68G", 639 executionTime = dateToInstant("2024-11-04"), 640 debtor = ibanPayto("CH7389144832588726658", "Mr Test") 641 ), 642 IncomingPayment( 643 id = IncomingId("7371795e-62fa-42dd-93b7-da89cc120faa", "41103099704.0003", "ZV20241104/765447/1"), 644 amount = TalerAmount("CHF:1"), 645 creditFee = TalerAmount("CHF:0.2"), 646 subject = "Random subject", 647 executionTime = dateToInstant("2024-11-04"), 648 debtor = ibanPayto("CH7389144832588726658", "Mr Test") 649 ), 650 IncomingPayment( 651 id = IncomingId(null, "50523424675.0001", "ZV20250523/851716/1"), 652 amount = TalerAmount("CHF:0.5"), 653 creditFee = TalerAmount("CHF:0.2"), 654 subject = null, 655 executionTime = dateToInstant("2025-05-23"), 656 debtor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 657 ), 658 IncomingPayment( 659 id = IncomingId("f203fbb4-6e13-4c78-9b2a-d852fea6374a", "41202060702.0001", "ZV20241202/778108/1"), 660 amount = TalerAmount("CHF:0.05"), 661 creditFee = TalerAmount("CHF:0.2"), 662 subject = "mini", 663 executionTime = dateToInstant("2024-12-02"), 664 debtor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 665 ), 666 IncomingPayment( 667 id = IncomingId("81b0d8c6-a677-4577-b75e-a639dcc03681", "41120636093.0001", "ZV20241121/773118/1"), 668 amount = TalerAmount("CHF:0.1"), 669 creditFee = TalerAmount("CHF:0.2"), 670 subject = "small transfer test", 671 executionTime = dateToInstant("2024-11-21"), 672 debtor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 673 ), 674 IncomingPayment( 675 id = IncomingId(null, null, "ZV20250114/796191/1"), 676 amount = TalerAmount("CHF:3003"), 677 subject = "Fix bad payment by MB.", 678 executionTime = dateToInstant("2025-01-27"), 679 debtor = null 680 ), 681 IncomingPayment( 682 id = IncomingId(null, "F000787951230001", "ZV20250526/852733/1"), 683 amount = TalerAmount("CHF:1.38"), 684 creditFee = TalerAmount("CHF:0.2"), 685 subject = "Taler XT3D9MADR4V85JBWX47SMJFDQD2FDZDHHPH8R25YDG1KNVTSEH6G", 686 executionTime = dateToInstant("2025-05-26"), 687 debtor = ibanPayto("DE20500105172419259181", "Mr German") 688 ), 689 ), 690 outgoing = listOf( 691 OutgoingPayment( 692 id = OutgoingId(null, "5IBJZOWESQGPCSOXSNNBBY49ZURI5W7Q4H", "ZV20241121/773541/1"), 693 amount = TalerAmount("CHF:0.1"), 694 subject = "multi 0 2024-11-21T15:21:59.8859234 63Z", 695 executionTime = dateToInstant("2024-11-27"), 696 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 697 ), 698 OutgoingPayment( 699 id = OutgoingId(null, "XZ15UR0XU52QWI7Q4XB88EDS44PLH7DYXH", "ZV20241121/773541/4"), 700 amount = TalerAmount("CHF:0.13"), 701 subject = "multi 3 2024-11-21T15:21:59.8859234 63Z", 702 executionTime = dateToInstant("2024-11-27"), 703 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 704 ), 705 OutgoingPayment( 706 id = OutgoingId(null, "A09R35EW0359SZ51464E7TC37A0P2CBK04", "ZV20241121/773541/3"), 707 amount = TalerAmount("CHF:0.12"), 708 subject = "multi 2 2024-11-21T15:21:59.8859234 63Z", 709 executionTime = dateToInstant("2024-11-27"), 710 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 711 ), 712 OutgoingPayment( 713 id = OutgoingId(null, "UYXZ78LE9KAIMBY6UNXFYT1K8KNY8VLZLT", "ZV20241121/773541/2"), 714 amount = TalerAmount("CHF:0.11"), 715 subject = "multi 1 2024-11-21T15:21:59.8859234 63Z", 716 executionTime = dateToInstant("2024-11-27"), 717 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 718 ), 719 OutgoingPayment( 720 id = OutgoingId(null, null, "GB20241220/205792/1"), 721 amount = TalerAmount("CHF:3000"), 722 subject = null, 723 executionTime = dateToInstant("2024-12-20"), 724 creditor = null 725 ), 726 ), 727 bounced = listOf( 728 OutgoingPayment( 729 id = OutgoingId(null, null, null), 730 amount = TalerAmount("CHF:1"), 731 subject = "bounce 7371795e-62fa-42dd-93b7-da89cc120faa: missing reserve public key", 732 executionTime = Instant.EPOCH, 733 creditor = ibanPayto("CH7389144832588726658", "Mr Test") 734 ), 735 OutgoingPayment( 736 id = OutgoingId(null, null, null), 737 amount = TalerAmount("CHF:0.5"), 738 subject = "bounce 50523424675.0001: missing subject", 739 executionTime = Instant.EPOCH, 740 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 741 ), 742 OutgoingPayment( 743 id = OutgoingId(null, null, null), 744 amount = TalerAmount("CHF:0.05"), 745 subject = "bounce f203fbb4-6e13-4c78-9b2a-d852fea6374a: missing reserve public key", 746 executionTime = Instant.EPOCH, 747 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 748 ), 749 OutgoingPayment( 750 id = OutgoingId(null, null, null), 751 amount = TalerAmount("CHF:0.1"), 752 subject = "bounce 81b0d8c6-a677-4577-b75e-a639dcc03681: missing reserve public key", 753 executionTime = Instant.EPOCH, 754 creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans") 755 ), 756 OutgoingPayment( 757 id = OutgoingId(null, null, null), 758 amount = TalerAmount("CHF:1.38"), 759 subject = "bounce F000787951230001: restricted account", 760 executionTime = Instant.EPOCH, 761 creditor = ibanPayto("DE20500105172419259181", "Mr German") 762 ) 763 ) 764 ) 765 } 766 }