@develit-services/bank 0.8.5 → 0.8.7
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/database/schema.cjs +1 -1
- package/dist/database/schema.d.cts +1 -1
- package/dist/database/schema.d.mts +1 -1
- package/dist/database/schema.d.ts +1 -1
- package/dist/database/schema.mjs +1 -1
- package/dist/export/worker.cjs +583 -146
- package/dist/export/worker.d.cts +286 -44
- package/dist/export/worker.d.mts +286 -44
- package/dist/export/worker.d.ts +286 -44
- package/dist/export/worker.mjs +584 -147
- package/dist/export/workflows.cjs +132 -16
- package/dist/export/workflows.mjs +133 -17
- package/dist/shared/{bank.C-T1FQxg.cjs → bank.62VzK9Aj.cjs} +1 -1
- package/dist/shared/{bank.SQ4Mmr8u.cjs → bank.BS7fFjGA.cjs} +34 -4
- package/dist/shared/{bank.6faPHUEY.d.ts → bank.BYRq3yJf.d.ts} +81 -11
- package/dist/shared/{bank.C4VOdIx1.mjs → bank.C0UN6luZ.mjs} +34 -4
- package/dist/shared/{bank.DDHrdFgy.mjs → bank.CA5ytXxp.mjs} +1 -1
- package/dist/shared/{bank.BoWCMu5n.d.cts → bank.CO89tR9U.d.cts} +81 -11
- package/dist/shared/{bank.BaMRCHk-.d.ts → bank.Cns5ss41.d.cts} +55 -18
- package/dist/shared/{bank.BaMRCHk-.d.cts → bank.Cns5ss41.d.mts} +55 -18
- package/dist/shared/{bank.BaMRCHk-.d.mts → bank.Cns5ss41.d.ts} +55 -18
- package/dist/shared/{bank.DOL1uM4n.d.mts → bank.CreoSb2d.d.mts} +81 -11
- package/dist/shared/{bank.DRrBrAdI.mjs → bank.D1jqaHaF.mjs} +91 -31
- package/dist/shared/{bank.Cpy9PULF.mjs → bank.DEmzZGZW.mjs} +31 -5
- package/dist/shared/{bank.BOnP9p9Y.cjs → bank.Dm8GHThw.cjs} +31 -5
- package/dist/shared/{bank.CQURey1E.cjs → bank.DwyCCyd0.cjs} +90 -31
- package/dist/types.cjs +3 -4
- package/dist/types.d.cts +8 -16
- package/dist/types.d.mts +8 -16
- package/dist/types.d.ts +8 -16
- package/dist/types.mjs +3 -3
- package/package.json +2 -2
|
@@ -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.
|
|
4
|
+
const drizzle = require('../shared/bank.Dm8GHThw.cjs');
|
|
5
5
|
const batchLifecycle = require('../shared/bank.Bg3Pdwm4.cjs');
|
|
6
|
-
const mock_connector = require('../shared/bank.
|
|
6
|
+
const mock_connector = require('../shared/bank.DwyCCyd0.cjs');
|
|
7
7
|
const drizzleOrm = require('drizzle-orm');
|
|
8
|
+
require('drizzle-orm/sqlite-core');
|
|
8
9
|
const cloudflare_workers = require('cloudflare:workers');
|
|
9
10
|
const cloudflare_workflows = require('cloudflare:workflows');
|
|
10
11
|
const d1 = require('drizzle-orm/d1');
|
|
11
12
|
require('node:crypto');
|
|
12
|
-
require('../shared/bank.
|
|
13
|
+
require('../shared/bank.BS7fFjGA.cjs');
|
|
13
14
|
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.
|
|
19
|
+
require('../shared/bank.62VzK9Aj.cjs');
|
|
20
20
|
|
|
21
21
|
const updateAccountLastSyncCommand = (db, {
|
|
22
22
|
lastSyncAt,
|
|
@@ -92,13 +92,18 @@ class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
92
92
|
const batchedPayments = paymentRequests.map(
|
|
93
93
|
mock_connector.toBatchedPaymentFromPaymentRequest
|
|
94
94
|
);
|
|
95
|
-
|
|
95
|
+
const currentBatch = await drizzle.getBatchByIdQuery(db, { batchId });
|
|
96
|
+
if (currentBatch?.batchPaymentInitiatedAt) {
|
|
96
97
|
return {
|
|
97
|
-
authorizationUrls:
|
|
98
|
+
authorizationUrls: currentBatch.authorizationUrls,
|
|
98
99
|
preparedPayments: batchedPayments.map(
|
|
99
|
-
(p) => mock_connector.toPreparedPayment(
|
|
100
|
+
(p) => mock_connector.toPreparedPayment(
|
|
101
|
+
p,
|
|
102
|
+
void 0,
|
|
103
|
+
currentBatch.batchPaymentInitiatedAt
|
|
104
|
+
)
|
|
100
105
|
),
|
|
101
|
-
metadata:
|
|
106
|
+
metadata: currentBatch.metadata
|
|
102
107
|
};
|
|
103
108
|
}
|
|
104
109
|
const account = await drizzle.getAccountByIdQuery(db, {
|
|
@@ -138,6 +143,17 @@ class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
138
143
|
const message = err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : String(err);
|
|
139
144
|
const status = typeof err === "object" && err !== null && "status" in err ? Number(err.status) : 0;
|
|
140
145
|
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
|
+
}
|
|
141
157
|
throw new cloudflare_workflows.NonRetryableError(message);
|
|
142
158
|
}
|
|
143
159
|
throw new Error(message);
|
|
@@ -201,7 +217,8 @@ class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
201
217
|
id: p.id,
|
|
202
218
|
status: "COMPLETED",
|
|
203
219
|
bankRefId: `MOCK-${backendSdk.uuidv4()}`,
|
|
204
|
-
processedAt
|
|
220
|
+
processedAt,
|
|
221
|
+
deletedAt: /* @__PURE__ */ new Date()
|
|
205
222
|
}).command
|
|
206
223
|
);
|
|
207
224
|
const updateBatchCommand = drizzle.upsertBatchCommand(db, {
|
|
@@ -330,11 +347,87 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
330
347
|
"process new payments and update lastSyncAt",
|
|
331
348
|
async () => {
|
|
332
349
|
const eventsToEmit = [];
|
|
333
|
-
const
|
|
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)
|
|
363
|
+
)
|
|
364
|
+
) : Promise.resolve([])
|
|
365
|
+
]);
|
|
366
|
+
const requestById = Object.fromEntries(
|
|
367
|
+
matchingByPrId.map((r) => [r.id, r])
|
|
368
|
+
);
|
|
369
|
+
const requestByBankRefId = Object.fromEntries(
|
|
370
|
+
matchingByBankRefId.map((r) => [r.bankRefId, r])
|
|
371
|
+
);
|
|
372
|
+
const enrichedPayments = paymentsToInsert.map((p) => {
|
|
373
|
+
const req = p.parsed.paymentRequestId ? requestById[p.parsed.paymentRequestId] : requestByBankRefId[p.parsed.bankRefId];
|
|
374
|
+
if (!req) return p;
|
|
375
|
+
return {
|
|
376
|
+
...p,
|
|
377
|
+
parsed: {
|
|
378
|
+
...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,
|
|
387
|
+
vs: p.parsed.vs ?? req.vs,
|
|
388
|
+
ss: p.parsed.ss ?? req.ss,
|
|
389
|
+
ks: p.parsed.ks ?? req.ks,
|
|
390
|
+
creditor: {
|
|
391
|
+
...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
|
|
395
|
+
},
|
|
396
|
+
debtor: {
|
|
397
|
+
...p.parsed.debtor,
|
|
398
|
+
holderName: p.parsed.debtor?.holderName ?? req.debtor?.holderName
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
});
|
|
403
|
+
const createCommands = enrichedPayments.map(
|
|
334
404
|
(p) => drizzle.createPaymentCommand(db, { payment: p.parsed }).command
|
|
335
405
|
);
|
|
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
|
+
)
|
|
428
|
+
);
|
|
336
429
|
eventsToEmit.push(
|
|
337
|
-
...
|
|
430
|
+
...enrichedPayments.map((p) => ({
|
|
338
431
|
eventType: "BANK_PAYMENT",
|
|
339
432
|
eventSignal: "paymentCreated",
|
|
340
433
|
bankPayment: p.parsed,
|
|
@@ -349,9 +442,7 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
349
442
|
payments: payments.length,
|
|
350
443
|
paymentsToProcess: paymentsToProcess.length,
|
|
351
444
|
paymentsInserted: paymentsToInsert.length,
|
|
352
|
-
lastSyncBankRefIds: paymentsToProcess.map(
|
|
353
|
-
(p) => p.parsed.bankRefId
|
|
354
|
-
),
|
|
445
|
+
lastSyncBankRefIds: paymentsToProcess.filter((p) => p.parsed.status === "COMPLETED").map((p) => p.parsed.bankRefId),
|
|
355
446
|
lastSyncPayments: lastSyncBankRefIds.length,
|
|
356
447
|
eventsEmitted: eventsToEmit.length
|
|
357
448
|
};
|
|
@@ -362,13 +453,38 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
362
453
|
}).command;
|
|
363
454
|
if (eventsToEmit.length) {
|
|
364
455
|
await db.batch(
|
|
365
|
-
backendSdk.asNonEmpty([
|
|
456
|
+
backendSdk.asNonEmpty([
|
|
457
|
+
updateLastSyncCommand,
|
|
458
|
+
...createCommands,
|
|
459
|
+
...softDeleteCommands
|
|
460
|
+
])
|
|
366
461
|
);
|
|
367
462
|
await pushToQueue(
|
|
368
463
|
this.env.QUEUE_BUS_QUEUE,
|
|
369
464
|
eventsToEmit
|
|
370
465
|
);
|
|
371
466
|
}
|
|
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
|
+
}
|
|
372
488
|
return {
|
|
373
489
|
...lastSyncMetadata,
|
|
374
490
|
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.
|
|
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';
|
|
3
3
|
import { f as isBatchSigned, a as isBatchFailed, c as isBatchOpen, g as isPaymentCompleted } from '../shared/bank.CbAwwIhZ.mjs';
|
|
4
|
-
import {
|
|
5
|
-
import { eq } from 'drizzle-orm';
|
|
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';
|
|
6
7
|
import { WorkflowEntrypoint } from 'cloudflare:workers';
|
|
7
8
|
import { NonRetryableError } from 'cloudflare:workflows';
|
|
8
9
|
import { drizzle } from 'drizzle-orm/d1';
|
|
9
10
|
import 'node:crypto';
|
|
10
|
-
import '../shared/bank.
|
|
11
|
+
import '../shared/bank.C0UN6luZ.mjs';
|
|
11
12
|
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.
|
|
17
|
+
import '../shared/bank.CA5ytXxp.mjs';
|
|
18
18
|
|
|
19
19
|
const updateAccountLastSyncCommand = (db, {
|
|
20
20
|
lastSyncAt,
|
|
@@ -90,13 +90,18 @@ class BankProcessBatch extends WorkflowEntrypoint {
|
|
|
90
90
|
const batchedPayments = paymentRequests.map(
|
|
91
91
|
toBatchedPaymentFromPaymentRequest
|
|
92
92
|
);
|
|
93
|
-
|
|
93
|
+
const currentBatch = await getBatchByIdQuery(db, { batchId });
|
|
94
|
+
if (currentBatch?.batchPaymentInitiatedAt) {
|
|
94
95
|
return {
|
|
95
|
-
authorizationUrls:
|
|
96
|
+
authorizationUrls: currentBatch.authorizationUrls,
|
|
96
97
|
preparedPayments: batchedPayments.map(
|
|
97
|
-
(p) => toPreparedPayment(
|
|
98
|
+
(p) => toPreparedPayment(
|
|
99
|
+
p,
|
|
100
|
+
void 0,
|
|
101
|
+
currentBatch.batchPaymentInitiatedAt
|
|
102
|
+
)
|
|
98
103
|
),
|
|
99
|
-
metadata:
|
|
104
|
+
metadata: currentBatch.metadata
|
|
100
105
|
};
|
|
101
106
|
}
|
|
102
107
|
const account = await getAccountByIdQuery(db, {
|
|
@@ -136,6 +141,17 @@ class BankProcessBatch extends WorkflowEntrypoint {
|
|
|
136
141
|
const message = err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : String(err);
|
|
137
142
|
const status = typeof err === "object" && err !== null && "status" in err ? Number(err.status) : 0;
|
|
138
143
|
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
|
+
}
|
|
139
155
|
throw new NonRetryableError(message);
|
|
140
156
|
}
|
|
141
157
|
throw new Error(message);
|
|
@@ -199,7 +215,8 @@ class BankProcessBatch extends WorkflowEntrypoint {
|
|
|
199
215
|
id: p.id,
|
|
200
216
|
status: "COMPLETED",
|
|
201
217
|
bankRefId: `MOCK-${uuidv4()}`,
|
|
202
|
-
processedAt
|
|
218
|
+
processedAt,
|
|
219
|
+
deletedAt: /* @__PURE__ */ new Date()
|
|
203
220
|
}).command
|
|
204
221
|
);
|
|
205
222
|
const updateBatchCommand = upsertBatchCommand(db, {
|
|
@@ -328,11 +345,87 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
|
|
|
328
345
|
"process new payments and update lastSyncAt",
|
|
329
346
|
async () => {
|
|
330
347
|
const eventsToEmit = [];
|
|
331
|
-
const
|
|
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)
|
|
361
|
+
)
|
|
362
|
+
) : Promise.resolve([])
|
|
363
|
+
]);
|
|
364
|
+
const requestById = Object.fromEntries(
|
|
365
|
+
matchingByPrId.map((r) => [r.id, r])
|
|
366
|
+
);
|
|
367
|
+
const requestByBankRefId = Object.fromEntries(
|
|
368
|
+
matchingByBankRefId.map((r) => [r.bankRefId, r])
|
|
369
|
+
);
|
|
370
|
+
const enrichedPayments = paymentsToInsert.map((p) => {
|
|
371
|
+
const req = p.parsed.paymentRequestId ? requestById[p.parsed.paymentRequestId] : requestByBankRefId[p.parsed.bankRefId];
|
|
372
|
+
if (!req) return p;
|
|
373
|
+
return {
|
|
374
|
+
...p,
|
|
375
|
+
parsed: {
|
|
376
|
+
...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,
|
|
385
|
+
vs: p.parsed.vs ?? req.vs,
|
|
386
|
+
ss: p.parsed.ss ?? req.ss,
|
|
387
|
+
ks: p.parsed.ks ?? req.ks,
|
|
388
|
+
creditor: {
|
|
389
|
+
...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
|
|
393
|
+
},
|
|
394
|
+
debtor: {
|
|
395
|
+
...p.parsed.debtor,
|
|
396
|
+
holderName: p.parsed.debtor?.holderName ?? req.debtor?.holderName
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
});
|
|
401
|
+
const createCommands = enrichedPayments.map(
|
|
332
402
|
(p) => createPaymentCommand(db, { payment: p.parsed }).command
|
|
333
403
|
);
|
|
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
|
+
)
|
|
426
|
+
);
|
|
334
427
|
eventsToEmit.push(
|
|
335
|
-
...
|
|
428
|
+
...enrichedPayments.map((p) => ({
|
|
336
429
|
eventType: "BANK_PAYMENT",
|
|
337
430
|
eventSignal: "paymentCreated",
|
|
338
431
|
bankPayment: p.parsed,
|
|
@@ -347,9 +440,7 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
|
|
|
347
440
|
payments: payments.length,
|
|
348
441
|
paymentsToProcess: paymentsToProcess.length,
|
|
349
442
|
paymentsInserted: paymentsToInsert.length,
|
|
350
|
-
lastSyncBankRefIds: paymentsToProcess.map(
|
|
351
|
-
(p) => p.parsed.bankRefId
|
|
352
|
-
),
|
|
443
|
+
lastSyncBankRefIds: paymentsToProcess.filter((p) => p.parsed.status === "COMPLETED").map((p) => p.parsed.bankRefId),
|
|
353
444
|
lastSyncPayments: lastSyncBankRefIds.length,
|
|
354
445
|
eventsEmitted: eventsToEmit.length
|
|
355
446
|
};
|
|
@@ -360,13 +451,38 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
|
|
|
360
451
|
}).command;
|
|
361
452
|
if (eventsToEmit.length) {
|
|
362
453
|
await db.batch(
|
|
363
|
-
asNonEmpty([
|
|
454
|
+
asNonEmpty([
|
|
455
|
+
updateLastSyncCommand,
|
|
456
|
+
...createCommands,
|
|
457
|
+
...softDeleteCommands
|
|
458
|
+
])
|
|
364
459
|
);
|
|
365
460
|
await pushToQueue(
|
|
366
461
|
this.env.QUEUE_BUS_QUEUE,
|
|
367
462
|
eventsToEmit
|
|
368
463
|
);
|
|
369
464
|
}
|
|
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
|
+
}
|
|
370
486
|
return {
|
|
371
487
|
...lastSyncMetadata,
|
|
372
488
|
newLastSyncAt: now
|
|
@@ -28,7 +28,8 @@ const BATCH_STATUSES = [
|
|
|
28
28
|
"READY_TO_SIGN",
|
|
29
29
|
"SIGNED",
|
|
30
30
|
"SIGNATURE_FAILED",
|
|
31
|
-
"FAILED"
|
|
31
|
+
"FAILED",
|
|
32
|
+
"COMPLETED"
|
|
32
33
|
];
|
|
33
34
|
const ACCOUNT_STATUSES = ["AUTHORIZED", "DISABLED", "EXPIRED"];
|
|
34
35
|
const COUNTRY_CODES = generalCodes.COUNTRY_CODES_2;
|
|
@@ -139,6 +140,7 @@ const payment = sqliteCore.sqliteTable(
|
|
|
139
140
|
{
|
|
140
141
|
...backendSdk.base,
|
|
141
142
|
correlationId: sqliteCore.text("correlation_id").notNull(),
|
|
143
|
+
paymentRequestId: sqliteCore.text("payment_request_id"),
|
|
142
144
|
refId: sqliteCore.text("ref_id"),
|
|
143
145
|
bankRefId: sqliteCore.text("bank_ref_id").notNull(),
|
|
144
146
|
accountId: sqliteCore.text("account_id").references(() => account.id, {
|
|
@@ -174,7 +176,18 @@ const payment = sqliteCore.sqliteTable(
|
|
|
174
176
|
debtor: sqliteCore.text("debtor", { mode: "json" }).$type().notNull(),
|
|
175
177
|
debtorIban: sqliteCore.text("debtor_iban")
|
|
176
178
|
},
|
|
177
|
-
(t) => [
|
|
179
|
+
(t) => [
|
|
180
|
+
sqliteCore.unique().on(t.connectorKey, t.bankRefId),
|
|
181
|
+
sqliteCore.index("payment_account_id_idx").on(t.accountId),
|
|
182
|
+
sqliteCore.index("payment_account_id_status_idx").on(t.accountId, t.status),
|
|
183
|
+
sqliteCore.index("payment_account_id_created_at_idx").on(t.accountId, t.createdAt),
|
|
184
|
+
sqliteCore.index("payment_created_at_idx").on(t.createdAt),
|
|
185
|
+
sqliteCore.index("payment_payment_request_id_idx").on(t.paymentRequestId),
|
|
186
|
+
sqliteCore.index("payment_direction_idx").on(t.direction),
|
|
187
|
+
sqliteCore.index("payment_batch_id_idx").on(t.batchId),
|
|
188
|
+
sqliteCore.index("payment_creditor_iban_idx").on(t.creditorIban),
|
|
189
|
+
sqliteCore.index("payment_debtor_iban_idx").on(t.debtorIban)
|
|
190
|
+
]
|
|
178
191
|
);
|
|
179
192
|
const paymentRelations = relations.relations(payment, ({ one }) => ({
|
|
180
193
|
batch: one(batch, { fields: [payment.batchId], references: [batch.id] })
|
|
@@ -217,9 +230,26 @@ const paymentRequest = sqliteCore.sqliteTable(
|
|
|
217
230
|
creditor: sqliteCore.text("creditor", { mode: "json" }).$type().notNull(),
|
|
218
231
|
creditorIban: sqliteCore.text("creditor_iban"),
|
|
219
232
|
debtor: sqliteCore.text("debtor", { mode: "json" }).$type().notNull(),
|
|
220
|
-
debtorIban: sqliteCore.text("debtor_iban")
|
|
233
|
+
debtorIban: sqliteCore.text("debtor_iban"),
|
|
234
|
+
sendAsSinglePayment: sqliteCore.integer("send_as_single_payment", {
|
|
235
|
+
mode: "boolean"
|
|
236
|
+
})
|
|
221
237
|
},
|
|
222
|
-
(t) => [
|
|
238
|
+
(t) => [
|
|
239
|
+
sqliteCore.index("payment_request_batch_id_idx").on(t.batchId),
|
|
240
|
+
sqliteCore.index("payment_request_account_id_idx").on(t.accountId),
|
|
241
|
+
sqliteCore.index("payment_request_creditor_iban_idx").on(t.creditorIban),
|
|
242
|
+
sqliteCore.index("payment_request_debtor_iban_idx").on(t.debtorIban),
|
|
243
|
+
sqliteCore.index("payment_request_status_idx").on(t.status),
|
|
244
|
+
sqliteCore.index("payment_request_created_at_idx").on(t.createdAt),
|
|
245
|
+
sqliteCore.index("payment_request_account_status_idx").on(t.accountId, t.status),
|
|
246
|
+
sqliteCore.index("payment_request_status_created_at_idx").on(t.status, t.createdAt),
|
|
247
|
+
sqliteCore.index("payment_request_account_status_created_at_idx").on(
|
|
248
|
+
t.accountId,
|
|
249
|
+
t.status,
|
|
250
|
+
t.createdAt
|
|
251
|
+
)
|
|
252
|
+
]
|
|
223
253
|
);
|
|
224
254
|
const paymentRequestRelations = relations.relations(paymentRequest, ({ one }) => ({
|
|
225
255
|
batch: one(batch, {
|