@develit-services/bank 0.8.6 → 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.
- 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 +550 -153
- package/dist/export/worker.d.cts +259 -47
- package/dist/export/worker.d.mts +259 -47
- package/dist/export/worker.d.ts +259 -47
- package/dist/export/worker.mjs +551 -154
- package/dist/export/workflows.cjs +95 -17
- package/dist/export/workflows.mjs +96 -18
- package/dist/export/wrangler.d.cts +2 -1
- package/dist/export/wrangler.d.mts +2 -1
- package/dist/export/wrangler.d.ts +2 -1
- package/dist/shared/{bank.BliD3oCT.d.ts → bank.B-NJB8GB.d.cts} +34 -10
- package/dist/shared/{bank.BliD3oCT.d.cts → bank.B-NJB8GB.d.mts} +34 -10
- package/dist/shared/{bank.BliD3oCT.d.mts → bank.B-NJB8GB.d.ts} +34 -10
- package/dist/shared/{bank.Cpy9PULF.mjs → bank.B5bZRvgq.mjs} +28 -5
- package/dist/shared/{bank.CQBfbG8u.d.cts → bank.BP_3WMIF.d.cts} +1 -0
- package/dist/shared/{bank.CQBfbG8u.d.mts → bank.BP_3WMIF.d.mts} +1 -0
- package/dist/shared/{bank.CQBfbG8u.d.ts → bank.BP_3WMIF.d.ts} +1 -0
- package/dist/shared/{bank.DDHrdFgy.mjs → bank.BoZtXQpG.mjs} +1 -1
- package/dist/shared/{bank.DRrBrAdI.mjs → bank.BtszLapg.mjs} +211 -43
- package/dist/shared/{bank.BoMDujsl.d.ts → bank.BzobShUU.d.cts} +17 -7
- package/dist/shared/{bank.C4VOdIx1.mjs → bank.C6jjS1Pl.mjs} +32 -4
- package/dist/shared/{bank.lbzMqyr3.d.cts → bank.CAVvvZZO.d.mts} +17 -7
- package/dist/shared/{bank.CQURey1E.cjs → bank.CtnsGHM8.cjs} +210 -43
- package/dist/shared/{bank.C-T1FQxg.cjs → bank.DJnDSYqE.cjs} +1 -1
- package/dist/shared/{bank.B6U8sUZn.d.mts → bank.DRTuKO8S.d.ts} +17 -7
- package/dist/shared/{bank.BOnP9p9Y.cjs → bank.DT6bg8k5.cjs} +28 -5
- package/dist/shared/{bank.SQ4Mmr8u.cjs → bank.JVlyPAAb.cjs} +32 -4
- package/dist/types.cjs +2 -3
- package/dist/types.d.cts +14 -18
- package/dist/types.d.mts +14 -18
- package/dist/types.d.ts +14 -18
- package/dist/types.mjs +2 -2
- package/package.json +2 -2
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const backendSdk = require('@develit-io/backend-sdk');
|
|
4
4
|
const dateFns = require('date-fns');
|
|
5
5
|
const generalCodes = require('@develit-io/general-codes');
|
|
6
|
-
require('./bank.
|
|
6
|
+
require('./bank.JVlyPAAb.cjs');
|
|
7
7
|
require('drizzle-orm');
|
|
8
8
|
const jose = require('jose');
|
|
9
9
|
require('node:crypto');
|
|
@@ -62,9 +62,6 @@ function toCompletedPayment(payment, status, bankRefId, processedAt) {
|
|
|
62
62
|
processedAt: processedAt ?? /* @__PURE__ */ new Date()
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
-
function batchTransform(payments, transformer) {
|
|
66
|
-
return payments.map(transformer);
|
|
67
|
-
}
|
|
68
65
|
function toPaymentRequestInsert(payment, batchId) {
|
|
69
66
|
return {
|
|
70
67
|
id: payment.id,
|
|
@@ -86,7 +83,8 @@ function toPaymentRequestInsert(payment, batchId) {
|
|
|
86
83
|
creditor: payment.creditor,
|
|
87
84
|
creditorIban: payment.creditorIban ?? null,
|
|
88
85
|
debtor: payment.debtor,
|
|
89
|
-
debtorIban: payment.debtorIban ?? null
|
|
86
|
+
debtorIban: payment.debtorIban ?? null,
|
|
87
|
+
sendAsSinglePayment: payment.sendAsSinglePayment ?? null
|
|
90
88
|
};
|
|
91
89
|
}
|
|
92
90
|
function toBatchedPaymentFromPaymentRequest(sp) {
|
|
@@ -119,6 +117,9 @@ function toBatchedPaymentFromPaymentRequest(sp) {
|
|
|
119
117
|
}
|
|
120
118
|
|
|
121
119
|
class IBankConnector {
|
|
120
|
+
supportsPaymentType(paymentType) {
|
|
121
|
+
return paymentType === "DOMESTIC";
|
|
122
|
+
}
|
|
122
123
|
// ── Deprecated methods (backward compatibility) ────────────────────
|
|
123
124
|
/**
|
|
124
125
|
* @deprecated Use initiateDomesticBatch, initiateSEPABatch, or initiateForeignBatch instead.
|
|
@@ -206,6 +207,17 @@ class IBankConnector {
|
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
|
|
210
|
+
function buildEndToEndId(payment) {
|
|
211
|
+
const parts = [];
|
|
212
|
+
if (payment.vs) parts.push(`VS${payment.vs}`);
|
|
213
|
+
if (payment.ss) parts.push(`SS${payment.ss}`);
|
|
214
|
+
if (payment.ks) parts.push(`KS${payment.ks}`);
|
|
215
|
+
if (parts.length === 0) {
|
|
216
|
+
return payment.id.replace(/-/g, "");
|
|
217
|
+
}
|
|
218
|
+
return `/${parts.join("/")}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
209
221
|
async function signFinbricksJws({
|
|
210
222
|
privateKeyPem,
|
|
211
223
|
merchantId,
|
|
@@ -262,6 +274,18 @@ const useFinbricksFetch = async (config, init) => {
|
|
|
262
274
|
if (correlationId) {
|
|
263
275
|
headers["Correlation-ID"] = correlationId;
|
|
264
276
|
}
|
|
277
|
+
console.log(
|
|
278
|
+
"[Finbricks] request",
|
|
279
|
+
JSON.stringify(
|
|
280
|
+
{
|
|
281
|
+
method,
|
|
282
|
+
url: url.toString(),
|
|
283
|
+
body: body ?? null
|
|
284
|
+
},
|
|
285
|
+
null,
|
|
286
|
+
2
|
|
287
|
+
)
|
|
288
|
+
);
|
|
265
289
|
const res = await fetch(url.toString(), {
|
|
266
290
|
method,
|
|
267
291
|
headers,
|
|
@@ -269,11 +293,30 @@ const useFinbricksFetch = async (config, init) => {
|
|
|
269
293
|
});
|
|
270
294
|
if (!res.ok) {
|
|
271
295
|
const text = await res.text().catch(() => "unknown error");
|
|
296
|
+
console.error("[Finbricks] error response", {
|
|
297
|
+
status: res.status,
|
|
298
|
+
statusText: res.statusText,
|
|
299
|
+
url: url.toString(),
|
|
300
|
+
body: text
|
|
301
|
+
});
|
|
272
302
|
throw new Error(
|
|
273
303
|
`Finbricks API error: ${res.status} ${res.statusText} \u2013 ${text}`
|
|
274
304
|
);
|
|
275
305
|
}
|
|
276
|
-
|
|
306
|
+
const json = await res.json();
|
|
307
|
+
console.log(
|
|
308
|
+
"[Finbricks] response",
|
|
309
|
+
JSON.stringify(
|
|
310
|
+
{
|
|
311
|
+
status: res.status,
|
|
312
|
+
url: url.toString(),
|
|
313
|
+
body: json
|
|
314
|
+
},
|
|
315
|
+
null,
|
|
316
|
+
2
|
|
317
|
+
)
|
|
318
|
+
);
|
|
319
|
+
return json;
|
|
277
320
|
};
|
|
278
321
|
class FinbricksClient {
|
|
279
322
|
constructor(baseUrl, merchantId, privateKeyPem, redirectUri) {
|
|
@@ -328,16 +371,16 @@ const mapFinbricksStatus = (status) => {
|
|
|
328
371
|
return "PENDING";
|
|
329
372
|
}
|
|
330
373
|
};
|
|
331
|
-
const mapFinbricksTransactionStatus = (status) => {
|
|
374
|
+
const mapFinbricksTransactionStatus = (status, finalBankStatus) => {
|
|
332
375
|
switch (status) {
|
|
333
376
|
case "OPENED":
|
|
334
|
-
return "
|
|
377
|
+
return "PREPARED";
|
|
335
378
|
case "AUTHORIZED":
|
|
336
|
-
return "PENDING";
|
|
379
|
+
return finalBankStatus ? "COMPLETED" : "PENDING";
|
|
337
380
|
case "COMPLETED":
|
|
338
|
-
return "COMPLETED";
|
|
381
|
+
return finalBankStatus ? "COMPLETED" : "PENDING";
|
|
339
382
|
case "BOOKED":
|
|
340
|
-
return "PENDING";
|
|
383
|
+
return finalBankStatus ? "COMPLETED" : "PENDING";
|
|
341
384
|
case "SETTLED":
|
|
342
385
|
return "COMPLETED";
|
|
343
386
|
case "REJECTED":
|
|
@@ -391,24 +434,81 @@ const mapReferencesToPayment = (reference) => {
|
|
|
391
434
|
return symbols;
|
|
392
435
|
};
|
|
393
436
|
|
|
437
|
+
const SEPA_COUNTRIES = /* @__PURE__ */ new Set([
|
|
438
|
+
"AT",
|
|
439
|
+
"BE",
|
|
440
|
+
"BG",
|
|
441
|
+
"CY",
|
|
442
|
+
"CZ",
|
|
443
|
+
"DE",
|
|
444
|
+
"DK",
|
|
445
|
+
"EE",
|
|
446
|
+
"ES",
|
|
447
|
+
"FI",
|
|
448
|
+
"FR",
|
|
449
|
+
"GR",
|
|
450
|
+
"HR",
|
|
451
|
+
"HU",
|
|
452
|
+
"IE",
|
|
453
|
+
"IT",
|
|
454
|
+
"LT",
|
|
455
|
+
"LU",
|
|
456
|
+
"LV",
|
|
457
|
+
"MT",
|
|
458
|
+
"NL",
|
|
459
|
+
"PL",
|
|
460
|
+
"PT",
|
|
461
|
+
"RO",
|
|
462
|
+
"SE",
|
|
463
|
+
"SI",
|
|
464
|
+
"SK",
|
|
465
|
+
"IS",
|
|
466
|
+
"LI",
|
|
467
|
+
"NO",
|
|
468
|
+
"CH",
|
|
469
|
+
"GB",
|
|
470
|
+
"MC",
|
|
471
|
+
"SM",
|
|
472
|
+
"VA",
|
|
473
|
+
"AD",
|
|
474
|
+
"GI",
|
|
475
|
+
"XK"
|
|
476
|
+
]);
|
|
477
|
+
function detectPaymentType(tx, isIncoming) {
|
|
478
|
+
const related = tx.entryDetails?.transactionDetails?.relatedParties;
|
|
479
|
+
const otherParty = isIncoming ? related?.debtorAccount : related?.creditorAccount;
|
|
480
|
+
if (otherParty?.identification?.other?.identification) return "DOMESTIC";
|
|
481
|
+
const otherIban = otherParty?.identification?.iban;
|
|
482
|
+
const otherCountry = otherIban?.slice(0, 2);
|
|
483
|
+
if (otherCountry === "CZ") return "DOMESTIC";
|
|
484
|
+
if (otherIban && otherCountry) {
|
|
485
|
+
if (SEPA_COUNTRIES.has(otherCountry) && tx.amount?.currency === "EUR")
|
|
486
|
+
return "SEPA";
|
|
487
|
+
return "SWIFT";
|
|
488
|
+
}
|
|
489
|
+
if (tx.amount?.currency === "EUR") return "SEPA";
|
|
490
|
+
if (tx.amount?.currency !== "CZK") return "SWIFT";
|
|
491
|
+
return "DOMESTIC";
|
|
492
|
+
}
|
|
394
493
|
const mapFinbricksTransactionToPayment = (tx, account) => {
|
|
395
494
|
const isIncoming = tx.creditDebitIndicator === "CRDT";
|
|
396
495
|
const related = tx.entryDetails?.transactionDetails?.relatedParties;
|
|
496
|
+
const endToEndId = tx.entryDetails.transactionDetails?.references?.endToEndIdentification;
|
|
497
|
+
const symbolsRegex = /^\/VS\d+/;
|
|
397
498
|
const base = {
|
|
398
499
|
id: backendSdk.uuidv4(),
|
|
399
500
|
correlationId: backendSdk.uuidv4(),
|
|
400
|
-
//TODO(kleinpetr): use real correlationId once available
|
|
401
501
|
connectorKey: account.connectorKey,
|
|
402
502
|
accountId: account.id,
|
|
403
503
|
bankRefId: tx.fbxReference,
|
|
404
504
|
amount: tx.amount?.value || 0,
|
|
405
505
|
currency: tx.amount?.currency || "CZK",
|
|
406
|
-
paymentType:
|
|
506
|
+
paymentType: detectPaymentType(tx, isIncoming),
|
|
407
507
|
status: mapFinbricksStatus(tx.status),
|
|
408
508
|
message: tx.entryDetails.transactionDetails?.remittanceInformation?.unstructured || tx.entryDetails.transactionDetails?.additionalRemittanceInformation || tx.entryDetails.transactionDetails?.additionalTransactionInformation || null,
|
|
409
509
|
processedAt: new Date(tx.bookingDate.date),
|
|
410
510
|
...mapReferencesToPayment(
|
|
411
|
-
tx.entryDetails.transactionDetails?.remittanceInformation?.structured?.creditorReferenceInformation?.reference ||
|
|
511
|
+
tx.entryDetails.transactionDetails?.remittanceInformation?.structured?.creditorReferenceInformation?.reference || (endToEndId && symbolsRegex.test(endToEndId) ? endToEndId : void 0)
|
|
412
512
|
),
|
|
413
513
|
creditor: {
|
|
414
514
|
holderName: related?.creditorAccount?.name || related?.creditor?.name || "Unknown",
|
|
@@ -435,6 +535,13 @@ const mapFinbricksTransactionToPayment = (tx, account) => {
|
|
|
435
535
|
return base;
|
|
436
536
|
};
|
|
437
537
|
|
|
538
|
+
function autoVariableSymbol(paymentId) {
|
|
539
|
+
let hash = 0;
|
|
540
|
+
for (let i = 0; i < paymentId.length; i++) {
|
|
541
|
+
hash = (hash << 5) - hash + paymentId.charCodeAt(i) | 0;
|
|
542
|
+
}
|
|
543
|
+
return String(Math.abs(hash) % 1e10).padStart(10, "0");
|
|
544
|
+
}
|
|
438
545
|
class FinbricksConnector extends IBankConnector {
|
|
439
546
|
constructor(provider, {
|
|
440
547
|
BASE_URI,
|
|
@@ -446,6 +553,7 @@ class FinbricksConnector extends IBankConnector {
|
|
|
446
553
|
}) {
|
|
447
554
|
super();
|
|
448
555
|
this.connectorKey = "FINBRICKS";
|
|
556
|
+
this.lifecycleMode = "batch";
|
|
449
557
|
this.connectedAccounts = [];
|
|
450
558
|
this.PROVIDER = provider;
|
|
451
559
|
this.finbricks = new FinbricksClient(
|
|
@@ -457,6 +565,9 @@ class FinbricksConnector extends IBankConnector {
|
|
|
457
565
|
this.connectedAccounts = connectedAccounts;
|
|
458
566
|
this.resolveCredentials = resolveCredentials;
|
|
459
567
|
}
|
|
568
|
+
supportsPaymentType(paymentType) {
|
|
569
|
+
return paymentType === "DOMESTIC" || paymentType === "SEPA" || paymentType === "SWIFT";
|
|
570
|
+
}
|
|
460
571
|
async getClientId(accountId) {
|
|
461
572
|
if (!this.resolveCredentials) {
|
|
462
573
|
throw backendSdk.createInternalError(null, {
|
|
@@ -646,7 +757,7 @@ class FinbricksConnector extends IBankConnector {
|
|
|
646
757
|
merchantTransactionId: p.id,
|
|
647
758
|
creditorAccountIban: p.creditor.iban,
|
|
648
759
|
amount: p.amount,
|
|
649
|
-
variableSymbol: p.vs,
|
|
760
|
+
variableSymbol: p.vs ?? (p.ss ? void 0 : autoVariableSymbol(p.id)),
|
|
650
761
|
specificSymbol: p.ss,
|
|
651
762
|
constantSymbol: p.ks,
|
|
652
763
|
description: p.message
|
|
@@ -754,7 +865,7 @@ class FinbricksConnector extends IBankConnector {
|
|
|
754
865
|
},
|
|
755
866
|
paymentIdentification: {
|
|
756
867
|
merchantTransactionId: bankRefId,
|
|
757
|
-
endToEndIdentification: payment
|
|
868
|
+
endToEndIdentification: buildEndToEndId(payment)
|
|
758
869
|
},
|
|
759
870
|
amount: payment.amount,
|
|
760
871
|
debtor: {
|
|
@@ -804,7 +915,7 @@ class FinbricksConnector extends IBankConnector {
|
|
|
804
915
|
creditorAccountIban: payment.creditor.iban,
|
|
805
916
|
creditorName: payment.creditor.holderName,
|
|
806
917
|
description: payment.message,
|
|
807
|
-
variableSymbol: payment.vs,
|
|
918
|
+
variableSymbol: payment.vs ?? (payment.ss ? void 0 : autoVariableSymbol(payment.id)),
|
|
808
919
|
callbackUrl: `${this.finbricks.REDIRECT_URI}?type=payment&paymentId=${payment.id}`,
|
|
809
920
|
paymentProvider: this.PROVIDER
|
|
810
921
|
}
|
|
@@ -895,7 +1006,10 @@ class FinbricksConnector extends IBankConnector {
|
|
|
895
1006
|
message: "Finbricks: failed to fetch payment status"
|
|
896
1007
|
});
|
|
897
1008
|
}
|
|
898
|
-
return mapFinbricksTransactionStatus(
|
|
1009
|
+
return mapFinbricksTransactionStatus(
|
|
1010
|
+
response.resultCode,
|
|
1011
|
+
response.finalBankStatus ?? false
|
|
1012
|
+
);
|
|
899
1013
|
}
|
|
900
1014
|
async getBatchStatus({
|
|
901
1015
|
batchId,
|
|
@@ -948,6 +1062,7 @@ class AirBankConnector extends FinbricksConnector {
|
|
|
948
1062
|
class CreditasConnector extends FinbricksConnector {
|
|
949
1063
|
constructor(config) {
|
|
950
1064
|
super("CREDITAS", config);
|
|
1065
|
+
this.lifecycleMode = "per-payment";
|
|
951
1066
|
}
|
|
952
1067
|
/**
|
|
953
1068
|
* Creditas bank doesn't support batch payments at all.
|
|
@@ -1147,24 +1262,64 @@ function mod97(string) {
|
|
|
1147
1262
|
}
|
|
1148
1263
|
return parseInt(checksum, 10);
|
|
1149
1264
|
}
|
|
1265
|
+
const parseCzechIban = (iban) => {
|
|
1266
|
+
const stripped = iban.replace(/\s/g, "").toUpperCase();
|
|
1267
|
+
if (!stripped.startsWith("CZ") || stripped.length !== 24) {
|
|
1268
|
+
throw new Error(`Invalid Czech IBAN: ${iban}`);
|
|
1269
|
+
}
|
|
1270
|
+
const bankCode = stripped.slice(4, 8);
|
|
1271
|
+
const prefix = stripped.slice(8, 14).replace(/^0+/, "");
|
|
1272
|
+
const main = stripped.slice(14, 24).replace(/^0+/, "");
|
|
1273
|
+
const accountNumber = prefix ? `${prefix}-${main}` : main;
|
|
1274
|
+
return { bankCode, accountNumber };
|
|
1275
|
+
};
|
|
1150
1276
|
const calculateCzechIban = (accountNumber, bankCode) => {
|
|
1151
1277
|
const paddedBankCode = bankCode.padStart(4, "0");
|
|
1152
1278
|
let prefix = "";
|
|
1153
1279
|
let mainAccount = accountNumber;
|
|
1154
1280
|
if (accountNumber.includes("-")) {
|
|
1155
1281
|
const parts = accountNumber.split("-");
|
|
1282
|
+
if (parts.length !== 2) {
|
|
1283
|
+
throw new Error(
|
|
1284
|
+
`Invalid account number format: expected "prefix-main" or "main", got "${accountNumber}"`
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1156
1287
|
prefix = parts[0];
|
|
1157
1288
|
mainAccount = parts[1];
|
|
1158
1289
|
}
|
|
1159
1290
|
const paddedPrefix = prefix.padStart(6, "0");
|
|
1160
1291
|
const paddedAccount = mainAccount.padStart(10, "0");
|
|
1161
1292
|
const basicIban = paddedBankCode + paddedPrefix + paddedAccount;
|
|
1162
|
-
const rearranged = basicIban + "
|
|
1293
|
+
const rearranged = basicIban + "123500";
|
|
1163
1294
|
const remainder = mod97(rearranged);
|
|
1164
1295
|
const checkDigits = (98 - remainder).toString().padStart(2, "0");
|
|
1165
1296
|
return `CZ${checkDigits}${basicIban}`;
|
|
1166
1297
|
};
|
|
1167
1298
|
|
|
1299
|
+
function parseFlatAddress(addr, fallbackCountry) {
|
|
1300
|
+
if (!addr) {
|
|
1301
|
+
return {
|
|
1302
|
+
streetName: null,
|
|
1303
|
+
buildingNumber: null,
|
|
1304
|
+
city: null,
|
|
1305
|
+
zip: null,
|
|
1306
|
+
countryCode: fallbackCountry
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
const parts = addr.split(",").map((p) => p.trim());
|
|
1310
|
+
const streetPart = parts[0];
|
|
1311
|
+
const cityPart = parts[1];
|
|
1312
|
+
const countryCode = parts[2]?.trim() || fallbackCountry;
|
|
1313
|
+
const streetMatch = streetPart?.match(/^(.+?)\s+(\d+\w*)$/);
|
|
1314
|
+
const cityMatch = cityPart?.match(/^(\d[\d ]*?)\s+(.+)$/);
|
|
1315
|
+
return {
|
|
1316
|
+
streetName: streetMatch?.[1] ?? streetPart ?? null,
|
|
1317
|
+
buildingNumber: streetMatch?.[2] ?? null,
|
|
1318
|
+
city: cityMatch?.[2] ?? cityPart ?? null,
|
|
1319
|
+
zip: cityMatch?.[1]?.replace(/\s/g, "") ?? null,
|
|
1320
|
+
countryCode
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1168
1323
|
class DbuConnector extends IBankConnector {
|
|
1169
1324
|
constructor({
|
|
1170
1325
|
BASE_URL,
|
|
@@ -1177,6 +1332,7 @@ class DbuConnector extends IBankConnector {
|
|
|
1177
1332
|
}) {
|
|
1178
1333
|
super();
|
|
1179
1334
|
this.connectorKey = "DBU";
|
|
1335
|
+
this.lifecycleMode = "per-payment";
|
|
1180
1336
|
this.connectedAccounts = [];
|
|
1181
1337
|
this.baseUrl = BASE_URL;
|
|
1182
1338
|
this.username = USERNAME;
|
|
@@ -1425,11 +1581,25 @@ class DbuConnector extends IBankConnector {
|
|
|
1425
1581
|
}
|
|
1426
1582
|
return Number(account.bankRefId);
|
|
1427
1583
|
}
|
|
1584
|
+
resolveAccountDetails(account) {
|
|
1585
|
+
if (account.number && account.bankCode) {
|
|
1586
|
+
return { number: account.number, bankCode: account.bankCode };
|
|
1587
|
+
}
|
|
1588
|
+
if (account.iban) {
|
|
1589
|
+
const { bankCode, accountNumber } = parseCzechIban(account.iban);
|
|
1590
|
+
return { number: accountNumber, bankCode };
|
|
1591
|
+
}
|
|
1592
|
+
throw backendSdk.createInternalError(null, {
|
|
1593
|
+
message: "DBU: account has neither number+bankCode nor iban",
|
|
1594
|
+
code: "DBU_MISSING_ACCOUNT_DETAILS"
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1428
1597
|
async initiateInstantPayment(payment) {
|
|
1598
|
+
const creditor = this.resolveAccountDetails(payment.creditor);
|
|
1429
1599
|
const body = {
|
|
1430
|
-
accountNumberCredit:
|
|
1600
|
+
accountNumberCredit: creditor.number,
|
|
1431
1601
|
idAccountDebit: this.resolveIdAccountDebit(payment),
|
|
1432
|
-
bankCodeCredit:
|
|
1602
|
+
bankCodeCredit: creditor.bankCode,
|
|
1433
1603
|
amount: payment.amount,
|
|
1434
1604
|
currencyCode: "CZK",
|
|
1435
1605
|
channelCode: "WWW",
|
|
@@ -1461,11 +1631,13 @@ class DbuConnector extends IBankConnector {
|
|
|
1461
1631
|
if (payment.instructionPriority === "INST") {
|
|
1462
1632
|
return this.initiateInstantPayment(payment);
|
|
1463
1633
|
}
|
|
1634
|
+
const debtor = this.resolveAccountDetails(payment.debtor);
|
|
1635
|
+
const creditor = this.resolveAccountDetails(payment.creditor);
|
|
1464
1636
|
const body = {
|
|
1465
|
-
accountNumberDebit:
|
|
1466
|
-
accountNumberCredit:
|
|
1467
|
-
bankCodeDebit:
|
|
1468
|
-
bankCodeCredit:
|
|
1637
|
+
accountNumberDebit: debtor.number,
|
|
1638
|
+
accountNumberCredit: creditor.number,
|
|
1639
|
+
bankCodeDebit: debtor.bankCode,
|
|
1640
|
+
bankCodeCredit: creditor.bankCode,
|
|
1469
1641
|
amount: payment.amount,
|
|
1470
1642
|
currencyCode: "CZK",
|
|
1471
1643
|
actionTypeCode: "TRANSFER",
|
|
@@ -1482,20 +1654,14 @@ class DbuConnector extends IBankConnector {
|
|
|
1482
1654
|
constSymbol: payment.ks,
|
|
1483
1655
|
uniqueExternalId: payment.id,
|
|
1484
1656
|
with4EyeApproval: "Y",
|
|
1485
|
-
debtorAddress:
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
streetName: null,
|
|
1494
|
-
buildingNumber: null,
|
|
1495
|
-
city: null,
|
|
1496
|
-
zip: null,
|
|
1497
|
-
countryCode: payment.creditor.countryCode || "CZ"
|
|
1498
|
-
}
|
|
1657
|
+
debtorAddress: parseFlatAddress(
|
|
1658
|
+
payment.debtor.address,
|
|
1659
|
+
payment.debtor.countryCode || "CZ"
|
|
1660
|
+
),
|
|
1661
|
+
creditorAddress: parseFlatAddress(
|
|
1662
|
+
payment.creditor.address,
|
|
1663
|
+
payment.creditor.countryCode || "CZ"
|
|
1664
|
+
)
|
|
1499
1665
|
};
|
|
1500
1666
|
const response = await this.makeRequest(
|
|
1501
1667
|
"/required-transactions",
|
|
@@ -1549,7 +1715,7 @@ class DbuConnector extends IBankConnector {
|
|
|
1549
1715
|
const limit = 10;
|
|
1550
1716
|
let hasMoreData = true;
|
|
1551
1717
|
let pageNumber = 1;
|
|
1552
|
-
const baseRequestId = backendSdk.uuidv4();
|
|
1718
|
+
const baseRequestId = backendSdk.uuidv4().replace(/-/g, "");
|
|
1553
1719
|
while (hasMoreData) {
|
|
1554
1720
|
const response = await this.makeRequest(
|
|
1555
1721
|
"/required-transactions",
|
|
@@ -1575,7 +1741,7 @@ class DbuConnector extends IBankConnector {
|
|
|
1575
1741
|
}
|
|
1576
1742
|
for (const transaction of response.transactions.transaction) {
|
|
1577
1743
|
const status = transaction.status.trim();
|
|
1578
|
-
if (status === "0" || status === "50") continue;
|
|
1744
|
+
if (status === "0" || status === "50" || status === "51") continue;
|
|
1579
1745
|
try {
|
|
1580
1746
|
const payment = this.mapDbuTransactionToPayment(
|
|
1581
1747
|
transaction,
|
|
@@ -1623,6 +1789,7 @@ class ErsteConnector extends IBankConnector {
|
|
|
1623
1789
|
constructor(config) {
|
|
1624
1790
|
super();
|
|
1625
1791
|
this.connectorKey = "ERSTE";
|
|
1792
|
+
this.lifecycleMode = "batch";
|
|
1626
1793
|
this.accessToken = null;
|
|
1627
1794
|
this.API_KEY = config.API_KEY;
|
|
1628
1795
|
this.CLIENT_ID = config.CLIENT_ID;
|
|
@@ -1728,7 +1895,7 @@ class ErsteConnector extends IBankConnector {
|
|
|
1728
1895
|
}
|
|
1729
1896
|
const paymentBody = {
|
|
1730
1897
|
paymentIdentification: {
|
|
1731
|
-
endToEndIdentification: payment
|
|
1898
|
+
endToEndIdentification: buildEndToEndId(payment),
|
|
1732
1899
|
instructionIdentification: payment.id
|
|
1733
1900
|
},
|
|
1734
1901
|
paymentTypeInformation: { instructionPriority: "NORM" },
|
|
@@ -1954,6 +2121,7 @@ class MockConnector extends IBankConnector {
|
|
|
1954
2121
|
constructor() {
|
|
1955
2122
|
super();
|
|
1956
2123
|
this.connectorKey = "MOCK";
|
|
2124
|
+
this.lifecycleMode = "batch";
|
|
1957
2125
|
this.connectedAccounts = [];
|
|
1958
2126
|
}
|
|
1959
2127
|
supportsBatch(_paymentType) {
|
|
@@ -2040,7 +2208,6 @@ exports.KBConnector = KBConnector;
|
|
|
2040
2208
|
exports.MockCobsConnector = MockCobsConnector;
|
|
2041
2209
|
exports.MockConnector = MockConnector;
|
|
2042
2210
|
exports.assignAccount = assignAccount;
|
|
2043
|
-
exports.batchTransform = batchTransform;
|
|
2044
2211
|
exports.initiateConnector = initiateConnector;
|
|
2045
2212
|
exports.mapFinbricksTransactionStatus = mapFinbricksTransactionStatus;
|
|
2046
2213
|
exports.signFinbricksJws = signFinbricksJws;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as CurrencyCode, J as BankCode, V as CountryCode } from './bank.B-NJB8GB.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
type ReferenceType = `${'VS' | 'SS' | 'KS'}:${number}`;
|
|
@@ -210,6 +210,18 @@ type FinbricksAccountTransactionsResponse = {
|
|
|
210
210
|
transactions: FinbricksTransaction[];
|
|
211
211
|
};
|
|
212
212
|
|
|
213
|
+
/**
|
|
214
|
+
* @deprecated Use IncomingPayment from './payment-lifecycle' instead
|
|
215
|
+
* This type is kept for backward compatibility during migration
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Message pushed to PAYMENTS_READY_TO_BATCH_QUEUE after payment_request is created in DB
|
|
220
|
+
*/
|
|
221
|
+
type PaymentQueueMessage = {
|
|
222
|
+
paymentId: string;
|
|
223
|
+
};
|
|
224
|
+
|
|
213
225
|
declare const sendPaymentInputSchema: z.ZodObject<{
|
|
214
226
|
correlationId: z.ZodString;
|
|
215
227
|
refId: z.ZodOptional<z.ZodString>;
|
|
@@ -225,7 +237,6 @@ declare const sendPaymentInputSchema: z.ZodObject<{
|
|
|
225
237
|
OUR: "OUR";
|
|
226
238
|
BEN: "BEN";
|
|
227
239
|
}>>;
|
|
228
|
-
executionDate: z.ZodOptional<z.ZodString>;
|
|
229
240
|
instructionPriority: z.ZodOptional<z.ZodEnum<{
|
|
230
241
|
NORM: "NORM";
|
|
231
242
|
HIGH: "HIGH";
|
|
@@ -980,12 +991,13 @@ declare const sendPaymentInputSchema: z.ZodObject<{
|
|
|
980
991
|
bsb: z.ZodOptional<z.ZodString>;
|
|
981
992
|
brBankNumber: z.ZodOptional<z.ZodString>;
|
|
982
993
|
}, z.core.$strip>;
|
|
983
|
-
purpose: z.ZodOptional<z.ZodString>;
|
|
984
994
|
sendAsSinglePayment: z.ZodOptional<z.ZodBoolean>;
|
|
985
995
|
}, z.core.$strip>;
|
|
986
996
|
interface SendPaymentInput extends z.infer<typeof sendPaymentInputSchema> {
|
|
987
997
|
}
|
|
988
|
-
type SendPaymentOutput =
|
|
998
|
+
type SendPaymentOutput = {
|
|
999
|
+
paymentId: string;
|
|
1000
|
+
};
|
|
989
1001
|
|
|
990
1002
|
declare const sendPaymentSyncInputSchema: z.ZodObject<{
|
|
991
1003
|
correlationId: z.ZodString;
|
|
@@ -1002,7 +1014,6 @@ declare const sendPaymentSyncInputSchema: z.ZodObject<{
|
|
|
1002
1014
|
OUR: "OUR";
|
|
1003
1015
|
BEN: "BEN";
|
|
1004
1016
|
}>>;
|
|
1005
|
-
executionDate: z.ZodOptional<z.ZodString>;
|
|
1006
1017
|
instructionPriority: z.ZodOptional<z.ZodEnum<{
|
|
1007
1018
|
NORM: "NORM";
|
|
1008
1019
|
HIGH: "HIGH";
|
|
@@ -1757,7 +1768,6 @@ declare const sendPaymentSyncInputSchema: z.ZodObject<{
|
|
|
1757
1768
|
bsb: z.ZodOptional<z.ZodString>;
|
|
1758
1769
|
brBankNumber: z.ZodOptional<z.ZodString>;
|
|
1759
1770
|
}, z.core.$strip>;
|
|
1760
|
-
purpose: z.ZodOptional<z.ZodString>;
|
|
1761
1771
|
sendAsSinglePayment: z.ZodOptional<z.ZodBoolean>;
|
|
1762
1772
|
}, z.core.$strip>;
|
|
1763
1773
|
interface SendPaymentSyncInput extends z.infer<typeof sendPaymentSyncInputSchema> {
|
|
@@ -1767,4 +1777,4 @@ type SendPaymentSyncOutput = {
|
|
|
1767
1777
|
authorizationUrl: string;
|
|
1768
1778
|
};
|
|
1769
1779
|
|
|
1770
|
-
export type { FinbricksSupportedBanksResponse as F, ReferenceType as R, SendPaymentInput as S, SendPaymentOutput as a, SendPaymentSyncInput as b, SendPaymentSyncOutput as c, FinbricksAccount as d, FinbricksAccountTransactionsResponse as e, FinbricksAccountsListResponse as f, FinbricksAuthTokenResponse as g, FinbricksBatchResponse as h, FinbricksConnectAccountResponse as i, FinbricksPaymentResponse as j, FinbricksSupportedBank as k };
|
|
1780
|
+
export type { FinbricksSupportedBanksResponse as F, PaymentQueueMessage as P, ReferenceType as R, SendPaymentInput as S, SendPaymentOutput as a, SendPaymentSyncInput as b, SendPaymentSyncOutput as c, FinbricksAccount as d, FinbricksAccountTransactionsResponse as e, FinbricksAccountsListResponse as f, FinbricksAuthTokenResponse as g, FinbricksBatchResponse as h, FinbricksConnectAccountResponse as i, FinbricksPaymentResponse as j, FinbricksSupportedBank as k };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const backendSdk = require('@develit-io/backend-sdk');
|
|
4
3
|
const drizzleOrm = require('drizzle-orm');
|
|
5
|
-
require('
|
|
4
|
+
const backendSdk = require('@develit-io/backend-sdk');
|
|
5
|
+
require('./bank.JVlyPAAb.cjs');
|
|
6
6
|
require('date-fns');
|
|
7
7
|
require('jose');
|
|
8
8
|
require('@develit-io/general-codes');
|
|
9
9
|
const node_crypto = require('node:crypto');
|
|
10
|
-
const database_schema = require('./bank.
|
|
10
|
+
const database_schema = require('./bank.DJnDSYqE.cjs');
|
|
11
11
|
|
|
12
12
|
const createPaymentCommand = (db, { payment }) => {
|
|
13
13
|
return {
|
|
@@ -15,6 +15,19 @@ const createPaymentCommand = (db, { payment }) => {
|
|
|
15
15
|
...payment,
|
|
16
16
|
creditorIban: payment.creditor.iban,
|
|
17
17
|
debtorIban: payment.debtor.iban
|
|
18
|
+
}).onConflictDoUpdate({
|
|
19
|
+
// Unique index: (connector_key, bank_ref_id)
|
|
20
|
+
target: [tables.payment.connectorKey, tables.payment.bankRefId],
|
|
21
|
+
set: {
|
|
22
|
+
status: drizzleOrm.sql`excluded.status`,
|
|
23
|
+
statusReason: drizzleOrm.sql`excluded.status_reason`,
|
|
24
|
+
processedAt: drizzleOrm.sql`excluded.processed_at`,
|
|
25
|
+
updatedAt: drizzleOrm.sql`excluded.updated_at`,
|
|
26
|
+
// Keep existing refId if already set, otherwise use enriched value
|
|
27
|
+
refId: drizzleOrm.sql`coalesce(payment.ref_id, excluded.ref_id)`,
|
|
28
|
+
// Keep existing batchId if already set, otherwise use enriched value
|
|
29
|
+
batchId: drizzleOrm.sql`coalesce(payment.bank_execution_batch_id, excluded.bank_execution_batch_id)`
|
|
30
|
+
}
|
|
18
31
|
}).returning()
|
|
19
32
|
};
|
|
20
33
|
};
|
|
@@ -39,7 +52,12 @@ const upsertBatchCommand = (db, { batch }) => {
|
|
|
39
52
|
const updatePaymentRequestStatusCommand = (db, values) => {
|
|
40
53
|
const { id, ...set } = values;
|
|
41
54
|
return {
|
|
42
|
-
command: db.update(tables.paymentRequest).set(set).where(
|
|
55
|
+
command: db.update(tables.paymentRequest).set(set).where(
|
|
56
|
+
drizzleOrm.and(
|
|
57
|
+
drizzleOrm.eq(tables.paymentRequest.id, id),
|
|
58
|
+
drizzleOrm.isNull(tables.paymentRequest.deletedAt)
|
|
59
|
+
)
|
|
60
|
+
).returning()
|
|
43
61
|
};
|
|
44
62
|
};
|
|
45
63
|
|
|
@@ -60,7 +78,12 @@ const getCredentialsByAccountId = async (db, encryptionKey, { accountId }) => {
|
|
|
60
78
|
};
|
|
61
79
|
|
|
62
80
|
const getPaymentRequestsByBatchIdQuery = async (db, { batchId }) => {
|
|
63
|
-
return await db.select().from(tables.paymentRequest).where(
|
|
81
|
+
return await db.select().from(tables.paymentRequest).where(
|
|
82
|
+
drizzleOrm.and(
|
|
83
|
+
drizzleOrm.eq(tables.paymentRequest.batchId, batchId),
|
|
84
|
+
drizzleOrm.isNull(tables.paymentRequest.deletedAt)
|
|
85
|
+
)
|
|
86
|
+
);
|
|
64
87
|
};
|
|
65
88
|
|
|
66
89
|
async function importAesKey(base64Key) {
|
|
@@ -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;
|
|
@@ -174,7 +175,17 @@ const payment = sqliteCore.sqliteTable(
|
|
|
174
175
|
debtor: sqliteCore.text("debtor", { mode: "json" }).$type().notNull(),
|
|
175
176
|
debtorIban: sqliteCore.text("debtor_iban")
|
|
176
177
|
},
|
|
177
|
-
(t) => [
|
|
178
|
+
(t) => [
|
|
179
|
+
sqliteCore.unique().on(t.connectorKey, t.bankRefId),
|
|
180
|
+
sqliteCore.index("payment_account_id_idx").on(t.accountId),
|
|
181
|
+
sqliteCore.index("payment_account_id_status_idx").on(t.accountId, t.status),
|
|
182
|
+
sqliteCore.index("payment_account_id_created_at_idx").on(t.accountId, t.createdAt),
|
|
183
|
+
sqliteCore.index("payment_created_at_idx").on(t.createdAt),
|
|
184
|
+
sqliteCore.index("payment_direction_idx").on(t.direction),
|
|
185
|
+
sqliteCore.index("payment_batch_id_idx").on(t.batchId),
|
|
186
|
+
sqliteCore.index("payment_creditor_iban_idx").on(t.creditorIban),
|
|
187
|
+
sqliteCore.index("payment_debtor_iban_idx").on(t.debtorIban)
|
|
188
|
+
]
|
|
178
189
|
);
|
|
179
190
|
const paymentRelations = relations.relations(payment, ({ one }) => ({
|
|
180
191
|
batch: one(batch, { fields: [payment.batchId], references: [batch.id] })
|
|
@@ -217,9 +228,26 @@ const paymentRequest = sqliteCore.sqliteTable(
|
|
|
217
228
|
creditor: sqliteCore.text("creditor", { mode: "json" }).$type().notNull(),
|
|
218
229
|
creditorIban: sqliteCore.text("creditor_iban"),
|
|
219
230
|
debtor: sqliteCore.text("debtor", { mode: "json" }).$type().notNull(),
|
|
220
|
-
debtorIban: sqliteCore.text("debtor_iban")
|
|
231
|
+
debtorIban: sqliteCore.text("debtor_iban"),
|
|
232
|
+
sendAsSinglePayment: sqliteCore.integer("send_as_single_payment", {
|
|
233
|
+
mode: "boolean"
|
|
234
|
+
})
|
|
221
235
|
},
|
|
222
|
-
(t) => [
|
|
236
|
+
(t) => [
|
|
237
|
+
sqliteCore.index("payment_request_batch_id_idx").on(t.batchId),
|
|
238
|
+
sqliteCore.index("payment_request_account_id_idx").on(t.accountId),
|
|
239
|
+
sqliteCore.index("payment_request_creditor_iban_idx").on(t.creditorIban),
|
|
240
|
+
sqliteCore.index("payment_request_debtor_iban_idx").on(t.debtorIban),
|
|
241
|
+
sqliteCore.index("payment_request_status_idx").on(t.status),
|
|
242
|
+
sqliteCore.index("payment_request_created_at_idx").on(t.createdAt),
|
|
243
|
+
sqliteCore.index("payment_request_account_status_idx").on(t.accountId, t.status),
|
|
244
|
+
sqliteCore.index("payment_request_status_created_at_idx").on(t.status, t.createdAt),
|
|
245
|
+
sqliteCore.index("payment_request_account_status_created_at_idx").on(
|
|
246
|
+
t.accountId,
|
|
247
|
+
t.status,
|
|
248
|
+
t.createdAt
|
|
249
|
+
)
|
|
250
|
+
]
|
|
223
251
|
);
|
|
224
252
|
const paymentRequestRelations = relations.relations(paymentRequest, ({ one }) => ({
|
|
225
253
|
batch: one(batch, {
|