@develit-services/bank 4.2.2 → 4.4.0

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.
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const backendSdk = require('@develit-io/backend-sdk');
4
- const ott_zod = require('../shared/bank.DiJmJkDt.cjs');
4
+ const ott_zod = require('../shared/bank.BSX82jhx.cjs');
5
5
  const drizzleOrm = require('drizzle-orm');
6
6
  const cloudflare_workers = require('cloudflare:workers');
7
7
  const d1 = require('drizzle-orm/d1');
@@ -11,7 +11,7 @@ const database_schema = require('../shared/bank.9Yw4KHyl.cjs');
11
11
  const generalCodes = require('@develit-io/general-codes');
12
12
  require('date-fns');
13
13
  require('node:crypto');
14
- const credentialsResolver = require('../shared/bank.CioJeFzf.cjs');
14
+ const credentialsResolver = require('../shared/bank.CibQRM2D.cjs');
15
15
  require('drizzle-orm/zod');
16
16
  require('drizzle-orm/sqlite-core');
17
17
 
@@ -88,6 +88,43 @@ const sendPaymentInputSchema = zod.z.object({
88
88
  creditor: backendSdk.bankAccountMetadataSchema,
89
89
  debtor: backendSdk.bankAccountMetadataSchema,
90
90
  sendAsSinglePayment: zod.z.boolean().optional()
91
+ }).superRefine((data, ctx) => {
92
+ if (data.paymentType === "UNKNOWN") {
93
+ ctx.addIssue({
94
+ code: "custom",
95
+ message: "paymentType=UNKNOWN is not supported. Use DOMESTIC, SEPA, or SWIFT.",
96
+ path: ["paymentType"]
97
+ });
98
+ return;
99
+ }
100
+ if (!data.debtor.iban) {
101
+ ctx.addIssue({
102
+ code: "custom",
103
+ message: "debtor.iban is required",
104
+ path: ["debtor", "iban"]
105
+ });
106
+ }
107
+ if (data.paymentType === "DOMESTIC" || data.paymentType === "SEPA") {
108
+ if (!data.creditor.iban) {
109
+ ctx.addIssue({
110
+ code: "custom",
111
+ message: `creditor.iban is required for ${data.paymentType} payments`,
112
+ path: ["creditor", "iban"]
113
+ });
114
+ }
115
+ return;
116
+ }
117
+ if (data.paymentType === "SWIFT") {
118
+ const hasIban = !!data.creditor.iban;
119
+ const hasBban = !!data.creditor.number && !!(data.creditor.swiftBic ?? data.creditor.bicCor);
120
+ if (!hasIban && !hasBban) {
121
+ ctx.addIssue({
122
+ code: "custom",
123
+ message: "SWIFT creditor requires either iban OR (number + swiftBic/bicCor)",
124
+ path: ["creditor"]
125
+ });
126
+ }
127
+ }
91
128
  });
92
129
 
93
130
  const sendBatchInputSchema = zod.z.object({
@@ -1,5 +1,5 @@
1
1
  import { bankAccountMetadataSchema, structuredAddressSchema, uuidv4, first, buildMultiFilterConditions as buildMultiFilterConditions$1, workflowInstanceStatusSchema, develitWorker, createInternalError, action, service } from '@develit-io/backend-sdk';
2
- import { G as tables, g as accountInsertSchema, H as relations, J as initiateConnector, o as isProcessedStatus, p as isTerminalStatus, L as getNonTerminalPaymentRequestsQuery, x as toIncomingPayment, j as assignAccount, u as toBatchedPayment, y as toPaymentRequestInsert, a as FinbricksClient, F as FINBRICKS_ENDPOINTS } from '../shared/bank.BOMobxtA.mjs';
2
+ import { G as tables, g as accountInsertSchema, H as relations, J as initiateConnector, o as isProcessedStatus, p as isTerminalStatus, L as getNonTerminalPaymentRequestsQuery, x as toIncomingPayment, j as assignAccount, u as toBatchedPayment, y as toPaymentRequestInsert, a as FinbricksClient, F as FINBRICKS_ENDPOINTS } from '../shared/bank.CZ8MQDPa.mjs';
3
3
  import { eq, sql, and, like, asc, desc, inArray, gte, lte, isNull, count } from 'drizzle-orm';
4
4
  import { WorkerEntrypoint } from 'cloudflare:workers';
5
5
  import { drizzle } from 'drizzle-orm/d1';
@@ -9,7 +9,7 @@ import { I as INSTRUCTION_PRIORITIES, C as CHARGE_BEARERS, g as PAYMENT_TYPES, b
9
9
  import { CURRENCY_CODES } from '@develit-io/general-codes';
10
10
  import 'date-fns';
11
11
  import 'node:crypto';
12
- import { h as encrypt, d as createCredentialsResolver, e as updatePaymentRequestStatusCommand, a as getPaymentRequestsByBatchIdQuery, g as getBatchByIdQuery, u as upsertBatchCommand, i as importAesKey, f as createPaymentCommand, b as getAccountByIdQuery } from '../shared/bank.BELDXSDV.mjs';
12
+ import { h as encrypt, d as createCredentialsResolver, e as updatePaymentRequestStatusCommand, a as getPaymentRequestsByBatchIdQuery, g as getBatchByIdQuery, u as upsertBatchCommand, i as importAesKey, f as createPaymentCommand, b as getAccountByIdQuery } from '../shared/bank.BdTj54NO.mjs';
13
13
  import 'drizzle-orm/zod';
14
14
  import 'drizzle-orm/sqlite-core';
15
15
 
@@ -86,6 +86,43 @@ const sendPaymentInputSchema = z.object({
86
86
  creditor: bankAccountMetadataSchema,
87
87
  debtor: bankAccountMetadataSchema,
88
88
  sendAsSinglePayment: z.boolean().optional()
89
+ }).superRefine((data, ctx) => {
90
+ if (data.paymentType === "UNKNOWN") {
91
+ ctx.addIssue({
92
+ code: "custom",
93
+ message: "paymentType=UNKNOWN is not supported. Use DOMESTIC, SEPA, or SWIFT.",
94
+ path: ["paymentType"]
95
+ });
96
+ return;
97
+ }
98
+ if (!data.debtor.iban) {
99
+ ctx.addIssue({
100
+ code: "custom",
101
+ message: "debtor.iban is required",
102
+ path: ["debtor", "iban"]
103
+ });
104
+ }
105
+ if (data.paymentType === "DOMESTIC" || data.paymentType === "SEPA") {
106
+ if (!data.creditor.iban) {
107
+ ctx.addIssue({
108
+ code: "custom",
109
+ message: `creditor.iban is required for ${data.paymentType} payments`,
110
+ path: ["creditor", "iban"]
111
+ });
112
+ }
113
+ return;
114
+ }
115
+ if (data.paymentType === "SWIFT") {
116
+ const hasIban = !!data.creditor.iban;
117
+ const hasBban = !!data.creditor.number && !!(data.creditor.swiftBic ?? data.creditor.bicCor);
118
+ if (!hasIban && !hasBban) {
119
+ ctx.addIssue({
120
+ code: "custom",
121
+ message: "SWIFT creditor requires either iban OR (number + swiftBic/bicCor)",
122
+ path: ["creditor"]
123
+ });
124
+ }
125
+ }
89
126
  });
90
127
 
91
128
  const sendBatchInputSchema = z.object({
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  const backendSdk = require('@develit-io/backend-sdk');
4
- const ott_zod = require('../shared/bank.DiJmJkDt.cjs');
4
+ const ott_zod = require('../shared/bank.BSX82jhx.cjs');
5
5
  const batchLifecycle = require('../shared/bank.NF8bZBy0.cjs');
6
6
  const drizzleOrm = require('drizzle-orm');
7
- const credentialsResolver = require('../shared/bank.CioJeFzf.cjs');
7
+ const credentialsResolver = require('../shared/bank.CibQRM2D.cjs');
8
8
  const cloudflare_workers = require('cloudflare:workers');
9
9
  const cloudflare_workflows = require('cloudflare:workflows');
10
10
  const d1 = require('drizzle-orm/d1');
@@ -1,8 +1,8 @@
1
1
  import { first, uuidv4, asNonEmpty } from '@develit-io/backend-sdk';
2
- import { G as tables, H as relations, v as toBatchedPaymentFromPaymentRequest, z as toPreparedPayment, J as initiateConnector, m as isPaymentCompleted } from '../shared/bank.BOMobxtA.mjs';
2
+ import { G as tables, H as relations, v as toBatchedPaymentFromPaymentRequest, z as toPreparedPayment, J as initiateConnector, m as isPaymentCompleted } from '../shared/bank.CZ8MQDPa.mjs';
3
3
  import { i as isBatchAuthorized, b as isBatchFailed, d as isBatchProcessing } from '../shared/bank.XqSw509X.mjs';
4
4
  import { eq, and, inArray } from 'drizzle-orm';
5
- import { 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.BELDXSDV.mjs';
5
+ import { 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.BdTj54NO.mjs';
6
6
  import { WorkflowEntrypoint } from 'cloudflare:workers';
7
7
  import { NonRetryableError } from 'cloudflare:workflows';
8
8
  import { drizzle } from 'drizzle-orm/d1';
@@ -322,12 +322,40 @@ function toBatchedPaymentFromPaymentRequest(sp) {
322
322
  };
323
323
  }
324
324
 
325
+ const MAX_REFERENCE_LENGTH = 30;
326
+ function buildPaymentReference(symbols) {
327
+ const parts = [
328
+ symbols.vs && `VS${symbols.vs}`,
329
+ symbols.ss && `SS${symbols.ss}`,
330
+ symbols.ks && `KS${symbols.ks}`
331
+ ].filter(Boolean);
332
+ return parts.join("").slice(0, MAX_REFERENCE_LENGTH);
333
+ }
334
+
335
+ const SYMBOL_LIMITS = { vs: 10, ss: 10, ks: 4 };
336
+ function assertSymbolLength(symbol, value) {
337
+ const max = SYMBOL_LIMITS[symbol];
338
+ if (value.length > max) {
339
+ throw new Error(
340
+ `buildEndToEndId: ${symbol.toUpperCase()} exceeds SEPA limit (${value.length} > ${max} chars): "${value}"`
341
+ );
342
+ }
343
+ }
325
344
  function buildEndToEndId(payment, options = {}) {
326
345
  const { separator = "/" } = options;
327
346
  const parts = [];
328
- if (payment.vs) parts.push(`VS${payment.vs}`);
329
- if (payment.ss) parts.push(`SS${payment.ss}`);
330
- if (payment.ks) parts.push(`KS${payment.ks}`);
347
+ if (payment.vs) {
348
+ assertSymbolLength("vs", payment.vs);
349
+ parts.push(`VS${payment.vs}`);
350
+ }
351
+ if (payment.ss) {
352
+ assertSymbolLength("ss", payment.ss);
353
+ parts.push(`SS${payment.ss}`);
354
+ }
355
+ if (payment.ks) {
356
+ assertSymbolLength("ks", payment.ks);
357
+ parts.push(`KS${payment.ks}`);
358
+ }
331
359
  if (parts.length === 0) {
332
360
  return payment.id.replace(/-/g, "");
333
361
  }
@@ -488,6 +516,10 @@ const mapFinbricksAccountInsert = ({
488
516
  iban: account.identification.iban
489
517
  });
490
518
 
519
+ const VS_REGEX = /VS[:/\s]*(\d+)/i;
520
+ const SS_REGEX = /SS[:/\s]*(\d+)/i;
521
+ const KS_REGEX = /KS[:/\s]*(\d+)/i;
522
+ const extract = (haystack, regex) => haystack.match(regex)?.[1];
491
523
  const mapReferencesToPayment = (reference) => {
492
524
  const symbols = {
493
525
  vs: void 0,
@@ -495,19 +527,10 @@ const mapReferencesToPayment = (reference) => {
495
527
  ks: void 0
496
528
  };
497
529
  if (!reference) return symbols;
498
- if (Array.isArray(reference) && reference.length > 0) {
499
- symbols.vs = reference.find((ref) => ref.includes("VS")) || void 0;
500
- symbols.ss = reference.find((ref) => ref.includes("SS")) || void 0;
501
- symbols.ks = reference.find((ref) => ref.includes("KS")) || void 0;
502
- }
503
- if (typeof reference === "string") {
504
- const vsMatch = reference.match(/VS[:\s]*(\d+)/i);
505
- const ssMatch = reference.match(/SS[:\s]*(\d+)/i);
506
- const ksMatch = reference.match(/KS[:\s]*(\d+)/i);
507
- if (vsMatch) symbols.vs = vsMatch[1];
508
- if (ssMatch) symbols.ss = ssMatch[1];
509
- if (ksMatch) symbols.ks = ksMatch[1];
510
- }
530
+ const haystack = Array.isArray(reference) ? reference.join(" ") : reference;
531
+ symbols.vs = extract(haystack, VS_REGEX);
532
+ symbols.ss = extract(haystack, SS_REGEX);
533
+ symbols.ks = extract(haystack, KS_REGEX);
511
534
  return symbols;
512
535
  };
513
536
 
@@ -590,8 +613,23 @@ function detectPaymentType(tx, isIncoming) {
590
613
  const mapFinbricksTransactionToPayment = (tx, account) => {
591
614
  const isIncoming = tx.creditDebitIndicator === "CRDT";
592
615
  const related = tx.entryDetails?.transactionDetails?.relatedParties;
593
- const endToEndId = tx.entryDetails.transactionDetails?.references?.endToEndIdentification;
594
- const symbolsRegex = /^\/?VS\d+/;
616
+ const td = tx.entryDetails?.transactionDetails;
617
+ const referenceSources = [
618
+ td?.remittanceInformation?.structured?.creditorReferenceInformation?.reference,
619
+ td?.references?.endToEndIdentification,
620
+ td?.remittanceInformation?.unstructured,
621
+ td?.additionalRemittanceInformation,
622
+ td?.additionalTransactionInformation
623
+ ];
624
+ const symbols = referenceSources.reduce((acc, src) => {
625
+ if (acc.vs && acc.ss && acc.ks) return acc;
626
+ const parsed = mapReferencesToPayment(src);
627
+ return {
628
+ vs: acc.vs ?? parsed.vs,
629
+ ss: acc.ss ?? parsed.ss,
630
+ ks: acc.ks ?? parsed.ks
631
+ };
632
+ }, {});
595
633
  const debtorParsed = parseOtherIdentification(
596
634
  related?.debtorAccount?.identification?.other?.identification
597
635
  );
@@ -608,11 +646,9 @@ const mapFinbricksTransactionToPayment = (tx, account) => {
608
646
  currency: tx.amount?.currency || "CZK",
609
647
  paymentType: detectPaymentType(tx, isIncoming),
610
648
  status: mapFinbricksStatus(tx.status, !!tx.bookingDate?.date),
611
- message: tx.entryDetails.transactionDetails?.remittanceInformation?.unstructured || tx.entryDetails.transactionDetails?.additionalRemittanceInformation || tx.entryDetails.transactionDetails?.additionalTransactionInformation || null,
649
+ message: td?.remittanceInformation?.unstructured || td?.additionalRemittanceInformation || td?.additionalTransactionInformation || null,
612
650
  processedAt: new Date(tx.bookingDate.date),
613
- ...mapReferencesToPayment(
614
- tx.entryDetails.transactionDetails?.remittanceInformation?.structured?.creditorReferenceInformation?.reference || (endToEndId && symbolsRegex.test(endToEndId) ? endToEndId : void 0)
615
- ),
651
+ ...symbols,
616
652
  creditor: {
617
653
  holderName: related?.creditorAccount?.name || related?.creditor?.name || "Unknown",
618
654
  iban: isIncoming ? account.iban || void 0 : related?.creditorAccount?.identification?.iban || creditorParsed.iban || void 0,
@@ -653,6 +689,21 @@ function autoVariableSymbol(paymentId) {
653
689
  }
654
690
  return String(Math.abs(hash) % 1e10).padStart(10, "0");
655
691
  }
692
+ function resolveAccountIdentification(account, currency) {
693
+ if (account.iban) {
694
+ return { type: "IBAN", value: account.iban };
695
+ }
696
+ if (account.number) {
697
+ return {
698
+ type: "BBAN",
699
+ value: account.number,
700
+ ...currency && { currency }
701
+ };
702
+ }
703
+ throw backendSdk.createInternalError(null, {
704
+ message: "Account has neither IBAN nor account number \u2014 cannot identify for foreign payment"
705
+ });
706
+ }
656
707
  function mapToFinbricksAddress(addr) {
657
708
  if (!addr) return void 0;
658
709
  return {
@@ -911,6 +962,21 @@ class FinbricksConnector extends IBankConnector {
911
962
  );
912
963
  const clientId = await this.getClientId(debtorAccount.id);
913
964
  const creditorAddress = mapToFinbricksAddress(payment.creditor.address);
965
+ const debtorIdentification = resolveAccountIdentification(payment.debtor);
966
+ const creditorIdentification = resolveAccountIdentification(
967
+ payment.creditor,
968
+ payment.currency
969
+ );
970
+ const creditorBic = payment.creditor.swiftBic ?? payment.creditor.bicCor;
971
+ const creditorAgent = creditorIdentification.type === "BBAN" && creditorBic ? {
972
+ financialInstitutionIdentification: { bic: creditorBic }
973
+ } : void 0;
974
+ const reference = buildPaymentReference({
975
+ vs: payment.vs,
976
+ ss: payment.ss,
977
+ ks: payment.ks
978
+ });
979
+ const unstructured = [reference, payment.message?.trim()].filter(Boolean).join(" ").slice(0, 140) || void 0;
914
980
  const bankRefId = backendSdk.uuidv4();
915
981
  const [response, error] = await backendSdk.useResult(
916
982
  this.finbricks.request({
@@ -930,21 +996,17 @@ class FinbricksConnector extends IBankConnector {
930
996
  },
931
997
  chargeBearer: payment.chargeBearer || "SHA",
932
998
  debtor: {
933
- accountIdentification: {
934
- type: "IBAN",
935
- value: payment.debtor.iban
936
- },
999
+ accountIdentification: debtorIdentification,
937
1000
  debtorName: payment.debtor.holderName,
938
1001
  paymentProvider: this.PROVIDER
939
1002
  },
940
1003
  creditor: {
941
- accountIdentification: {
942
- type: "IBAN",
943
- value: payment.creditor.iban
944
- },
1004
+ accountIdentification: creditorIdentification,
945
1005
  creditorName: payment.creditor.holderName || "",
946
1006
  ...creditorAddress && { postalAddress: creditorAddress }
947
1007
  },
1008
+ ...creditorAgent && { creditorAgent },
1009
+ ...unstructured && { remittanceInformation: { unstructured } },
948
1010
  callbackUrl: `${this.finbricks.REDIRECT_URI}?type=paymentRequest&paymentRequestId=${payment.id}`
949
1011
  }
950
1012
  })
@@ -1046,6 +1108,8 @@ class FinbricksConnector extends IBankConnector {
1046
1108
  creditorName: payment.creditor.holderName,
1047
1109
  description: payment.message,
1048
1110
  variableSymbol: payment.vs ?? (payment.ss ? void 0 : autoVariableSymbol(payment.id)),
1111
+ specificSymbol: payment.ss,
1112
+ constantSymbol: payment.ks,
1049
1113
  callbackUrl: `${this.finbricks.REDIRECT_URI}?type=paymentRequest&paymentRequestId=${payment.id}`,
1050
1114
  paymentProvider: this.PROVIDER
1051
1115
  }
@@ -1,5 +1,5 @@
1
1
  import { sql, and, eq, isNull } from 'drizzle-orm';
2
- import { G as tables } from './bank.BOMobxtA.mjs';
2
+ import { G as tables } from './bank.CZ8MQDPa.mjs';
3
3
  import { uuidv4 } from '@develit-io/backend-sdk';
4
4
  import './bank.BzDNLxB_.mjs';
5
5
  import 'date-fns';
@@ -320,12 +320,40 @@ function toBatchedPaymentFromPaymentRequest(sp) {
320
320
  };
321
321
  }
322
322
 
323
+ const MAX_REFERENCE_LENGTH = 30;
324
+ function buildPaymentReference(symbols) {
325
+ const parts = [
326
+ symbols.vs && `VS${symbols.vs}`,
327
+ symbols.ss && `SS${symbols.ss}`,
328
+ symbols.ks && `KS${symbols.ks}`
329
+ ].filter(Boolean);
330
+ return parts.join("").slice(0, MAX_REFERENCE_LENGTH);
331
+ }
332
+
333
+ const SYMBOL_LIMITS = { vs: 10, ss: 10, ks: 4 };
334
+ function assertSymbolLength(symbol, value) {
335
+ const max = SYMBOL_LIMITS[symbol];
336
+ if (value.length > max) {
337
+ throw new Error(
338
+ `buildEndToEndId: ${symbol.toUpperCase()} exceeds SEPA limit (${value.length} > ${max} chars): "${value}"`
339
+ );
340
+ }
341
+ }
323
342
  function buildEndToEndId(payment, options = {}) {
324
343
  const { separator = "/" } = options;
325
344
  const parts = [];
326
- if (payment.vs) parts.push(`VS${payment.vs}`);
327
- if (payment.ss) parts.push(`SS${payment.ss}`);
328
- if (payment.ks) parts.push(`KS${payment.ks}`);
345
+ if (payment.vs) {
346
+ assertSymbolLength("vs", payment.vs);
347
+ parts.push(`VS${payment.vs}`);
348
+ }
349
+ if (payment.ss) {
350
+ assertSymbolLength("ss", payment.ss);
351
+ parts.push(`SS${payment.ss}`);
352
+ }
353
+ if (payment.ks) {
354
+ assertSymbolLength("ks", payment.ks);
355
+ parts.push(`KS${payment.ks}`);
356
+ }
329
357
  if (parts.length === 0) {
330
358
  return payment.id.replace(/-/g, "");
331
359
  }
@@ -486,6 +514,10 @@ const mapFinbricksAccountInsert = ({
486
514
  iban: account.identification.iban
487
515
  });
488
516
 
517
+ const VS_REGEX = /VS[:/\s]*(\d+)/i;
518
+ const SS_REGEX = /SS[:/\s]*(\d+)/i;
519
+ const KS_REGEX = /KS[:/\s]*(\d+)/i;
520
+ const extract = (haystack, regex) => haystack.match(regex)?.[1];
489
521
  const mapReferencesToPayment = (reference) => {
490
522
  const symbols = {
491
523
  vs: void 0,
@@ -493,19 +525,10 @@ const mapReferencesToPayment = (reference) => {
493
525
  ks: void 0
494
526
  };
495
527
  if (!reference) return symbols;
496
- if (Array.isArray(reference) && reference.length > 0) {
497
- symbols.vs = reference.find((ref) => ref.includes("VS")) || void 0;
498
- symbols.ss = reference.find((ref) => ref.includes("SS")) || void 0;
499
- symbols.ks = reference.find((ref) => ref.includes("KS")) || void 0;
500
- }
501
- if (typeof reference === "string") {
502
- const vsMatch = reference.match(/VS[:\s]*(\d+)/i);
503
- const ssMatch = reference.match(/SS[:\s]*(\d+)/i);
504
- const ksMatch = reference.match(/KS[:\s]*(\d+)/i);
505
- if (vsMatch) symbols.vs = vsMatch[1];
506
- if (ssMatch) symbols.ss = ssMatch[1];
507
- if (ksMatch) symbols.ks = ksMatch[1];
508
- }
528
+ const haystack = Array.isArray(reference) ? reference.join(" ") : reference;
529
+ symbols.vs = extract(haystack, VS_REGEX);
530
+ symbols.ss = extract(haystack, SS_REGEX);
531
+ symbols.ks = extract(haystack, KS_REGEX);
509
532
  return symbols;
510
533
  };
511
534
 
@@ -588,8 +611,23 @@ function detectPaymentType(tx, isIncoming) {
588
611
  const mapFinbricksTransactionToPayment = (tx, account) => {
589
612
  const isIncoming = tx.creditDebitIndicator === "CRDT";
590
613
  const related = tx.entryDetails?.transactionDetails?.relatedParties;
591
- const endToEndId = tx.entryDetails.transactionDetails?.references?.endToEndIdentification;
592
- const symbolsRegex = /^\/?VS\d+/;
614
+ const td = tx.entryDetails?.transactionDetails;
615
+ const referenceSources = [
616
+ td?.remittanceInformation?.structured?.creditorReferenceInformation?.reference,
617
+ td?.references?.endToEndIdentification,
618
+ td?.remittanceInformation?.unstructured,
619
+ td?.additionalRemittanceInformation,
620
+ td?.additionalTransactionInformation
621
+ ];
622
+ const symbols = referenceSources.reduce((acc, src) => {
623
+ if (acc.vs && acc.ss && acc.ks) return acc;
624
+ const parsed = mapReferencesToPayment(src);
625
+ return {
626
+ vs: acc.vs ?? parsed.vs,
627
+ ss: acc.ss ?? parsed.ss,
628
+ ks: acc.ks ?? parsed.ks
629
+ };
630
+ }, {});
593
631
  const debtorParsed = parseOtherIdentification(
594
632
  related?.debtorAccount?.identification?.other?.identification
595
633
  );
@@ -606,11 +644,9 @@ const mapFinbricksTransactionToPayment = (tx, account) => {
606
644
  currency: tx.amount?.currency || "CZK",
607
645
  paymentType: detectPaymentType(tx, isIncoming),
608
646
  status: mapFinbricksStatus(tx.status, !!tx.bookingDate?.date),
609
- message: tx.entryDetails.transactionDetails?.remittanceInformation?.unstructured || tx.entryDetails.transactionDetails?.additionalRemittanceInformation || tx.entryDetails.transactionDetails?.additionalTransactionInformation || null,
647
+ message: td?.remittanceInformation?.unstructured || td?.additionalRemittanceInformation || td?.additionalTransactionInformation || null,
610
648
  processedAt: new Date(tx.bookingDate.date),
611
- ...mapReferencesToPayment(
612
- tx.entryDetails.transactionDetails?.remittanceInformation?.structured?.creditorReferenceInformation?.reference || (endToEndId && symbolsRegex.test(endToEndId) ? endToEndId : void 0)
613
- ),
649
+ ...symbols,
614
650
  creditor: {
615
651
  holderName: related?.creditorAccount?.name || related?.creditor?.name || "Unknown",
616
652
  iban: isIncoming ? account.iban || void 0 : related?.creditorAccount?.identification?.iban || creditorParsed.iban || void 0,
@@ -651,6 +687,21 @@ function autoVariableSymbol(paymentId) {
651
687
  }
652
688
  return String(Math.abs(hash) % 1e10).padStart(10, "0");
653
689
  }
690
+ function resolveAccountIdentification(account, currency) {
691
+ if (account.iban) {
692
+ return { type: "IBAN", value: account.iban };
693
+ }
694
+ if (account.number) {
695
+ return {
696
+ type: "BBAN",
697
+ value: account.number,
698
+ ...currency && { currency }
699
+ };
700
+ }
701
+ throw createInternalError(null, {
702
+ message: "Account has neither IBAN nor account number \u2014 cannot identify for foreign payment"
703
+ });
704
+ }
654
705
  function mapToFinbricksAddress(addr) {
655
706
  if (!addr) return void 0;
656
707
  return {
@@ -909,6 +960,21 @@ class FinbricksConnector extends IBankConnector {
909
960
  );
910
961
  const clientId = await this.getClientId(debtorAccount.id);
911
962
  const creditorAddress = mapToFinbricksAddress(payment.creditor.address);
963
+ const debtorIdentification = resolveAccountIdentification(payment.debtor);
964
+ const creditorIdentification = resolveAccountIdentification(
965
+ payment.creditor,
966
+ payment.currency
967
+ );
968
+ const creditorBic = payment.creditor.swiftBic ?? payment.creditor.bicCor;
969
+ const creditorAgent = creditorIdentification.type === "BBAN" && creditorBic ? {
970
+ financialInstitutionIdentification: { bic: creditorBic }
971
+ } : void 0;
972
+ const reference = buildPaymentReference({
973
+ vs: payment.vs,
974
+ ss: payment.ss,
975
+ ks: payment.ks
976
+ });
977
+ const unstructured = [reference, payment.message?.trim()].filter(Boolean).join(" ").slice(0, 140) || void 0;
912
978
  const bankRefId = uuidv4();
913
979
  const [response, error] = await useResult(
914
980
  this.finbricks.request({
@@ -928,21 +994,17 @@ class FinbricksConnector extends IBankConnector {
928
994
  },
929
995
  chargeBearer: payment.chargeBearer || "SHA",
930
996
  debtor: {
931
- accountIdentification: {
932
- type: "IBAN",
933
- value: payment.debtor.iban
934
- },
997
+ accountIdentification: debtorIdentification,
935
998
  debtorName: payment.debtor.holderName,
936
999
  paymentProvider: this.PROVIDER
937
1000
  },
938
1001
  creditor: {
939
- accountIdentification: {
940
- type: "IBAN",
941
- value: payment.creditor.iban
942
- },
1002
+ accountIdentification: creditorIdentification,
943
1003
  creditorName: payment.creditor.holderName || "",
944
1004
  ...creditorAddress && { postalAddress: creditorAddress }
945
1005
  },
1006
+ ...creditorAgent && { creditorAgent },
1007
+ ...unstructured && { remittanceInformation: { unstructured } },
946
1008
  callbackUrl: `${this.finbricks.REDIRECT_URI}?type=paymentRequest&paymentRequestId=${payment.id}`
947
1009
  }
948
1010
  })
@@ -1044,6 +1106,8 @@ class FinbricksConnector extends IBankConnector {
1044
1106
  creditorName: payment.creditor.holderName,
1045
1107
  description: payment.message,
1046
1108
  variableSymbol: payment.vs ?? (payment.ss ? void 0 : autoVariableSymbol(payment.id)),
1109
+ specificSymbol: payment.ss,
1110
+ constantSymbol: payment.ks,
1047
1111
  callbackUrl: `${this.finbricks.REDIRECT_URI}?type=paymentRequest&paymentRequestId=${payment.id}`,
1048
1112
  paymentProvider: this.PROVIDER
1049
1113
  }
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const drizzleOrm = require('drizzle-orm');
4
- const ott_zod = require('./bank.DiJmJkDt.cjs');
4
+ const ott_zod = require('./bank.BSX82jhx.cjs');
5
5
  const backendSdk = require('@develit-io/backend-sdk');
6
6
  require('./bank.9Yw4KHyl.cjs');
7
7
  require('date-fns');
package/dist/types.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const ott_zod = require('./shared/bank.DiJmJkDt.cjs');
3
+ const ott_zod = require('./shared/bank.BSX82jhx.cjs');
4
4
  const database_schema = require('./shared/bank.9Yw4KHyl.cjs');
5
5
  const batchLifecycle = require('./shared/bank.NF8bZBy0.cjs');
6
6
  const generalCodes = require('@develit-io/general-codes');
package/dist/types.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { B as BASE_TERMINAL_STATUSES, C as CsobConnector, D as DbuConnector, E as ErsteConnector, F as FINBRICKS_ENDPOINTS, a as FinbricksClient, b as FinbricksConnector, I as IBankConnector, K as KBConnector, M as MockCobsConnector, c as MockConnector, d as accountCredentialsInsertSchema, e as accountCredentialsSelectSchema, f as accountCredentialsUpdateSchema, g as accountInsertSchema, h as accountSelectSchema, i as accountUpdateSchema, j as assignAccount, k as dbuAccountConfigSchema, l as hasPaymentAccountAssigned, m as isPaymentCompleted, n as isPendingStatus, o as isProcessedStatus, p as isTerminalStatus, q as ottInsertSchema, r as ottSelectSchema, s as ottUpdateSchema, t as signFinbricksJws, u as toBatchedPayment, v as toBatchedPaymentFromPaymentRequest, w as toCompletedPayment, x as toIncomingPayment, y as toPaymentRequestInsert, z as toPreparedPayment, A as useFinbricksFetch } from './shared/bank.BOMobxtA.mjs';
1
+ export { B as BASE_TERMINAL_STATUSES, C as CsobConnector, D as DbuConnector, E as ErsteConnector, F as FINBRICKS_ENDPOINTS, a as FinbricksClient, b as FinbricksConnector, I as IBankConnector, K as KBConnector, M as MockCobsConnector, c as MockConnector, d as accountCredentialsInsertSchema, e as accountCredentialsSelectSchema, f as accountCredentialsUpdateSchema, g as accountInsertSchema, h as accountSelectSchema, i as accountUpdateSchema, j as assignAccount, k as dbuAccountConfigSchema, l as hasPaymentAccountAssigned, m as isPaymentCompleted, n as isPendingStatus, o as isProcessedStatus, p as isTerminalStatus, q as ottInsertSchema, r as ottSelectSchema, s as ottUpdateSchema, t as signFinbricksJws, u as toBatchedPayment, v as toBatchedPaymentFromPaymentRequest, w as toCompletedPayment, x as toIncomingPayment, y as toPaymentRequestInsert, z as toPreparedPayment, A as useFinbricksFetch } from './shared/bank.CZ8MQDPa.mjs';
2
2
  export { A as ACCOUNT_STATUSES, B as BATCH_MODES, a as BATCH_STATUES, a as BATCH_STATUSES, C as CHARGE_BEARERS, b as CONNECTOR_KEYS, c as COUNTRY_CODES, d as CREDENTIALS_TYPES, I as INSTRUCTION_PRIORITIES, P as PAYMENT_DIRECTIONS, e as PAYMENT_REQUEST_STATUSES, f as PAYMENT_STATUSES, g as PAYMENT_TYPES, T as TOKEN_TYPES } from './shared/bank.BzDNLxB_.mjs';
3
3
  export { i as isBatchAuthorized, a as isBatchCompleted, b as isBatchFailed, c as isBatchInitiated, d as isBatchProcessing, e as isBatchReadyToSign } from './shared/bank.XqSw509X.mjs';
4
4
  export { BANK_CODES, CURRENCY_CODES } from '@develit-io/general-codes';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@develit-services/bank",
3
- "version": "4.2.2",
3
+ "version": "4.4.0",
4
4
  "author": "Develit.io s.r.o.",
5
5
  "type": "module",
6
6
  "exports": {