@develit-services/bank 5.2.1 → 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.d.cts CHANGED
@@ -2699,9 +2699,9 @@ declare const getBatchesInputSchema: z.ZodObject<{
2699
2699
  limit: z.ZodNumber;
2700
2700
  sort: z.ZodObject<{
2701
2701
  column: z.ZodEnum<{
2702
+ batchPaymentInitiatedAt: "batchPaymentInitiatedAt";
2702
2703
  createdAt: "createdAt";
2703
2704
  updatedAt: "updatedAt";
2704
- batchPaymentInitiatedAt: "batchPaymentInitiatedAt";
2705
2705
  }>;
2706
2706
  direction: z.ZodEnum<{
2707
2707
  asc: "asc";
@@ -2825,9 +2825,9 @@ declare const getPaymentsInputSchema: z.ZodObject<{
2825
2825
  limit: z.ZodNumber;
2826
2826
  sort: z.ZodObject<{
2827
2827
  column: z.ZodEnum<{
2828
+ amount: "amount";
2828
2829
  createdAt: "createdAt";
2829
2830
  updatedAt: "updatedAt";
2830
- amount: "amount";
2831
2831
  }>;
2832
2832
  direction: z.ZodEnum<{
2833
2833
  asc: "asc";
@@ -3875,9 +3875,9 @@ declare const getPaymentRequestsInputSchema: z.ZodObject<{
3875
3875
  limit: z.ZodNumber;
3876
3876
  sort: z.ZodObject<{
3877
3877
  column: z.ZodEnum<{
3878
+ amount: "amount";
3878
3879
  createdAt: "createdAt";
3879
3880
  updatedAt: "updatedAt";
3880
- amount: "amount";
3881
3881
  }>;
3882
3882
  direction: z.ZodEnum<{
3883
3883
  asc: "asc";
package/dist/base.d.mts CHANGED
@@ -2699,9 +2699,9 @@ declare const getBatchesInputSchema: z.ZodObject<{
2699
2699
  limit: z.ZodNumber;
2700
2700
  sort: z.ZodObject<{
2701
2701
  column: z.ZodEnum<{
2702
+ batchPaymentInitiatedAt: "batchPaymentInitiatedAt";
2702
2703
  createdAt: "createdAt";
2703
2704
  updatedAt: "updatedAt";
2704
- batchPaymentInitiatedAt: "batchPaymentInitiatedAt";
2705
2705
  }>;
2706
2706
  direction: z.ZodEnum<{
2707
2707
  asc: "asc";
@@ -2825,9 +2825,9 @@ declare const getPaymentsInputSchema: z.ZodObject<{
2825
2825
  limit: z.ZodNumber;
2826
2826
  sort: z.ZodObject<{
2827
2827
  column: z.ZodEnum<{
2828
+ amount: "amount";
2828
2829
  createdAt: "createdAt";
2829
2830
  updatedAt: "updatedAt";
2830
- amount: "amount";
2831
2831
  }>;
2832
2832
  direction: z.ZodEnum<{
2833
2833
  asc: "asc";
@@ -3875,9 +3875,9 @@ declare const getPaymentRequestsInputSchema: z.ZodObject<{
3875
3875
  limit: z.ZodNumber;
3876
3876
  sort: z.ZodObject<{
3877
3877
  column: z.ZodEnum<{
3878
+ amount: "amount";
3878
3879
  createdAt: "createdAt";
3879
3880
  updatedAt: "updatedAt";
3880
- amount: "amount";
3881
3881
  }>;
3882
3882
  direction: z.ZodEnum<{
3883
3883
  asc: "asc";
package/dist/base.d.ts CHANGED
@@ -2699,9 +2699,9 @@ declare const getBatchesInputSchema: z.ZodObject<{
2699
2699
  limit: z.ZodNumber;
2700
2700
  sort: z.ZodObject<{
2701
2701
  column: z.ZodEnum<{
2702
+ batchPaymentInitiatedAt: "batchPaymentInitiatedAt";
2702
2703
  createdAt: "createdAt";
2703
2704
  updatedAt: "updatedAt";
2704
- batchPaymentInitiatedAt: "batchPaymentInitiatedAt";
2705
2705
  }>;
2706
2706
  direction: z.ZodEnum<{
2707
2707
  asc: "asc";
@@ -2825,9 +2825,9 @@ declare const getPaymentsInputSchema: z.ZodObject<{
2825
2825
  limit: z.ZodNumber;
2826
2826
  sort: z.ZodObject<{
2827
2827
  column: z.ZodEnum<{
2828
+ amount: "amount";
2828
2829
  createdAt: "createdAt";
2829
2830
  updatedAt: "updatedAt";
2830
- amount: "amount";
2831
2831
  }>;
2832
2832
  direction: z.ZodEnum<{
2833
2833
  asc: "asc";
@@ -3875,9 +3875,9 @@ declare const getPaymentRequestsInputSchema: z.ZodObject<{
3875
3875
  limit: z.ZodNumber;
3876
3876
  sort: z.ZodObject<{
3877
3877
  column: z.ZodEnum<{
3878
+ amount: "amount";
3878
3879
  createdAt: "createdAt";
3879
3880
  updatedAt: "updatedAt";
3880
- amount: "amount";
3881
3881
  }>;
3882
3882
  direction: z.ZodEnum<{
3883
3883
  asc: "asc";
@@ -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`
@@ -313,16 +313,12 @@ async function pushToQueue(queue, message) {
313
313
  await queue.send(message, { contentType: "v8" });
314
314
  return;
315
315
  }
316
- const QUEUE_BATCH_SIZE = 100;
317
- for (let i = 0; i < message.length; i += QUEUE_BATCH_SIZE) {
318
- const chunk = message.slice(i, i + QUEUE_BATCH_SIZE);
319
- await queue.sendBatch(
320
- chunk.map((m) => ({
321
- body: m,
322
- contentType: "v8"
323
- }))
324
- );
325
- }
316
+ await queue.sendBatch(
317
+ message.map((m) => ({
318
+ body: m,
319
+ contentType: "v8"
320
+ }))
321
+ );
326
322
  }
327
323
  class BankSyncAccountPayments extends WorkflowEntrypoint {
328
324
  async run(event, step) {
@@ -348,13 +344,13 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
348
344
  if (!account.lastSyncAt) {
349
345
  throw new Error(`lastSyncedAt is not set for account: ${accountId}`);
350
346
  }
351
- await step.do(
352
- "fetch and process payments",
347
+ const payments = await step.do(
348
+ "fetch bank payments",
353
349
  {
354
350
  retries: { limit: 5, delay: "2 minutes", backoff: "exponential" },
355
- timeout: "5 minutes"
351
+ timeout: "2 minutes"
356
352
  },
357
- async (ctx) => {
353
+ async () => {
358
354
  try {
359
355
  logger.info("payments.fetch.started", {
360
356
  accountId,
@@ -377,192 +373,19 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
377
373
  }
378
374
  ]
379
375
  });
380
- const payments = await connector.getAllAccountPayments({
376
+ const result = await connector.getAllAccountPayments({
381
377
  account,
382
378
  filter: { dateFrom: account.lastSyncAt }
383
379
  });
384
380
  logger.info("payments.fetch.completed", {
385
381
  accountId,
386
382
  connectorKey: account.connectorKey,
387
- paymentsCount: payments.length
388
- });
389
- const paymentsToProcess = payments.filter(
390
- (p) => isPaymentCompleted(p.parsed)
391
- );
392
- logger.info("payments.filtered.toProcess", {
393
- accountId,
394
- totalFetched: payments.length,
395
- paymentsToProcess: paymentsToProcess.length,
396
- sampleStatuses: payments.slice(0, 5).map((p) => ({
397
- bankRefId: p.parsed.bankRefId,
398
- status: p.parsed.status,
399
- isCompleted: isPaymentCompleted(p.parsed)
400
- }))
401
- });
402
- const lastSyncBankRefIds = account.lastSyncMetadata?.lastSyncBankRefIds || [];
403
- const paymentsToInsert = paymentsToProcess.filter(
404
- (p) => !lastSyncBankRefIds.includes(p.parsed.bankRefId)
405
- );
406
- logger.info("payments.filtered.toInsert", {
407
- accountId,
408
- paymentsToProcess: paymentsToProcess.length,
409
- paymentsToInsert: paymentsToInsert.length,
410
- lastSyncBankRefIdsCount: lastSyncBankRefIds.length,
411
- sampleLastSyncBankRefIds: lastSyncBankRefIds.slice(0, 10),
412
- sampleToInsert: paymentsToInsert.slice(0, 5).map((p) => p.parsed.bankRefId)
413
- });
414
- const eventsToEmit = [];
415
- const bankRefIds = paymentsToInsert.map((p) => p.parsed.bankRefId).filter((id) => id != null);
416
- const BANK_REF_ID_CHUNK_SIZE = 90;
417
- const matchingRequests = [];
418
- for (let i = 0; i < bankRefIds.length; i += BANK_REF_ID_CHUNK_SIZE) {
419
- const chunkIds = bankRefIds.slice(i, i + BANK_REF_ID_CHUNK_SIZE);
420
- const rows = await db.select().from(tables.paymentRequest).where(
421
- and(
422
- inArray(tables.paymentRequest.bankRefId, chunkIds),
423
- eq(tables.paymentRequest.accountId, account.id),
424
- eq(
425
- tables.paymentRequest.connectorKey,
426
- account.connectorKey
427
- )
428
- )
429
- );
430
- matchingRequests.push(...rows);
431
- }
432
- const requestByBankRefId = Object.fromEntries(
433
- matchingRequests.map((r) => [r.bankRefId, r])
434
- );
435
- const enrichedPayments = paymentsToInsert.map((p) => {
436
- const req = p.parsed.bankRefId && requestByBankRefId[p.parsed.bankRefId] || null;
437
- if (!req) return p;
438
- return {
439
- ...p,
440
- parsed: {
441
- ...p.parsed,
442
- // queue-bus: transaction matching (DBU doesn't echo these in statements)
443
- vs: p.parsed.vs ?? req.vs,
444
- ss: p.parsed.ss ?? req.ss,
445
- ks: p.parsed.ks ?? req.ks,
446
- message: p.parsed.message ?? req.message,
447
- // queue-bus: party creation
448
- creditor: {
449
- ...p.parsed.creditor,
450
- holderName: p.parsed.creditor?.holderName ?? req.creditor?.holderName
451
- },
452
- debtor: {
453
- ...p.parsed.debtor,
454
- holderName: p.parsed.debtor?.holderName ?? req.debtor?.holderName
455
- }
456
- // NOT enriched: chargeBearer, instructionPriority, refId, batchId,
457
- // createdAt, address, swiftBic — no downstream consumer in payment table
458
- }
459
- };
460
- });
461
- const createCommands = enrichedPayments.map(
462
- (p) => createPaymentCommand(db, { payment: p.parsed }).command
463
- );
464
- logger.info("payments.commands.created", {
465
- accountId,
466
- createCommandsCount: createCommands.length,
467
- enrichedPaymentsCount: enrichedPayments.length
468
- });
469
- eventsToEmit.push(
470
- ...enrichedPayments.map((p) => ({
471
- eventType: "BANK_PAYMENT",
472
- eventSignal: "paymentFetched",
473
- bankPayment: p.parsed,
474
- metadata: {
475
- correlationId: p.parsed.correlationId,
476
- entityId: p.parsed.id,
477
- timestamp: /* @__PURE__ */ new Date()
478
- }
479
- }))
480
- );
481
- const lastSyncMetadata = {
482
- payments: payments.length,
483
- paymentsToProcess: paymentsToProcess.length,
484
- paymentsInserted: paymentsToInsert.length,
485
- lastSyncBankRefIds: paymentsToProcess.filter((p) => p.parsed.status === "BOOKED").map((p) => p.parsed.bankRefId),
486
- lastSyncPayments: lastSyncBankRefIds.length,
487
- eventsEmitted: eventsToEmit.length,
488
- iterationCount: getStepCount(ctx),
489
- workflowStartedAt
490
- };
491
- const updateLastSyncCommand = updateAccountLastSyncCommand(db, {
492
- accountId: account.id,
493
- lastSyncAt: now,
494
- lastSyncMetadata
495
- }).command;
496
- logger.info("payments.database.beforeBatch", {
497
- accountId,
498
- createCommandsCount: createCommands.length,
499
- willUseBatch: createCommands.length > 0
383
+ paymentsCount: result.length
500
384
  });
501
- if (createCommands.length) {
502
- logger.info("payments.database.batchStart", {
503
- accountId,
504
- totalCommands: createCommands.length + 1,
505
- paymentsToInsert: createCommands.length
506
- });
507
- const BATCH_CHUNK_SIZE = 90;
508
- let totalInserted = 0;
509
- for (let i = 0; i < createCommands.length; i += BATCH_CHUNK_SIZE) {
510
- const chunkCommands = createCommands.slice(
511
- i,
512
- i + BATCH_CHUNK_SIZE
513
- );
514
- const isLastChunk = i + BATCH_CHUNK_SIZE >= createCommands.length;
515
- const batchCommands = isLastChunk ? asNonEmpty([updateLastSyncCommand, ...chunkCommands]) : asNonEmpty(chunkCommands);
516
- logger.info("payments.database.batchChunk", {
517
- accountId,
518
- chunkIndex: Math.floor(i / BATCH_CHUNK_SIZE),
519
- chunkSize: chunkCommands.length,
520
- isLastChunk
521
- });
522
- await db.batch(batchCommands);
523
- totalInserted += chunkCommands.length;
524
- }
525
- logger.info("payments.database.batchComplete", {
526
- accountId,
527
- inserted: totalInserted,
528
- chunks: Math.ceil(createCommands.length / BATCH_CHUNK_SIZE)
529
- });
530
- } else {
531
- logger.info("payments.database.updateOnly", {
532
- accountId,
533
- reason: "no new payments to insert"
534
- });
535
- await updateLastSyncCommand.execute();
536
- logger.info("payments.database.updateComplete", {
537
- accountId
538
- });
539
- }
540
- if (eventsToEmit.length) {
541
- logger.info("payments.queue.sending", {
542
- accountId,
543
- eventsCount: eventsToEmit.length
544
- });
545
- await pushToQueue(
546
- this.env.QUEUE_BUS_QUEUE,
547
- eventsToEmit
548
- );
549
- logger.info("payments.queue.sent", {
550
- accountId,
551
- eventsCount: eventsToEmit.length
552
- });
553
- }
554
- logger.info("payments.process.complete", {
555
- accountId,
556
- ...lastSyncMetadata,
557
- newLastSyncAt: now
558
- });
559
- return {
560
- ...lastSyncMetadata,
561
- newLastSyncAt: now
562
- };
385
+ return result;
563
386
  } catch (err) {
564
387
  const message = err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : JSON.stringify(err);
565
- logger.error("payments.process.failed", {
388
+ logger.error("payments.fetch.failed", {
566
389
  accountId,
567
390
  connectorKey: account.connectorKey,
568
391
  error: message
@@ -571,6 +394,109 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
571
394
  }
572
395
  }
573
396
  );
397
+ const paymentsToProcess = payments.filter(
398
+ (p) => isPaymentCompleted(p.parsed)
399
+ );
400
+ const lastSyncBankRefIds = account.lastSyncMetadata?.lastSyncBankRefIds || [];
401
+ const paymentsToInsert = paymentsToProcess.filter(
402
+ (p) => !lastSyncBankRefIds.includes(p.parsed.bankRefId)
403
+ );
404
+ await step.do(
405
+ "process new payments and update lastSyncAt",
406
+ async (ctx) => {
407
+ const eventsToEmit = [];
408
+ const bankRefIds = paymentsToInsert.map((p) => p.parsed.bankRefId).filter((id) => id != null);
409
+ const BANK_REF_ID_CHUNK_SIZE = 90;
410
+ const matchingRequests = [];
411
+ for (let i = 0; i < bankRefIds.length; i += BANK_REF_ID_CHUNK_SIZE) {
412
+ const chunkIds = bankRefIds.slice(i, i + BANK_REF_ID_CHUNK_SIZE);
413
+ const rows = await db.select().from(tables.paymentRequest).where(
414
+ and(
415
+ inArray(tables.paymentRequest.bankRefId, chunkIds),
416
+ eq(tables.paymentRequest.accountId, account.id),
417
+ eq(tables.paymentRequest.connectorKey, account.connectorKey)
418
+ )
419
+ );
420
+ matchingRequests.push(...rows);
421
+ }
422
+ const requestByBankRefId = Object.fromEntries(
423
+ matchingRequests.map((r) => [r.bankRefId, r])
424
+ );
425
+ const enrichedPayments = paymentsToInsert.map((p) => {
426
+ const req = p.parsed.bankRefId && requestByBankRefId[p.parsed.bankRefId] || null;
427
+ if (!req) return p;
428
+ return {
429
+ ...p,
430
+ parsed: {
431
+ ...p.parsed,
432
+ // queue-bus: transaction matching (DBU doesn't echo these in statements)
433
+ vs: p.parsed.vs ?? req.vs,
434
+ ss: p.parsed.ss ?? req.ss,
435
+ ks: p.parsed.ks ?? req.ks,
436
+ message: p.parsed.message ?? req.message,
437
+ // queue-bus: party creation
438
+ creditor: {
439
+ ...p.parsed.creditor,
440
+ holderName: p.parsed.creditor?.holderName ?? req.creditor?.holderName
441
+ },
442
+ debtor: {
443
+ ...p.parsed.debtor,
444
+ holderName: p.parsed.debtor?.holderName ?? req.debtor?.holderName
445
+ }
446
+ // NOT enriched: chargeBearer, instructionPriority, refId, batchId,
447
+ // createdAt, address, swiftBic — no downstream consumer in payment table
448
+ }
449
+ };
450
+ });
451
+ const createCommands = enrichedPayments.map(
452
+ (p) => createPaymentCommand(db, { payment: p.parsed }).command
453
+ );
454
+ eventsToEmit.push(
455
+ ...enrichedPayments.map((p) => ({
456
+ eventType: "BANK_PAYMENT",
457
+ eventSignal: "paymentFetched",
458
+ bankPayment: p.parsed,
459
+ metadata: {
460
+ correlationId: p.parsed.correlationId,
461
+ entityId: p.parsed.id,
462
+ timestamp: /* @__PURE__ */ new Date()
463
+ }
464
+ }))
465
+ );
466
+ const lastSyncMetadata = {
467
+ payments: payments.length,
468
+ paymentsToProcess: paymentsToProcess.length,
469
+ paymentsInserted: paymentsToInsert.length,
470
+ lastSyncBankRefIds: paymentsToProcess.filter((p) => p.parsed.status === "BOOKED").map((p) => p.parsed.bankRefId),
471
+ lastSyncPayments: lastSyncBankRefIds.length,
472
+ eventsEmitted: eventsToEmit.length,
473
+ iterationCount: getStepCount(ctx),
474
+ workflowStartedAt
475
+ };
476
+ const updateLastSyncCommand = updateAccountLastSyncCommand(db, {
477
+ accountId: account.id,
478
+ lastSyncAt: now,
479
+ lastSyncMetadata
480
+ }).command;
481
+ if (createCommands.length) {
482
+ await db.batch(
483
+ asNonEmpty([updateLastSyncCommand, ...createCommands])
484
+ );
485
+ } else {
486
+ await updateLastSyncCommand;
487
+ }
488
+ if (eventsToEmit.length) {
489
+ await pushToQueue(
490
+ this.env.QUEUE_BUS_QUEUE,
491
+ eventsToEmit
492
+ );
493
+ }
494
+ return {
495
+ ...lastSyncMetadata,
496
+ newLastSyncAt: now
497
+ };
498
+ }
499
+ );
574
500
  await step.sleep(
575
501
  "Sleep for next sync",
576
502
  `${account.syncIntervalS} seconds`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@develit-services/bank",
3
- "version": "5.2.1",
3
+ "version": "5.2.2",
4
4
  "author": "Develit.io s.r.o.",
5
5
  "type": "module",
6
6
  "exports": {