@develit-services/bank 0.8.7 → 0.8.8

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.
Files changed (38) hide show
  1. package/dist/database/schema.cjs +1 -1
  2. package/dist/database/schema.d.cts +1 -1
  3. package/dist/database/schema.d.mts +1 -1
  4. package/dist/database/schema.d.ts +1 -1
  5. package/dist/database/schema.mjs +1 -1
  6. package/dist/export/worker.cjs +236 -276
  7. package/dist/export/worker.d.cts +26 -26
  8. package/dist/export/worker.d.mts +26 -26
  9. package/dist/export/worker.d.ts +26 -26
  10. package/dist/export/worker.mjs +237 -277
  11. package/dist/export/workflows.cjs +58 -96
  12. package/dist/export/workflows.mjs +59 -97
  13. package/dist/export/wrangler.d.cts +2 -1
  14. package/dist/export/wrangler.d.mts +2 -1
  15. package/dist/export/wrangler.d.ts +2 -1
  16. package/dist/shared/{bank.Cns5ss41.d.cts → bank.B-NJB8GB.d.cts} +12 -25
  17. package/dist/shared/{bank.Cns5ss41.d.mts → bank.B-NJB8GB.d.mts} +12 -25
  18. package/dist/shared/{bank.Cns5ss41.d.ts → bank.B-NJB8GB.d.ts} +12 -25
  19. package/dist/shared/{bank.DEmzZGZW.mjs → bank.B5bZRvgq.mjs} +2 -5
  20. package/dist/shared/{bank.CQBfbG8u.d.cts → bank.BP_3WMIF.d.cts} +1 -0
  21. package/dist/shared/{bank.CQBfbG8u.d.mts → bank.BP_3WMIF.d.mts} +1 -0
  22. package/dist/shared/{bank.CQBfbG8u.d.ts → bank.BP_3WMIF.d.ts} +1 -0
  23. package/dist/shared/{bank.CA5ytXxp.mjs → bank.BoZtXQpG.mjs} +1 -1
  24. package/dist/shared/{bank.D1jqaHaF.mjs → bank.BtszLapg.mjs} +128 -20
  25. package/dist/shared/{bank.CO89tR9U.d.cts → bank.BzobShUU.d.cts} +1 -1
  26. package/dist/shared/{bank.C0UN6luZ.mjs → bank.C6jjS1Pl.mjs} +0 -2
  27. package/dist/shared/{bank.CreoSb2d.d.mts → bank.CAVvvZZO.d.mts} +1 -1
  28. package/dist/shared/{bank.DwyCCyd0.cjs → bank.CtnsGHM8.cjs} +128 -20
  29. package/dist/shared/{bank.62VzK9Aj.cjs → bank.DJnDSYqE.cjs} +1 -1
  30. package/dist/shared/{bank.BYRq3yJf.d.ts → bank.DRTuKO8S.d.ts} +1 -1
  31. package/dist/shared/{bank.Dm8GHThw.cjs → bank.DT6bg8k5.cjs} +2 -5
  32. package/dist/shared/{bank.BS7fFjGA.cjs → bank.JVlyPAAb.cjs} +0 -2
  33. package/dist/types.cjs +3 -3
  34. package/dist/types.d.cts +10 -6
  35. package/dist/types.d.mts +10 -6
  36. package/dist/types.d.ts +10 -6
  37. package/dist/types.mjs +3 -3
  38. package/package.json +1 -1
@@ -1,22 +1,22 @@
1
1
  'use strict';
2
2
 
3
3
  const backendSdk = require('@develit-io/backend-sdk');
4
- const drizzle = require('../shared/bank.Dm8GHThw.cjs');
4
+ const drizzle = require('../shared/bank.DT6bg8k5.cjs');
5
5
  const batchLifecycle = require('../shared/bank.Bg3Pdwm4.cjs');
6
- const mock_connector = require('../shared/bank.DwyCCyd0.cjs');
6
+ const mock_connector = require('../shared/bank.CtnsGHM8.cjs');
7
7
  const drizzleOrm = require('drizzle-orm');
8
- require('drizzle-orm/sqlite-core');
9
8
  const cloudflare_workers = require('cloudflare:workers');
10
9
  const cloudflare_workflows = require('cloudflare:workflows');
11
10
  const d1 = require('drizzle-orm/d1');
12
11
  require('node:crypto');
13
- require('../shared/bank.BS7fFjGA.cjs');
12
+ require('../shared/bank.JVlyPAAb.cjs');
14
13
  require('drizzle-orm/relations');
14
+ require('drizzle-orm/sqlite-core');
15
15
  require('date-fns');
16
16
  require('jose');
17
17
  require('@develit-io/general-codes');
18
18
  require('drizzle-zod');
19
- require('../shared/bank.62VzK9Aj.cjs');
19
+ require('../shared/bank.DJnDSYqE.cjs');
20
20
 
21
21
  const updateAccountLastSyncCommand = (db, {
22
22
  lastSyncAt,
@@ -41,6 +41,23 @@ function pushToQueue$1(queue, message) {
41
41
  }))
42
42
  );
43
43
  }
44
+ async function failBatchAndPayments(db, batch, paymentRequests, reason) {
45
+ const prCmds = paymentRequests.filter((p) => p.status !== "FAILED").map(
46
+ (p) => drizzle.updatePaymentRequestStatusCommand(db, {
47
+ id: p.id,
48
+ status: "FAILED",
49
+ statusReason: reason,
50
+ processedAt: /* @__PURE__ */ new Date()
51
+ }).command
52
+ );
53
+ const batchCmd = drizzle.upsertBatchCommand(db, {
54
+ batch: { ...batch, status: "FAILED" }
55
+ }).command;
56
+ const allCmds = [batchCmd, ...prCmds];
57
+ if (allCmds.length > 0) {
58
+ await db.batch(allCmds);
59
+ }
60
+ }
44
61
  class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
45
62
  async run(event, step) {
46
63
  const { batchId } = event.payload;
@@ -110,7 +127,9 @@ class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
110
127
  accountId: batch.accountId
111
128
  });
112
129
  if (!account) {
113
- throw new cloudflare_workflows.NonRetryableError(`Account not found: ${batch.accountId}`);
130
+ const msg = `Account not found: ${batch.accountId}`;
131
+ await failBatchAndPayments(db, batch, paymentRequests, msg);
132
+ throw new cloudflare_workflows.NonRetryableError(msg);
114
133
  }
115
134
  const resolveCredentials = await drizzle.createCredentialsResolver(db, this.env);
116
135
  const connector = await mock_connector.initiateConnector({
@@ -143,17 +162,7 @@ class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
143
162
  const message = err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : String(err);
144
163
  const status = typeof err === "object" && err !== null && "status" in err ? Number(err.status) : 0;
145
164
  if (status >= 400 && status < 500) {
146
- const updateCommands = paymentRequests.map(
147
- (p) => drizzle.updatePaymentRequestStatusCommand(db, {
148
- id: p.id,
149
- status: "FAILED",
150
- statusReason: message,
151
- processedAt: /* @__PURE__ */ new Date()
152
- }).command
153
- );
154
- if (updateCommands.length > 0) {
155
- await db.batch(updateCommands);
156
- }
165
+ await failBatchAndPayments(db, batch, paymentRequests, message);
157
166
  throw new cloudflare_workflows.NonRetryableError(message);
158
167
  }
159
168
  throw new Error(message);
@@ -164,7 +173,9 @@ class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
164
173
  metadata
165
174
  } = result;
166
175
  if (!authorizationUrls || authorizationUrls.length === 0) {
167
- throw new cloudflare_workflows.NonRetryableError("Failed to retrieve authorization URLs");
176
+ const msg = "Failed to retrieve authorization URLs";
177
+ await failBatchAndPayments(db, batch, paymentRequests, msg);
178
+ throw new cloudflare_workflows.NonRetryableError(msg);
168
179
  }
169
180
  return {
170
181
  authorizationUrls,
@@ -217,14 +228,13 @@ class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
217
228
  id: p.id,
218
229
  status: "COMPLETED",
219
230
  bankRefId: `MOCK-${backendSdk.uuidv4()}`,
220
- processedAt,
221
- deletedAt: /* @__PURE__ */ new Date()
231
+ processedAt
222
232
  }).command
223
233
  );
224
234
  const updateBatchCommand = drizzle.upsertBatchCommand(db, {
225
235
  batch: {
226
236
  ...batch,
227
- status: "SIGNED",
237
+ status: "COMPLETED",
228
238
  authorizationUrls: batchResult.authorizationUrls,
229
239
  metadata: batchResult.metadata,
230
240
  batchPaymentInitiatedAt: /* @__PURE__ */ new Date()
@@ -347,84 +357,57 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
347
357
  "process new payments and update lastSyncAt",
348
358
  async () => {
349
359
  const eventsToEmit = [];
350
- const prIds = paymentsToInsert.map((p) => p.parsed.paymentRequestId).filter((id) => id != null);
351
- const bankRefIds = paymentsToInsert.filter((p) => p.parsed.paymentRequestId == null).map((p) => p.parsed.bankRefId);
352
- const [matchingByPrId, matchingByBankRefId] = await Promise.all([
353
- prIds.length > 0 ? db.select().from(drizzle.tables.paymentRequest).where(drizzleOrm.inArray(drizzle.tables.paymentRequest.id, prIds)) : Promise.resolve([]),
354
- bankRefIds.length > 0 ? db.select().from(drizzle.tables.paymentRequest).where(
355
- drizzleOrm.and(
356
- drizzleOrm.inArray(drizzle.tables.paymentRequest.bankRefId, bankRefIds),
357
- drizzleOrm.eq(drizzle.tables.paymentRequest.accountId, account.id),
358
- drizzleOrm.eq(
359
- drizzle.tables.paymentRequest.connectorKey,
360
- account.connectorKey
361
- ),
362
- drizzleOrm.isNull(drizzle.tables.paymentRequest.deletedAt)
360
+ const bankRefIds = paymentsToInsert.map((p) => p.parsed.bankRefId).filter((id) => id != null);
361
+ const matchingRequests = bankRefIds.length > 0 ? await db.select().from(drizzle.tables.paymentRequest).where(
362
+ drizzleOrm.and(
363
+ drizzleOrm.inArray(drizzle.tables.paymentRequest.bankRefId, bankRefIds),
364
+ drizzleOrm.eq(drizzle.tables.paymentRequest.accountId, account.id),
365
+ drizzleOrm.eq(
366
+ drizzle.tables.paymentRequest.connectorKey,
367
+ account.connectorKey
363
368
  )
364
- ) : Promise.resolve([])
365
- ]);
366
- const requestById = Object.fromEntries(
367
- matchingByPrId.map((r) => [r.id, r])
368
- );
369
+ )
370
+ ) : [];
369
371
  const requestByBankRefId = Object.fromEntries(
370
- matchingByBankRefId.map((r) => [r.bankRefId, r])
372
+ matchingRequests.map((r) => [r.bankRefId, r])
371
373
  );
372
374
  const enrichedPayments = paymentsToInsert.map((p) => {
373
- const req = p.parsed.paymentRequestId ? requestById[p.parsed.paymentRequestId] : requestByBankRefId[p.parsed.bankRefId];
375
+ const req = p.parsed.bankRefId && requestByBankRefId[p.parsed.bankRefId] || null;
374
376
  if (!req) return p;
375
377
  return {
376
378
  ...p,
377
379
  parsed: {
378
380
  ...p.parsed,
379
- // Set paymentRequestId if missing (bankRefId fallback match)
380
- paymentRequestId: p.parsed.paymentRequestId ?? req.id,
381
- createdAt: req.createdAt ?? p.parsed.createdAt,
382
- batchId: p.parsed.batchId ?? req.batchId,
383
- refId: p.parsed.refId ?? req.refId,
384
- chargeBearer: p.parsed.chargeBearer ?? req.chargeBearer,
385
- instructionPriority: p.parsed.instructionPriority ?? req.instructionPriority,
386
- message: p.parsed.message ?? req.message,
381
+ // queue-bus: transaction matching (DBU doesn't echo these in statements)
387
382
  vs: p.parsed.vs ?? req.vs,
388
383
  ss: p.parsed.ss ?? req.ss,
389
384
  ks: p.parsed.ks ?? req.ks,
385
+ message: p.parsed.message ?? req.message,
386
+ // queue-bus: party creation
390
387
  creditor: {
391
388
  ...p.parsed.creditor,
392
- holderName: p.parsed.creditor?.holderName ?? req.creditor?.holderName,
393
- address: p.parsed.creditor?.address ?? req.creditor?.address,
394
- swiftBic: p.parsed.creditor?.swiftBic ?? req.creditor?.swiftBic
389
+ holderName: p.parsed.creditor?.holderName ?? req.creditor?.holderName
395
390
  },
396
391
  debtor: {
397
392
  ...p.parsed.debtor,
398
393
  holderName: p.parsed.debtor?.holderName ?? req.debtor?.holderName
399
394
  }
395
+ // NOT enriched: chargeBearer, instructionPriority, refId, batchId,
396
+ // createdAt, address, swiftBic — no downstream consumer in payment table
400
397
  }
401
398
  };
402
399
  });
403
400
  const createCommands = enrichedPayments.map(
404
401
  (p) => drizzle.createPaymentCommand(db, { payment: p.parsed }).command
405
402
  );
406
- const softDeleteCommands = enrichedPayments.map(
407
- (p) => db.update(drizzle.tables.paymentRequest).set({
408
- deletedAt: now,
409
- status: p.parsed.status,
410
- statusReason: p.parsed.statusReason ?? null
411
- }).where(
412
- drizzleOrm.or(
413
- // Primary: match via paymentRequestId (our ID echoed back by bank)
414
- p.parsed.paymentRequestId ? drizzleOrm.eq(drizzle.tables.paymentRequest.id, p.parsed.paymentRequestId) : void 0,
415
- // Fallback: match via bankRefId (set during processBatch)
416
- drizzleOrm.and(
417
- drizzleOrm.isNotNull(drizzle.tables.paymentRequest.bankRefId),
418
- drizzleOrm.eq(drizzle.tables.paymentRequest.bankRefId, p.parsed.bankRefId),
419
- drizzleOrm.eq(drizzle.tables.paymentRequest.accountId, account.id),
420
- drizzleOrm.eq(
421
- drizzle.tables.paymentRequest.connectorKey,
422
- account.connectorKey
423
- ),
424
- drizzleOrm.isNull(drizzle.tables.paymentRequest.deletedAt)
425
- )
426
- )
427
- )
403
+ const prCompleteCommands = matchingRequests.filter(
404
+ (req) => req.status !== "COMPLETED" && req.status !== "FAILED"
405
+ ).map(
406
+ (req) => drizzle.updatePaymentRequestStatusCommand(db, {
407
+ id: req.id,
408
+ status: "COMPLETED",
409
+ processedAt: /* @__PURE__ */ new Date()
410
+ }).command
428
411
  );
429
412
  eventsToEmit.push(
430
413
  ...enrichedPayments.map((p) => ({
@@ -456,7 +439,7 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
456
439
  backendSdk.asNonEmpty([
457
440
  updateLastSyncCommand,
458
441
  ...createCommands,
459
- ...softDeleteCommands
442
+ ...prCompleteCommands
460
443
  ])
461
444
  );
462
445
  await pushToQueue(
@@ -464,27 +447,6 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
464
447
  eventsToEmit
465
448
  );
466
449
  }
467
- const paymentRequestIds = enrichedPayments.map((p) => p.parsed.paymentRequestId).filter((id) => id != null);
468
- if (paymentRequestIds.length > 0) {
469
- const processedPRs = await db.select({ batchId: drizzle.tables.paymentRequest.batchId }).from(drizzle.tables.paymentRequest).where(drizzleOrm.inArray(drizzle.tables.paymentRequest.id, paymentRequestIds));
470
- const batchIds = [
471
- ...new Set(
472
- processedPRs.map((pr) => pr.batchId).filter((id) => id != null)
473
- )
474
- ];
475
- for (const batchId of batchIds) {
476
- const allPRsInBatch = await db.select({ deletedAt: drizzle.tables.paymentRequest.deletedAt }).from(drizzle.tables.paymentRequest).where(drizzleOrm.eq(drizzle.tables.paymentRequest.batchId, batchId));
477
- const allDone = allPRsInBatch.length > 0 && allPRsInBatch.every((pr) => pr.deletedAt != null);
478
- if (allDone) {
479
- const batch = await drizzle.getBatchByIdQuery(db, { batchId });
480
- if (batch && batch.status !== "COMPLETED" && batch.status !== "FAILED") {
481
- await drizzle.upsertBatchCommand(db, {
482
- batch: { ...batch, status: "COMPLETED" }
483
- }).command.execute();
484
- }
485
- }
486
- }
487
- }
488
450
  return {
489
451
  ...lastSyncMetadata,
490
452
  newLastSyncAt: now
@@ -1,20 +1,20 @@
1
1
  import { first, uuidv4, asNonEmpty } from '@develit-io/backend-sdk';
2
- import { t as tables, g as getBatchByIdQuery, a as getPaymentRequestsByBatchIdQuery, c as checksum, u as upsertBatchCommand, b as getAccountByIdQuery, d as createCredentialsResolver, e as updatePaymentRequestStatusCommand, f as createPaymentCommand } from '../shared/bank.DEmzZGZW.mjs';
2
+ import { t as tables, g as getBatchByIdQuery, a as getPaymentRequestsByBatchIdQuery, c as checksum, u as upsertBatchCommand, b as getAccountByIdQuery, d as createCredentialsResolver, e as updatePaymentRequestStatusCommand, f as createPaymentCommand } from '../shared/bank.B5bZRvgq.mjs';
3
3
  import { f as isBatchSigned, a as isBatchFailed, c as isBatchOpen, g as isPaymentCompleted } from '../shared/bank.CbAwwIhZ.mjs';
4
- import { e as toBatchedPaymentFromPaymentRequest, i as toPreparedPayment, j as initiateConnector } from '../shared/bank.D1jqaHaF.mjs';
5
- import { eq, inArray, and, isNull, or, isNotNull } from 'drizzle-orm';
6
- import 'drizzle-orm/sqlite-core';
4
+ import { e as toBatchedPaymentFromPaymentRequest, i as toPreparedPayment, j as initiateConnector } from '../shared/bank.BtszLapg.mjs';
5
+ import { eq, and, inArray } from 'drizzle-orm';
7
6
  import { WorkflowEntrypoint } from 'cloudflare:workers';
8
7
  import { NonRetryableError } from 'cloudflare:workflows';
9
8
  import { drizzle } from 'drizzle-orm/d1';
10
9
  import 'node:crypto';
11
- import '../shared/bank.C0UN6luZ.mjs';
10
+ import '../shared/bank.C6jjS1Pl.mjs';
12
11
  import 'drizzle-orm/relations';
12
+ import 'drizzle-orm/sqlite-core';
13
13
  import 'date-fns';
14
14
  import 'jose';
15
15
  import '@develit-io/general-codes';
16
16
  import 'drizzle-zod';
17
- import '../shared/bank.CA5ytXxp.mjs';
17
+ import '../shared/bank.BoZtXQpG.mjs';
18
18
 
19
19
  const updateAccountLastSyncCommand = (db, {
20
20
  lastSyncAt,
@@ -39,6 +39,23 @@ function pushToQueue$1(queue, message) {
39
39
  }))
40
40
  );
41
41
  }
42
+ async function failBatchAndPayments(db, batch, paymentRequests, reason) {
43
+ const prCmds = paymentRequests.filter((p) => p.status !== "FAILED").map(
44
+ (p) => updatePaymentRequestStatusCommand(db, {
45
+ id: p.id,
46
+ status: "FAILED",
47
+ statusReason: reason,
48
+ processedAt: /* @__PURE__ */ new Date()
49
+ }).command
50
+ );
51
+ const batchCmd = upsertBatchCommand(db, {
52
+ batch: { ...batch, status: "FAILED" }
53
+ }).command;
54
+ const allCmds = [batchCmd, ...prCmds];
55
+ if (allCmds.length > 0) {
56
+ await db.batch(allCmds);
57
+ }
58
+ }
42
59
  class BankProcessBatch extends WorkflowEntrypoint {
43
60
  async run(event, step) {
44
61
  const { batchId } = event.payload;
@@ -108,7 +125,9 @@ class BankProcessBatch extends WorkflowEntrypoint {
108
125
  accountId: batch.accountId
109
126
  });
110
127
  if (!account) {
111
- throw new NonRetryableError(`Account not found: ${batch.accountId}`);
128
+ const msg = `Account not found: ${batch.accountId}`;
129
+ await failBatchAndPayments(db, batch, paymentRequests, msg);
130
+ throw new NonRetryableError(msg);
112
131
  }
113
132
  const resolveCredentials = await createCredentialsResolver(db, this.env);
114
133
  const connector = await initiateConnector({
@@ -141,17 +160,7 @@ class BankProcessBatch extends WorkflowEntrypoint {
141
160
  const message = err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : String(err);
142
161
  const status = typeof err === "object" && err !== null && "status" in err ? Number(err.status) : 0;
143
162
  if (status >= 400 && status < 500) {
144
- const updateCommands = paymentRequests.map(
145
- (p) => updatePaymentRequestStatusCommand(db, {
146
- id: p.id,
147
- status: "FAILED",
148
- statusReason: message,
149
- processedAt: /* @__PURE__ */ new Date()
150
- }).command
151
- );
152
- if (updateCommands.length > 0) {
153
- await db.batch(updateCommands);
154
- }
163
+ await failBatchAndPayments(db, batch, paymentRequests, message);
155
164
  throw new NonRetryableError(message);
156
165
  }
157
166
  throw new Error(message);
@@ -162,7 +171,9 @@ class BankProcessBatch extends WorkflowEntrypoint {
162
171
  metadata
163
172
  } = result;
164
173
  if (!authorizationUrls || authorizationUrls.length === 0) {
165
- throw new NonRetryableError("Failed to retrieve authorization URLs");
174
+ const msg = "Failed to retrieve authorization URLs";
175
+ await failBatchAndPayments(db, batch, paymentRequests, msg);
176
+ throw new NonRetryableError(msg);
166
177
  }
167
178
  return {
168
179
  authorizationUrls,
@@ -215,14 +226,13 @@ class BankProcessBatch extends WorkflowEntrypoint {
215
226
  id: p.id,
216
227
  status: "COMPLETED",
217
228
  bankRefId: `MOCK-${uuidv4()}`,
218
- processedAt,
219
- deletedAt: /* @__PURE__ */ new Date()
229
+ processedAt
220
230
  }).command
221
231
  );
222
232
  const updateBatchCommand = upsertBatchCommand(db, {
223
233
  batch: {
224
234
  ...batch,
225
- status: "SIGNED",
235
+ status: "COMPLETED",
226
236
  authorizationUrls: batchResult.authorizationUrls,
227
237
  metadata: batchResult.metadata,
228
238
  batchPaymentInitiatedAt: /* @__PURE__ */ new Date()
@@ -345,84 +355,57 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
345
355
  "process new payments and update lastSyncAt",
346
356
  async () => {
347
357
  const eventsToEmit = [];
348
- const prIds = paymentsToInsert.map((p) => p.parsed.paymentRequestId).filter((id) => id != null);
349
- const bankRefIds = paymentsToInsert.filter((p) => p.parsed.paymentRequestId == null).map((p) => p.parsed.bankRefId);
350
- const [matchingByPrId, matchingByBankRefId] = await Promise.all([
351
- prIds.length > 0 ? db.select().from(tables.paymentRequest).where(inArray(tables.paymentRequest.id, prIds)) : Promise.resolve([]),
352
- bankRefIds.length > 0 ? db.select().from(tables.paymentRequest).where(
353
- and(
354
- inArray(tables.paymentRequest.bankRefId, bankRefIds),
355
- eq(tables.paymentRequest.accountId, account.id),
356
- eq(
357
- tables.paymentRequest.connectorKey,
358
- account.connectorKey
359
- ),
360
- isNull(tables.paymentRequest.deletedAt)
358
+ const bankRefIds = paymentsToInsert.map((p) => p.parsed.bankRefId).filter((id) => id != null);
359
+ const matchingRequests = bankRefIds.length > 0 ? await db.select().from(tables.paymentRequest).where(
360
+ and(
361
+ inArray(tables.paymentRequest.bankRefId, bankRefIds),
362
+ eq(tables.paymentRequest.accountId, account.id),
363
+ eq(
364
+ tables.paymentRequest.connectorKey,
365
+ account.connectorKey
361
366
  )
362
- ) : Promise.resolve([])
363
- ]);
364
- const requestById = Object.fromEntries(
365
- matchingByPrId.map((r) => [r.id, r])
366
- );
367
+ )
368
+ ) : [];
367
369
  const requestByBankRefId = Object.fromEntries(
368
- matchingByBankRefId.map((r) => [r.bankRefId, r])
370
+ matchingRequests.map((r) => [r.bankRefId, r])
369
371
  );
370
372
  const enrichedPayments = paymentsToInsert.map((p) => {
371
- const req = p.parsed.paymentRequestId ? requestById[p.parsed.paymentRequestId] : requestByBankRefId[p.parsed.bankRefId];
373
+ const req = p.parsed.bankRefId && requestByBankRefId[p.parsed.bankRefId] || null;
372
374
  if (!req) return p;
373
375
  return {
374
376
  ...p,
375
377
  parsed: {
376
378
  ...p.parsed,
377
- // Set paymentRequestId if missing (bankRefId fallback match)
378
- paymentRequestId: p.parsed.paymentRequestId ?? req.id,
379
- createdAt: req.createdAt ?? p.parsed.createdAt,
380
- batchId: p.parsed.batchId ?? req.batchId,
381
- refId: p.parsed.refId ?? req.refId,
382
- chargeBearer: p.parsed.chargeBearer ?? req.chargeBearer,
383
- instructionPriority: p.parsed.instructionPriority ?? req.instructionPriority,
384
- message: p.parsed.message ?? req.message,
379
+ // queue-bus: transaction matching (DBU doesn't echo these in statements)
385
380
  vs: p.parsed.vs ?? req.vs,
386
381
  ss: p.parsed.ss ?? req.ss,
387
382
  ks: p.parsed.ks ?? req.ks,
383
+ message: p.parsed.message ?? req.message,
384
+ // queue-bus: party creation
388
385
  creditor: {
389
386
  ...p.parsed.creditor,
390
- holderName: p.parsed.creditor?.holderName ?? req.creditor?.holderName,
391
- address: p.parsed.creditor?.address ?? req.creditor?.address,
392
- swiftBic: p.parsed.creditor?.swiftBic ?? req.creditor?.swiftBic
387
+ holderName: p.parsed.creditor?.holderName ?? req.creditor?.holderName
393
388
  },
394
389
  debtor: {
395
390
  ...p.parsed.debtor,
396
391
  holderName: p.parsed.debtor?.holderName ?? req.debtor?.holderName
397
392
  }
393
+ // NOT enriched: chargeBearer, instructionPriority, refId, batchId,
394
+ // createdAt, address, swiftBic — no downstream consumer in payment table
398
395
  }
399
396
  };
400
397
  });
401
398
  const createCommands = enrichedPayments.map(
402
399
  (p) => createPaymentCommand(db, { payment: p.parsed }).command
403
400
  );
404
- const softDeleteCommands = enrichedPayments.map(
405
- (p) => db.update(tables.paymentRequest).set({
406
- deletedAt: now,
407
- status: p.parsed.status,
408
- statusReason: p.parsed.statusReason ?? null
409
- }).where(
410
- or(
411
- // Primary: match via paymentRequestId (our ID echoed back by bank)
412
- p.parsed.paymentRequestId ? eq(tables.paymentRequest.id, p.parsed.paymentRequestId) : void 0,
413
- // Fallback: match via bankRefId (set during processBatch)
414
- and(
415
- isNotNull(tables.paymentRequest.bankRefId),
416
- eq(tables.paymentRequest.bankRefId, p.parsed.bankRefId),
417
- eq(tables.paymentRequest.accountId, account.id),
418
- eq(
419
- tables.paymentRequest.connectorKey,
420
- account.connectorKey
421
- ),
422
- isNull(tables.paymentRequest.deletedAt)
423
- )
424
- )
425
- )
401
+ const prCompleteCommands = matchingRequests.filter(
402
+ (req) => req.status !== "COMPLETED" && req.status !== "FAILED"
403
+ ).map(
404
+ (req) => updatePaymentRequestStatusCommand(db, {
405
+ id: req.id,
406
+ status: "COMPLETED",
407
+ processedAt: /* @__PURE__ */ new Date()
408
+ }).command
426
409
  );
427
410
  eventsToEmit.push(
428
411
  ...enrichedPayments.map((p) => ({
@@ -454,7 +437,7 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
454
437
  asNonEmpty([
455
438
  updateLastSyncCommand,
456
439
  ...createCommands,
457
- ...softDeleteCommands
440
+ ...prCompleteCommands
458
441
  ])
459
442
  );
460
443
  await pushToQueue(
@@ -462,27 +445,6 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
462
445
  eventsToEmit
463
446
  );
464
447
  }
465
- const paymentRequestIds = enrichedPayments.map((p) => p.parsed.paymentRequestId).filter((id) => id != null);
466
- if (paymentRequestIds.length > 0) {
467
- const processedPRs = await db.select({ batchId: tables.paymentRequest.batchId }).from(tables.paymentRequest).where(inArray(tables.paymentRequest.id, paymentRequestIds));
468
- const batchIds = [
469
- ...new Set(
470
- processedPRs.map((pr) => pr.batchId).filter((id) => id != null)
471
- )
472
- ];
473
- for (const batchId of batchIds) {
474
- const allPRsInBatch = await db.select({ deletedAt: tables.paymentRequest.deletedAt }).from(tables.paymentRequest).where(eq(tables.paymentRequest.batchId, batchId));
475
- const allDone = allPRsInBatch.length > 0 && allPRsInBatch.every((pr) => pr.deletedAt != null);
476
- if (allDone) {
477
- const batch = await getBatchByIdQuery(db, { batchId });
478
- if (batch && batch.status !== "COMPLETED" && batch.status !== "FAILED") {
479
- await upsertBatchCommand(db, {
480
- batch: { ...batch, status: "COMPLETED" }
481
- }).command.execute();
482
- }
483
- }
484
- }
485
- }
486
448
  return {
487
449
  ...lastSyncMetadata,
488
450
  newLastSyncAt: now
@@ -1,4 +1,4 @@
1
- import { B as BankServiceWranglerConfig } from '../shared/bank.CQBfbG8u.cjs';
1
+ import { B as BankServiceWranglerConfig } from '../shared/bank.BP_3WMIF.cjs';
2
2
 
3
3
  declare function defineBankServiceWrangler(config: BankServiceWranglerConfig): {
4
4
  vars: {
@@ -9,6 +9,7 @@ declare function defineBankServiceWrangler(config: BankServiceWranglerConfig): {
9
9
  FINBRICKS_BASE_URI: "https://api.sandbox.finbricks.com";
10
10
  FINBRICKS_MERCHANT_ID: string;
11
11
  CRON_BATCH_STATUSES: string;
12
+ CRON_PAYMENT_STATUSES: string;
12
13
  BANK_AUTH_RECIPIENT: string;
13
14
  DBUCS_BASE_URI: string;
14
15
  DBUCS_USERNAME: string;
@@ -1,4 +1,4 @@
1
- import { B as BankServiceWranglerConfig } from '../shared/bank.CQBfbG8u.mjs';
1
+ import { B as BankServiceWranglerConfig } from '../shared/bank.BP_3WMIF.mjs';
2
2
 
3
3
  declare function defineBankServiceWrangler(config: BankServiceWranglerConfig): {
4
4
  vars: {
@@ -9,6 +9,7 @@ declare function defineBankServiceWrangler(config: BankServiceWranglerConfig): {
9
9
  FINBRICKS_BASE_URI: "https://api.sandbox.finbricks.com";
10
10
  FINBRICKS_MERCHANT_ID: string;
11
11
  CRON_BATCH_STATUSES: string;
12
+ CRON_PAYMENT_STATUSES: string;
12
13
  BANK_AUTH_RECIPIENT: string;
13
14
  DBUCS_BASE_URI: string;
14
15
  DBUCS_USERNAME: string;
@@ -1,4 +1,4 @@
1
- import { B as BankServiceWranglerConfig } from '../shared/bank.CQBfbG8u.js';
1
+ import { B as BankServiceWranglerConfig } from '../shared/bank.BP_3WMIF.js';
2
2
 
3
3
  declare function defineBankServiceWrangler(config: BankServiceWranglerConfig): {
4
4
  vars: {
@@ -9,6 +9,7 @@ declare function defineBankServiceWrangler(config: BankServiceWranglerConfig): {
9
9
  FINBRICKS_BASE_URI: "https://api.sandbox.finbricks.com";
10
10
  FINBRICKS_MERCHANT_ID: string;
11
11
  CRON_BATCH_STATUSES: string;
12
+ CRON_PAYMENT_STATUSES: string;
12
13
  BANK_AUTH_RECIPIENT: string;
13
14
  DBUCS_BASE_URI: string;
14
15
  DBUCS_USERNAME: string;
@@ -90,6 +90,12 @@ type ConnectedAccount = {
90
90
  declare abstract class IBankConnector {
91
91
  /** Unique identifier for this bank connector (e.g., 'ERSTE', 'FINBRICKS', 'MOCK') */
92
92
  abstract connectorKey: ConnectorKey;
93
+ /**
94
+ * Lifecycle mode for payment status resolution:
95
+ * - 'batch': statuses are resolved via updateBatchStatuses (getBatchStatus polling)
96
+ * - 'per-payment': statuses are resolved via updatePaymentStatuses (getPaymentStatus polling per payment)
97
+ */
98
+ abstract readonly lifecycleMode: 'batch' | 'per-payment';
93
99
  /** List of bank accounts connected through this connector instance */
94
100
  abstract connectedAccounts: ConnectedAccount[];
95
101
  /** Optional resolver for loading credentials on demand */
@@ -2630,7 +2636,7 @@ declare const batch: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
2630
2636
  tableName: "batch";
2631
2637
  dataType: "string";
2632
2638
  columnType: "SQLiteText";
2633
- data: "COMPLETED" | "SIGNED" | "FAILED" | "OPEN" | "PROCESSING" | "READY_TO_SIGN" | "SIGNATURE_FAILED";
2639
+ data: "OPEN" | "PROCESSING" | "READY_TO_SIGN" | "SIGNED" | "SIGNATURE_FAILED" | "FAILED" | "COMPLETED";
2634
2640
  driverParam: string;
2635
2641
  notNull: false;
2636
2642
  hasDefault: false;
@@ -2643,7 +2649,7 @@ declare const batch: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
2643
2649
  generated: undefined;
2644
2650
  }, {}, {
2645
2651
  length: number | undefined;
2646
- $type: "COMPLETED" | "SIGNED" | "FAILED" | "OPEN" | "PROCESSING" | "READY_TO_SIGN" | "SIGNATURE_FAILED";
2652
+ $type: "OPEN" | "PROCESSING" | "READY_TO_SIGN" | "SIGNED" | "SIGNATURE_FAILED" | "FAILED" | "COMPLETED";
2647
2653
  }>;
2648
2654
  statusReason: drizzle_orm_sqlite_core.SQLiteColumn<{
2649
2655
  name: "status_reason";
@@ -2895,25 +2901,6 @@ declare const payment: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
2895
2901
  }, {}, {
2896
2902
  length: number | undefined;
2897
2903
  }>;
2898
- paymentRequestId: drizzle_orm_sqlite_core.SQLiteColumn<{
2899
- name: "payment_request_id";
2900
- tableName: "payment";
2901
- dataType: "string";
2902
- columnType: "SQLiteText";
2903
- data: string;
2904
- driverParam: string;
2905
- notNull: false;
2906
- hasDefault: false;
2907
- isPrimaryKey: false;
2908
- isAutoincrement: false;
2909
- hasRuntimeDefault: false;
2910
- enumValues: [string, ...string[]];
2911
- baseColumn: never;
2912
- identity: undefined;
2913
- generated: undefined;
2914
- }, {}, {
2915
- length: number | undefined;
2916
- }>;
2917
2904
  refId: drizzle_orm_sqlite_core.SQLiteColumn<{
2918
2905
  name: "ref_id";
2919
2906
  tableName: "payment";
@@ -3073,7 +3060,7 @@ declare const payment: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
3073
3060
  tableName: "payment";
3074
3061
  dataType: "string";
3075
3062
  columnType: "SQLiteText";
3076
- data: "COMPLETED" | "CREATED" | "PREPARED" | "SIGNED" | "PENDING" | "FAILED";
3063
+ data: "SIGNED" | "FAILED" | "COMPLETED" | "CREATED" | "PREPARED" | "PENDING";
3077
3064
  driverParam: string;
3078
3065
  notNull: true;
3079
3066
  hasDefault: false;
@@ -3086,7 +3073,7 @@ declare const payment: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
3086
3073
  generated: undefined;
3087
3074
  }, {}, {
3088
3075
  length: number | undefined;
3089
- $type: "COMPLETED" | "CREATED" | "PREPARED" | "SIGNED" | "PENDING" | "FAILED";
3076
+ $type: "SIGNED" | "FAILED" | "COMPLETED" | "CREATED" | "PREPARED" | "PENDING";
3090
3077
  }>;
3091
3078
  statusReason: drizzle_orm_sqlite_core.SQLiteColumn<{
3092
3079
  name: "status_reason";
@@ -3667,7 +3654,7 @@ declare const paymentRequest: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
3667
3654
  tableName: "payment_request";
3668
3655
  dataType: "string";
3669
3656
  columnType: "SQLiteText";
3670
- data: "COMPLETED" | "CREATED" | "PREPARED" | "SIGNED" | "PENDING" | "FAILED";
3657
+ data: "SIGNED" | "FAILED" | "COMPLETED" | "CREATED" | "PREPARED" | "PENDING";
3671
3658
  driverParam: string;
3672
3659
  notNull: true;
3673
3660
  hasDefault: false;
@@ -3680,7 +3667,7 @@ declare const paymentRequest: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
3680
3667
  generated: undefined;
3681
3668
  }, {}, {
3682
3669
  length: number | undefined;
3683
- $type: "COMPLETED" | "CREATED" | "PREPARED" | "SIGNED" | "PENDING" | "FAILED";
3670
+ $type: "SIGNED" | "FAILED" | "COMPLETED" | "CREATED" | "PREPARED" | "PENDING";
3684
3671
  }>;
3685
3672
  statusReason: drizzle_orm_sqlite_core.SQLiteColumn<{
3686
3673
  name: "status_reason";