@develit-services/bank 5.4.2 → 5.5.1

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.
@@ -0,0 +1,360 @@
1
+ 'use strict';
2
+
3
+ const drizzleOrm = require('drizzle-orm');
4
+ const paymentDirection = require('./bank.DMrkshJa.cjs');
5
+ const backendSdk = require('@develit-io/backend-sdk');
6
+ require('./bank.9Yw4KHyl.cjs');
7
+ require('date-fns');
8
+ require('jose');
9
+ require('@develit-io/general-codes');
10
+ const node_crypto = require('node:crypto');
11
+
12
+ const createPaymentCommand = (db, { payment }) => {
13
+ return {
14
+ command: db.insert(paymentDirection.tables.payment).values({
15
+ ...payment,
16
+ creditorIban: payment.creditor.iban,
17
+ debtorIban: payment.debtor.iban
18
+ }).onConflictDoUpdate({
19
+ // Unique index: (connector_key, account_id, bank_ref_id)
20
+ target: [
21
+ paymentDirection.tables.payment.connectorKey,
22
+ paymentDirection.tables.payment.accountId,
23
+ paymentDirection.tables.payment.bankRefId
24
+ ],
25
+ set: {
26
+ status: drizzleOrm.sql`excluded.status`,
27
+ statusReason: drizzleOrm.sql`excluded.status_reason`,
28
+ processedAt: drizzleOrm.sql`excluded.processed_at`,
29
+ updatedAt: drizzleOrm.sql`excluded.updated_at`,
30
+ // Keep existing refId if already set, otherwise use enriched value
31
+ refId: drizzleOrm.sql`coalesce(payment.ref_id, excluded.ref_id)`,
32
+ // Keep existing batchId if already set, otherwise use enriched value
33
+ batchId: drizzleOrm.sql`coalesce(payment.bank_execution_batch_id, excluded.bank_execution_batch_id)`
34
+ }
35
+ }).returning()
36
+ };
37
+ };
38
+
39
+ const upsertBatchCommand = (db, { batch }) => {
40
+ const id = batch.id || backendSdk.uuidv4();
41
+ const command = db.insert(paymentDirection.tables.batch).values({
42
+ ...batch,
43
+ id
44
+ }).onConflictDoUpdate({
45
+ target: paymentDirection.tables.batch.id,
46
+ set: {
47
+ ...batch
48
+ }
49
+ }).returning();
50
+ return {
51
+ id,
52
+ command
53
+ };
54
+ };
55
+
56
+ const updatePaymentRequestStatusCommand = (db, values) => {
57
+ const { id, ...set } = values;
58
+ return {
59
+ command: db.update(paymentDirection.tables.paymentRequest).set(set).where(
60
+ drizzleOrm.and(
61
+ drizzleOrm.eq(paymentDirection.tables.paymentRequest.id, id),
62
+ drizzleOrm.isNull(paymentDirection.tables.paymentRequest.deletedAt)
63
+ )
64
+ ).returning()
65
+ };
66
+ };
67
+
68
+ const getAccountByIdQuery = async (db, { accountId }) => {
69
+ return await db.select().from(paymentDirection.tables.account).where(drizzleOrm.eq(paymentDirection.tables.account.id, accountId)).get();
70
+ };
71
+
72
+ const getBatchByIdQuery = async (db, { batchId }) => {
73
+ return await db.select().from(paymentDirection.tables.batch).where(drizzleOrm.eq(paymentDirection.tables.batch.id, batchId)).get();
74
+ };
75
+
76
+ const getCredentialsByAccountId = async (db, encryptionKey, { accountId }) => {
77
+ const cred = await db.select().from(paymentDirection.tables.accountCredentials).where(drizzleOrm.eq(paymentDirection.tables.accountCredentials.accountId, accountId)).get();
78
+ return cred ? {
79
+ ...cred,
80
+ value: await decrypt(cred.value, encryptionKey)
81
+ } : void 0;
82
+ };
83
+
84
+ const getPaymentRequestsByBatchIdQuery = async (db, { batchId }) => {
85
+ return await db.select().from(paymentDirection.tables.paymentRequest).where(
86
+ drizzleOrm.and(
87
+ drizzleOrm.eq(paymentDirection.tables.paymentRequest.batchId, batchId),
88
+ drizzleOrm.isNull(paymentDirection.tables.paymentRequest.deletedAt)
89
+ )
90
+ );
91
+ };
92
+
93
+ async function importAesKey(base64Key) {
94
+ const raw = Uint8Array.from(atob(base64Key), (c) => c.charCodeAt(0));
95
+ return await crypto.subtle.importKey("raw", raw, { name: "AES-GCM" }, false, [
96
+ "encrypt",
97
+ "decrypt"
98
+ ]);
99
+ }
100
+ async function encrypt(value, key) {
101
+ const encoder = new TextEncoder();
102
+ const iv = crypto.getRandomValues(new Uint8Array(12));
103
+ const ciphertext = await crypto.subtle.encrypt(
104
+ { name: "AES-GCM", iv },
105
+ key,
106
+ encoder.encode(value)
107
+ );
108
+ const combined = new Uint8Array(iv.length + ciphertext.byteLength);
109
+ combined.set(iv, 0);
110
+ combined.set(new Uint8Array(ciphertext), iv.length);
111
+ return btoa(String.fromCharCode(...combined));
112
+ }
113
+ async function decrypt(base64, key) {
114
+ const raw = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
115
+ const iv = raw.slice(0, 12);
116
+ const ciphertext = raw.slice(12);
117
+ const decrypted = await crypto.subtle.decrypt(
118
+ { name: "AES-GCM", iv },
119
+ key,
120
+ ciphertext
121
+ );
122
+ return new TextDecoder().decode(decrypted);
123
+ }
124
+ function canonicalize(value) {
125
+ if (value === null || typeof value !== "object") return value;
126
+ if (Array.isArray(value)) {
127
+ return value.map(canonicalize);
128
+ }
129
+ const obj = value;
130
+ const sorted = Object.keys(obj).sort().reduce((acc, key) => {
131
+ acc[key] = canonicalize(obj[key]);
132
+ return acc;
133
+ }, {});
134
+ return sorted;
135
+ }
136
+ function checksum(input) {
137
+ const canonical = canonicalize(input);
138
+ const json = JSON.stringify(canonical);
139
+ return node_crypto.createHash("sha256").update(json).digest("hex");
140
+ }
141
+
142
+ const createCredentialsResolver = async (db, env) => {
143
+ const encryptionKey = await importAesKey(
144
+ (await env.SECRETS_STORE.get({
145
+ secretName: "BANK_SERVICE_ENCRYPTION_KEY"
146
+ })).data?.secretValue || ""
147
+ );
148
+ return (accountId) => getCredentialsByAccountId(db, encryptionKey, { accountId });
149
+ };
150
+
151
+ class AirBankConnector extends paymentDirection.FinbricksConnector {
152
+ constructor(config) {
153
+ super("AIRBANK", config);
154
+ }
155
+ supportsBatch() {
156
+ return false;
157
+ }
158
+ }
159
+
160
+ class CreditasConnector extends paymentDirection.FinbricksConnector {
161
+ constructor(config) {
162
+ super("CREDITAS", config);
163
+ }
164
+ /**
165
+ * Creditas bank doesn't support batch payments at all.
166
+ */
167
+ supportsBatch() {
168
+ return false;
169
+ }
170
+ }
171
+
172
+ class CSASConnector extends paymentDirection.FinbricksConnector {
173
+ constructor(config) {
174
+ super("CSAS", config);
175
+ }
176
+ supportsBatch(paymentType) {
177
+ return paymentType === "DOMESTIC";
178
+ }
179
+ }
180
+
181
+ class FioConnector extends paymentDirection.FinbricksConnector {
182
+ constructor(config) {
183
+ super("FIO", config);
184
+ }
185
+ /**
186
+ * FIO supports batch only for DOMESTIC (CZK) payments.
187
+ * SEPA and SWIFT batch support is planned by Finbricks.
188
+ */
189
+ supportsBatch(paymentType) {
190
+ return paymentType === "DOMESTIC";
191
+ }
192
+ }
193
+
194
+ class MonetaConnector extends paymentDirection.FinbricksConnector {
195
+ constructor(config) {
196
+ super("MONETA", config);
197
+ }
198
+ /**
199
+ * MONETA supports batch only for DOMESTIC (CZK) payments.
200
+ * SEPA and SWIFT batch support is planned by Finbricks.
201
+ */
202
+ supportsBatch(paymentType) {
203
+ return paymentType === "DOMESTIC";
204
+ }
205
+ /**
206
+ * MONETA rejects all special characters in endToEndIdentification.
207
+ * Boundaries between segments are unambiguous without a separator —
208
+ * `VS`/`SS`/`KS` prefixes are alphabetic and values are always numeric,
209
+ * so the parsing regex `VS[:\s]*(\d+)` extracts each value correctly.
210
+ *
211
+ * Format: `VS1234SS5678KS0308` (max 30 chars).
212
+ * Confirmed by Finbricks support (2026-04-23).
213
+ */
214
+ buildEndToEndId(payment) {
215
+ return paymentDirection.buildEndToEndId(payment, { separator: "" });
216
+ }
217
+ }
218
+
219
+ const initiateConnector = async ({
220
+ bank,
221
+ env,
222
+ connectedAccounts,
223
+ resolveCredentials
224
+ }) => {
225
+ switch (bank) {
226
+ case "ERSTE":
227
+ return new paymentDirection.ErsteConnector({
228
+ API_KEY: (await env.SECRETS_STORE.get({
229
+ secretName: "BANK_SERVICE_ERSTE_API_KEY"
230
+ })).data?.secretValue || "",
231
+ CLIENT_ID: (await env.SECRETS_STORE.get({
232
+ secretName: "BANK_SERVICE_ERSTE_CLIENT_ID"
233
+ })).data?.secretValue || "",
234
+ CLIENT_SECRET: (await env.SECRETS_STORE.get({
235
+ secretName: "BANK_SERVICE_ERSTE_CLIENT_SECRET"
236
+ })).data?.secretValue || "",
237
+ REDIRECT_URI: env.REDIRECT_URI,
238
+ AUTH_URI: env.ERSTE_AUTH_URI,
239
+ PAYMENTS_URI: env.ERSTE_PAYMENTS_URI,
240
+ ACCOUNTS_URI: env.ERSTE_ACCOUNTS_URI,
241
+ connectedAccounts,
242
+ resolveCredentials
243
+ });
244
+ case "CREDITAS":
245
+ return new CreditasConnector({
246
+ BASE_URI: env.FINBRICKS_BASE_URI,
247
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
248
+ PRIVATE_KEY_PEM: (await env.SECRETS_STORE.get({
249
+ secretName: "BANK_SERVICE_FINBRICKS_PRIVATE_KEY_PEM"
250
+ })).data?.secretValue || "",
251
+ REDIRECT_URI: env.REDIRECT_URI,
252
+ connectedAccounts,
253
+ resolveCredentials
254
+ });
255
+ case "MOCK_COBS":
256
+ return new paymentDirection.MockCobsConnector({
257
+ BASE_URI: env.FINBRICKS_BASE_URI,
258
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
259
+ PRIVATE_KEY_PEM: (await env.SECRETS_STORE.get({
260
+ secretName: "BANK_SERVICE_FINBRICKS_PRIVATE_KEY_PEM"
261
+ })).data?.secretValue || "",
262
+ REDIRECT_URI: env.REDIRECT_URI,
263
+ connectedAccounts,
264
+ resolveCredentials
265
+ });
266
+ case "FIO":
267
+ return new FioConnector({
268
+ BASE_URI: env.FINBRICKS_BASE_URI,
269
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
270
+ PRIVATE_KEY_PEM: (await env.SECRETS_STORE.get({
271
+ secretName: "BANK_SERVICE_FINBRICKS_PRIVATE_KEY_PEM"
272
+ })).data?.secretValue || "",
273
+ REDIRECT_URI: env.REDIRECT_URI,
274
+ connectedAccounts,
275
+ resolveCredentials
276
+ });
277
+ case "MONETA":
278
+ return new MonetaConnector({
279
+ BASE_URI: env.FINBRICKS_BASE_URI,
280
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
281
+ PRIVATE_KEY_PEM: (await env.SECRETS_STORE.get({
282
+ secretName: "BANK_SERVICE_FINBRICKS_PRIVATE_KEY_PEM"
283
+ })).data?.secretValue || "",
284
+ REDIRECT_URI: env.REDIRECT_URI,
285
+ connectedAccounts,
286
+ resolveCredentials
287
+ });
288
+ case "AIRBANK":
289
+ return new AirBankConnector({
290
+ BASE_URI: env.FINBRICKS_BASE_URI,
291
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
292
+ PRIVATE_KEY_PEM: (await env.SECRETS_STORE.get({
293
+ secretName: "BANK_SERVICE_FINBRICKS_PRIVATE_KEY_PEM"
294
+ })).data?.secretValue || "",
295
+ REDIRECT_URI: env.REDIRECT_URI,
296
+ connectedAccounts,
297
+ resolveCredentials
298
+ });
299
+ case "CSAS":
300
+ return new CSASConnector({
301
+ BASE_URI: env.FINBRICKS_BASE_URI,
302
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
303
+ PRIVATE_KEY_PEM: (await env.SECRETS_STORE.get({
304
+ secretName: "BANK_SERVICE_FINBRICKS_PRIVATE_KEY_PEM"
305
+ })).data?.secretValue || "",
306
+ REDIRECT_URI: env.REDIRECT_URI,
307
+ connectedAccounts,
308
+ resolveCredentials
309
+ });
310
+ case "KB":
311
+ return new paymentDirection.KBConnector({
312
+ BASE_URI: env.FINBRICKS_BASE_URI,
313
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
314
+ PRIVATE_KEY_PEM: (await env.SECRETS_STORE.get({
315
+ secretName: "BANK_SERVICE_FINBRICKS_PRIVATE_KEY_PEM"
316
+ })).data?.secretValue || "",
317
+ REDIRECT_URI: env.REDIRECT_URI,
318
+ connectedAccounts,
319
+ resolveCredentials
320
+ });
321
+ case "CSOB":
322
+ return new paymentDirection.CsobConnector({
323
+ BASE_URI: env.FINBRICKS_BASE_URI,
324
+ MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
325
+ PRIVATE_KEY_PEM: (await env.SECRETS_STORE.get({
326
+ secretName: "BANK_SERVICE_FINBRICKS_PRIVATE_KEY_PEM"
327
+ })).data?.secretValue || "",
328
+ REDIRECT_URI: env.REDIRECT_URI,
329
+ connectedAccounts,
330
+ resolveCredentials
331
+ });
332
+ case "DBU":
333
+ return new paymentDirection.DbuConnector({
334
+ BASE_URL: env.DBUCS_BASE_URI,
335
+ USERNAME: env.DBUCS_USERNAME,
336
+ APPLICATION_CODE: env.DBUCS_APPLICATION_CODE,
337
+ KV: env.BANK_KV,
338
+ API: env.DBU_CBS_BACKOFFICE_DEV,
339
+ REDIRECT_URI: env.REDIRECT_URI,
340
+ TX_AUTH_URI: env.DBUCS_TX_AUTH_URI,
341
+ connectedAccounts
342
+ });
343
+ default:
344
+ const mockConnector = new paymentDirection.MockConnector();
345
+ mockConnector.connectedAccounts = connectedAccounts;
346
+ return mockConnector;
347
+ }
348
+ };
349
+
350
+ exports.checksum = checksum;
351
+ exports.createCredentialsResolver = createCredentialsResolver;
352
+ exports.createPaymentCommand = createPaymentCommand;
353
+ exports.encrypt = encrypt;
354
+ exports.getAccountByIdQuery = getAccountByIdQuery;
355
+ exports.getBatchByIdQuery = getBatchByIdQuery;
356
+ exports.getPaymentRequestsByBatchIdQuery = getPaymentRequestsByBatchIdQuery;
357
+ exports.importAesKey = importAesKey;
358
+ exports.initiateConnector = initiateConnector;
359
+ exports.updatePaymentRequestStatusCommand = updatePaymentRequestStatusCommand;
360
+ exports.upsertBatchCommand = upsertBatchCommand;
@@ -1,4 +1,4 @@
1
- import { e as CurrencyCode, R as BankCode, a1 as CountryCode, P as PaymentRequestSelectType } from './bank.BrBu52ls.mjs';
1
+ import { e as CurrencyCode, R as BankCode, a1 as CountryCode, P as PaymentRequestSelectType } from './bank.CdkOsZE8.mjs';
2
2
  import { z } from 'zod';
3
3
 
4
4
  type ReferenceType = `${'VS' | 'SS' | 'KS'}:${number}`;
@@ -3606,7 +3606,7 @@ declare const payment: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
3606
3606
  name: string;
3607
3607
  tableName: "payment";
3608
3608
  dataType: "string enum";
3609
- data: "BOOKED" | "REJECTED" | "PROCESSING" | "PENDING" | "CANCELLED" | "SCHEDULED" | "HOLD" | "INFO";
3609
+ data: "BOOKED" | "REJECTED" | "PENDING" | "PROCESSING" | "CANCELLED" | "SCHEDULED" | "HOLD" | "INFO";
3610
3610
  driverParam: string;
3611
3611
  notNull: true;
3612
3612
  hasDefault: false;
@@ -3606,7 +3606,7 @@ declare const payment: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
3606
3606
  name: string;
3607
3607
  tableName: "payment";
3608
3608
  dataType: "string enum";
3609
- data: "BOOKED" | "REJECTED" | "PROCESSING" | "PENDING" | "CANCELLED" | "SCHEDULED" | "HOLD" | "INFO";
3609
+ data: "BOOKED" | "REJECTED" | "PENDING" | "PROCESSING" | "CANCELLED" | "SCHEDULED" | "HOLD" | "INFO";
3610
3610
  driverParam: string;
3611
3611
  notNull: true;
3612
3612
  hasDefault: false;
@@ -3606,7 +3606,7 @@ declare const payment: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
3606
3606
  name: string;
3607
3607
  tableName: "payment";
3608
3608
  dataType: "string enum";
3609
- data: "BOOKED" | "REJECTED" | "PROCESSING" | "PENDING" | "CANCELLED" | "SCHEDULED" | "HOLD" | "INFO";
3609
+ data: "BOOKED" | "REJECTED" | "PENDING" | "PROCESSING" | "CANCELLED" | "SCHEDULED" | "HOLD" | "INFO";
3610
3610
  driverParam: string;
3611
3611
  notNull: true;
3612
3612
  hasDefault: false;