@develit-services/bank 0.0.3 → 0.0.4

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,724 @@
1
+ 'use strict';
2
+
3
+ const backendSdk = require('@develit-io/backend-sdk');
4
+ const database_schema = require('./bank.CrAQe4PH.cjs');
5
+ const drizzleOrm = require('drizzle-orm');
6
+ const dateFns = require('date-fns');
7
+ const jose = require('jose');
8
+ const generalCodes = require('@develit-io/general-codes');
9
+ const zod = require('zod');
10
+
11
+ const tables = database_schema.schema;
12
+
13
+ const PAYMENT_TYPES = ["SEPA", "SWIFT", "IFSC", "DOMESTIC"];
14
+ const PAYMENT_STATUSES = [
15
+ "PREPARED",
16
+ "INITIALIZED",
17
+ "FAILED",
18
+ "PENDING",
19
+ "COMPLETED",
20
+ "CREATED"
21
+ ];
22
+ const PAYMENT_DIRECTIONS = ["INCOMING", "OUTGOING"];
23
+ const BATCH_STATUSES = [
24
+ "OPEN",
25
+ "WAITING_FOR_PROCESSING",
26
+ "PROCESSING",
27
+ "READY_TO_SIGN",
28
+ "PREPARED",
29
+ "COMPLETED",
30
+ "FAILED"
31
+ ];
32
+ const COUNTRY_CODES = generalCodes.COUNTRY_CODES_2;
33
+
34
+ class IBankConnector {
35
+ }
36
+
37
+ const isDeposit = (payment, creditorIban) => {
38
+ return payment.creditorIban === creditorIban;
39
+ };
40
+ const getPaymentDirection = (payment, iban) => {
41
+ if (isDeposit(payment, iban)) return "INCOMING";
42
+ return "OUTGOING";
43
+ };
44
+
45
+ const mapReferencesToPayment = (reference) => {
46
+ const symbols = {
47
+ vs: void 0,
48
+ ss: void 0,
49
+ ks: void 0
50
+ };
51
+ if (!reference) return symbols;
52
+ if (Array.isArray(reference) && reference.length > 0) {
53
+ symbols.vs = reference.find((ref) => ref.includes("VS")) || void 0;
54
+ symbols.ss = reference.find((ref) => ref.includes("SS")) || void 0;
55
+ symbols.ks = reference.find((ref) => ref.includes("KS")) || void 0;
56
+ }
57
+ if (typeof reference === "string") {
58
+ const vsMatch = reference.match(/VS[:\s]*(\d+)/i);
59
+ const ssMatch = reference.match(/SS[:\s]*(\d+)/i);
60
+ const ksMatch = reference.match(/KS[:\s]*(\d+)/i);
61
+ if (vsMatch) symbols.vs = vsMatch[1];
62
+ if (ssMatch) symbols.ss = ssMatch[1];
63
+ if (ksMatch) symbols.ks = ksMatch[1];
64
+ }
65
+ return symbols;
66
+ };
67
+
68
+ class ErsteConnector extends IBankConnector {
69
+ constructor(config) {
70
+ super();
71
+ this.connectorKey = "ERSTE";
72
+ this.accessToken = null;
73
+ this.API_KEY = config.API_KEY;
74
+ this.CLIENT_ID = config.CLIENT_ID;
75
+ this.CLIENT_SECRET = config.CLIENT_SECRET;
76
+ this.REDIRECT_URI = config.REDIRECT_URI;
77
+ this.AUTH_URI = config.AUTH_URI;
78
+ this.PAYMENTS_URI = config.PAYMENTS_URI;
79
+ this.ACCOUNTS_URI = config.ACCOUNTS_URI;
80
+ }
81
+ static {
82
+ this.FETCH_INTERVAL = 60 * 60 * 5;
83
+ }
84
+ get adminCodeCreationURI() {
85
+ const params = new URLSearchParams({
86
+ redirect_uri: `${this.REDIRECT_URI}`,
87
+ client_id: this.CLIENT_ID,
88
+ response_type: "code",
89
+ access_type: "offline",
90
+ state: "csas-auth",
91
+ prompt: "consent"
92
+ });
93
+ return `${this.AUTH_URI}/auth?${params.toString()}`;
94
+ }
95
+ async authenticate({ token, refreshToken }) {
96
+ const grantType = refreshToken ? "refresh_token" : "authorization_code";
97
+ let bodyParams = {
98
+ client_id: this.CLIENT_ID,
99
+ client_secret: this.CLIENT_SECRET,
100
+ grant_type: grantType
101
+ };
102
+ if (token) {
103
+ bodyParams = {
104
+ ...bodyParams,
105
+ code: token,
106
+ redirect_uri: this.REDIRECT_URI
107
+ };
108
+ }
109
+ if (refreshToken) bodyParams.refresh_token = refreshToken;
110
+ const body = new URLSearchParams(bodyParams);
111
+ const result = await fetch(`${this.AUTH_URI}/token`, {
112
+ method: "POST",
113
+ body: body.toString(),
114
+ headers: { "Content-Type": "application/x-www-form-urlencoded" }
115
+ });
116
+ if (result.status !== 200) {
117
+ throw backendSdk.createInternalError(null, {
118
+ message: await result.text()
119
+ });
120
+ }
121
+ const json = await result.json();
122
+ this.accessToken = json.access_token;
123
+ return json.refresh_token || "";
124
+ }
125
+ async preparePayment(payment) {
126
+ if (!this.accessToken) {
127
+ throw backendSdk.createInternalError(null, {
128
+ message: "Internal authentication failed",
129
+ status: 500
130
+ });
131
+ }
132
+ const paymentBody = {
133
+ paymentIdentification: {
134
+ endToEndIdentification: payment.id,
135
+ instructionIdentification: payment.id
136
+ },
137
+ paymentTypeInformation: { instructionPriority: "NORM" },
138
+ amount: {
139
+ instructedAmount: { value: payment.amount, currency: payment.currency }
140
+ },
141
+ debtorAccount: { identification: { iban: payment.debtorIban } },
142
+ creditorAccount: { identification: { iban: payment.creditorIban } },
143
+ creditor: {
144
+ name: payment.creditorHolderName
145
+ }
146
+ };
147
+ const [data, error] = await backendSdk.useResult(
148
+ fetch(`${this.PAYMENTS_URI}/my/payments`, {
149
+ method: "POST",
150
+ headers: {
151
+ "WEB-API-key": this.API_KEY,
152
+ "Content-Type": "application/json",
153
+ Authorization: `Bearer ${this.accessToken}`
154
+ },
155
+ body: JSON.stringify(paymentBody)
156
+ })
157
+ );
158
+ if (error || !data) {
159
+ throw error;
160
+ }
161
+ const erstePayment = await data.json();
162
+ return {
163
+ ...payment,
164
+ id: backendSdk.uuidv4(),
165
+ bankRefId: erstePayment.signInfo?.signId,
166
+ refId: payment.refId,
167
+ direction: "OUTGOING",
168
+ paymentType: "DOMESTIC",
169
+ status: "PREPARED",
170
+ initiatedAt: /* @__PURE__ */ new Date()
171
+ };
172
+ }
173
+ async initiateBatchFromPayments({
174
+ payments
175
+ }) {
176
+ const allRefIds = payments.map((payment) => payment.bankRefId);
177
+ const batchId = backendSdk.uuidv4();
178
+ if (allRefIds.length === 0) {
179
+ throw backendSdk.createInternalError(null, {
180
+ message: "No valid payments to include in batch",
181
+ status: 400
182
+ });
183
+ }
184
+ const [signInfo, signError] = await backendSdk.useResult(
185
+ this.sendBatchPaymentForAuthorization(allRefIds)
186
+ );
187
+ if (signError) {
188
+ throw backendSdk.createInternalError(signError);
189
+ }
190
+ const [authUri, authUriError] = await backendSdk.useResult(
191
+ this.getBatchAuthorizationURI(signInfo)
192
+ );
193
+ if (authUriError) {
194
+ throw backendSdk.createInternalError(authUriError);
195
+ }
196
+ return {
197
+ id: batchId,
198
+ authorizationUrls: [authUri || ""],
199
+ payments: payments.map((payment) => ({
200
+ ...payment,
201
+ status: "INITIALIZED"
202
+ })),
203
+ metadata: signInfo
204
+ };
205
+ }
206
+ initiateSinglePayment() {
207
+ throw new Error("Method not implemented.");
208
+ }
209
+ async sendBatchPaymentForAuthorization(paymentIds) {
210
+ const [data, error] = await backendSdk.useResult(
211
+ fetch(`${this.PAYMENTS_URI}/my/batchpayments`, {
212
+ method: "POST",
213
+ headers: {
214
+ "Content-Type": "application/json",
215
+ Authorization: `Bearer ${this.accessToken}`,
216
+ "WEB-API-key": this.CLIENT_SECRET
217
+ },
218
+ body: JSON.stringify({
219
+ exchangeIdentification: "658576010faf0a23dc",
220
+ payments: paymentIds
221
+ })
222
+ })
223
+ );
224
+ if (error) throw error;
225
+ const batch = await data.json();
226
+ return {
227
+ signHash: batch.signInfo.hash,
228
+ signId: batch.signInfo.signId
229
+ };
230
+ }
231
+ async getBatchAuthorizationURI({
232
+ signId,
233
+ signHash
234
+ }) {
235
+ const [data, error] = await backendSdk.useResult(
236
+ fetch(
237
+ `${this.PAYMENTS_URI}/my/batchpayments/federate/sign/${signId}/hash/${signHash}`,
238
+ {
239
+ method: "GET",
240
+ headers: {
241
+ "Callback-Uri": `${this.REDIRECT_URI}/payment-authorization-complete`,
242
+ Authorization: `Bearer ${this.accessToken}`,
243
+ "WEB-API-key": this.CLIENT_SECRET
244
+ }
245
+ }
246
+ )
247
+ );
248
+ if (error) {
249
+ throw error;
250
+ }
251
+ return (await data.json()).signingUrl;
252
+ }
253
+ async getAllAccountPayments({
254
+ account,
255
+ lastSync
256
+ }) {
257
+ const erstePayments = [];
258
+ const dateFormat = "yyyy-MM-dd";
259
+ const fromDate = dateFns.format(lastSync.lastSyncedAt, dateFormat);
260
+ const toDate = dateFns.format(/* @__PURE__ */ new Date(), dateFormat);
261
+ let page = 0;
262
+ const pageSize = 200;
263
+ const pageCount = 1;
264
+ while (page < pageCount) {
265
+ const response = await fetch(
266
+ `${this.ACCOUNTS_URI}/my/accounts/${account.id}/transactions?fromDate=${fromDate}&toDate=${toDate}&size=${pageSize}&page=${page}&sort=bookingDate&order=desc`,
267
+ {
268
+ headers: {
269
+ "WEB-API-key": this.API_KEY,
270
+ Authorization: `Bearer ${this.accessToken}`
271
+ }
272
+ }
273
+ );
274
+ const data = await response.json();
275
+ erstePayments.push(...data.transactions);
276
+ page += 1;
277
+ }
278
+ const payments = erstePayments.map((payment) => {
279
+ const reference = payment.entryDetails.transactionDetails.remittanceInformation?.structured.creditorReferenceInformation?.reference || [];
280
+ const symbols = mapReferencesToPayment(reference);
281
+ const paymentInsert = {
282
+ id: backendSdk.uuidv4(),
283
+ bankRefId: payment.entryReference,
284
+ amount: payment.amount.value,
285
+ currency: payment.amount.currency.code,
286
+ debtorHolderName: payment.entryDetails.transactionDetails.relatedParties.debtor?.name,
287
+ debtorIban: payment.entryDetails.transactionDetails.relatedParties.debtorAccount?.identification.iban,
288
+ debtorAccountNumberWithBankCode: payment.entryDetails.transactionDetails.relatedParties.debtorAccount?.identification.other?.identification,
289
+ creditorHolderName: payment.entryDetails.transactionDetails.relatedParties.creditor?.name || "",
290
+ creditorIban: payment.entryDetails.transactionDetails.relatedParties.creditorAccount?.identification.iban,
291
+ creditorAccountNumberWithBankCode: payment.entryDetails.transactionDetails.relatedParties.creditorAccount?.identification.other?.identification,
292
+ paymentType: "DOMESTIC",
293
+ direction: "INCOMING",
294
+ message: payment.entryDetails.transactionDetails.remittanceInformation?.unstructured,
295
+ vs: symbols.vs,
296
+ ss: symbols.ss,
297
+ ks: symbols.ks,
298
+ processedAt: dateFns.parseISO(payment.bookingDate.date),
299
+ status: "COMPLETED"
300
+ };
301
+ return {
302
+ ...paymentInsert,
303
+ direction: getPaymentDirection(
304
+ paymentInsert,
305
+ account.identification.iban
306
+ )
307
+ };
308
+ });
309
+ return payments;
310
+ }
311
+ }
312
+
313
+ const mapFinbricksStatus = (status) => {
314
+ switch (status) {
315
+ case "BOOK":
316
+ return "COMPLETED";
317
+ case "PDNG":
318
+ return "PENDING";
319
+ case "CANCL":
320
+ return "FAILED";
321
+ case "RJCT":
322
+ return "FAILED";
323
+ case "SCHDL":
324
+ return "PENDING";
325
+ case "HOLD":
326
+ return "PENDING";
327
+ case "INFO":
328
+ return "PENDING";
329
+ default:
330
+ return "PENDING";
331
+ }
332
+ };
333
+
334
+ class FinbricksConnector extends IBankConnector {
335
+ constructor(PROVIDER, { BASE_URI, MERCHANT_ID, PRIVATE_KEY_PEM }) {
336
+ super();
337
+ this.connectorKey = "FINBRICKS";
338
+ this.apiDictionary = /* @__PURE__ */ new Map([
339
+ ["single-payment-domestic", "/transaction/platform/init"],
340
+ ["batch-payment-domestic", "/transaction/platform/batchPayment/init"],
341
+ ["get-accounts", "/account/list"],
342
+ ["get-account-transactions", "/account/transactions"],
343
+ ["authenticate-client", "/v2/auth/authenticate"],
344
+ ["expiration", "/auth/token"]
345
+ ]);
346
+ this.PROVIDER = PROVIDER;
347
+ this.BASE_URI = BASE_URI;
348
+ this.MERCHANT_ID = MERCHANT_ID;
349
+ this.PRIVATE_KEY_PEM = PRIVATE_KEY_PEM;
350
+ }
351
+ static {
352
+ this.FETCH_INTERVAL = 60 * 60 * 5;
353
+ }
354
+ async signFinbricksJws({ jwsData }) {
355
+ const privateKey = await jose.importPKCS8(this.PRIVATE_KEY_PEM, "RS256");
356
+ const payload = JSON.stringify(jwsData);
357
+ const jws = await new jose.SignJWT(JSON.parse(payload)).setProtectedHeader({
358
+ alg: "RS256",
359
+ kid: this.MERCHANT_ID,
360
+ typ: "JWT"
361
+ }).sign(privateKey);
362
+ return jws;
363
+ }
364
+ async authenticate({ token }) {
365
+ this.lastValidClientId = token;
366
+ return this.lastValidClientId;
367
+ }
368
+ async isClientIdExpired(clientId) {
369
+ const query = new URLSearchParams({
370
+ merchantId: this.MERCHANT_ID,
371
+ provider: this.PROVIDER,
372
+ clientId
373
+ });
374
+ const uri = this.apiDictionary.get("expiration");
375
+ const fullUri = `${uri}?${query.toString()}`;
376
+ const signature = await this.signFinbricksJws({
377
+ jwsData: {
378
+ body: "",
379
+ uri: fullUri
380
+ }
381
+ });
382
+ const [data, error] = await backendSdk.useResult(
383
+ fetch(`${this.BASE_URI}${fullUri}`, {
384
+ method: "POST",
385
+ headers: {
386
+ "JWS-Signature": signature,
387
+ "Content-Type": "application/json"
388
+ },
389
+ body: ""
390
+ })
391
+ );
392
+ if (error) {
393
+ const body = await data?.json();
394
+ throw backendSdk.createInternalError(error, {
395
+ message: body?.message || "Finbricks API error"
396
+ });
397
+ }
398
+ if (!data?.ok) {
399
+ const body = await data.json();
400
+ throw backendSdk.createInternalError(null, {
401
+ message: body.message || "Finbricks API error",
402
+ code: "FINBRICKS_API_ERROR",
403
+ status: data?.status || 500
404
+ });
405
+ }
406
+ const tokens = await data.json();
407
+ return !!tokens.find((token) => token.clientId === clientId);
408
+ }
409
+ async preparePayment(payment) {
410
+ const bankRefId = backendSdk.uuidv4();
411
+ return {
412
+ ...payment,
413
+ id: backendSdk.uuidv4(),
414
+ bankRefId,
415
+ direction: "OUTGOING",
416
+ paymentType: "DOMESTIC",
417
+ status: "PREPARED",
418
+ initiatedAt: /* @__PURE__ */ new Date()
419
+ };
420
+ }
421
+ async initiateBatchFromPayments({
422
+ payments
423
+ }) {
424
+ const uri = this.apiDictionary.get("batch-payment-domestic");
425
+ const batchId = backendSdk.uuidv4();
426
+ const batchBody = {
427
+ batchPaymentIdentification: {
428
+ merchantId: this.MERCHANT_ID,
429
+ debtorAccountIban: payments[0].debtorIban,
430
+ clientId: this.lastValidClientId,
431
+ callbackUrl: "https://example.com/callback",
432
+ merchantBatchId: batchId
433
+ },
434
+ payments: payments.map((payment) => ({
435
+ merchantTransactionId: payment.bankRefId,
436
+ creditorAccountIban: payment.creditorIban,
437
+ amount: payment.amount
438
+ }))
439
+ };
440
+ const payload = JSON.stringify(batchBody);
441
+ const jwsData = {
442
+ uri,
443
+ body: payload
444
+ };
445
+ const signature = await this.signFinbricksJws({
446
+ jwsData
447
+ });
448
+ const [data, error] = await backendSdk.useResult(
449
+ fetch(`${this.BASE_URI}${uri}`, {
450
+ method: "POST",
451
+ headers: {
452
+ "JWS-Signature": signature,
453
+ "Content-Type": "application/json"
454
+ },
455
+ body: payload
456
+ })
457
+ );
458
+ const body = await data?.json();
459
+ if (error) {
460
+ throw backendSdk.createInternalError(error, {
461
+ message: body?.message || "Finbricks API error"
462
+ });
463
+ }
464
+ if (!data?.ok) {
465
+ console.log("FINBRICKSBODY", body);
466
+ throw backendSdk.createInternalError(null, {
467
+ message: body?.message || "Finbricks API error",
468
+ code: "FINBRICKS_API_ERROR",
469
+ status: data?.status || 500
470
+ });
471
+ }
472
+ const { redirectUrl } = body;
473
+ return {
474
+ id: batchId,
475
+ authorizationUrls: [redirectUrl],
476
+ payments: payments.map((payment) => ({
477
+ ...payment,
478
+ status: "INITIALIZED"
479
+ }))
480
+ };
481
+ }
482
+ async initiateSinglePayment(payment) {
483
+ const uri = this.apiDictionary.get("single-payment-domestic");
484
+ const paymentBody = {
485
+ merchantId: this.MERCHANT_ID,
486
+ merchantTransactionId: payment.bankRefId,
487
+ totalPrice: payment.amount,
488
+ creditorAccountIban: payment.creditorIban,
489
+ debtorAccountIban: payment.debtorIban,
490
+ creditorName: payment.creditorHolderName,
491
+ description: payment.message || "",
492
+ variableSymbol: payment.vs || "",
493
+ callbackUrl: "example.com",
494
+ paymentProvider: this.PROVIDER
495
+ };
496
+ const signature = await this.signFinbricksJws({
497
+ jwsData: {
498
+ body: JSON.stringify(paymentBody),
499
+ uri
500
+ }
501
+ });
502
+ const [data, error] = await backendSdk.useResult(
503
+ fetch(`${this.BASE_URI}${uri}`, {
504
+ method: "POST",
505
+ headers: {
506
+ "JWS-Signature": signature,
507
+ "Content-Type": "application/json"
508
+ },
509
+ body: JSON.stringify(paymentBody)
510
+ })
511
+ );
512
+ const body = await data?.json();
513
+ if (error) {
514
+ throw backendSdk.createInternalError(error, {
515
+ message: body?.message || "Finbricks API error"
516
+ });
517
+ }
518
+ if (!data?.ok) {
519
+ console.log("FINBRICKSBODY", body);
520
+ throw backendSdk.createInternalError(null, {
521
+ message: body?.message || "Finbricks API error",
522
+ code: "FINBRICKS_API_ERROR",
523
+ status: data?.status || 500
524
+ });
525
+ }
526
+ const redirectUrl = (await data.json()).redirectUrl;
527
+ return {
528
+ authorizationUrl: redirectUrl,
529
+ payment: { ...payment, status: "INITIALIZED" }
530
+ };
531
+ }
532
+ async getAllAccountPayments({
533
+ account,
534
+ lastSync
535
+ }) {
536
+ const uri = this.apiDictionary.get("get-account-transactions");
537
+ const dateFormat = "yyyy-MM-dd";
538
+ let cursor = null;
539
+ let allPayments = [];
540
+ const dateFrom = dateFns.format(lastSync.lastSyncedAt, dateFormat);
541
+ const dateTo = dateFns.format(/* @__PURE__ */ new Date(), dateFormat);
542
+ do {
543
+ const query = new URLSearchParams({
544
+ merchantId: this.MERCHANT_ID,
545
+ clientId: this.lastValidClientId,
546
+ paymentProvider: this.PROVIDER,
547
+ bankAccountId: account.identification.number,
548
+ dateFrom,
549
+ dateTo,
550
+ currency: account.currency,
551
+ size: "20"
552
+ });
553
+ console.log("query", query);
554
+ if (cursor) query.set("cursor", cursor);
555
+ const fullUri = `${uri}?${query.toString()}`;
556
+ const jwsData = {
557
+ uri: fullUri,
558
+ body: ""
559
+ };
560
+ const signature = await this.signFinbricksJws({ jwsData });
561
+ console.log("signature ready", signature);
562
+ const [data, error] = await backendSdk.useResult(
563
+ fetch(`${this.BASE_URI}${fullUri}`, {
564
+ method: "GET",
565
+ headers: {
566
+ "JWS-Signature": signature,
567
+ "Content-Type": "application/json"
568
+ }
569
+ })
570
+ );
571
+ if (error) {
572
+ const body = await data?.json();
573
+ throw backendSdk.createInternalError(error, {
574
+ message: body?.message || "Finbricks API error"
575
+ });
576
+ }
577
+ if (!data?.ok) {
578
+ const body = await data.json();
579
+ throw backendSdk.createInternalError(null, {
580
+ message: body.message || "Finbricks API error",
581
+ code: "FINBRICKS_API_ERROR",
582
+ status: data?.status || 500
583
+ });
584
+ }
585
+ const { transactions, links } = await data.json();
586
+ cursor = links?.[0]?.value || null;
587
+ allPayments = [
588
+ ...allPayments,
589
+ ...transactions.map((tx) => {
590
+ const isIncoming = tx.creditDebitIndicator === "CRDT";
591
+ const relatedParties = tx.entryDetails?.transactionDetails?.relatedParties;
592
+ const paymentInsert = {
593
+ id: backendSdk.uuidv4(),
594
+ bankRefId: tx.entryReference || tx.fbxReference,
595
+ amount: tx.amount?.value || 0,
596
+ currency: tx.amount?.currency || "CZK",
597
+ debtorHolderName: isIncoming ? relatedParties?.debtor?.name || "Unknown" : "Unknown",
598
+ debtorIban: isIncoming ? relatedParties?.debtorAccount?.identification?.iban || account.identification.iban : account.identification.iban,
599
+ debtorAccountNumberWithBankCode: isIncoming ? relatedParties?.debtorAccount?.identification?.other?.identification || `${account.identification.number}/${account.identification.bankCode}` : `${account.identification.number}/${account.identification.bankCode}`,
600
+ creditorHolderName: isIncoming ? "Unknown" : relatedParties?.creditor?.name || "Unknown",
601
+ creditorIban: isIncoming ? account.identification.iban : relatedParties?.creditorAccount?.identification?.iban || account.identification.iban,
602
+ creditorAccountNumberWithBankCode: isIncoming ? `${account.identification.number}/${account.identification.bankCode}` : relatedParties?.creditorAccount?.identification?.other?.identification || `${account.identification.number}/${account.identification.bankCode}`,
603
+ paymentType: "DOMESTIC",
604
+ direction: isIncoming ? "INCOMING" : "OUTGOING",
605
+ message: tx.entryDetails?.transactionDetails?.remittanceInformation?.unstructured || tx.entryDetails?.transactionDetails?.additionalRemittanceInformation || null,
606
+ ...mapReferencesToPayment(
607
+ tx.entryDetails?.transactionDetails?.remittanceInformation?.structured?.creditorReferenceInformation?.reference || tx.entryDetails?.transactionDetails?.references?.endToEndIdentification
608
+ ),
609
+ processedAt: new Date(tx.bookingDate.date),
610
+ status: "COMPLETED"
611
+ };
612
+ return {
613
+ ...paymentInsert,
614
+ direction: getPaymentDirection(
615
+ paymentInsert,
616
+ account.identification.iban
617
+ ),
618
+ status: mapFinbricksStatus(tx.status)
619
+ };
620
+ })
621
+ ];
622
+ } while (cursor != null);
623
+ return allPayments;
624
+ }
625
+ }
626
+
627
+ class MockConnector extends IBankConnector {
628
+ constructor() {
629
+ super(...arguments);
630
+ this.connectorKey = "MOCK";
631
+ }
632
+ static {
633
+ this.FETCH_INTERVAL = 60 * 60 * 5;
634
+ }
635
+ async authenticate() {
636
+ return "";
637
+ }
638
+ async preparePayment(payment) {
639
+ const bankRefId = backendSdk.uuidv4();
640
+ return {
641
+ ...payment,
642
+ id: backendSdk.uuidv4(),
643
+ bankRefId,
644
+ direction: "OUTGOING",
645
+ paymentType: "DOMESTIC",
646
+ status: "PREPARED",
647
+ initiatedAt: /* @__PURE__ */ new Date()
648
+ };
649
+ }
650
+ async initiateSinglePayment(payment) {
651
+ return {
652
+ payment: {
653
+ ...payment,
654
+ status: "INITIALIZED"
655
+ },
656
+ authorizationUrl: "http://mock.com"
657
+ };
658
+ }
659
+ async initiateBatchFromPayments({
660
+ payments
661
+ }) {
662
+ return {
663
+ id: backendSdk.uuidv4(),
664
+ authorizationUrls: ["http://mock.com"],
665
+ payments: payments.map((payment) => ({
666
+ ...payment,
667
+ status: "INITIALIZED"
668
+ }))
669
+ };
670
+ }
671
+ async getAllAccountPayments({
672
+ db
673
+ }) {
674
+ const payments = await db.select().from(tables.payment).where(
675
+ drizzleOrm.and(
676
+ drizzleOrm.eq(tables.payment.status, "INITIALIZED"),
677
+ drizzleOrm.eq(tables.payment.direction, "OUTGOING")
678
+ )
679
+ );
680
+ return payments.map((payment) => ({
681
+ ...payment,
682
+ status: "COMPLETED"
683
+ }));
684
+ }
685
+ }
686
+
687
+ class MockCobsConnector extends FinbricksConnector {
688
+ constructor(config) {
689
+ super("MOCK_COBS", config);
690
+ }
691
+ }
692
+
693
+ const paymentInsertTypeZod = zod.z.object({
694
+ refId: zod.z.string(),
695
+ bankRefId: zod.z.string(),
696
+ amount: zod.z.number(),
697
+ direction: zod.z.enum(PAYMENT_DIRECTIONS),
698
+ paymentType: zod.z.enum(PAYMENT_TYPES),
699
+ currency: zod.z.enum(generalCodes.CURRENCY_CODES),
700
+ status: zod.z.enum(PAYMENT_STATUSES),
701
+ batchId: zod.z.string(),
702
+ bankPaymentInitiatedAt: zod.z.coerce.date(),
703
+ bankPaymentProcessedAt: zod.z.coerce.date(),
704
+ bankingVs: zod.z.string().max(10),
705
+ message: zod.z.string(),
706
+ creditorIban: zod.z.string().max(34),
707
+ creditorHolderName: zod.z.string(),
708
+ debtorIban: zod.z.string().max(34),
709
+ debtorHolderName: zod.z.string().nullable()
710
+ });
711
+
712
+ exports.BATCH_STATUSES = BATCH_STATUSES;
713
+ exports.COUNTRY_CODES = COUNTRY_CODES;
714
+ exports.ErsteConnector = ErsteConnector;
715
+ exports.FinbricksConnector = FinbricksConnector;
716
+ exports.IBankConnector = IBankConnector;
717
+ exports.MockCobsConnector = MockCobsConnector;
718
+ exports.MockConnector = MockConnector;
719
+ exports.PAYMENT_DIRECTIONS = PAYMENT_DIRECTIONS;
720
+ exports.PAYMENT_STATUSES = PAYMENT_STATUSES;
721
+ exports.PAYMENT_TYPES = PAYMENT_TYPES;
722
+ exports.getPaymentDirection = getPaymentDirection;
723
+ exports.paymentInsertTypeZod = paymentInsertTypeZod;
724
+ exports.tables = tables;