bysquare 3.0.0 → 3.2.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 +41 -39
- package/lib/cli.js +179 -106
- package/lib/decode.js +12 -32
- package/lib/encode.d.ts +5 -7
- package/lib/encode.js +2 -18
- package/lib/index.d.ts +1 -1
- package/lib/types.d.ts +15 -7
- package/lib/types.js +6 -0
- package/lib/validations.d.ts +3 -3
- package/lib/validations.js +43 -3
- package/package.json +2 -2
- package/src/cli.ts +189 -109
- package/src/decode.ts +14 -37
- package/src/encode.ts +7 -26
- package/src/index.ts +1 -1
- package/src/types.ts +17 -7
- package/src/validations.ts +60 -5
- package/LICENSE +0 -202
package/README.md
CHANGED
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
<h1 align="center">bysquare</h1>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
"PAY by square" is a national standard for QR code payments that was adopted
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
"PAY by square" is a national standard for QR code payments that was adopted by
|
|
5
|
+
the Slovak Banking Association in 2013. It is incorporated into a variety of
|
|
6
|
+
invoices, reminders and other payment regulations.
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="#features">Features</a> •
|
|
11
|
+
<a href="#installation">Installation</a> •
|
|
12
|
+
<a href="#usage">Usage</a> •
|
|
13
|
+
<a href="#cli">CLI</a> •
|
|
14
|
+
<a href="#validation">Validation</a>
|
|
15
|
+
</p>
|
|
14
16
|
|
|
15
17
|
## Features
|
|
16
18
|
|
|
17
19
|
- TypeScript support
|
|
20
|
+
- CLI tooling
|
|
18
21
|
- Compatible with Slovak banking apps
|
|
19
|
-
- Isomorphic Browser & Runtime-independent
|
|
22
|
+
- Isomorphic Browser & Runtime-independent (Browser, Node.js, Bun, Deno)
|
|
23
|
+
|
|
24
|
+
> [!NOTE]
|
|
25
|
+
> Since v3, the implementation is considered stable and specification-complete.\
|
|
26
|
+
> No breaking changes are planned. Only minor improvements and bug fixes may be
|
|
27
|
+
> introduced.
|
|
20
28
|
|
|
21
29
|
## Installation
|
|
22
30
|
|
|
@@ -34,13 +42,11 @@ $ npm install bysquare
|
|
|
34
42
|
</script>
|
|
35
43
|
```
|
|
36
44
|
|
|
37
|
-
##
|
|
45
|
+
## Usage
|
|
38
46
|
|
|
39
47
|
This library provides `encode` and `decode` functions to work with the data
|
|
40
48
|
model directly, allowing you to create customized payment solutions.
|
|
41
49
|
|
|
42
|
-
### Guides
|
|
43
|
-
|
|
44
50
|
### HTML example
|
|
45
51
|
|
|
46
52
|
This example shows how to generate a payment QR code and display it in a web
|
|
@@ -67,9 +73,8 @@ page:
|
|
|
67
73
|
amount: 123.45,
|
|
68
74
|
variableSymbol: "987654",
|
|
69
75
|
currencyCode: CurrencyCode.EUR,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
],
|
|
76
|
+
beneficiary: { name: "John Doe" },
|
|
77
|
+
bankAccounts: [{ iban: "SK9611000000002918599669" }],
|
|
73
78
|
},
|
|
74
79
|
],
|
|
75
80
|
});
|
|
@@ -102,9 +107,8 @@ const qrstring = encode({
|
|
|
102
107
|
amount: 50.75,
|
|
103
108
|
variableSymbol: "123456",
|
|
104
109
|
currencyCode: CurrencyCode.EUR, // "EUR"
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
],
|
|
110
|
+
beneficiary: { name: "John Doe" },
|
|
111
|
+
bankAccounts: [{ iban: "SK9611000000002918599669" }],
|
|
108
112
|
},
|
|
109
113
|
],
|
|
110
114
|
});
|
|
@@ -120,9 +124,8 @@ const qrstring = encode({
|
|
|
120
124
|
day: 15,
|
|
121
125
|
month: Month.January, // Single month
|
|
122
126
|
periodicity: Periodicity.Monthly, // "m"
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
],
|
|
127
|
+
beneficiary: { name: "John Doe" },
|
|
128
|
+
bankAccounts: [{ iban: "SK9611000000002918599669" }],
|
|
126
129
|
},
|
|
127
130
|
],
|
|
128
131
|
});
|
|
@@ -135,9 +138,8 @@ const qrstring = encode({
|
|
|
135
138
|
amount: 25.0,
|
|
136
139
|
variableSymbol: "789012",
|
|
137
140
|
currencyCode: CurrencyCode.EUR, // "EUR"
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
],
|
|
141
|
+
beneficiary: { name: "John Doe" },
|
|
142
|
+
bankAccounts: [{ iban: "SK9611000000002918599669" }],
|
|
141
143
|
},
|
|
142
144
|
],
|
|
143
145
|
});
|
|
@@ -170,9 +172,8 @@ const qrstring = encode({
|
|
|
170
172
|
month: Month.January | Month.July | Month.October, // Results in 577
|
|
171
173
|
periodicity: Periodicity.Monthly,
|
|
172
174
|
lastDate: "20251231",
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
],
|
|
175
|
+
beneficiary: { name: "John Doe" },
|
|
176
|
+
bankAccounts: [{ iban: "SK9611000000002918599669" }],
|
|
176
177
|
},
|
|
177
178
|
],
|
|
178
179
|
});
|
|
@@ -192,9 +193,8 @@ const qrstring2 = encode({
|
|
|
192
193
|
month: encodedMonths, // Same result: 577
|
|
193
194
|
periodicity: Periodicity.Monthly,
|
|
194
195
|
lastDate: "20251231",
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
],
|
|
196
|
+
beneficiary: { name: "John Doe" },
|
|
197
|
+
bankAccounts: [{ iban: "SK9611000000002918599669" }],
|
|
198
198
|
},
|
|
199
199
|
],
|
|
200
200
|
});
|
|
@@ -235,6 +235,7 @@ const data = {
|
|
|
235
235
|
amount: 100.0,
|
|
236
236
|
variableSymbol: "123",
|
|
237
237
|
paymentNote: "hello world",
|
|
238
|
+
beneficiary: { name: "John Doe" },
|
|
238
239
|
bankAccounts: [
|
|
239
240
|
{ iban: "SK9611000000002918599669" },
|
|
240
241
|
// ...more bank accounts
|
|
@@ -315,15 +316,16 @@ all XSD schema restrictions.
|
|
|
315
316
|
|
|
316
317
|
### Validation Behavior
|
|
317
318
|
|
|
318
|
-
| Aspect
|
|
319
|
-
|
|
|
320
|
-
| IBAN
|
|
321
|
-
| BIC
|
|
322
|
-
| Currency
|
|
323
|
-
| Date
|
|
324
|
-
|
|
|
325
|
-
|
|
|
326
|
-
|
|
|
319
|
+
| Aspect | Behavior |
|
|
320
|
+
| ---------------- | --------------------------------------------------------------- |
|
|
321
|
+
| IBAN | Validated (format + checksum via ISO 13616) |
|
|
322
|
+
| BIC | Validated (format via ISO 9362) |
|
|
323
|
+
| Currency | Validated (ISO 4217, case-insensitive, includes XXX) |
|
|
324
|
+
| Date | Validated (ISO 8601 format) |
|
|
325
|
+
| Beneficiary name | Required (per spec v1.2.0) |
|
|
326
|
+
| Symbols | Permissive (accepts letters, spaces - XSD pattern not enforced) |
|
|
327
|
+
| Amounts | Permissive (accepts negative values) |
|
|
328
|
+
| Field lengths | Not enforced |
|
|
327
329
|
|
|
328
330
|
### XSD Field Constraints Reference
|
|
329
331
|
|
package/lib/cli.js
CHANGED
|
@@ -1,119 +1,192 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readFileSync, } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, statSync, } from "node:fs";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { decode } from "./decode.js";
|
|
6
|
-
import { encode } from "./encode.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
6
|
+
import { encode, } from "./encode.js";
|
|
7
|
+
import { Version } from "./types.js";
|
|
8
|
+
const version = "3.1.0";
|
|
9
|
+
const usage = `bysquare - Slovak PAY by square QR payment standard
|
|
10
|
+
|
|
11
|
+
USAGE:
|
|
12
|
+
bysquare encode [OPTIONS] <input.json>
|
|
13
|
+
bysquare decode <qr-string>
|
|
14
|
+
bysquare version
|
|
15
|
+
|
|
16
|
+
COMMANDS:
|
|
17
|
+
encode Encode JSON payment data to BySquare QR string
|
|
18
|
+
decode Decode BySquare QR string to JSON payment data
|
|
19
|
+
version Print version information
|
|
20
|
+
|
|
21
|
+
ENCODE OPTIONS:
|
|
22
|
+
-D, --no-deburr Keep diacritics (deburr enabled by default)
|
|
23
|
+
-V, --no-validate Skip validation (validation enabled by default)
|
|
24
|
+
-s, --spec-version VER Specification version: 1.0.0, 1.1.0, 1.2.0 (default: 1.2.0)
|
|
25
|
+
|
|
26
|
+
EXAMPLES:
|
|
27
|
+
# Encode with defaults (deburr=true, validate=true, version=1.2.0)
|
|
28
|
+
$ bysquare encode payment.json
|
|
29
|
+
|
|
30
|
+
# Encode with specific options
|
|
31
|
+
$ bysquare encode --no-deburr payment.json
|
|
32
|
+
$ bysquare encode --spec-version 1.1.0 payment.json
|
|
33
|
+
$ bysquare encode -s 1.0.0 --no-validate payment.json
|
|
34
|
+
|
|
35
|
+
# Encode from stdin
|
|
36
|
+
$ echo '{"payments":[...]}' | bysquare encode -
|
|
37
|
+
|
|
38
|
+
# Encode multiple files (including JSONL)
|
|
39
|
+
$ bysquare encode file1.json file2.jsonl
|
|
40
|
+
|
|
41
|
+
# Decode QR string
|
|
42
|
+
$ bysquare decode "00D80..."
|
|
43
|
+
|
|
44
|
+
# Decode from file
|
|
45
|
+
$ bysquare decode qr.txt
|
|
46
|
+
|
|
47
|
+
For more information, visit: https://github.com/xseman/bysquare
|
|
48
|
+
`;
|
|
49
|
+
function errorMessage(error) {
|
|
50
|
+
return error instanceof Error
|
|
51
|
+
? error.message
|
|
52
|
+
: String(error);
|
|
53
|
+
}
|
|
54
|
+
async function readStdin() {
|
|
55
|
+
const chunks = [];
|
|
56
|
+
for await (const chunk of process.stdin) {
|
|
57
|
+
chunks.push(chunk);
|
|
58
|
+
}
|
|
59
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
60
|
+
}
|
|
61
|
+
async function readInput(path) {
|
|
62
|
+
if (path === "-") {
|
|
63
|
+
return readStdin();
|
|
64
|
+
}
|
|
65
|
+
if (!existsSync(path)) {
|
|
66
|
+
throw new Error(`file ${path} doesn't exist`);
|
|
67
|
+
}
|
|
68
|
+
return readFileSync(path, "utf8");
|
|
69
|
+
}
|
|
70
|
+
async function cmdEncode(args) {
|
|
71
|
+
const parsed = parseArgs({
|
|
72
|
+
args,
|
|
73
|
+
allowPositionals: true,
|
|
74
|
+
options: {
|
|
75
|
+
"no-deburr": {
|
|
76
|
+
type: "boolean",
|
|
77
|
+
short: "D",
|
|
78
|
+
},
|
|
79
|
+
"no-validate": {
|
|
80
|
+
type: "boolean",
|
|
81
|
+
short: "V",
|
|
82
|
+
},
|
|
83
|
+
"spec-version": {
|
|
84
|
+
type: "string",
|
|
85
|
+
short: "s",
|
|
86
|
+
default: "1.2.0",
|
|
87
|
+
},
|
|
29
88
|
},
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
89
|
+
});
|
|
90
|
+
if (parsed.positionals.length === 0) {
|
|
91
|
+
console.error("Error: missing input file argument");
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
const versionStr = parsed.values["spec-version"];
|
|
95
|
+
if (!(versionStr in Version)) {
|
|
96
|
+
console.error("Error: unsupported spec version:", parsed.values["spec-version"]);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
const encodeOpts = {
|
|
100
|
+
validate: !parsed.values["no-validate"],
|
|
101
|
+
deburr: !parsed.values["no-deburr"],
|
|
102
|
+
version: Version[versionStr],
|
|
103
|
+
};
|
|
104
|
+
for (const inputFile of parsed.positionals) {
|
|
105
|
+
let input;
|
|
106
|
+
try {
|
|
107
|
+
input = await readInput(inputFile);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.error("Error:", errorMessage(error));
|
|
36
111
|
process.exit(1);
|
|
37
112
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (file.endsWith(".json") === false
|
|
44
|
-
&& file.endsWith(".jsonl") === false) {
|
|
45
|
-
console.error(`Unsupported file format for ${file}`);
|
|
46
|
-
process.exit(1);
|
|
113
|
+
if (inputFile.endsWith(".jsonl")) {
|
|
114
|
+
for (const line of input.split("\n")) {
|
|
115
|
+
if (!line.trim())
|
|
116
|
+
continue;
|
|
117
|
+
encodeAndPrint(line, encodeOpts);
|
|
47
118
|
}
|
|
48
|
-
|
|
49
|
-
if (file.endsWith(".jsonl")) {
|
|
50
|
-
const lines = data.split("\n");
|
|
51
|
-
for (const line of lines) {
|
|
52
|
-
if (!line)
|
|
53
|
-
continue;
|
|
54
|
-
const json = JSON.parse(line);
|
|
55
|
-
console.log(encode(json));
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (file.endsWith(".json")) {
|
|
59
|
-
console.log(encode(JSON.parse(data), {
|
|
60
|
-
validate: Boolean(args.values.validate),
|
|
61
|
-
deburr: Boolean(args.values.deburr),
|
|
62
|
-
}));
|
|
63
|
-
}
|
|
64
|
-
process.exit(0);
|
|
119
|
+
continue;
|
|
65
120
|
}
|
|
121
|
+
encodeAndPrint(input.trim(), encodeOpts);
|
|
66
122
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
123
|
+
}
|
|
124
|
+
function encodeAndPrint(jsonStr, opts) {
|
|
125
|
+
try {
|
|
126
|
+
const data = JSON.parse(jsonStr);
|
|
127
|
+
const result = encode(data, opts);
|
|
128
|
+
console.log(result);
|
|
71
129
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"NAME",
|
|
76
|
-
" bysquare - Simple Node.js library to generate and parse PAY bysquare standard",
|
|
77
|
-
"",
|
|
78
|
-
"SYNOPSIS",
|
|
79
|
-
" bysquare [OPTIONS] [FILES...]",
|
|
80
|
-
"",
|
|
81
|
-
"DESCRIPTION",
|
|
82
|
-
" bysquare is a command-line tool that provides a simple Node.js library to generate ",
|
|
83
|
-
" and parse PAY bysquare standard. It offers functionality to encode JSON data into a ",
|
|
84
|
-
" corresponding QR code and decode a QR code string to obtain the associated JSON data.",
|
|
85
|
-
"",
|
|
86
|
-
"OPTIONS",
|
|
87
|
-
" -d, --decode <qrstring>",
|
|
88
|
-
" Decode the specified QR code string and print the corresponding JSON data.",
|
|
89
|
-
" The qrstring argument should be a valid QR code string.",
|
|
90
|
-
"",
|
|
91
|
-
" -e, --encode",
|
|
92
|
-
" Encode JSON data from one or more files and print the corresponding QR code.",
|
|
93
|
-
"",
|
|
94
|
-
" -v, --validate",
|
|
95
|
-
" Validate JSON data from one or more files before encoding.",
|
|
96
|
-
"",
|
|
97
|
-
" -b, --deburr",
|
|
98
|
-
" Deburr JSON data from one or more files before encoding.",
|
|
99
|
-
"",
|
|
100
|
-
" -h, --help",
|
|
101
|
-
" Display the help message and exit.",
|
|
102
|
-
"",
|
|
103
|
-
"USAGE",
|
|
104
|
-
" Encoding JSON data from one or more files",
|
|
105
|
-
"",
|
|
106
|
-
` ${process.argv[1]} --encode file1.json file2.json ...`,
|
|
107
|
-
" The file1.json, file2.json, ... arguments should be the paths to the JSON or JSONL",
|
|
108
|
-
" files you want to encode. The tool will read each file, generate a QR code representing",
|
|
109
|
-
" the JSON data, and print them.",
|
|
110
|
-
"",
|
|
111
|
-
" Decoding a QR code string",
|
|
112
|
-
"",
|
|
113
|
-
` ${process.argv[1]} --decode <qrstring>`,
|
|
114
|
-
" Replace qrstring with the QR code string you want to decode.",
|
|
115
|
-
" The program will parse the QR code string and print the resulting JSON data.",
|
|
116
|
-
"",
|
|
117
|
-
].join("\n"));
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error("Error:", errorMessage(error));
|
|
132
|
+
process.exit(1);
|
|
118
133
|
}
|
|
119
134
|
}
|
|
135
|
+
async function cmdDecode(args) {
|
|
136
|
+
if (args.length === 0) {
|
|
137
|
+
console.error("Error: missing QR string argument");
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
const qrInput = args[0];
|
|
141
|
+
try {
|
|
142
|
+
let qr;
|
|
143
|
+
if (qrInput === "-") {
|
|
144
|
+
qr = await readStdin();
|
|
145
|
+
}
|
|
146
|
+
else if (existsSync(qrInput) && statSync(qrInput).isFile()) {
|
|
147
|
+
qr = readFileSync(qrInput, "utf8");
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
qr = qrInput;
|
|
151
|
+
}
|
|
152
|
+
const model = decode(qr.trim());
|
|
153
|
+
console.log(JSON.stringify(model, null, 2));
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
console.error("Error:", errorMessage(error));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function main() {
|
|
161
|
+
if (process.argv.length < 3) {
|
|
162
|
+
console.error(usage);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
const command = process.argv[2];
|
|
166
|
+
switch (command) {
|
|
167
|
+
case "encode":
|
|
168
|
+
await cmdEncode(process.argv.slice(3));
|
|
169
|
+
break;
|
|
170
|
+
case "decode":
|
|
171
|
+
await cmdDecode(process.argv.slice(3));
|
|
172
|
+
break;
|
|
173
|
+
case "version":
|
|
174
|
+
case "-v":
|
|
175
|
+
case "--version":
|
|
176
|
+
console.log(`bysquare version ${version}`);
|
|
177
|
+
break;
|
|
178
|
+
case "help":
|
|
179
|
+
case "-h":
|
|
180
|
+
case "--help":
|
|
181
|
+
console.log(usage);
|
|
182
|
+
break;
|
|
183
|
+
default:
|
|
184
|
+
console.error("Unknown command:", command);
|
|
185
|
+
console.error(usage);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
main().catch((error) => {
|
|
190
|
+
console.error("Fatal error:", error);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
});
|
package/lib/decode.js
CHANGED
|
@@ -29,25 +29,6 @@ function decodeNumber(value) {
|
|
|
29
29
|
function decodeString(value) {
|
|
30
30
|
return value?.length ? value : undefined;
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Converts date from YYYYMMDD format to ISO 8601 format (YYYY-MM-DD)
|
|
34
|
-
* per Pay by Square specification section 3.7.
|
|
35
|
-
*
|
|
36
|
-
* Note: This conversion is only used for paymentDueDate per specification.
|
|
37
|
-
* lastDate remains in YYYYMMDD format.
|
|
38
|
-
*
|
|
39
|
-
* @param input - Date in YYYYMMDD format
|
|
40
|
-
* @returns Date in ISO 8601 format (YYYY-MM-DD) | undefined
|
|
41
|
-
*/
|
|
42
|
-
function deserializeDate(input) {
|
|
43
|
-
if (!input || input.length !== 8) {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
const year = input.slice(0, 4);
|
|
47
|
-
const month = input.slice(4, 6);
|
|
48
|
-
const day = input.slice(6, 8);
|
|
49
|
-
return year + "-" + month + "-" + day;
|
|
50
|
-
}
|
|
51
32
|
/**
|
|
52
33
|
* Parse a tab-separated intermediate format into DataModel.
|
|
53
34
|
*
|
|
@@ -123,12 +104,13 @@ export function deserialize(qr) {
|
|
|
123
104
|
type: Number(paymentOptions),
|
|
124
105
|
currencyCode: currency ?? CurrencyCode.EUR,
|
|
125
106
|
amount: Number(ammount),
|
|
126
|
-
paymentDueDate:
|
|
107
|
+
paymentDueDate: dueDate || undefined,
|
|
127
108
|
variableSymbol: variableSymbol || undefined,
|
|
128
109
|
constantSymbol: constantSymbol || undefined,
|
|
129
110
|
specificSymbol: specificSymbol || undefined,
|
|
130
111
|
originatorsReferenceInformation: originatorRefInfo || undefined,
|
|
131
112
|
paymentNote: paymentNote || undefined,
|
|
113
|
+
beneficiary: { name: "" },
|
|
132
114
|
bankAccounts: [],
|
|
133
115
|
};
|
|
134
116
|
const numberOfAccounts = Number(data.shift());
|
|
@@ -174,17 +156,15 @@ export function deserialize(qr) {
|
|
|
174
156
|
output.payments.push(payment);
|
|
175
157
|
}
|
|
176
158
|
for (let i = 0; i < paymentslen; i++) {
|
|
177
|
-
const name = data.shift();
|
|
178
|
-
const addressLine1 = data.shift();
|
|
179
|
-
const addressLine2 = data.shift();
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
output.payments[i].beneficiary = beneficiary;
|
|
187
|
-
}
|
|
159
|
+
const name = data.shift() ?? "";
|
|
160
|
+
const addressLine1 = data.shift() ?? "";
|
|
161
|
+
const addressLine2 = data.shift() ?? "";
|
|
162
|
+
const beneficiary = {
|
|
163
|
+
name: name,
|
|
164
|
+
street: addressLine1 || undefined,
|
|
165
|
+
city: addressLine2 || undefined,
|
|
166
|
+
};
|
|
167
|
+
output.payments[i].beneficiary = beneficiary;
|
|
188
168
|
}
|
|
189
169
|
return output;
|
|
190
170
|
}
|
|
@@ -227,7 +207,7 @@ export function decode(qr) {
|
|
|
227
207
|
const bytes = base32hex.decode(qr);
|
|
228
208
|
const headerBytes = bytes.slice(0, 2);
|
|
229
209
|
const headerData = bysquareHeaderDecoder(headerBytes);
|
|
230
|
-
if ((headerData.version > Version["1.
|
|
210
|
+
if ((headerData.version > Version["1.2.0"])) {
|
|
231
211
|
throw new DecodeError(DecodeErrorMessage.UnsupportedVersion, {
|
|
232
212
|
version: headerData.version,
|
|
233
213
|
});
|
package/lib/encode.d.ts
CHANGED
|
@@ -141,7 +141,7 @@ export declare function addChecksum(tabbedPayload: string): Uint8Array;
|
|
|
141
141
|
*/
|
|
142
142
|
export declare function serialize(data: DataModel): string;
|
|
143
143
|
export declare function removeDiacritics(model: DataModel): void;
|
|
144
|
-
type
|
|
144
|
+
export type EncodeOptions = {
|
|
145
145
|
/**
|
|
146
146
|
* Many banking apps do not support diacritics, which results in errors when
|
|
147
147
|
* serializing data from QR codes.
|
|
@@ -158,11 +158,10 @@ type Options = {
|
|
|
158
158
|
/**
|
|
159
159
|
* Version of the BySquare format to use.
|
|
160
160
|
*
|
|
161
|
-
* Note: Version 1.
|
|
162
|
-
*
|
|
163
|
-
* compatibility.
|
|
161
|
+
* Note: Version 1.2.0 requires beneficiary name. Earlier versions (1.0.0, 1.1.0)
|
|
162
|
+
* do not require it but may have limited banking app support for beneficiary fields.
|
|
164
163
|
*
|
|
165
|
-
* @default Version["1.
|
|
164
|
+
* @default Version["1.2.0"]
|
|
166
165
|
*/
|
|
167
166
|
version?: Version;
|
|
168
167
|
};
|
|
@@ -200,5 +199,4 @@ type Options = {
|
|
|
200
199
|
*
|
|
201
200
|
* @see 3.16.
|
|
202
201
|
*/
|
|
203
|
-
export declare function encode(model: DataModel, options?:
|
|
204
|
-
export {};
|
|
202
|
+
export declare function encode(model: DataModel, options?: EncodeOptions): string;
|
package/lib/encode.js
CHANGED
|
@@ -4,22 +4,6 @@ import { crc32 } from "./crc32.js";
|
|
|
4
4
|
import { deburr } from "./deburr.js";
|
|
5
5
|
import { Month, PaymentOptions, Version, } from "./types.js";
|
|
6
6
|
import { validateDataModel } from "./validations.js";
|
|
7
|
-
/**
|
|
8
|
-
* Converts date from ISO 8601 format (YYYY-MM-DD) to YYYYMMDD format
|
|
9
|
-
* per Pay by Square specification section 3.7.
|
|
10
|
-
*
|
|
11
|
-
* Note: This conversion is only used for paymentDueDate per specification.
|
|
12
|
-
* lastDate expects YYYYMMDD format directly.
|
|
13
|
-
*
|
|
14
|
-
* @param input - Date in ISO 8601 format (YYYY-MM-DD)
|
|
15
|
-
* @returns Date in YYYYMMDD format | undefined
|
|
16
|
-
*/
|
|
17
|
-
function serializeDate(input) {
|
|
18
|
-
if (!input) {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
return input.split("-").join("");
|
|
22
|
-
}
|
|
23
7
|
export const EncodeErrorMessage = {
|
|
24
8
|
/**
|
|
25
9
|
* @description - find invalid value in extensions
|
|
@@ -206,7 +190,7 @@ export function serialize(data) {
|
|
|
206
190
|
serialized.push(p.type.toString());
|
|
207
191
|
serialized.push(p.amount?.toString());
|
|
208
192
|
serialized.push(p.currencyCode);
|
|
209
|
-
serialized.push(
|
|
193
|
+
serialized.push(p.paymentDueDate);
|
|
210
194
|
serialized.push(p.variableSymbol);
|
|
211
195
|
serialized.push(p.constantSymbol);
|
|
212
196
|
serialized.push(p.specificSymbol);
|
|
@@ -345,7 +329,7 @@ export function encode(model, options = { deburr: true, validate: true }) {
|
|
|
345
329
|
*/
|
|
346
330
|
const _lzmaHeader = payloadCompressed.subarray(0, 13);
|
|
347
331
|
const lzmaBody = payloadCompressed.subarray(13);
|
|
348
|
-
const version = options.version ?? Version["1.
|
|
332
|
+
const version = options.version ?? Version["1.2.0"];
|
|
349
333
|
const output = new Uint8Array([
|
|
350
334
|
...buildBysquareHeader([0x00, version, 0x00, 0x00]),
|
|
351
335
|
...buildPayloadLength(payloadChecked.byteLength),
|
package/lib/index.d.ts
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { decodeOptions, encodeOptions } from "./classifier.js";
|
|
7
7
|
export { decode } from "./decode.js";
|
|
8
|
-
export { encode } from "./encode.js";
|
|
8
|
+
export { encode, type EncodeOptions } from "./encode.js";
|
|
9
9
|
export { validateDataModel } from "./validations.js";
|
|
10
10
|
export * from "./types.js";
|
package/lib/types.d.ts
CHANGED
|
@@ -16,6 +16,12 @@ export declare const Version: {
|
|
|
16
16
|
* **Released Date:** 2015-06-24
|
|
17
17
|
*/
|
|
18
18
|
readonly "1.1.0": 1;
|
|
19
|
+
/**
|
|
20
|
+
* Beneficiary name is now a required field
|
|
21
|
+
*
|
|
22
|
+
* **Released Date:** 2025-04-01
|
|
23
|
+
*/
|
|
24
|
+
readonly "1.2.0": 2;
|
|
19
25
|
};
|
|
20
26
|
export type Version = typeof Version[keyof typeof Version];
|
|
21
27
|
/**
|
|
@@ -149,11 +155,11 @@ export declare const DirectDebitType: {
|
|
|
149
155
|
export type DirectDebitType = typeof DirectDebitType[keyof typeof DirectDebitType];
|
|
150
156
|
export type Beneficiary = {
|
|
151
157
|
/**
|
|
152
|
-
* Beneficiary name.
|
|
158
|
+
* Beneficiary name (required since v1.2.0).
|
|
153
159
|
*
|
|
154
160
|
* @maxLength 70
|
|
155
161
|
*/
|
|
156
|
-
name
|
|
162
|
+
name: string;
|
|
157
163
|
/**
|
|
158
164
|
* Beneficiary street address.
|
|
159
165
|
*
|
|
@@ -190,14 +196,13 @@ export type SimplePayment = {
|
|
|
190
196
|
*/
|
|
191
197
|
currencyCode: string | keyof typeof CurrencyCode;
|
|
192
198
|
/**
|
|
193
|
-
* Payment due date.
|
|
199
|
+
* Payment due date in YYYYMMDD format per v1.2 specification section 3.7.
|
|
194
200
|
*
|
|
195
201
|
* For standing orders, this indicates the first payment date.
|
|
196
|
-
* The date will be converted to YYYYMMDD format during encoding per specification section 3.7.
|
|
197
202
|
*
|
|
198
203
|
* @format date
|
|
199
|
-
* @example "
|
|
200
|
-
* @pattern \d{
|
|
204
|
+
* @example "20241231"
|
|
205
|
+
* @pattern \d{8}
|
|
201
206
|
*/
|
|
202
207
|
paymentDueDate?: string;
|
|
203
208
|
/**
|
|
@@ -240,7 +245,10 @@ export type SimplePayment = {
|
|
|
240
245
|
* @minItems 1
|
|
241
246
|
*/
|
|
242
247
|
bankAccounts: BankAccount[];
|
|
243
|
-
|
|
248
|
+
/**
|
|
249
|
+
* Beneficiary information (required since v1.2.0).
|
|
250
|
+
*/
|
|
251
|
+
beneficiary: Beneficiary;
|
|
244
252
|
};
|
|
245
253
|
export type PaymentOrder = SimplePayment & {
|
|
246
254
|
type: typeof PaymentOptions.PaymentOrder;
|