bysquare 1.2.1 → 1.2.2
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 +28 -18
- package/lib/cli.js +2 -2
- package/lib/generate.d.ts +47 -3
- package/lib/generate.js +153 -47
- package/lib/index.d.ts +2 -2
- package/lib/index.js +4 -6
- package/lib/parse.d.ts +27 -3
- package/lib/parse.js +118 -30
- package/lib/types.d.ts +2 -4
- package/package.json +3 -2
- package/lib/utils.d.ts +0 -32
- package/lib/utils.js +0 -106
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ npm install --global bysquare
|
|
|
39
39
|
|
|
40
40
|
### **generate(model: Model): Promise\<string>**
|
|
41
41
|
|
|
42
|
-
```
|
|
42
|
+
```ts
|
|
43
43
|
import { generate, Model, parse } from "bysquare"
|
|
44
44
|
|
|
45
45
|
const model: Model = {
|
|
@@ -58,9 +58,9 @@ generate(model).then((qrString: string) => {
|
|
|
58
58
|
})
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
### **parse(
|
|
61
|
+
### **parse(qr: string): Promise\<Model>**
|
|
62
62
|
|
|
63
|
-
```
|
|
63
|
+
```ts
|
|
64
64
|
import { Model, parse } from "bysquare"
|
|
65
65
|
|
|
66
66
|
const qrString = "0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000"
|
|
@@ -70,6 +70,17 @@ parse(qrString).then((model: Model) => {
|
|
|
70
70
|
})
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
### **detect(qr: string): Boolean**
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { detect } from "bysquare"
|
|
77
|
+
|
|
78
|
+
const qrString = "0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000"
|
|
79
|
+
const isBysquare = detect(qrString)
|
|
80
|
+
|
|
81
|
+
// your logic...
|
|
82
|
+
```
|
|
83
|
+
|
|
73
84
|
## CLI
|
|
74
85
|
|
|
75
86
|
You can use json file with valid model to generate qr-string.
|
|
@@ -86,25 +97,23 @@ You can use json file with valid model to generate qr-string.
|
|
|
86
97
|
# "BankAccounts": 1
|
|
87
98
|
# }
|
|
88
99
|
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
$ npx bysquare ./example.json
|
|
101
|
+
$ 0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000
|
|
91
102
|
```
|
|
92
103
|
|
|
93
104
|
You can also use stdin.
|
|
94
105
|
|
|
95
106
|
```sh
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
| bysquare
|
|
107
|
-
> 0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000
|
|
107
|
+
$ bysquare <<< '{
|
|
108
|
+
"IBAN": "SK9611000000002918599669",
|
|
109
|
+
"Amount": 100.0,
|
|
110
|
+
"CurrencyCode": "EUR",
|
|
111
|
+
"VariableSymbol": "123",
|
|
112
|
+
"Payments": 1,
|
|
113
|
+
"PaymentOptions": 1,
|
|
114
|
+
"BankAccounts": 1
|
|
115
|
+
}'
|
|
116
|
+
$ 0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000
|
|
108
117
|
```
|
|
109
118
|
|
|
110
119
|
## Model
|
|
@@ -173,6 +182,7 @@ Versioning
|
|
|
173
182
|
- Commit and tag
|
|
174
183
|
- Run the `postversion` script
|
|
175
184
|
- Checkout to master
|
|
176
|
-
- Push commits and tag, git push
|
|
185
|
+
- Push commits and tag, git push && git push --tags
|
|
186
|
+
- npm publish --dry-run
|
|
177
187
|
- Publish to npm, npm publish
|
|
178
188
|
-->
|
package/lib/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
const fs_1 = require("fs");
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const readline_1 = require("readline");
|
|
10
|
-
const generate_1 =
|
|
10
|
+
const generate_1 = require("./generate");
|
|
11
11
|
if (process.stdin.isTTY) {
|
|
12
12
|
/** bysquare "file" */
|
|
13
13
|
handleInput(process.argv[2]);
|
|
@@ -47,7 +47,7 @@ async function jsonStringToQrString(stdin) {
|
|
|
47
47
|
return new Promise((resolve, reject) => {
|
|
48
48
|
try {
|
|
49
49
|
const data = JSON.parse(stdin);
|
|
50
|
-
const qrString = (0, generate_1.
|
|
50
|
+
const qrString = (0, generate_1.generate)(data);
|
|
51
51
|
resolve(qrString);
|
|
52
52
|
}
|
|
53
53
|
catch (e) {
|
package/lib/generate.d.ts
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Model } from "./index";
|
|
3
|
+
/**
|
|
4
|
+
* ```
|
|
5
|
+
* | Attribute | Number of bits | Possible values | Note
|
|
6
|
+
* --------------------------------------------------------------------------------------------
|
|
7
|
+
* | BySquareType | 4 | 0-15 | by square type
|
|
8
|
+
* | Version | 4 | 0-15 | version of the by square type
|
|
9
|
+
* | DocumentType | 4 | 0-15 | document type within given by square type
|
|
10
|
+
* | Reserved | 4 | 0-15 | bits reserved for future needs
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* @see {spec 3.5.}
|
|
14
|
+
*/
|
|
15
|
+
export declare function createBysquareHeader(header?: [
|
|
16
|
+
bySquareType: number,
|
|
17
|
+
version: number,
|
|
18
|
+
documentType: number,
|
|
19
|
+
reserved: number
|
|
20
|
+
]): Buffer;
|
|
21
|
+
export declare function createChecksum(tabbedInput: string): Buffer;
|
|
22
|
+
/**
|
|
23
|
+
* Appending CRC32 checksum
|
|
24
|
+
*
|
|
25
|
+
* @see {spec 3.10.}
|
|
26
|
+
*/
|
|
27
|
+
export declare function dataWithChecksum(model: Model): Buffer;
|
|
28
|
+
/**
|
|
29
|
+
* Order keys by specification
|
|
30
|
+
* Fill empty values
|
|
31
|
+
* Transform to tabbed string
|
|
32
|
+
*/
|
|
33
|
+
export declare function createTabbedString(model: Model): string;
|
|
34
|
+
/**
|
|
35
|
+
* The bit sequence is split into 5 bit chunks which are mapped onto the
|
|
36
|
+
* characters
|
|
37
|
+
*
|
|
38
|
+
* @see {spec 3.13. Table 9 – Encoding table}
|
|
39
|
+
*/
|
|
40
|
+
export declare const SUBST = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
|
41
|
+
/**
|
|
42
|
+
* Alphanumeric conversion using Base32hex
|
|
43
|
+
*
|
|
44
|
+
* @see {spec 3.13.}
|
|
45
|
+
*/
|
|
46
|
+
export declare function alphanumericConversion(data: Buffer): string;
|
|
47
|
+
export declare function generate(model: Model): Promise<string>;
|
package/lib/generate.js
CHANGED
|
@@ -1,18 +1,154 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
4
20
|
};
|
|
5
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
|
|
7
|
-
const
|
|
22
|
+
exports.generate = exports.alphanumericConversion = exports.SUBST = exports.createTabbedString = exports.dataWithChecksum = exports.createChecksum = exports.createBysquareHeader = void 0;
|
|
23
|
+
const lzma = __importStar(require("lzma-native"));
|
|
24
|
+
const index_1 = require("./index");
|
|
25
|
+
// echo "Hello" | xz --format=raw --lzma1=lc=3,lp=0,pb=2,dict=32KiB --stdout | hexdump -C
|
|
26
|
+
/**
|
|
27
|
+
* ```
|
|
28
|
+
* | Attribute | Number of bits | Possible values | Note
|
|
29
|
+
* --------------------------------------------------------------------------------------------
|
|
30
|
+
* | BySquareType | 4 | 0-15 | by square type
|
|
31
|
+
* | Version | 4 | 0-15 | version of the by square type
|
|
32
|
+
* | DocumentType | 4 | 0-15 | document type within given by square type
|
|
33
|
+
* | Reserved | 4 | 0-15 | bits reserved for future needs
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @see {spec 3.5.}
|
|
37
|
+
*/
|
|
38
|
+
function createBysquareHeader(
|
|
39
|
+
// prettier-ignore
|
|
40
|
+
header = [
|
|
41
|
+
0b0000_0000, 0b0000_0000,
|
|
42
|
+
0b0000_0000, 0b0000_0000
|
|
43
|
+
]) {
|
|
44
|
+
const isValid = header.every((nibble) => 0 <= nibble && nibble <= 15);
|
|
45
|
+
if (!isValid) {
|
|
46
|
+
throw new Error("Header range of values must be <0,15>");
|
|
47
|
+
}
|
|
48
|
+
const [BySquareType, Version, DocumentType, Reserved] = header;
|
|
49
|
+
/** Combine 4-nibbles to 2-bytes */
|
|
50
|
+
const headerBuffer = Buffer.from([
|
|
51
|
+
(BySquareType << 4) | (Version << 0),
|
|
52
|
+
(DocumentType << 4) | (Reserved << 0)
|
|
53
|
+
]);
|
|
54
|
+
return headerBuffer;
|
|
55
|
+
}
|
|
56
|
+
exports.createBysquareHeader = createBysquareHeader;
|
|
57
|
+
/**
|
|
58
|
+
* LZMA Compression header
|
|
59
|
+
*
|
|
60
|
+
* @see {spec 3.11.}
|
|
61
|
+
*/
|
|
62
|
+
function createCompresionHeader(byteLength) {
|
|
63
|
+
const dataSize = Buffer.alloc(2);
|
|
64
|
+
dataSize.writeInt16LE(byteLength, 0);
|
|
65
|
+
return dataSize;
|
|
66
|
+
}
|
|
67
|
+
function createChecksum(tabbedInput) {
|
|
68
|
+
// @ts-ignore: Wrong return type
|
|
69
|
+
const data = lzma.crc32(tabbedInput);
|
|
70
|
+
const crc32 = Buffer.alloc(4);
|
|
71
|
+
crc32.writeUInt32LE(data);
|
|
72
|
+
return crc32;
|
|
73
|
+
}
|
|
74
|
+
exports.createChecksum = createChecksum;
|
|
75
|
+
/**
|
|
76
|
+
* Appending CRC32 checksum
|
|
77
|
+
*
|
|
78
|
+
* @see {spec 3.10.}
|
|
79
|
+
*/
|
|
80
|
+
function dataWithChecksum(model) {
|
|
81
|
+
const tabbedString = createTabbedString(model);
|
|
82
|
+
const checksum = createChecksum(tabbedString);
|
|
83
|
+
const merged = Buffer.concat([
|
|
84
|
+
checksum,
|
|
85
|
+
Buffer.from(tabbedString, "utf-8")
|
|
86
|
+
]);
|
|
87
|
+
return merged;
|
|
88
|
+
}
|
|
89
|
+
exports.dataWithChecksum = dataWithChecksum;
|
|
90
|
+
/**
|
|
91
|
+
* Order keys by specification
|
|
92
|
+
* Fill empty values
|
|
93
|
+
* Transform to tabbed string
|
|
94
|
+
*/
|
|
95
|
+
function createTabbedString(model) {
|
|
96
|
+
const tabbedModel = Object.keys(model)
|
|
97
|
+
.reduce((acc, key) => {
|
|
98
|
+
acc[index_1.ModelOrdered[key]] = String(model[key] ?? "");
|
|
99
|
+
return acc;
|
|
100
|
+
}, Array(33).fill(""))
|
|
101
|
+
.join("\t");
|
|
102
|
+
return tabbedModel;
|
|
103
|
+
}
|
|
104
|
+
exports.createTabbedString = createTabbedString;
|
|
105
|
+
/**
|
|
106
|
+
* The bit sequence is split into 5 bit chunks which are mapped onto the
|
|
107
|
+
* characters
|
|
108
|
+
*
|
|
109
|
+
* @see {spec 3.13. Table 9 – Encoding table}
|
|
110
|
+
*/
|
|
111
|
+
exports.SUBST = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
|
112
|
+
/**
|
|
113
|
+
* Alphanumeric conversion using Base32hex
|
|
114
|
+
*
|
|
115
|
+
* @see {spec 3.13.}
|
|
116
|
+
*/
|
|
117
|
+
function alphanumericConversion(data) {
|
|
118
|
+
let paddedBinString = data.reduce((acc, byte) => acc + byte.toString(2).padStart(8, "0"), "");
|
|
119
|
+
let paddedLength = paddedBinString.length;
|
|
120
|
+
const remainder = paddedLength % 5;
|
|
121
|
+
if (remainder) {
|
|
122
|
+
paddedBinString += Array(5 - remainder)
|
|
123
|
+
.fill("0")
|
|
124
|
+
.join("");
|
|
125
|
+
paddedLength += 5 - remainder;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Map a binary number of 5 bits to a string representation 2^5
|
|
129
|
+
* SUBST[0...32] represents char
|
|
130
|
+
*
|
|
131
|
+
* @see {@link SUBST}
|
|
132
|
+
*/
|
|
133
|
+
let encoded = "";
|
|
134
|
+
for (let i = 0; i < paddedLength / 5; i++) {
|
|
135
|
+
const binStart = 5 * i;
|
|
136
|
+
const binEnd = 5 * i + 5;
|
|
137
|
+
const sliced = paddedBinString.slice(binStart, binEnd);
|
|
138
|
+
const key = parseInt(sliced, 2);
|
|
139
|
+
encoded += exports.SUBST[key];
|
|
140
|
+
}
|
|
141
|
+
return encoded;
|
|
142
|
+
}
|
|
143
|
+
exports.alphanumericConversion = alphanumericConversion;
|
|
8
144
|
function generate(model) {
|
|
9
|
-
const dataBuffer =
|
|
145
|
+
const dataBuffer = dataWithChecksum(model);
|
|
10
146
|
const dataChunks = [];
|
|
11
147
|
return new Promise((resolve, reject) => {
|
|
12
|
-
const encoder =
|
|
148
|
+
const encoder = lzma.createStream("rawEncoder", {
|
|
13
149
|
synchronous: true,
|
|
14
150
|
// @ts-ignore: Missing filter types
|
|
15
|
-
filters: [{ id:
|
|
151
|
+
filters: [{ id: lzma.FILTER_LZMA1 }]
|
|
16
152
|
});
|
|
17
153
|
encoder
|
|
18
154
|
.on("data", (chunk) => {
|
|
@@ -20,50 +156,20 @@ function generate(model) {
|
|
|
20
156
|
})
|
|
21
157
|
.on("error", reject)
|
|
22
158
|
.on("end", () => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
size.writeInt16LE(dataBuffer.byteLength, 0);
|
|
30
|
-
/** (spec 3.5) */
|
|
31
|
-
const header = (0, utils_1.createHeader)();
|
|
32
|
-
/** Merged binary data (spec 3.15.) */
|
|
33
|
-
const merged = Buffer.concat([
|
|
34
|
-
header,
|
|
35
|
-
size,
|
|
36
|
-
Buffer.concat(dataChunks)
|
|
159
|
+
const headerBysquare = createBysquareHeader();
|
|
160
|
+
const headerCompression = createCompresionHeader(dataBuffer.byteLength);
|
|
161
|
+
const mergeData = Buffer.concat([
|
|
162
|
+
headerBysquare,
|
|
163
|
+
headerCompression,
|
|
164
|
+
...dataChunks
|
|
37
165
|
]);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const remainder = paddedBinLength % 5;
|
|
41
|
-
if (remainder) {
|
|
42
|
-
paddedBinString += Array(5 - remainder)
|
|
43
|
-
.fill("0")
|
|
44
|
-
.join("");
|
|
45
|
-
paddedBinLength += 5 - remainder;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Map a binary number of 5 bits to a string representation 2^5
|
|
49
|
-
* '0123456789ABCDEFGHIJKLMNOPQRSTUV'[0...32] represents char
|
|
50
|
-
*/
|
|
51
|
-
let output = "";
|
|
52
|
-
for (let i = 0; i < paddedBinLength / 5; i++) {
|
|
53
|
-
const binStart = 5 * i;
|
|
54
|
-
const binEnd = 5 * i + 5;
|
|
55
|
-
const slice = paddedBinString.slice(binStart, binEnd);
|
|
56
|
-
const key = parseInt(slice, 2);
|
|
57
|
-
output += utils_1.SUBST[key];
|
|
58
|
-
}
|
|
59
|
-
return resolve(output);
|
|
166
|
+
const output = alphanumericConversion(mergeData);
|
|
167
|
+
resolve(output);
|
|
60
168
|
})
|
|
61
169
|
.write(dataBuffer, (error) => {
|
|
62
|
-
|
|
63
|
-
reject(error);
|
|
64
|
-
}
|
|
170
|
+
error && reject(error);
|
|
65
171
|
encoder.end();
|
|
66
172
|
});
|
|
67
173
|
});
|
|
68
174
|
}
|
|
69
|
-
exports.
|
|
175
|
+
exports.generate = generate;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { parse, detect } from "./parse";
|
|
2
|
+
export { generate } from "./generate";
|
|
3
3
|
export * from "./types";
|
package/lib/index.js
CHANGED
|
@@ -9,13 +9,11 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
9
9
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
10
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
11
|
};
|
|
12
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
-
};
|
|
15
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.generate = exports.parse = void 0;
|
|
13
|
+
exports.generate = exports.detect = exports.parse = void 0;
|
|
17
14
|
var parse_1 = require("./parse");
|
|
18
|
-
Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return
|
|
15
|
+
Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return parse_1.parse; } });
|
|
16
|
+
Object.defineProperty(exports, "detect", { enumerable: true, get: function () { return parse_1.detect; } });
|
|
19
17
|
var generate_1 = require("./generate");
|
|
20
|
-
Object.defineProperty(exports, "generate", { enumerable: true, get: function () { return
|
|
18
|
+
Object.defineProperty(exports, "generate", { enumerable: true, get: function () { return generate_1.generate; } });
|
|
21
19
|
__exportStar(require("./types"), exports);
|
package/lib/parse.d.ts
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Model } from ".";
|
|
3
|
+
/**
|
|
4
|
+
* Generating by square Code
|
|
5
|
+
*
|
|
6
|
+
* @see {spec 3.14.}
|
|
7
|
+
*/
|
|
8
|
+
export declare function createModel(tabbedString: string): Model;
|
|
9
|
+
/**
|
|
10
|
+
* Decoding client data from QR Code 2005 symbol
|
|
11
|
+
*
|
|
12
|
+
* @see {spec 3.16.}
|
|
13
|
+
*/
|
|
14
|
+
export declare function parse(qr: string): Promise<Model>;
|
|
15
|
+
/**
|
|
16
|
+
* Reverse alphanumeric conversion using Base32hex
|
|
17
|
+
*
|
|
18
|
+
* @see {spec 3.13.}
|
|
19
|
+
*/
|
|
20
|
+
export declare function inverseAlphanumericConversion(qr: string): Buffer;
|
|
21
|
+
/**
|
|
22
|
+
* Simple binary header detector
|
|
23
|
+
*
|
|
24
|
+
* The Bysquare header is pretty useless, so the detection isn't as reliable as
|
|
25
|
+
* I'd like
|
|
26
|
+
*/
|
|
27
|
+
export declare function detect(qr: string): boolean;
|
package/lib/parse.js
CHANGED
|
@@ -1,49 +1,137 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
4
20
|
};
|
|
5
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
22
|
+
exports.detect = exports.inverseAlphanumericConversion = exports.parse = exports.createModel = void 0;
|
|
23
|
+
const lzma = __importStar(require("lzma-native"));
|
|
24
|
+
const _1 = require(".");
|
|
25
|
+
const generate_1 = require("./generate");
|
|
26
|
+
/**
|
|
27
|
+
* Generating by square Code
|
|
28
|
+
*
|
|
29
|
+
* @see {spec 3.14.}
|
|
30
|
+
*/
|
|
31
|
+
function createModel(tabbedString) {
|
|
32
|
+
const model = tabbedString
|
|
33
|
+
.split("\t")
|
|
34
|
+
.filter((ch) => !(ch === "\x00"))
|
|
35
|
+
.reduce((acc, value, i) => {
|
|
36
|
+
const key = _1.ModelOrdered[i];
|
|
37
|
+
if (value === "") {
|
|
38
|
+
return acc;
|
|
39
|
+
}
|
|
40
|
+
const numericKeys = [
|
|
41
|
+
"Payments",
|
|
42
|
+
"PaymentOptions",
|
|
43
|
+
"Amount",
|
|
44
|
+
"BankAccounts",
|
|
45
|
+
"StandingOrderExt",
|
|
46
|
+
"Day",
|
|
47
|
+
"Month",
|
|
48
|
+
"DirectDebitExt",
|
|
49
|
+
"DirectDebitScheme",
|
|
50
|
+
"DirectDebitType",
|
|
51
|
+
"MaxAmount"
|
|
52
|
+
];
|
|
53
|
+
if (numericKeys.includes(key)) {
|
|
54
|
+
acc[key] = Number(value);
|
|
55
|
+
return acc;
|
|
56
|
+
}
|
|
57
|
+
acc[key] = value;
|
|
11
58
|
return acc;
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
59
|
+
}, {});
|
|
60
|
+
return model;
|
|
61
|
+
}
|
|
62
|
+
exports.createModel = createModel;
|
|
63
|
+
/**
|
|
64
|
+
* Decoding client data from QR Code 2005 symbol
|
|
65
|
+
*
|
|
66
|
+
* @see {spec 3.16.}
|
|
67
|
+
*/
|
|
68
|
+
function parse(qr) {
|
|
69
|
+
const inversed = inverseAlphanumericConversion(qr);
|
|
70
|
+
const headerBysquare = inversed.slice(0, 2);
|
|
71
|
+
const headerCompression = inversed.slice(2, 4);
|
|
72
|
+
const compressedData = inversed.slice(4);
|
|
24
73
|
// @ts-ignore: Missing decored types
|
|
25
|
-
const decoder =
|
|
74
|
+
const decoder = lzma.createStream("rawDecoder", {
|
|
26
75
|
synchronous: true,
|
|
27
76
|
// @ts-ignore: Missing filter types
|
|
28
|
-
filters: [{ id:
|
|
77
|
+
filters: [{ id: lzma.FILTER_LZMA1 }]
|
|
29
78
|
});
|
|
30
79
|
return new Promise((resolve, reject) => {
|
|
31
80
|
decoder
|
|
32
81
|
.on("error", reject)
|
|
33
82
|
.on("data", (decompress) => {
|
|
34
|
-
const
|
|
83
|
+
const _crc32 = decompress.slice(0, 4);
|
|
35
84
|
const data = decompress.slice(4).toString();
|
|
36
|
-
|
|
37
|
-
// if (!createChecksum(data).equals(checksum)) {
|
|
38
|
-
// reject("Checksum conflict")
|
|
39
|
-
// }
|
|
40
|
-
const model = (0, utils_1.createModel)(data);
|
|
85
|
+
const model = createModel(data);
|
|
41
86
|
resolve(model);
|
|
42
87
|
})
|
|
43
|
-
.write(
|
|
44
|
-
|
|
88
|
+
.write(compressedData, (error) => {
|
|
89
|
+
error && reject(error);
|
|
45
90
|
decoder.end();
|
|
46
91
|
});
|
|
47
92
|
});
|
|
48
93
|
}
|
|
49
|
-
exports.
|
|
94
|
+
exports.parse = parse;
|
|
95
|
+
/**
|
|
96
|
+
* Reverse alphanumeric conversion using Base32hex
|
|
97
|
+
*
|
|
98
|
+
* @see {spec 3.13.}
|
|
99
|
+
*/
|
|
100
|
+
function inverseAlphanumericConversion(qr) {
|
|
101
|
+
const binary = [...qr].reduce((acc, char) => {
|
|
102
|
+
acc += generate_1.SUBST.indexOf(char).toString(2).padStart(5, "0");
|
|
103
|
+
return acc;
|
|
104
|
+
}, "");
|
|
105
|
+
let bytes = [];
|
|
106
|
+
for (let nth = 0, leftCount = 0; binary.length > leftCount; nth++) {
|
|
107
|
+
/** string representation of 8-bits */
|
|
108
|
+
const slice = binary.slice(leftCount, (leftCount += 8));
|
|
109
|
+
const byte = parseInt(slice, 2);
|
|
110
|
+
bytes[nth] = byte;
|
|
111
|
+
}
|
|
112
|
+
return Buffer.from(bytes);
|
|
113
|
+
}
|
|
114
|
+
exports.inverseAlphanumericConversion = inverseAlphanumericConversion;
|
|
115
|
+
/**
|
|
116
|
+
* Simple binary header detector
|
|
117
|
+
*
|
|
118
|
+
* The Bysquare header is pretty useless, so the detection isn't as reliable as
|
|
119
|
+
* I'd like
|
|
120
|
+
*/
|
|
121
|
+
function detect(qr) {
|
|
122
|
+
const inversed = inverseAlphanumericConversion(qr);
|
|
123
|
+
if (inversed.byteLength < 2) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
const headerBysquare = inversed.slice(0, 2);
|
|
127
|
+
return [...headerBysquare.toString("hex")]
|
|
128
|
+
.map((nibble) => parseInt(nibble, 16))
|
|
129
|
+
.every((nibble, index) => {
|
|
130
|
+
// check version, actual v1.x.x
|
|
131
|
+
if (index === 1) {
|
|
132
|
+
return nibble <= 2;
|
|
133
|
+
}
|
|
134
|
+
return 0 <= nibble && nibble <= 15;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
exports.detect = detect;
|
package/lib/types.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Table 15. PAY by square sequence data model (page 30.)
|
|
3
|
-
* @see{./docs/specification_v1.1.0.pdf}
|
|
4
3
|
*/
|
|
5
4
|
export interface Model {
|
|
6
5
|
/**
|
|
@@ -12,8 +11,6 @@ export interface Model {
|
|
|
12
11
|
*/
|
|
13
12
|
Payments: number;
|
|
14
13
|
/**
|
|
15
|
-
* Needs to be filled in with “paymentorder” option
|
|
16
|
-
*
|
|
17
14
|
* Max length 1
|
|
18
15
|
*/
|
|
19
16
|
PaymentOptions: number;
|
|
@@ -72,8 +69,9 @@ export interface Model {
|
|
|
72
69
|
*/
|
|
73
70
|
IBAN: string;
|
|
74
71
|
/**
|
|
75
|
-
* Max length 11
|
|
76
72
|
* Format ISO 9362, 8 or 11 characters long
|
|
73
|
+
*
|
|
74
|
+
* Max length 11
|
|
77
75
|
*/
|
|
78
76
|
BIC?: string;
|
|
79
77
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bysquare",
|
|
3
3
|
"description": "It's a national standard for payment QR codes adopted by Slovak Banking Association (SBA)",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.2",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Filip Seman <filip.seman@protonmail.com>",
|
|
7
7
|
"keywords": [
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"homepage": "https://github.com/xseman/bysquare#readme",
|
|
22
22
|
"funding": "https://github.com/sponsors/xseman",
|
|
23
23
|
"scripts": {
|
|
24
|
+
"prebuild": "rm -rf ./lib || :",
|
|
24
25
|
"build": "tsc && chmod +x ./lib/cli.js",
|
|
25
26
|
"test": "vitest",
|
|
26
27
|
"test:watch": "vitest --watch",
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
"format": "npx prettier --write ./*/**.ts --ignore-path ./.gitignore",
|
|
29
30
|
"version": "git flow release start v$npm_package_version",
|
|
30
31
|
"prepare": "",
|
|
31
|
-
"prepublishOnly": "
|
|
32
|
+
"prepublishOnly": "npm run build",
|
|
32
33
|
"preversion": "git checkout develop",
|
|
33
34
|
"postversion": ""
|
|
34
35
|
},
|
package/lib/utils.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { Model } from "./types";
|
|
3
|
-
/**
|
|
4
|
-
* spec 3.13 (Table 9 – Encoding table)
|
|
5
|
-
*/
|
|
6
|
-
declare const SUBST = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
|
7
|
-
/**
|
|
8
|
-
* ```
|
|
9
|
-
* | Attribute | Number of bits | Possible values | Note
|
|
10
|
-
* --------------------------------------------------------------------------------------------
|
|
11
|
-
* | BySquareType | 4 | 0-15 | by square type
|
|
12
|
-
* | Version | 4 | 0-15 | version 4 015 version of the by sq
|
|
13
|
-
* | DocumentType | 4 | 0-15 | document type within given by square type
|
|
14
|
-
* | Reserved | 4 | 0-15 | bits reserved for future needs
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
declare function createHeader(header?: [
|
|
18
|
-
BySquareType: number,
|
|
19
|
-
Version: number,
|
|
20
|
-
DocumentType: number,
|
|
21
|
-
Reserved: number
|
|
22
|
-
]): Buffer;
|
|
23
|
-
declare function createChecksum(tabbedInput: string): Buffer;
|
|
24
|
-
declare function dataWithChecksum(model: Model): Buffer;
|
|
25
|
-
/**
|
|
26
|
-
* Order keys by specification
|
|
27
|
-
* Fill empty values
|
|
28
|
-
* Transform to tabbed string
|
|
29
|
-
*/
|
|
30
|
-
declare function createTabbedString(model: Model): string;
|
|
31
|
-
declare function createModel(tabbedString: string): Model;
|
|
32
|
-
export { createChecksum, createHeader, createModel, createTabbedString, dataWithChecksum, SUBST };
|
package/lib/utils.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.SUBST = exports.dataWithChecksum = exports.createTabbedString = exports.createModel = exports.createHeader = exports.createChecksum = void 0;
|
|
7
|
-
const lzma_native_1 = __importDefault(require("lzma-native"));
|
|
8
|
-
const types_1 = require("./types");
|
|
9
|
-
/**
|
|
10
|
-
* spec 3.13 (Table 9 – Encoding table)
|
|
11
|
-
*/
|
|
12
|
-
const SUBST = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
|
13
|
-
exports.SUBST = SUBST;
|
|
14
|
-
/**
|
|
15
|
-
* ```
|
|
16
|
-
* | Attribute | Number of bits | Possible values | Note
|
|
17
|
-
* --------------------------------------------------------------------------------------------
|
|
18
|
-
* | BySquareType | 4 | 0-15 | by square type
|
|
19
|
-
* | Version | 4 | 0-15 | version 4 015 version of the by sq
|
|
20
|
-
* | DocumentType | 4 | 0-15 | document type within given by square type
|
|
21
|
-
* | Reserved | 4 | 0-15 | bits reserved for future needs
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
function createHeader(
|
|
25
|
-
// prettier-ignore
|
|
26
|
-
header = [
|
|
27
|
-
0b0000_0000, 0b0000_0000,
|
|
28
|
-
0b0000_0000, 0b0000_0000
|
|
29
|
-
]) {
|
|
30
|
-
const isValid = header.every((nibble) => 0 <= nibble && nibble <= 15);
|
|
31
|
-
if (!isValid)
|
|
32
|
-
throw new Error();
|
|
33
|
-
const [BySquareType, Version, DocumentType, Reserved] = header;
|
|
34
|
-
/** Combine 4-nibbles to 2-bytes */
|
|
35
|
-
const headerBuffer = Buffer.from([
|
|
36
|
-
(BySquareType << 4) | (Version << 0),
|
|
37
|
-
(DocumentType << 4) | (Reserved << 0)
|
|
38
|
-
]);
|
|
39
|
-
return headerBuffer;
|
|
40
|
-
}
|
|
41
|
-
exports.createHeader = createHeader;
|
|
42
|
-
function createChecksum(tabbedInput) {
|
|
43
|
-
// @ts-ignore: Wrong return type
|
|
44
|
-
const data = lzma_native_1.default.crc32(tabbedInput);
|
|
45
|
-
const crc32 = Buffer.alloc(4);
|
|
46
|
-
crc32.writeUInt32LE(data);
|
|
47
|
-
return crc32;
|
|
48
|
-
}
|
|
49
|
-
exports.createChecksum = createChecksum;
|
|
50
|
-
function dataWithChecksum(model) {
|
|
51
|
-
const tabbedString = createTabbedString(model);
|
|
52
|
-
const checksum = createChecksum(tabbedString);
|
|
53
|
-
const merged = Buffer.concat([
|
|
54
|
-
checksum,
|
|
55
|
-
Buffer.from(tabbedString, "utf-8")
|
|
56
|
-
]);
|
|
57
|
-
return merged;
|
|
58
|
-
}
|
|
59
|
-
exports.dataWithChecksum = dataWithChecksum;
|
|
60
|
-
/**
|
|
61
|
-
* Order keys by specification
|
|
62
|
-
* Fill empty values
|
|
63
|
-
* Transform to tabbed string
|
|
64
|
-
*/
|
|
65
|
-
function createTabbedString(model) {
|
|
66
|
-
const tabbedModel = Object.keys(model)
|
|
67
|
-
.reduce((acc, key) => {
|
|
68
|
-
acc[types_1.ModelOrdered[key]] = String(model[key] ?? "");
|
|
69
|
-
return acc;
|
|
70
|
-
}, Array(33).fill(""))
|
|
71
|
-
.join("\t");
|
|
72
|
-
return tabbedModel;
|
|
73
|
-
}
|
|
74
|
-
exports.createTabbedString = createTabbedString;
|
|
75
|
-
function createModel(tabbedString) {
|
|
76
|
-
const model = tabbedString
|
|
77
|
-
.split("\t")
|
|
78
|
-
.filter((ch) => !(ch === "\x00"))
|
|
79
|
-
.reduce((acc, value, i) => {
|
|
80
|
-
const key = types_1.ModelOrdered[i];
|
|
81
|
-
if (value === "") {
|
|
82
|
-
return acc;
|
|
83
|
-
}
|
|
84
|
-
const numericKeys = [
|
|
85
|
-
"Payments",
|
|
86
|
-
"PaymentOptions",
|
|
87
|
-
"Amount",
|
|
88
|
-
"BankAccounts",
|
|
89
|
-
"StandingOrderExt",
|
|
90
|
-
"Day",
|
|
91
|
-
"Month",
|
|
92
|
-
"DirectDebitExt",
|
|
93
|
-
"DirectDebitScheme",
|
|
94
|
-
"DirectDebitType",
|
|
95
|
-
"MaxAmount"
|
|
96
|
-
];
|
|
97
|
-
if (numericKeys.includes(key)) {
|
|
98
|
-
acc[key] = Number(value);
|
|
99
|
-
return acc;
|
|
100
|
-
}
|
|
101
|
-
acc[key] = value;
|
|
102
|
-
return acc;
|
|
103
|
-
}, {});
|
|
104
|
-
return model;
|
|
105
|
-
}
|
|
106
|
-
exports.createModel = createModel;
|