bysquare 2.13.2 → 3.0.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.
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
- # bysquare
1
+ <h1 align="center">bysquare</h1>
2
2
 
3
- "PAY by square" is a national standard for QR code payments that was adopted by
4
- the Slovak Banking Association in 2013. It is incorporated into a variety of
5
- invoices, reminders and other payment regulations.
3
+ <p align="center">
4
+ "PAY by square" is a national standard for QR code payments that was adopted
5
+ by the Slovak Banking Association in 2013. It is incorporated into a variety
6
+ of invoices, reminders and other payment regulations.
7
+ </p>
6
8
 
7
9
  ## Why
8
10
 
@@ -14,18 +16,10 @@ individuals and businesses to create QR codes for their invoices.
14
16
 
15
17
  - TypeScript support
16
18
  - Compatible with Slovak banking apps
17
- - Runtime-independent JavaScript implementation
19
+ - Isomorphic Browser & Runtime-independent implementation
18
20
 
19
21
  ## Installation
20
22
 
21
- > [!NOTE]
22
- > This package is native [ESM][mozzila-esm] and no longer provides a
23
- > CommonJS export. If your project uses CommonJS, you will have to convert to ESM
24
- > or use the dynamic [`import()`][mozzila-import] function.
25
-
26
- [mozzila-esm]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
27
- [mozzila-import]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
28
-
29
23
  ### [npm](https://npmjs.com/bysquare)
30
24
 
31
25
  ```sh
@@ -45,20 +39,62 @@ $ npm install bysquare
45
39
  This library provides `encode` and `decode` functions to work with the data
46
40
  model directly, allowing you to create customized payment solutions.
47
41
 
42
+ ### Guides
43
+
44
+ ### HTML example
45
+
46
+ This example shows how to generate a payment QR code and display it in a web
47
+ page:
48
+
49
+ ```html
50
+ <!DOCTYPE html>
51
+ <html lang="en">
52
+ <head>
53
+ <meta charset="UTF-8" />
54
+ <title>Payment QR Code</title>
55
+ </head>
56
+ <body>
57
+ <div id="qrcode" style="width: 200px"></div>
58
+
59
+ <script type="module">
60
+ import { QRCode } from "https://esm.sh/@lostinbrittany/qr-esm@latest";
61
+ import { encode, PaymentOptions, CurrencyCode } from "https://esm.sh/bysquare@latest";
62
+
63
+ const qrstring = encode({
64
+ payments: [
65
+ {
66
+ type: PaymentOptions.PaymentOrder,
67
+ amount: 123.45,
68
+ variableSymbol: "987654",
69
+ currencyCode: CurrencyCode.EUR,
70
+ bankAccounts: [
71
+ { iban: "SK9611000000002918599669" }
72
+ ],
73
+ },
74
+ ],
75
+ });
76
+
77
+ const qrElement = document.getElementById("qrcode");
78
+ qrElement.appendChild(QRCode.generateSVG(qrstring));
79
+ </script>
80
+ </body>
81
+ </html>
82
+ ```
83
+
48
84
  ### Creating Payment QR Codes
49
85
 
50
86
  To generate QR codes for different payment types, use the `encode` function with
51
87
  the appropriate payment configuration:
52
88
 
53
- #### Simple Payment (Payment Order)
54
-
55
89
  ```js
56
90
  import {
57
91
  CurrencyCode,
58
92
  encode,
59
93
  PaymentOptions,
94
+ Periodicity,
60
95
  } from "bysquare";
61
96
 
97
+ // Simple Payment (Payment Order)
62
98
  const qrstring = encode({
63
99
  payments: [
64
100
  {
@@ -72,53 +108,33 @@ const qrstring = encode({
72
108
  },
73
109
  ],
74
110
  });
75
- ```
76
-
77
- #### Direct Debit
78
-
79
- ```js
80
- import {
81
- CurrencyCode,
82
- encode,
83
- PaymentOptions,
84
- } from "bysquare";
85
111
 
112
+ // Standing Order
86
113
  const qrstring = encode({
87
114
  payments: [
88
115
  {
89
- type: PaymentOptions.DirectDebit, // 2
90
- amount: 25.0,
91
- variableSymbol: "789012",
116
+ type: PaymentOptions.StandingOrder, // 3
117
+ amount: 100.0,
118
+ variableSymbol: "654321",
92
119
  currencyCode: CurrencyCode.EUR, // "EUR"
120
+ day: 15,
121
+ month: Month.January, // Single month
122
+ periodicity: Periodicity.Monthly, // "m"
93
123
  bankAccounts: [
94
124
  { iban: "SK9611000000002918599669" },
95
125
  ],
96
126
  },
97
127
  ],
98
128
  });
99
- ```
100
-
101
- #### Standing Order
102
-
103
- ```js
104
- import {
105
- CurrencyCode,
106
- encode,
107
- Month,
108
- PaymentOptions,
109
- Periodicity,
110
- } from "bysquare";
111
129
 
130
+ // Direct Debit
112
131
  const qrstring = encode({
113
132
  payments: [
114
133
  {
115
- type: PaymentOptions.StandingOrder, // 3
116
- amount: 100.0,
117
- variableSymbol: "654321",
134
+ type: PaymentOptions.DirectDebit, // 2
135
+ amount: 25.0,
136
+ variableSymbol: "789012",
118
137
  currencyCode: CurrencyCode.EUR, // "EUR"
119
- day: 15,
120
- month: Month.January, // Single month
121
- periodicity: Periodicity.Monthly, // "m"
122
138
  bankAccounts: [
123
139
  { iban: "SK9611000000002918599669" },
124
140
  ],
@@ -184,45 +200,10 @@ const qrstring2 = encode({
184
200
  });
185
201
  ```
186
202
 
187
- ### HTML example
188
-
189
- This example shows how to generate a payment QR code and display it in a web
190
- page:
191
-
192
- ```html
193
- <!DOCTYPE html>
194
- <html lang="en">
195
- <head>
196
- <meta charset="UTF-8" />
197
- <title>Payment QR Code</title>
198
- </head>
199
- <body>
200
- <div id="qrcode" style="width: 200px"></div>
201
-
202
- <script type="module">
203
- import { QRCode } from "https://esm.sh/@lostinbrittany/qr-esm@latest";
204
- import { encode, PaymentOptions, CurrencyCode } from "https://esm.sh/bysquare@latest";
205
-
206
- const qrstring = encode({
207
- payments: [
208
- {
209
- type: PaymentOptions.PaymentOrder,
210
- amount: 123.45,
211
- variableSymbol: "987654",
212
- currencyCode: CurrencyCode.EUR,
213
- bankAccounts: [
214
- { iban: "SK9611000000002918599669" }
215
- ],
216
- },
217
- ],
218
- });
219
-
220
- const qrElement = document.getElementById("qrcode");
221
- qrElement.appendChild(QRCode.generateSVG(qrstring));
222
- </script>
223
- </body>
224
- </html>
225
- ```
203
+ > [!NOTE]
204
+ > **Date Format:** Provide date inputs (e.g., `paymentDueDate`, `lastDate`)
205
+ > in ISO 8601 format (`YYYY-MM-DD`). They are automatically converted to
206
+ > `YYYYMMDD` during encoding to match the Pay by Square specification.
226
207
 
227
208
  ### Advanced usage
228
209
 
@@ -324,13 +305,53 @@ $ bysquare --decode <qrstring>
324
305
 
325
306
  ### Encoding/Decoding Architecture
326
307
 
327
- <image src="./docs/logic.svg" alt="encode" width="500px">
308
+ <image src="./docs/logic.excalidraw.svg" alt="encode" width="500px">
309
+
310
+ ## Validation
311
+
312
+ This library uses **permissive validation** to ensure maximum compatibility with
313
+ various Slovak banking applications. The validation does not strictly enforce
314
+ all XSD schema restrictions.
315
+
316
+ ### Validation Behavior
317
+
318
+ | Aspect | Behavior |
319
+ | ------------- | --------------------------------------------------------------- |
320
+ | IBAN | Validated (format + checksum via ISO 13616) |
321
+ | BIC | Validated (format via ISO 9362) |
322
+ | Currency | Validated (ISO 4217, case-insensitive, includes XXX) |
323
+ | Date | Validated (ISO 8601 format) |
324
+ | Symbols | Permissive (accepts letters, spaces - XSD pattern not enforced) |
325
+ | Amounts | Permissive (accepts negative values) |
326
+ | Field lengths | Not enforced |
327
+
328
+ ### XSD Field Constraints Reference
329
+
330
+ <https://www.bsqr.co/schema/>
331
+
332
+ For reference, the official XSD schema defines these constraints (not enforced
333
+ by this library):
334
+
335
+ | Field | Max Length | Pattern |
336
+ | --------------------------------- | ---------- | ------------- |
337
+ | `variableSymbol` | 10 | `[0-9]{0,10}` |
338
+ | `constantSymbol` | 4 | `[0-9]{0,4}` |
339
+ | `specificSymbol` | 10 | `[0-9]{0,10}` |
340
+ | `paymentNote` | 140 | - |
341
+ | `originatorsReferenceInformation` | 35 | - |
342
+ | `invoiceId` | 10 | - |
343
+ | `beneficiary.name` | 70 | - |
344
+ | `beneficiary.street` | 70 | - |
345
+ | `beneficiary.city` | 70 | - |
346
+ | `mandateId` | 35 | - |
347
+ | `creditorId` | 35 | - |
348
+ | `contractId` | 35 | - |
349
+ | `amount` | 15 | `>= 0` |
328
350
 
329
351
  ## Related
330
352
 
331
353
  - <https://bysquare.com/>
332
354
  - <https://devel.cz/otazka/qr-kod-pay-by-square>
333
355
  - <https://github.com/matusf/pay-by-square>
334
- - <https://www.bsqr.co/schema/>
335
356
  - <https://www.sbaonline.sk/wp-content/uploads/2020/03/pay-by-square-specifications-1_1_0.pdf>
336
357
  - <https://www.vutbr.cz/studenti/zav-prace/detail/78439>
@@ -1,2 +1,39 @@
1
+ /**
2
+ * Encodes bytes to base32hex by converting 8-bit bytes to 5-bit groups.
3
+ *
4
+ * ```
5
+ * Bit packing process (40 bits = 5 bytes → 8 base32hex chars):
6
+ *
7
+ * Input bytes: [ B0 ][ B1 ][ B2 ][ B3 ][ B4 ]
8
+ * Bit positions: 76543210 76543210 76543210 76543210 76543210
9
+ *
10
+ * Output groups: [C0 ][C1 ][C2 ][C3 ][C4 ][C5 ][C6 ][C7 ]
11
+ * Bit positions: 43210 43210 43210 43210 43210 43210 43210 43210
12
+ *
13
+ * C0 = B0[7:3] (top 5 bits of B0)
14
+ * C1 = B0[2:0] + B1[7:6] (bottom 3 bits of B0 + top 2 bits of B1)
15
+ * C2 = B1[5:1] (middle 5 bits of B1)
16
+ * C3 = B1[0] + B2[7:4] (bottom 1 bit of B1 + top 4 bits of B2)
17
+ * ... and so on
18
+ * ```
19
+ */
1
20
  export declare function encode(input: ArrayLike<number>, addPadding?: boolean): string;
21
+ /**
22
+ * Decodes base32hex string back to bytes by converting 5-bit groups to 8-bit bytes.
23
+ *
24
+ * ```
25
+ * Bit unpacking process (8 base32hex chars → 5 bytes):
26
+ *
27
+ * Input groups: [C0 ][C1 ][C2 ][C3 ][C4 ][C5 ][C6 ][C7 ]
28
+ * Bit positions: 43210 43210 43210 43210 43210 43210 43210 43210
29
+ *
30
+ * Output bytes: [ B0 ][ B1 ][ B2 ][ B3 ][ B4 ]
31
+ * Bit positions: 76543210 76543210 76543210 76543210 76543210
32
+ *
33
+ * B0 = C0[4:0] + C1[4:3] (all of C0 + top 2 bits of C1)
34
+ * B1 = C1[2:0] + C2[4:0] (bottom 3 bits of C1 + all of C2)
35
+ * B2 = C3[4:0] + C4[4:2] (all of C3 + top 3 bits of C4)
36
+ * ... and so on
37
+ * ```
38
+ */
2
39
  export declare function decode(input: string, isLoose?: boolean): Uint8Array;
package/lib/base32hex.js CHANGED
@@ -3,6 +3,25 @@ const base32Hex = {
3
3
  bits: 5,
4
4
  mask: 0b11111, // Mask to extract 5 bits
5
5
  };
6
+ /**
7
+ * Encodes bytes to base32hex by converting 8-bit bytes to 5-bit groups.
8
+ *
9
+ * ```
10
+ * Bit packing process (40 bits = 5 bytes → 8 base32hex chars):
11
+ *
12
+ * Input bytes: [ B0 ][ B1 ][ B2 ][ B3 ][ B4 ]
13
+ * Bit positions: 76543210 76543210 76543210 76543210 76543210
14
+ *
15
+ * Output groups: [C0 ][C1 ][C2 ][C3 ][C4 ][C5 ][C6 ][C7 ]
16
+ * Bit positions: 43210 43210 43210 43210 43210 43210 43210 43210
17
+ *
18
+ * C0 = B0[7:3] (top 5 bits of B0)
19
+ * C1 = B0[2:0] + B1[7:6] (bottom 3 bits of B0 + top 2 bits of B1)
20
+ * C2 = B1[5:1] (middle 5 bits of B1)
21
+ * C3 = B1[0] + B2[7:4] (bottom 1 bit of B1 + top 4 bits of B2)
22
+ * ... and so on
23
+ * ```
24
+ */
6
25
  export function encode(input, addPadding = true) {
7
26
  const output = Array();
8
27
  let buffer = 0;
@@ -27,6 +46,24 @@ export function encode(input, addPadding = true) {
27
46
  }
28
47
  return base32hex;
29
48
  }
49
+ /**
50
+ * Decodes base32hex string back to bytes by converting 5-bit groups to 8-bit bytes.
51
+ *
52
+ * ```
53
+ * Bit unpacking process (8 base32hex chars → 5 bytes):
54
+ *
55
+ * Input groups: [C0 ][C1 ][C2 ][C3 ][C4 ][C5 ][C6 ][C7 ]
56
+ * Bit positions: 43210 43210 43210 43210 43210 43210 43210 43210
57
+ *
58
+ * Output bytes: [ B0 ][ B1 ][ B2 ][ B3 ][ B4 ]
59
+ * Bit positions: 76543210 76543210 76543210 76543210 76543210
60
+ *
61
+ * B0 = C0[4:0] + C1[4:3] (all of C0 + top 2 bits of C1)
62
+ * B1 = C1[2:0] + C2[4:0] (bottom 3 bits of C1 + all of C2)
63
+ * B2 = C3[4:0] + C4[4:2] (all of C3 + top 3 bits of C4)
64
+ * ... and so on
65
+ * ```
66
+ */
30
67
  export function decode(input, isLoose = false) {
31
68
  if (isLoose) {
32
69
  input = input.toUpperCase();
@@ -4,6 +4,32 @@
4
4
  *
5
5
  * Supports Month Classifier (Table 10) and other classifiers that use summed
6
6
  * values.
7
+ *
8
+ * Bit-flag encoding example (Month Classifier):
9
+ * ```
10
+ * Month | Binary | Decimal | Bit Position
11
+ * -----------+----------------+---------+-------------
12
+ * January | 0b000000000001 | 1 | Bit 0
13
+ * February | 0b000000000010 | 2 | Bit 1
14
+ * March | 0b000000000100 | 4 | Bit 2
15
+ * April | 0b000000001000 | 8 | Bit 3
16
+ * May | 0b000000010000 | 16 | Bit 4
17
+ * June | 0b000000100000 | 32 | Bit 5
18
+ * July | 0b000001000000 | 64 | Bit 6
19
+ * August | 0b000010000000 | 128 | Bit 7
20
+ * September | 0b000100000000 | 256 | Bit 8
21
+ * October | 0b001000000000 | 512 | Bit 9
22
+ * November | 0b010000000000 | 1024 | Bit 10
23
+ * December | 0b100000000000 | 2048 | Bit 11
24
+ *
25
+ * Combined example:
26
+ * January + July + October = 1 + 64 + 512 = 577
27
+ * Binary: 0b001001000001
28
+ * ^^^ ^ ^
29
+ * | | └─ Bit 0 (January)
30
+ * | └──────── Bit 6 (July)
31
+ * └───────────── Bit 9 (October)
32
+ * ```
7
33
  */
8
34
  /**
9
35
  * Encodes multiple classifier options into a single number by summing their
package/lib/classifier.js CHANGED
@@ -4,6 +4,32 @@
4
4
  *
5
5
  * Supports Month Classifier (Table 10) and other classifiers that use summed
6
6
  * values.
7
+ *
8
+ * Bit-flag encoding example (Month Classifier):
9
+ * ```
10
+ * Month | Binary | Decimal | Bit Position
11
+ * -----------+----------------+---------+-------------
12
+ * January | 0b000000000001 | 1 | Bit 0
13
+ * February | 0b000000000010 | 2 | Bit 1
14
+ * March | 0b000000000100 | 4 | Bit 2
15
+ * April | 0b000000001000 | 8 | Bit 3
16
+ * May | 0b000000010000 | 16 | Bit 4
17
+ * June | 0b000000100000 | 32 | Bit 5
18
+ * July | 0b000001000000 | 64 | Bit 6
19
+ * August | 0b000010000000 | 128 | Bit 7
20
+ * September | 0b000100000000 | 256 | Bit 8
21
+ * October | 0b001000000000 | 512 | Bit 9
22
+ * November | 0b010000000000 | 1024 | Bit 10
23
+ * December | 0b100000000000 | 2048 | Bit 11
24
+ *
25
+ * Combined example:
26
+ * January + July + October = 1 + 64 + 512 = 577
27
+ * Binary: 0b001001000001
28
+ * ^^^ ^ ^
29
+ * | | └─ Bit 0 (January)
30
+ * | └──────── Bit 6 (July)
31
+ * └───────────── Bit 9 (October)
32
+ * ```
7
33
  */
8
34
  /**
9
35
  * Encodes multiple classifier options into a single number by summing their
package/lib/decode.d.ts CHANGED
@@ -20,13 +20,59 @@ export declare class DecodeError extends Error {
20
20
  });
21
21
  }
22
22
  /**
23
- * Generating by square Code
23
+ * Parse a tab-separated intermediate format into DataModel.
24
24
  *
25
- * @see 3.14.
25
+ * Base fields
26
+ * - Field 0: invoiceId
27
+ * - Field 1: paymentsCount
28
+ *
29
+ * Payment block (repeated `paymentsCount` times)
30
+ * - Field +0: type
31
+ * - Field +1: amount
32
+ * - Field +2: currencyCode
33
+ * - Field +3: paymentDueDate (YYYYMMDD)
34
+ * - Field +4: variableSymbol
35
+ * - Field +5: constantSymbol
36
+ * - Field +6: specificSymbol
37
+ * - Field +7: originatorsReferenceInformation
38
+ * - Field +8: paymentNote
39
+ * - Field +9: bankAccountsCount
40
+ *
41
+ * Bank account block (nested, repeated `bankAccountsCount` times)
42
+ * - Field +0: iban
43
+ * - Field +1: bic
44
+ *
45
+ * Standing order extension
46
+ * - Field +X: standingOrderExt ("0" | "1")
47
+ * - if "1":
48
+ * - Field +1: day
49
+ * - Field +2: month (classifier sum)
50
+ * - Field +3: periodicity
51
+ * - Field +4: lastDate (YYYYMMDD)
52
+ *
53
+ * Direct debit extension
54
+ * - Field +Y: directDebitExt ("0" | "1")
55
+ * - if "1":
56
+ * - Field +1: directDebitScheme
57
+ * - Field +2: directDebitType
58
+ * - Field +3: variableSymbol
59
+ * - Field +4: specificSymbol
60
+ * - Field +5: originatorsReferenceInformation
61
+ * - Field +6: mandateId
62
+ * - Field +7: creditorId
63
+ * - Field +8: contractId
64
+ * - Field +9: maxAmount
65
+ * - Field +10: validTillDate
66
+ *
67
+ * Beneficiary block (repeated per payment)
68
+ * - Field +0: beneficiaryName
69
+ * - Field +1: beneficiaryStreet
70
+ * - Field +2: beneficiaryCity
71
+ *
72
+ * @see 3.14
73
+ * @see Table 15
26
74
  */
27
75
  export declare function deserialize(qr: string): DataModel;
28
- /** @deprecated */
29
- export declare const parse: typeof decode;
30
76
  /**
31
77
  * Decoding client data from QR Code 2005 symbol
32
78
  *
@@ -34,12 +80,3 @@ export declare const parse: typeof decode;
34
80
  * @param qr base32hex encoded bysqaure binary data
35
81
  */
36
82
  export declare function decode(qr: string): DataModel;
37
- /**
38
- * Detect if qr string contains bysquare header.
39
- *
40
- * There is not magic header in the bysquare specification.
41
- * Version is just 4 bites, so it is possible to have false positives.
42
- *
43
- * @deprecated Will be removed as of v3.
44
- */
45
- export declare function detect(qr: string): boolean;