@develit-services/bank 5.2.0 → 5.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/base.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { uuidv4, first, buildMultiFilterConditions as buildMultiFilterConditions$1, bankAccountMetadataSchema, structuredAddressSchema, workflowInstanceStatusSchema, develitWorker, createInternalError, action, service } from '@develit-io/backend-sdk';
2
- import { G as tables, g as accountInsertSchema, H as relations, J as initiateConnector, o as isProcessedStatus, p as isTerminalStatus, L as getNonTerminalPaymentRequestsQuery, x as toIncomingPayment, N as calculateCzechIban, j as assignAccount, u as toBatchedPayment, y as toPaymentRequestInsert, a as FinbricksClient, F as FINBRICKS_ENDPOINTS } from './shared/bank.Bkxo76q4.mjs';
2
+ import { G as tables, g as accountInsertSchema, H as relations, J as initiateConnector, o as isProcessedStatus, p as isTerminalStatus, L as getNonTerminalPaymentRequestsQuery, x as toIncomingPayment, N as calculateCzechIban, j as assignAccount, u as toBatchedPayment, y as toPaymentRequestInsert, a as FinbricksClient, F as FINBRICKS_ENDPOINTS } from './shared/bank.Cg2epnnD.mjs';
3
3
  import { eq, sql, and, like, asc, desc, inArray, gte, lte, isNull, count } from 'drizzle-orm';
4
4
  import { WorkerEntrypoint } from 'cloudflare:workers';
5
5
  import { drizzle } from 'drizzle-orm/d1';
@@ -9,7 +9,7 @@ import { I as INSTRUCTION_PRIORITIES, C as CHARGE_BEARERS, g as PAYMENT_TYPES, b
9
9
  import { CURRENCY_CODES } from '@develit-io/general-codes';
10
10
  import 'date-fns';
11
11
  import 'node:crypto';
12
- import { h as encrypt, d as createCredentialsResolver, e as updatePaymentRequestStatusCommand, a as getPaymentRequestsByBatchIdQuery, g as getBatchByIdQuery, u as upsertBatchCommand, i as importAesKey, f as createPaymentCommand, b as getAccountByIdQuery } from './shared/bank.BRD2WfnT.mjs';
12
+ import { h as encrypt, d as createCredentialsResolver, e as updatePaymentRequestStatusCommand, a as getPaymentRequestsByBatchIdQuery, g as getBatchByIdQuery, u as upsertBatchCommand, i as importAesKey, f as createPaymentCommand, b as getAccountByIdQuery } from './shared/bank.5RiBQTe0.mjs';
13
13
  import 'drizzle-orm/zod';
14
14
  import 'drizzle-orm/sqlite-core';
15
15
 
@@ -1,8 +1,7 @@
1
- export { aB as account, aC as accountCredentials, aD as batch, aE as ott, aF as payment, aG as paymentRequest } from '../shared/bank.BJTq7Qw9.cjs';
2
- import 'drizzle-orm';
1
+ export { aB as account, aC as accountCredentials, aD as batch, aE as ott, aF as payment, aG as paymentRequest } from '../shared/bank.CdkOsZE8.cjs';
3
2
  import 'drizzle-orm/sqlite-core';
3
+ import 'drizzle-orm';
4
4
  import '@develit-io/general-codes';
5
5
  import 'zod';
6
6
  import 'drizzle-orm/zod';
7
- import 'zod/v4/core';
8
7
  import '@develit-io/backend-sdk';
@@ -1,8 +1,7 @@
1
- export { aB as account, aC as accountCredentials, aD as batch, aE as ott, aF as payment, aG as paymentRequest } from '../shared/bank.BJTq7Qw9.mjs';
2
- import 'drizzle-orm';
1
+ export { aB as account, aC as accountCredentials, aD as batch, aE as ott, aF as payment, aG as paymentRequest } from '../shared/bank.CdkOsZE8.mjs';
3
2
  import 'drizzle-orm/sqlite-core';
3
+ import 'drizzle-orm';
4
4
  import '@develit-io/general-codes';
5
5
  import 'zod';
6
6
  import 'drizzle-orm/zod';
7
- import 'zod/v4/core';
8
7
  import '@develit-io/backend-sdk';
@@ -1,8 +1,7 @@
1
- export { aB as account, aC as accountCredentials, aD as batch, aE as ott, aF as payment, aG as paymentRequest } from '../shared/bank.BJTq7Qw9.js';
2
- import 'drizzle-orm';
1
+ export { aB as account, aC as accountCredentials, aD as batch, aE as ott, aF as payment, aG as paymentRequest } from '../shared/bank.CdkOsZE8.js';
3
2
  import 'drizzle-orm/sqlite-core';
3
+ import 'drizzle-orm';
4
4
  import '@develit-io/general-codes';
5
5
  import 'zod';
6
6
  import 'drizzle-orm/zod';
7
- import 'zod/v4/core';
8
7
  import '@develit-io/backend-sdk';
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  const backendSdk = require('@develit-io/backend-sdk');
4
- const bank = require('../shared/bank.BkctD4hR.cjs');
4
+ const bank = require('../shared/bank.R64Uwo7k.cjs');
5
5
  const batchLifecycle = require('../shared/bank.NF8bZBy0.cjs');
6
6
  const drizzleOrm = require('drizzle-orm');
7
- const credentialsResolver = require('../shared/bank.Bgz1SSIP.cjs');
7
+ const credentialsResolver = require('../shared/bank.d0MTVrcR.cjs');
8
8
  const cloudflare_workers = require('cloudflare:workers');
9
9
  const cloudflare_workflows = require('cloudflare:workflows');
10
10
  const d1 = require('drizzle-orm/d1');
@@ -315,16 +315,12 @@ async function pushToQueue(queue, message) {
315
315
  await queue.send(message, { contentType: "v8" });
316
316
  return;
317
317
  }
318
- const QUEUE_BATCH_SIZE = 100;
319
- for (let i = 0; i < message.length; i += QUEUE_BATCH_SIZE) {
320
- const chunk = message.slice(i, i + QUEUE_BATCH_SIZE);
321
- await queue.sendBatch(
322
- chunk.map((m) => ({
323
- body: m,
324
- contentType: "v8"
325
- }))
326
- );
327
- }
318
+ await queue.sendBatch(
319
+ message.map((m) => ({
320
+ body: m,
321
+ contentType: "v8"
322
+ }))
323
+ );
328
324
  }
329
325
  class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
330
326
  async run(event, step) {
@@ -350,13 +346,13 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
350
346
  if (!account.lastSyncAt) {
351
347
  throw new Error(`lastSyncedAt is not set for account: ${accountId}`);
352
348
  }
353
- await step.do(
354
- "fetch and process payments",
349
+ const payments = await step.do(
350
+ "fetch bank payments",
355
351
  {
356
352
  retries: { limit: 5, delay: "2 minutes", backoff: "exponential" },
357
- timeout: "5 minutes"
353
+ timeout: "2 minutes"
358
354
  },
359
- async (ctx) => {
355
+ async () => {
360
356
  try {
361
357
  logger.info("payments.fetch.started", {
362
358
  accountId,
@@ -379,192 +375,19 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
379
375
  }
380
376
  ]
381
377
  });
382
- const payments = await connector.getAllAccountPayments({
378
+ const result = await connector.getAllAccountPayments({
383
379
  account,
384
380
  filter: { dateFrom: account.lastSyncAt }
385
381
  });
386
382
  logger.info("payments.fetch.completed", {
387
383
  accountId,
388
384
  connectorKey: account.connectorKey,
389
- paymentsCount: payments.length
390
- });
391
- const paymentsToProcess = payments.filter(
392
- (p) => bank.isPaymentCompleted(p.parsed)
393
- );
394
- logger.info("payments.filtered.toProcess", {
395
- accountId,
396
- totalFetched: payments.length,
397
- paymentsToProcess: paymentsToProcess.length,
398
- sampleStatuses: payments.slice(0, 5).map((p) => ({
399
- bankRefId: p.parsed.bankRefId,
400
- status: p.parsed.status,
401
- isCompleted: bank.isPaymentCompleted(p.parsed)
402
- }))
403
- });
404
- const lastSyncBankRefIds = account.lastSyncMetadata?.lastSyncBankRefIds || [];
405
- const paymentsToInsert = paymentsToProcess.filter(
406
- (p) => !lastSyncBankRefIds.includes(p.parsed.bankRefId)
407
- );
408
- logger.info("payments.filtered.toInsert", {
409
- accountId,
410
- paymentsToProcess: paymentsToProcess.length,
411
- paymentsToInsert: paymentsToInsert.length,
412
- lastSyncBankRefIdsCount: lastSyncBankRefIds.length,
413
- sampleLastSyncBankRefIds: lastSyncBankRefIds.slice(0, 10),
414
- sampleToInsert: paymentsToInsert.slice(0, 5).map((p) => p.parsed.bankRefId)
415
- });
416
- const eventsToEmit = [];
417
- const bankRefIds = paymentsToInsert.map((p) => p.parsed.bankRefId).filter((id) => id != null);
418
- const BANK_REF_ID_CHUNK_SIZE = 90;
419
- const matchingRequests = [];
420
- for (let i = 0; i < bankRefIds.length; i += BANK_REF_ID_CHUNK_SIZE) {
421
- const chunkIds = bankRefIds.slice(i, i + BANK_REF_ID_CHUNK_SIZE);
422
- const rows = await db.select().from(bank.tables.paymentRequest).where(
423
- drizzleOrm.and(
424
- drizzleOrm.inArray(bank.tables.paymentRequest.bankRefId, chunkIds),
425
- drizzleOrm.eq(bank.tables.paymentRequest.accountId, account.id),
426
- drizzleOrm.eq(
427
- bank.tables.paymentRequest.connectorKey,
428
- account.connectorKey
429
- )
430
- )
431
- );
432
- matchingRequests.push(...rows);
433
- }
434
- const requestByBankRefId = Object.fromEntries(
435
- matchingRequests.map((r) => [r.bankRefId, r])
436
- );
437
- const enrichedPayments = paymentsToInsert.map((p) => {
438
- const req = p.parsed.bankRefId && requestByBankRefId[p.parsed.bankRefId] || null;
439
- if (!req) return p;
440
- return {
441
- ...p,
442
- parsed: {
443
- ...p.parsed,
444
- // queue-bus: transaction matching (DBU doesn't echo these in statements)
445
- vs: p.parsed.vs ?? req.vs,
446
- ss: p.parsed.ss ?? req.ss,
447
- ks: p.parsed.ks ?? req.ks,
448
- message: p.parsed.message ?? req.message,
449
- // queue-bus: party creation
450
- creditor: {
451
- ...p.parsed.creditor,
452
- holderName: p.parsed.creditor?.holderName ?? req.creditor?.holderName
453
- },
454
- debtor: {
455
- ...p.parsed.debtor,
456
- holderName: p.parsed.debtor?.holderName ?? req.debtor?.holderName
457
- }
458
- // NOT enriched: chargeBearer, instructionPriority, refId, batchId,
459
- // createdAt, address, swiftBic — no downstream consumer in payment table
460
- }
461
- };
462
- });
463
- const createCommands = enrichedPayments.map(
464
- (p) => credentialsResolver.createPaymentCommand(db, { payment: p.parsed }).command
465
- );
466
- logger.info("payments.commands.created", {
467
- accountId,
468
- createCommandsCount: createCommands.length,
469
- enrichedPaymentsCount: enrichedPayments.length
470
- });
471
- eventsToEmit.push(
472
- ...enrichedPayments.map((p) => ({
473
- eventType: "BANK_PAYMENT",
474
- eventSignal: "paymentFetched",
475
- bankPayment: p.parsed,
476
- metadata: {
477
- correlationId: p.parsed.correlationId,
478
- entityId: p.parsed.id,
479
- timestamp: /* @__PURE__ */ new Date()
480
- }
481
- }))
482
- );
483
- const lastSyncMetadata = {
484
- payments: payments.length,
485
- paymentsToProcess: paymentsToProcess.length,
486
- paymentsInserted: paymentsToInsert.length,
487
- lastSyncBankRefIds: paymentsToProcess.filter((p) => p.parsed.status === "BOOKED").map((p) => p.parsed.bankRefId),
488
- lastSyncPayments: lastSyncBankRefIds.length,
489
- eventsEmitted: eventsToEmit.length,
490
- iterationCount: getStepCount(ctx),
491
- workflowStartedAt
492
- };
493
- const updateLastSyncCommand = updateAccountLastSyncCommand(db, {
494
- accountId: account.id,
495
- lastSyncAt: now,
496
- lastSyncMetadata
497
- }).command;
498
- logger.info("payments.database.beforeBatch", {
499
- accountId,
500
- createCommandsCount: createCommands.length,
501
- willUseBatch: createCommands.length > 0
385
+ paymentsCount: result.length
502
386
  });
503
- if (createCommands.length) {
504
- logger.info("payments.database.batchStart", {
505
- accountId,
506
- totalCommands: createCommands.length + 1,
507
- paymentsToInsert: createCommands.length
508
- });
509
- const BATCH_CHUNK_SIZE = 90;
510
- let totalInserted = 0;
511
- for (let i = 0; i < createCommands.length; i += BATCH_CHUNK_SIZE) {
512
- const chunkCommands = createCommands.slice(
513
- i,
514
- i + BATCH_CHUNK_SIZE
515
- );
516
- const isLastChunk = i + BATCH_CHUNK_SIZE >= createCommands.length;
517
- const batchCommands = isLastChunk ? backendSdk.asNonEmpty([updateLastSyncCommand, ...chunkCommands]) : backendSdk.asNonEmpty(chunkCommands);
518
- logger.info("payments.database.batchChunk", {
519
- accountId,
520
- chunkIndex: Math.floor(i / BATCH_CHUNK_SIZE),
521
- chunkSize: chunkCommands.length,
522
- isLastChunk
523
- });
524
- await db.batch(batchCommands);
525
- totalInserted += chunkCommands.length;
526
- }
527
- logger.info("payments.database.batchComplete", {
528
- accountId,
529
- inserted: totalInserted,
530
- chunks: Math.ceil(createCommands.length / BATCH_CHUNK_SIZE)
531
- });
532
- } else {
533
- logger.info("payments.database.updateOnly", {
534
- accountId,
535
- reason: "no new payments to insert"
536
- });
537
- await updateLastSyncCommand.execute();
538
- logger.info("payments.database.updateComplete", {
539
- accountId
540
- });
541
- }
542
- if (eventsToEmit.length) {
543
- logger.info("payments.queue.sending", {
544
- accountId,
545
- eventsCount: eventsToEmit.length
546
- });
547
- await pushToQueue(
548
- this.env.QUEUE_BUS_QUEUE,
549
- eventsToEmit
550
- );
551
- logger.info("payments.queue.sent", {
552
- accountId,
553
- eventsCount: eventsToEmit.length
554
- });
555
- }
556
- logger.info("payments.process.complete", {
557
- accountId,
558
- ...lastSyncMetadata,
559
- newLastSyncAt: now
560
- });
561
- return {
562
- ...lastSyncMetadata,
563
- newLastSyncAt: now
564
- };
387
+ return result;
565
388
  } catch (err) {
566
389
  const message = err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : JSON.stringify(err);
567
- logger.error("payments.process.failed", {
390
+ logger.error("payments.fetch.failed", {
568
391
  accountId,
569
392
  connectorKey: account.connectorKey,
570
393
  error: message
@@ -573,6 +396,109 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
573
396
  }
574
397
  }
575
398
  );
399
+ const paymentsToProcess = payments.filter(
400
+ (p) => bank.isPaymentCompleted(p.parsed)
401
+ );
402
+ const lastSyncBankRefIds = account.lastSyncMetadata?.lastSyncBankRefIds || [];
403
+ const paymentsToInsert = paymentsToProcess.filter(
404
+ (p) => !lastSyncBankRefIds.includes(p.parsed.bankRefId)
405
+ );
406
+ await step.do(
407
+ "process new payments and update lastSyncAt",
408
+ async (ctx) => {
409
+ const eventsToEmit = [];
410
+ const bankRefIds = paymentsToInsert.map((p) => p.parsed.bankRefId).filter((id) => id != null);
411
+ const BANK_REF_ID_CHUNK_SIZE = 90;
412
+ const matchingRequests = [];
413
+ for (let i = 0; i < bankRefIds.length; i += BANK_REF_ID_CHUNK_SIZE) {
414
+ const chunkIds = bankRefIds.slice(i, i + BANK_REF_ID_CHUNK_SIZE);
415
+ const rows = await db.select().from(bank.tables.paymentRequest).where(
416
+ drizzleOrm.and(
417
+ drizzleOrm.inArray(bank.tables.paymentRequest.bankRefId, chunkIds),
418
+ drizzleOrm.eq(bank.tables.paymentRequest.accountId, account.id),
419
+ drizzleOrm.eq(bank.tables.paymentRequest.connectorKey, account.connectorKey)
420
+ )
421
+ );
422
+ matchingRequests.push(...rows);
423
+ }
424
+ const requestByBankRefId = Object.fromEntries(
425
+ matchingRequests.map((r) => [r.bankRefId, r])
426
+ );
427
+ const enrichedPayments = paymentsToInsert.map((p) => {
428
+ const req = p.parsed.bankRefId && requestByBankRefId[p.parsed.bankRefId] || null;
429
+ if (!req) return p;
430
+ return {
431
+ ...p,
432
+ parsed: {
433
+ ...p.parsed,
434
+ // queue-bus: transaction matching (DBU doesn't echo these in statements)
435
+ vs: p.parsed.vs ?? req.vs,
436
+ ss: p.parsed.ss ?? req.ss,
437
+ ks: p.parsed.ks ?? req.ks,
438
+ message: p.parsed.message ?? req.message,
439
+ // queue-bus: party creation
440
+ creditor: {
441
+ ...p.parsed.creditor,
442
+ holderName: p.parsed.creditor?.holderName ?? req.creditor?.holderName
443
+ },
444
+ debtor: {
445
+ ...p.parsed.debtor,
446
+ holderName: p.parsed.debtor?.holderName ?? req.debtor?.holderName
447
+ }
448
+ // NOT enriched: chargeBearer, instructionPriority, refId, batchId,
449
+ // createdAt, address, swiftBic — no downstream consumer in payment table
450
+ }
451
+ };
452
+ });
453
+ const createCommands = enrichedPayments.map(
454
+ (p) => credentialsResolver.createPaymentCommand(db, { payment: p.parsed }).command
455
+ );
456
+ eventsToEmit.push(
457
+ ...enrichedPayments.map((p) => ({
458
+ eventType: "BANK_PAYMENT",
459
+ eventSignal: "paymentFetched",
460
+ bankPayment: p.parsed,
461
+ metadata: {
462
+ correlationId: p.parsed.correlationId,
463
+ entityId: p.parsed.id,
464
+ timestamp: /* @__PURE__ */ new Date()
465
+ }
466
+ }))
467
+ );
468
+ const lastSyncMetadata = {
469
+ payments: payments.length,
470
+ paymentsToProcess: paymentsToProcess.length,
471
+ paymentsInserted: paymentsToInsert.length,
472
+ lastSyncBankRefIds: paymentsToProcess.filter((p) => p.parsed.status === "BOOKED").map((p) => p.parsed.bankRefId),
473
+ lastSyncPayments: lastSyncBankRefIds.length,
474
+ eventsEmitted: eventsToEmit.length,
475
+ iterationCount: getStepCount(ctx),
476
+ workflowStartedAt
477
+ };
478
+ const updateLastSyncCommand = updateAccountLastSyncCommand(db, {
479
+ accountId: account.id,
480
+ lastSyncAt: now,
481
+ lastSyncMetadata
482
+ }).command;
483
+ if (createCommands.length) {
484
+ await db.batch(
485
+ backendSdk.asNonEmpty([updateLastSyncCommand, ...createCommands])
486
+ );
487
+ } else {
488
+ await updateLastSyncCommand;
489
+ }
490
+ if (eventsToEmit.length) {
491
+ await pushToQueue(
492
+ this.env.QUEUE_BUS_QUEUE,
493
+ eventsToEmit
494
+ );
495
+ }
496
+ return {
497
+ ...lastSyncMetadata,
498
+ newLastSyncAt: now
499
+ };
500
+ }
501
+ );
576
502
  await step.sleep(
577
503
  "Sleep for next sync",
578
504
  `${account.syncIntervalS} seconds`