@develit-services/bank 0.3.43 → 0.3.45

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 (37) hide show
  1. package/dist/database/schema.cjs +4 -4
  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 +4 -4
  6. package/dist/export/worker.cjs +166 -183
  7. package/dist/export/worker.d.cts +43 -22
  8. package/dist/export/worker.d.mts +43 -22
  9. package/dist/export/worker.d.ts +43 -22
  10. package/dist/export/worker.mjs +126 -143
  11. package/dist/export/workflows.cjs +214 -18
  12. package/dist/export/workflows.mjs +207 -12
  13. package/dist/export/wrangler.d.cts +1 -2
  14. package/dist/export/wrangler.d.mts +1 -2
  15. package/dist/export/wrangler.d.ts +1 -2
  16. package/dist/shared/{bank.YiArmBW2.mjs → bank.B1Gpn3ht.mjs} +12 -174
  17. package/dist/shared/{bank.xrXNjWCo.d.cts → bank.BEL1HIxZ.d.cts} +21 -1
  18. package/dist/shared/{bank.xrXNjWCo.d.mts → bank.BEL1HIxZ.d.mts} +21 -1
  19. package/dist/shared/{bank.xrXNjWCo.d.ts → bank.BEL1HIxZ.d.ts} +21 -1
  20. package/dist/shared/bank.BUEmFxS8.mjs +174 -0
  21. package/dist/shared/{bank.CLF9wee9.cjs → bank.CPYfE-Ei.cjs} +12 -199
  22. package/dist/shared/bank.CpwLFudl.cjs +198 -0
  23. package/dist/shared/bank.D-3fzX63.mjs +170 -0
  24. package/dist/shared/bank.D0a-MZon.cjs +184 -0
  25. package/dist/shared/{bank.BchnXQDL.d.cts → bank.Dh_H_5rC.d.cts} +0 -1
  26. package/dist/shared/{bank.BchnXQDL.d.mts → bank.Dh_H_5rC.d.mts} +0 -1
  27. package/dist/shared/{bank.BchnXQDL.d.ts → bank.Dh_H_5rC.d.ts} +0 -1
  28. package/dist/types.cjs +14 -14
  29. package/dist/types.d.cts +9 -9
  30. package/dist/types.d.mts +9 -9
  31. package/dist/types.d.ts +9 -9
  32. package/dist/types.mjs +5 -5
  33. package/package.json +1 -1
  34. package/dist/shared/bank.CNtiZSem.mjs +0 -9
  35. package/dist/shared/bank.CUU0Daxj.mjs +0 -391
  36. package/dist/shared/bank.ChffLgyT.cjs +0 -401
  37. package/dist/shared/bank.DfE0M5H3.cjs +0 -11
@@ -1,401 +0,0 @@
1
- 'use strict';
2
-
3
- const database_schema = require('./bank.CLF9wee9.cjs');
4
- const backendSdk = require('@develit-io/backend-sdk');
5
- const cloudflare_workers = require('cloudflare:workers');
6
- const cloudflare_workflows = require('cloudflare:workflows');
7
- const d1 = require('drizzle-orm/d1');
8
- const drizzleOrm = require('drizzle-orm');
9
- require('jose');
10
- require('@develit-io/general-codes');
11
- const node_crypto = require('node:crypto');
12
- const mockCobs_connector = require('./bank.DfE0M5H3.cjs');
13
-
14
- const createPaymentCommand = (db, { payment }) => {
15
- return {
16
- command: db.insert(database_schema.tables.payment).values({
17
- ...payment,
18
- creditorIban: payment.creditor.iban,
19
- debtorIban: payment.debtor.iban
20
- }).returning()
21
- };
22
- };
23
-
24
- const upsertBatchCommand = (db, { batch }) => {
25
- const id = batch.id || backendSdk.uuidv4();
26
- const command = db.insert(database_schema.tables.batch).values({
27
- ...batch,
28
- id
29
- }).onConflictDoUpdate({
30
- target: database_schema.tables.batch.id,
31
- set: {
32
- ...batch
33
- }
34
- }).returning();
35
- return {
36
- id,
37
- command
38
- };
39
- };
40
-
41
- const getAccountByIdQuery = async (db, { accountId }) => {
42
- return await db.select().from(database_schema.tables.account).where(drizzleOrm.eq(database_schema.tables.account.id, accountId)).get();
43
- };
44
-
45
- const getBatchByIdQuery = async (db, { batchId }) => {
46
- return await db.select().from(database_schema.tables.batch).where(drizzleOrm.eq(database_schema.tables.batch.id, batchId)).get();
47
- };
48
-
49
- const getCredentialsByAccountId = async (db, encryptionKey, { accountId }) => {
50
- const cred = await db.select().from(database_schema.tables.accountCredentials).where(drizzleOrm.eq(database_schema.tables.accountCredentials.accountId, accountId)).get();
51
- return cred ? {
52
- ...cred,
53
- value: await decrypt(cred.value, encryptionKey)
54
- } : void 0;
55
- };
56
-
57
- class CreditasConnector extends database_schema.FinbricksConnector {
58
- constructor(config) {
59
- super("CREDITAS", config);
60
- }
61
- }
62
-
63
- class FioConnector extends database_schema.FinbricksConnector {
64
- constructor(config) {
65
- super("FIO", config);
66
- }
67
- }
68
-
69
- class MonetaConnector extends database_schema.FinbricksConnector {
70
- constructor(config) {
71
- super("MONETA", config);
72
- }
73
- }
74
-
75
- const initiateConnector = ({
76
- bank,
77
- env,
78
- connectedAccounts
79
- }) => {
80
- switch (bank) {
81
- case "ERSTE":
82
- return new database_schema.ErsteConnector({
83
- API_KEY: env.ERSTE_API_KEY,
84
- CLIENT_ID: env.ERSTE_CLIENT_ID,
85
- CLIENT_SECRET: env.ERSTE_CLIENT_SECRET,
86
- REDIRECT_URI: env.REDIRECT_URI,
87
- AUTH_URI: env.ERSTE_AUTH_URI,
88
- PAYMENTS_URI: env.ERSTE_PAYMENTS_URI,
89
- ACCOUNTS_URI: env.ERSTE_ACCOUNTS_URI,
90
- connectedAccounts
91
- });
92
- case "CREDITAS":
93
- return new CreditasConnector({
94
- BASE_URI: env.FINBRICKS_BASE_URI,
95
- MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
96
- PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
97
- REDIRECT_URI: env.REDIRECT_URI,
98
- connectedAccounts
99
- });
100
- case "MOCK_COBS":
101
- return new mockCobs_connector.MockCobsConnector({
102
- BASE_URI: env.FINBRICKS_BASE_URI,
103
- MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
104
- PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
105
- REDIRECT_URI: env.REDIRECT_URI,
106
- connectedAccounts
107
- });
108
- case "FIO":
109
- return new FioConnector({
110
- BASE_URI: env.FINBRICKS_BASE_URI,
111
- MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
112
- PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
113
- REDIRECT_URI: env.REDIRECT_URI,
114
- connectedAccounts
115
- });
116
- case "MONETA":
117
- return new MonetaConnector({
118
- BASE_URI: env.FINBRICKS_BASE_URI,
119
- MERCHANT_ID: env.FINBRICKS_MERCHANT_ID,
120
- PRIVATE_KEY_PEM: env.FINBRICKS_PRIVATE_KEY_PEM,
121
- REDIRECT_URI: env.REDIRECT_URI,
122
- connectedAccounts
123
- });
124
- default:
125
- return new database_schema.MockConnector();
126
- }
127
- };
128
-
129
- async function importAesKey(base64Key) {
130
- const raw = Uint8Array.from(atob(base64Key), (c) => c.charCodeAt(0));
131
- return await crypto.subtle.importKey("raw", raw, { name: "AES-GCM" }, false, [
132
- "encrypt",
133
- "decrypt"
134
- ]);
135
- }
136
- async function encrypt(value, key) {
137
- const encoder = new TextEncoder();
138
- const iv = crypto.getRandomValues(new Uint8Array(12));
139
- const ciphertext = await crypto.subtle.encrypt(
140
- { name: "AES-GCM", iv },
141
- key,
142
- encoder.encode(value)
143
- );
144
- const combined = new Uint8Array(iv.length + ciphertext.byteLength);
145
- combined.set(iv, 0);
146
- combined.set(new Uint8Array(ciphertext), iv.length);
147
- return btoa(String.fromCharCode(...combined));
148
- }
149
- async function decrypt(base64, key) {
150
- const raw = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
151
- const iv = raw.slice(0, 12);
152
- const ciphertext = raw.slice(12);
153
- const decrypted = await crypto.subtle.decrypt(
154
- { name: "AES-GCM", iv },
155
- key,
156
- ciphertext
157
- );
158
- return new TextDecoder().decode(decrypted);
159
- }
160
- function canonicalize(value) {
161
- if (value === null || typeof value !== "object") return value;
162
- if (Array.isArray(value)) {
163
- return value.map(canonicalize);
164
- }
165
- const obj = value;
166
- const sorted = Object.keys(obj).sort().reduce((acc, key) => {
167
- acc[key] = canonicalize(obj[key]);
168
- return acc;
169
- }, {});
170
- return sorted;
171
- }
172
- function checksum(input) {
173
- const canonical = canonicalize(input);
174
- const json = JSON.stringify(canonical);
175
- return node_crypto.createHash("sha256").update(json).digest("hex");
176
- }
177
-
178
- const PROCESS_BATCH_WORKFLOW_EVENTS = {
179
- batchAuthorized: "batch-authorized"
180
- };
181
- function pushToQueue(queue, message) {
182
- if (!Array.isArray(message)) return queue.send(message, { contentType: "v8" });
183
- return queue.sendBatch(
184
- message.map((m) => ({
185
- body: m,
186
- contentType: "v8"
187
- }))
188
- );
189
- }
190
- class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
191
- async run(event, step) {
192
- const { batchId } = event.payload;
193
- const db = d1.drizzle(this.env.BANK_D1, { schema: database_schema.tables });
194
- const batch = await step.do("load batch", async () => {
195
- const batch2 = await getBatchByIdQuery(db, { batchId });
196
- if (!batch2) {
197
- throw new cloudflare_workflows.NonRetryableError(`Batch not found`);
198
- }
199
- if (batch2.status === "SIGNED")
200
- throw new cloudflare_workflows.NonRetryableError(`Batch already signed`);
201
- return batch2;
202
- });
203
- await step.do("lock batch", async () => {
204
- const paymentsChecksum = checksum(batch.payments);
205
- if (batch.paymentsChecksum && batch.paymentsChecksum !== paymentsChecksum)
206
- throw new cloudflare_workflows.NonRetryableError(
207
- `Batch payments have been modified externally, manual review needed`
208
- );
209
- if (batch.status === "FAILED")
210
- throw new cloudflare_workflows.NonRetryableError(
211
- `Batch is in FAILED status and cannot be processed, manual review needed`
212
- );
213
- if (batch.status === "OPEN" || batch.status === "FULL") {
214
- const updatedBatch = await upsertBatchCommand(db, {
215
- batch: {
216
- ...batch,
217
- status: "PROCESSING",
218
- paymentsChecksum
219
- }
220
- }).command.execute().then(backendSdk.first);
221
- return { status: updatedBatch.status };
222
- }
223
- return { status: batch.status };
224
- });
225
- const preparedPayments = await step.do(
226
- "prepare payments",
227
- {
228
- retries: { limit: 5, delay: 2e3, backoff: "constant" },
229
- timeout: "30 seconds"
230
- },
231
- async () => {
232
- const account = await getAccountByIdQuery(db, {
233
- accountId: batch.accountId
234
- });
235
- if (!account) {
236
- throw new cloudflare_workflows.NonRetryableError(`Account not found: ${batch.accountId}`);
237
- }
238
- const encryptionKey = await importAesKey(this.env.ENCRYPTION_KEY);
239
- const credentials = await getCredentialsByAccountId(db, encryptionKey, {
240
- accountId: account.id
241
- });
242
- if (!credentials) {
243
- throw new cloudflare_workflows.NonRetryableError(
244
- `No credentials found for account: ${account.id}`
245
- );
246
- }
247
- if (credentials.expiresAt < /* @__PURE__ */ new Date()) {
248
- throw new cloudflare_workflows.NonRetryableError(
249
- `Credentials have expired for account: ${account.id}`
250
- );
251
- }
252
- const connector = initiateConnector({
253
- env: this.env,
254
- bank: account.connectorKey,
255
- connectedAccounts: [
256
- {
257
- ...account,
258
- iban: account.iban,
259
- token: credentials.value
260
- }
261
- ]
262
- });
263
- const preparedPayments2 = [];
264
- for (const payment of batch.payments) {
265
- const prepared = await connector.preparePayment(payment);
266
- if (prepared) {
267
- preparedPayments2.push(prepared);
268
- }
269
- }
270
- return preparedPayments2;
271
- }
272
- );
273
- const batchResult = await step.do(
274
- "initiate batch from payments",
275
- {
276
- retries: { limit: 5, delay: 2e3, backoff: "constant" },
277
- timeout: "30 seconds"
278
- },
279
- async () => {
280
- if (batch.batchPaymentInitiatedAt) {
281
- return {
282
- authorizationUrls: batch.authorizationUrls,
283
- initializedPayments: preparedPayments.map((p) => ({
284
- ...p,
285
- status: "INITIALIZED"
286
- })),
287
- metadata: batch.metadata
288
- };
289
- }
290
- const account = await getAccountByIdQuery(db, {
291
- accountId: batch.accountId
292
- });
293
- if (!account) {
294
- throw new cloudflare_workflows.NonRetryableError(`Account not found: ${batch.accountId}`);
295
- }
296
- const encryptionKey = await importAesKey(this.env.ENCRYPTION_KEY);
297
- const credentials = await getCredentialsByAccountId(db, encryptionKey, {
298
- accountId: account.id
299
- });
300
- if (!credentials) {
301
- throw new cloudflare_workflows.NonRetryableError(
302
- `No credentials found for account: ${account.id}`
303
- );
304
- }
305
- if (credentials.expiresAt < /* @__PURE__ */ new Date()) {
306
- throw new cloudflare_workflows.NonRetryableError(
307
- `Credentials have expired for account: ${account.id}`
308
- );
309
- }
310
- const connector = initiateConnector({
311
- env: this.env,
312
- bank: account.connectorKey,
313
- connectedAccounts: [
314
- {
315
- ...account,
316
- iban: account.iban,
317
- token: credentials.value
318
- }
319
- ]
320
- });
321
- const result = await connector.initiateBatchFromPayments({
322
- payments: preparedPayments
323
- });
324
- const {
325
- authorizationUrls,
326
- payments: initializedPayments,
327
- metadata
328
- } = result;
329
- if (!authorizationUrls || authorizationUrls.length === 0) {
330
- throw new Error("Failed to retrieve authorization URLs");
331
- }
332
- return {
333
- authorizationUrls,
334
- initializedPayments,
335
- metadata
336
- };
337
- }
338
- );
339
- await step.do("update batch and payments to initialized", async () => {
340
- const upsertBatch = upsertBatchCommand(db, {
341
- batch: {
342
- ...batch,
343
- authorizationUrls: batchResult.authorizationUrls,
344
- metadata: batchResult.metadata,
345
- status: "READY_TO_SIGN",
346
- batchPaymentInitiatedAt: /* @__PURE__ */ new Date()
347
- }
348
- }).command;
349
- await upsertBatch.execute();
350
- return {
351
- status: "READY_TO_SIGN",
352
- paymentsInitialized: batchResult.initializedPayments.length
353
- };
354
- });
355
- await step.do("send authorization email", async () => {
356
- await pushToQueue(this.env.NOTIFICATIONS_QUEUE, {
357
- type: "email",
358
- payload: {
359
- email: {
360
- to: [this.env.BANK_AUTH_RECIPIENT],
361
- subject: "Payment Authorization",
362
- text: Array.isArray(batchResult.authorizationUrls) ? batchResult.authorizationUrls[0] || "No Authorization URL" : batchResult.authorizationUrls || "No Authorization URL"
363
- }
364
- },
365
- metadata: {
366
- initiator: {
367
- service: "bank"
368
- }
369
- }
370
- });
371
- return { emailSent: true };
372
- });
373
- const authorizationEvent = await step.waitForEvent(
374
- "wait for batch authorization",
375
- {
376
- type: "batch-authorized",
377
- timeout: "3 days"
378
- }
379
- );
380
- await step.do("handle authorization result", async () => {
381
- const authPayload = authorizationEvent.payload;
382
- const updatedBatch = await upsertBatchCommand(db, {
383
- batch: {
384
- ...batch,
385
- status: authPayload.authorized ? "SIGNED" : "SIGNATURE_FAILED"
386
- }
387
- }).command.execute().then(backendSdk.first);
388
- return { updatedBatch };
389
- });
390
- }
391
- }
392
-
393
- exports.BankProcessBatch = BankProcessBatch;
394
- exports.PROCESS_BATCH_WORKFLOW_EVENTS = PROCESS_BATCH_WORKFLOW_EVENTS;
395
- exports.createPaymentCommand = createPaymentCommand;
396
- exports.encrypt = encrypt;
397
- exports.getAccountByIdQuery = getAccountByIdQuery;
398
- exports.getCredentialsByAccountId = getCredentialsByAccountId;
399
- exports.importAesKey = importAesKey;
400
- exports.initiateConnector = initiateConnector;
401
- exports.upsertBatchCommand = upsertBatchCommand;
@@ -1,11 +0,0 @@
1
- 'use strict';
2
-
3
- const database_schema = require('./bank.CLF9wee9.cjs');
4
-
5
- class MockCobsConnector extends database_schema.FinbricksConnector {
6
- constructor(config) {
7
- super("MOCK_COBS", config);
8
- }
9
- }
10
-
11
- exports.MockCobsConnector = MockCobsConnector;