From 3c3785c53dfbc39027483dafb65df140937782dc Mon Sep 17 00:00:00 2001 From: Daniel Derevjanik Date: Fri, 9 Aug 2024 20:22:33 +0200 Subject: [PATCH 1/6] docs: update --- README.md | 6 ++ src/deburr.ts | 16 ++-- src/decode.test.ts | 10 +-- src/decode.ts | 15 ++-- src/encode.test.ts | 4 +- src/encode.ts | 28 ++++--- src/types.ts | 190 ++++++++++++++++++++++++++------------------- 7 files changed, 159 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 4fc9ac5..8d72b2a 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,12 @@ to use without additional setup, showing its improved maturity. The latest version of Chrome, Firefox, and Safari. +## Troubleshooting & Recommendations + +### Encoded data are without diacritics + +The library removes all diacritics from the input data to ensure maximum compatibility, as not all banks support diacritics, which may lead to errors. If you need to retain diacritics, disable deburr option when encoding data - `encode(model, { deburr: false })`. + ## Related - diff --git a/src/deburr.ts b/src/deburr.ts index 5417820..8728167 100644 --- a/src/deburr.ts +++ b/src/deburr.ts @@ -1,5 +1,5 @@ -/** Used to map Latin Unicode letters to basic Latin letters. */ /** dprint-ignore */ +/** Used to map Latin Unicode letters to basic Latin letters. */ const deburredLettersMap: { [key: string]: string; } = { // Latin-1 Supplement block. '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', @@ -61,14 +61,20 @@ export function deburrLetter(key: string) { return deburredLettersMap[key]; } -/** Used to match Latin Unicode letters (excluding mathematical operators). */ +/** + * Used to match Latin Unicode letters (excluding mathematical operators). + */ const reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; -/** Used to compose unicode character classes. */ +/** + * Used to compose unicode character classes. + */ const rsComboMarksRange = "\\u0300-\\u036f\\ufe20-\\ufe23", rsComboSymbolsRange = "\\u20d0-\\u20f0"; -/** Used to compose unicode capture groups. */ +/** + * Used to compose unicode capture groups. + */ const rsCombo = "[" + rsComboMarksRange + rsComboSymbolsRange + "]"; /** @@ -78,7 +84,7 @@ const rsCombo = "[" + rsComboMarksRange + rsComboSymbolsRange + "]"; const reComboMark = RegExp(rsCombo, "g"); /** - * @desc Deburrs string by converting Latin-1 Supplement and Latin Extended-A letters to basic Latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * Deburrs string by converting Latin-1 Supplement and Latin Extended-A letters to basic Latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */ export function deburr(text: string): string { return text.replace(reLatin, deburrLetter).replace(reComboMark, ""); diff --git a/src/decode.test.ts b/src/decode.test.ts index 35e2156..5a29ce9 100644 --- a/src/decode.test.ts +++ b/src/decode.test.ts @@ -10,14 +10,14 @@ import { encode } from "./encode.js"; import { CurrencyCode, DataModel, - PaymentOptions, + PaymentOptionsFlag, } from "./types.js"; export const payload = { invoiceId: "random-id", payments: [ { - type: PaymentOptions.PaymentOrder, + type: PaymentOptionsFlag.PaymentOrder, amount: 100.0, bankAccounts: [ { iban: "SK9611000000002918599669" }, @@ -66,7 +66,7 @@ test("decode - serialization", () => { invoiceId: "random-id", payments: [ { - type: PaymentOptions.PaymentOrder, + type: PaymentOptionsFlag.PaymentOrder, amount: 100, currencyCode: CurrencyCode.EUR, variableSymbol: "123", @@ -105,7 +105,7 @@ test("decode - multiple data", () => { payments: [{ amount: 25.30, currencyCode: CurrencyCode.EUR, - type: PaymentOptions.PaymentOrder, + type: PaymentOptionsFlag.PaymentOrder, bankAccounts: [{ iban: "SK4523585719461382368397" }], beneficiary: { name: "John Doe" }, }], @@ -118,7 +118,7 @@ test("decode - multiple data", () => { payments: [{ amount: 45.55, currencyCode: CurrencyCode.EUR, - type: PaymentOptions.PaymentOrder, + type: PaymentOptionsFlag.PaymentOrder, bankAccounts: [{ iban: "SK2738545237537948273958" }], beneficiary: { name: "Jane Doe" }, paymentNote: "bendzín", diff --git a/src/decode.ts b/src/decode.ts index 35e9dbe..d5805af 100644 --- a/src/decode.ts +++ b/src/decode.ts @@ -8,7 +8,7 @@ import { Day, type DirectDebit, Payment, - PaymentOptions, + PaymentOptionsFlag as PaymentFlag, Periodicity, type StandingOrder, Version, @@ -99,10 +99,10 @@ export function deserialize(qr: string): DataModel { // narrowing payment type switch (payment.type) { - case PaymentOptions.PaymentOrder: + case PaymentFlag.PaymentOrder: break; - case PaymentOptions.StandingOrder: + case PaymentFlag.StandingOrder: payment = { ...payment, day: Number(data.shift()) as Day, @@ -112,7 +112,7 @@ export function deserialize(qr: string): DataModel { } satisfies StandingOrder; break; - case PaymentOptions.DirectDebit: + case PaymentFlag.DirectDebit: payment = { ...payment, directDebitScheme: Number(data.shift()), @@ -171,7 +171,7 @@ interface Header { * the input header array into four nibbles representing the bysquare header * values. * - * @param header 2-bytes sie + * @param header 2-bytes size */ function bysquareHeaderDecoder(header: Uint8Array): Header { const bytes = (header[0] << 8) | header[1]; @@ -212,8 +212,9 @@ export function decode(qr: string): DataModel { } const bysquareHeader = bytes.slice(0, 2); - if ((bysquareHeaderDecoder(bysquareHeader).version > Version["1.1.0"])) { - throw new Error("Unsupported Bysquare version"); + const decodedBysquareHeader = bysquareHeaderDecoder(bysquareHeader); + if ((decodedBysquareHeader.version > Version["1.1.0"])) { + throw new Error(`Unsupported Bysquare version '${decodedBysquareHeader.version}' in header detected. Only '0' and '1' are supported`); } /** diff --git a/src/encode.test.ts b/src/encode.test.ts index c3f1839..584c9b6 100644 --- a/src/encode.test.ts +++ b/src/encode.test.ts @@ -11,14 +11,14 @@ import { import { CurrencyCode, DataModel, - PaymentOptions, + PaymentOptionsFlag, } from "./types.js"; export const payload = { invoiceId: "random-id", payments: [ { - type: PaymentOptions.PaymentOrder, + type: PaymentOptionsFlag.PaymentOrder, amount: 100.0, bankAccounts: [ { iban: "SK9611000000002918599669" }, diff --git a/src/encode.ts b/src/encode.ts index 5e6f053..63fecb3 100644 --- a/src/encode.ts +++ b/src/encode.ts @@ -4,7 +4,8 @@ import { base32hex } from "rfc4648"; import { deburr } from "./deburr.js"; import { DataModel, - PaymentOptions, + PaymentOptionsFlag, + Version, } from "./types.js"; /** @@ -32,9 +33,17 @@ export function headerBysquare( 0x00, 0x00 ], ): Uint8Array { - const isValid = header.every((nibble) => 0 <= nibble && nibble <= 15); - if (!isValid) { - throw new Error("Invalid header byte value, valid range <0,15>"); + if (header[0] < 0 || header[0] > 15) { + throw new Error("Invalid 'BySquareType' value in header, valid range <0,15>"); + } + if (header[1] < 0 || header[1] > 15) { + throw new Error("Invalid 'Version' value in header, valid range <0,15>"); + } + if (header[2] < 0 || header[2] > 15) { + throw new Error("Invalid 'DocumentType' value in header, valid range <0,15>"); + } + if (header[3] < 0 || header[3] > 15) { + throw new Error("Invalid 'Reserved' value in header, valid range <0,15>"); } const [ @@ -114,7 +123,7 @@ export function serialize(data: DataModel): string { serialized.push(ba.bic); } - if (p.type === PaymentOptions.StandingOrder) { + if (p.type === PaymentOptionsFlag.StandingOrder) { serialized.push("1"); serialized.push(p.day?.toString()); serialized.push(p.month?.toString()); @@ -124,7 +133,7 @@ export function serialize(data: DataModel): string { serialized.push("0"); } - if (p.type === PaymentOptions.DirectDebit) { + if (p.type === PaymentOptionsFlag.DirectDebit) { serialized.push("1"); serialized.push(p.directDebitScheme?.toString()); serialized.push(p.directDebitType?.toString()); @@ -202,11 +211,10 @@ export function encode( const lzmaBody = Uint8Array.from(compressed.subarray(13)); const output = Uint8Array.from([ - // FIXME: - // for now other implementation of bysquare doesn't recognize header if - // version is specified like TatraBanka + // NOTE: Newer version 1.1.0 is not supported by all apps (e.g., TatraBanka). + // We recommend using version "1.0.0" for better compatibility. // ...headerBysquare([0x00, Version["1.1.0"], 0x00, 0x00]), - ...headerBysquare([0x00, 0x00, 0x00, 0x00]), + ...headerBysquare([0x00, Version["1.0.0"], 0x00, 0x00]), ...headerDataLength(withChecksum.byteLength), ...lzmaBody, ]); diff --git a/src/types.ts b/src/types.ts index 6516b27..f12ab2a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,13 +6,15 @@ */ export enum Version { /** - * 2013-02-22 - * Created this document from original by square specifications + * Created this document from original by square specifications. + * + * **Released Date:** 2013-02-22 */ "1.0.0" = 0x00, /** - * 2015-06-24 * Added fields for beneficiary name and address + * + * **Released Date:** 2015-06-24 */ "1.1.0" = 0x01, } @@ -20,19 +22,19 @@ export enum Version { /** * Kalendárny mesiac. */ -export enum MonthClassifier { - January = 1, - February = 2, - March = 4, - April = 8, - May = 16, - June = 32, - July = 64, - August = 128, - September = 256, - October = 512, - November = 1_024, - December = 2_048, +export enum MonthFlag { + January = 1 << 0, + February = 1 << 1, + March = 1 << 2, + April = 1 << 3, + May = 1 << 4, + June = 1 << 5, + July = 1 << 6, + August = 1 << 7, + September = 1 << 8, + October = 1 << 9, + November = 1 << 10, + December = 1 << 11, } /** @@ -95,14 +97,23 @@ export type Day = * Možnosti platby sa dajú kombinovať. Oddeľujú sa medzerou a treba uviesť vždy * aspoň jednu z možností: * - * - paymentorder: platobný príkaz - * - standingorder: trvalý príkaz, údaje sa vyplnia do StandingOrderExt - * - directdebit: inkaso, údaje sa vyplnia do DirectDebitExt + * - `PaymentOrder`: platobný príkaz + * - `StandingOrder`: trvalý príkaz, údaje sa vyplnia do StandingOrderExt + * - `DirectDebit`: inkaso, údaje sa vyplnia do DirectDebitExt */ -export enum PaymentOptions { - PaymentOrder = 1, - StandingOrder = 2, - DirectDebit = 4, +export enum PaymentOptionsFlag { + /** + * Platobný príkaz + */ + PaymentOrder = 1 << 0, + /** + * Trvalý príkaz, údaje sa vyplnia do StandingOrderExt + */ + StandingOrder = 1 << 1, + /** + * Inkaso, údaje sa vyplnia do DirectDebitExt + */ + DirectDebit = 1 << 2, } /** @@ -110,21 +121,22 @@ export enum PaymentOptions { */ export type BankAccount = { /** + * Medzinárodné číslo bankového účtu vo formáte IBAN. Príklad: + * * Maximálna dĺžka 34 - * Pattern: [A-Z]{2}[0-9]{2}[A-Z0-9]{0,30} * - * Medzinárodné číslo bankového účtu vo formáte IBAN. Príklad: - * "SK8209000000000011424060". Viac na - * http://www.sbaonline.sk/sk/projekty/financne-vzdelavanie/slovnik-bankovych-pojmov/iii/. + * Pattern: `[A-Z]{2}[0-9]{2}[A-Z0-9]{0,30}` + * + * @example `"SK8209000000000011424060"` */ iban: string; /** - * Formát ISO 9362 (swift) 8 or 11 characters long - * Pattern: [A-Z]{4}[A-Z]{2}[A-Z\d]{2}([A-Z\d]{3})? - * * Medzinárodný bankový identifikačný kód (z ang. Bank Identification Code). - * Viac na http://www.sbaonline.sk/sk/projekty/financne-vzdelavanie/slovnik-bankovych-pojmov/bbb/bic + * + * Formát [ISO 9362](https://en.wikipedia.org/wiki/ISO_9362) (swift) 8 or 11 characters long + * + * Pattern: `[A-Z]{4}[A-Z]{2}[A-Z\d]{2}([A-Z\d]{3})?` */ bic?: string; }; @@ -132,104 +144,120 @@ export type BankAccount = { /** * Inksaná schéma. Uvádza ja jedna z možností: * - * SEPA - Inkaso zodpovedá schéme - * SEPA. other - iné + * - SEPA - Inkaso zodpovedá schéme + * - SEPA. other - iné */ export enum DirectDebitScheme { + /** + * other - iné + */ Other = 0, + /** + * SEPA - Inkaso zodpovedá schéme + */ Sepa = 1, } /** - * Maximálna dĺžka 1 - * * Typ inkasa. Uvádza ja jedna z možností: +* + * Maximálna dĺžka 1 * - * one-off - jednorázové inkaso - * recurrent - opakované inkaso + * - one-off - jednorázové inkaso + * - recurrent - opakované inkaso */ export enum DirectDebitType { + /** + * Jednorázové inkaso + */ OneOff = 0, + /** + * Opakované inkaso + */ Recurrent = 1, } export type Beneficiary = { /** - * Maximálna dĺžka 70 - * * Rozšírenie o meno príjemcu + * + * Maximálna dĺžka 70 */ name?: string; /** - * Maximálna dĺžka 70 - * * Rozšírenie o adresu príjemcu + * + * Maximálna dĺžka 70 */ street?: string; /** - * Maximálna dĺžka 70 - * * Rozšírenie o adresu príjemcu (druhý riadok) + * + * Maximálna dĺžka 70 */ city?: string; }; export type SimplePayment = { /** - * Maximálna dĺžka 15 - * * Čiastka platby. Povolené sú len kladné hodnoty. Desatinná čast je * oddelená bodkou. Môže ostať nevyplnené, napríklad pre dobrovoľný - * príspevok (donations). Príklad: Tisíc sa uvádza ako "1000". Jedna celá - * deväťdesiatdeväť sa uvádza ako "1.99". Desať celých peťdesiat sa uvádza - * ako "10.5". Nula celá nula osem sa uvádza ako "0.08". + * príspevok (donations). + * + * Príklad: Tisíc sa uvádza ako `1000`. Jedna celá + * deväťdesiatdeväť sa uvádza ako `1.99`. Desať celých peťdesiat sa uvádza + * ako `10.5`. Nula celá nula osem sa uvádza ako `0.08`. + * + * Maximálna dĺžka 15 */ amount?: number; /** + * Mena v [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) formáte (3 písmená). + * * Pattern: [A-Z]{3} * - * Mena v ISO 4217 formáte (3 písmená). Príklad: "EUR" + * @example "EUR" */ currencyCode: string | CurrencyCode; /** - * Formát YYYYMMDD + * Dátum splatnosti vo formáte [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) `"RRRR-MM-DD"`. + * Vprípade trvalého príkazu označuje dátum prvej platby. * - * Dátum splatnosti vo formáte ISO 8601 "RRRR-MM-DD". Nepovinný údaj. V - * prípade trvalého príkazu označuje dátum prvej platby. + * Formát `YYYYMMDD` */ paymentDueDate?: string; /** + * Variabilný symbol je maximálne 10 miestne číslo. + * * Maximálna dĺžka 10 * Pattern: [0-9]{0,10} - * - * Variabilný symbol je maximálne 10 miestne číslo. Nepovinný údaj. */ variableSymbol?: string; /** + * Konštantný symbol je 4 miestne identifikačné číslo. + * * Maximálna dĺžka 4 * Pattern: [0-9]{0,4} - * - * Konštantný symbol je 4 miestne identifikačné číslo. Nepovinný údaj. */ constantSymbol?: string; /** + * Špecifický symbol je maximálne 10 miestne číslo. + * * Maximálna dĺžka 10 * Pattern: [0-9]{0,10} - * - * Špecifický symbol je maximálne 10 miestne číslo. Nepovinný údaj. */ specificSymbol?: string; /** - * Maximálna dĺžka 35 - * * Referenčná informácia prijímateľa podľa SEPA. + * + * Maximálna dĺžka 35 */ originatorsReferenceInformation?: string; /** - * Maximálna dĺžka 140 - * * Správa pre prijímateľa. Údaje o platbe, na základe ktorých príjemca bude - * môcť platbu identifikovať. Odporúča sa maximálne 140 Unicode znakov. + * môcť platbu identifikovať. + * + * Maximálna dĺžka 140 */ paymentNote?: string; /** @@ -240,14 +268,14 @@ export type SimplePayment = { }; export type PaymentOrder = SimplePayment & { - type: PaymentOptions.PaymentOrder; + type: PaymentOptionsFlag.PaymentOrder; }; /** * Rozšírenie platobných údajov o údaje pre nastavenie trvalého príkazu. */ export type StandingOrder = SimplePayment & { - type: PaymentOptions.StandingOrder; + type: PaymentOptionsFlag.StandingOrder; /** * Deň platby vyplývajúci z opakovania (Periodicity). Deň v mesiaci je číslo * medzi 1 a 31. Deň v týždni je číslo medzi 1 a 7 (1 = pondelok, 2 =utorok, @@ -257,7 +285,7 @@ export type StandingOrder = SimplePayment & { /** * Medzerou oddelený zoznam mesiacov, v ktoré sa má platba uskutočniť. */ - month?: MonthClassifier; + month?: MonthFlag; /** * Opakovanie (periodicita) trvalého príkazu. */ @@ -265,7 +293,7 @@ export type StandingOrder = SimplePayment & { /** * Dátum poslednej platby v trvalom príkaze. * - * Formát YYYYMMDD + * Formát `YYYYMMDD` */ lastDate?: string; }; @@ -274,38 +302,38 @@ export type StandingOrder = SimplePayment & { * Rozšírenie platobných údajov o údaje pre nastavenie a identifikáciu inkasa. */ export type DirectDebit = SimplePayment & { - type: PaymentOptions.DirectDebit; + type: PaymentOptionsFlag.DirectDebit; directDebitScheme?: DirectDebitScheme; directDebitType?: DirectDebitType; /** - * Maximálna dĺžka 35 - * * Identifikácia mandátu medzi veriteľom a dlžníkom podľa SEPA. + * + * Maximálna dĺžka 35 */ mandateId?: string; /** - * Maximálna dĺžka 35 - * * Identifikácia veriteľa podľa SEPA. + * + * Maximálna dĺžka 35 */ creditorId?: string; /** - * Maximálna dĺžka 35 - * * Identifikácia zmluvy medzi veriteľom a dlžníkom podľa SEPA. + * + * Maximálna dĺžka 35 */ contractId?: string; /** - * Maximálna dĺžka 15 - * * Maximálna čiastka inkasa. + * + * Maximálna dĺžka 15 */ maxAmount?: number; /** + * Dátum platnosti inkasa. Platnosť inkasa zaníka dňom tohto dátumu. + * * Maximálna dĺžka 8 * Formát YYYYMMDD - * - * Dátum platnosti inkasa. Platnosť inkasa zaníka dňom tohto dátumu. */ validTillDate?: string; }; @@ -317,10 +345,10 @@ export type Payment = PaymentOrder | StandingOrder | DirectDebit; export type DataModel = { /** - * Maximálna dĺžka 10 - * * Číslo faktúry v prípade, že údaje sú súčasťou faktúry, alebo * identifikátor pre intérne potreby vystavovateľa. + * + * Maximálna dĺžka 10 */ invoiceId?: string; /** @@ -331,7 +359,7 @@ export type DataModel = { }; /** - * ISO-4217 + * [ISO-4217](https://en.wikipedia.org/wiki/ISO_4217) */ export enum CurrencyCode { AED = "AED", From dafbd9674ccfa12c8d7a339647f26268424e35ee Mon Sep 17 00:00:00 2001 From: Daniel Derevjanik Date: Mon, 12 Aug 2024 19:43:50 +0200 Subject: [PATCH 2/6] refactor: revert payment flag --- src/decode.test.ts | 10 +++++----- src/decode.ts | 12 +++++++----- src/encode.test.ts | 4 ++-- src/encode.ts | 8 +++++--- src/types.ts | 12 ++++++------ 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/decode.test.ts b/src/decode.test.ts index 5a29ce9..35e2156 100644 --- a/src/decode.test.ts +++ b/src/decode.test.ts @@ -10,14 +10,14 @@ import { encode } from "./encode.js"; import { CurrencyCode, DataModel, - PaymentOptionsFlag, + PaymentOptions, } from "./types.js"; export const payload = { invoiceId: "random-id", payments: [ { - type: PaymentOptionsFlag.PaymentOrder, + type: PaymentOptions.PaymentOrder, amount: 100.0, bankAccounts: [ { iban: "SK9611000000002918599669" }, @@ -66,7 +66,7 @@ test("decode - serialization", () => { invoiceId: "random-id", payments: [ { - type: PaymentOptionsFlag.PaymentOrder, + type: PaymentOptions.PaymentOrder, amount: 100, currencyCode: CurrencyCode.EUR, variableSymbol: "123", @@ -105,7 +105,7 @@ test("decode - multiple data", () => { payments: [{ amount: 25.30, currencyCode: CurrencyCode.EUR, - type: PaymentOptionsFlag.PaymentOrder, + type: PaymentOptions.PaymentOrder, bankAccounts: [{ iban: "SK4523585719461382368397" }], beneficiary: { name: "John Doe" }, }], @@ -118,7 +118,7 @@ test("decode - multiple data", () => { payments: [{ amount: 45.55, currencyCode: CurrencyCode.EUR, - type: PaymentOptionsFlag.PaymentOrder, + type: PaymentOptions.PaymentOrder, bankAccounts: [{ iban: "SK2738545237537948273958" }], beneficiary: { name: "Jane Doe" }, paymentNote: "bendzín", diff --git a/src/decode.ts b/src/decode.ts index d5805af..e717709 100644 --- a/src/decode.ts +++ b/src/decode.ts @@ -8,7 +8,7 @@ import { Day, type DirectDebit, Payment, - PaymentOptionsFlag as PaymentFlag, + PaymentOptions, Periodicity, type StandingOrder, Version, @@ -99,10 +99,10 @@ export function deserialize(qr: string): DataModel { // narrowing payment type switch (payment.type) { - case PaymentFlag.PaymentOrder: + case PaymentOptions.PaymentOrder: break; - case PaymentFlag.StandingOrder: + case PaymentOptions.StandingOrder: payment = { ...payment, day: Number(data.shift()) as Day, @@ -112,7 +112,7 @@ export function deserialize(qr: string): DataModel { } satisfies StandingOrder; break; - case PaymentFlag.DirectDebit: + case PaymentOptions.DirectDebit: payment = { ...payment, directDebitScheme: Number(data.shift()), @@ -214,7 +214,9 @@ export function decode(qr: string): DataModel { const bysquareHeader = bytes.slice(0, 2); const decodedBysquareHeader = bysquareHeaderDecoder(bysquareHeader); if ((decodedBysquareHeader.version > Version["1.1.0"])) { - throw new Error(`Unsupported Bysquare version '${decodedBysquareHeader.version}' in header detected. Only '0' and '1' are supported`); + throw new Error( + `Unsupported Bysquare version '${decodedBysquareHeader.version}' in header detected. Only '0' and '1' are supported`, + ); } /** diff --git a/src/encode.test.ts b/src/encode.test.ts index 584c9b6..c3f1839 100644 --- a/src/encode.test.ts +++ b/src/encode.test.ts @@ -11,14 +11,14 @@ import { import { CurrencyCode, DataModel, - PaymentOptionsFlag, + PaymentOptions, } from "./types.js"; export const payload = { invoiceId: "random-id", payments: [ { - type: PaymentOptionsFlag.PaymentOrder, + type: PaymentOptions.PaymentOrder, amount: 100.0, bankAccounts: [ { iban: "SK9611000000002918599669" }, diff --git a/src/encode.ts b/src/encode.ts index 63fecb3..873c22a 100644 --- a/src/encode.ts +++ b/src/encode.ts @@ -4,10 +4,12 @@ import { base32hex } from "rfc4648"; import { deburr } from "./deburr.js"; import { DataModel, - PaymentOptionsFlag, + PaymentOptions, Version, } from "./types.js"; +const MAX_COMPRESSED_SIZE = 131_072; // 2^17 + /** * Returns a 2 byte buffer that represents the header of the bysquare * specification @@ -123,7 +125,7 @@ export function serialize(data: DataModel): string { serialized.push(ba.bic); } - if (p.type === PaymentOptionsFlag.StandingOrder) { + if (p.type === PaymentOptions.StandingOrder) { serialized.push("1"); serialized.push(p.day?.toString()); serialized.push(p.month?.toString()); @@ -133,7 +135,7 @@ export function serialize(data: DataModel): string { serialized.push("0"); } - if (p.type === PaymentOptionsFlag.DirectDebit) { + if (p.type === PaymentOptions.DirectDebit) { serialized.push("1"); serialized.push(p.directDebitScheme?.toString()); serialized.push(p.directDebitType?.toString()); diff --git a/src/types.ts b/src/types.ts index f12ab2a..489d42e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -101,7 +101,7 @@ export type Day = * - `StandingOrder`: trvalý príkaz, údaje sa vyplnia do StandingOrderExt * - `DirectDebit`: inkaso, údaje sa vyplnia do DirectDebitExt */ -export enum PaymentOptionsFlag { +export enum PaymentOptions { /** * Platobný príkaz */ @@ -160,7 +160,7 @@ export enum DirectDebitScheme { /** * Typ inkasa. Uvádza ja jedna z možností: -* + * * Maximálna dĺžka 1 * * - one-off - jednorázové inkaso @@ -223,7 +223,7 @@ export type SimplePayment = { * Dátum splatnosti vo formáte [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) `"RRRR-MM-DD"`. * Vprípade trvalého príkazu označuje dátum prvej platby. * - * Formát `YYYYMMDD` + * Formát `YYYY-MM-DD` */ paymentDueDate?: string; /** @@ -268,14 +268,14 @@ export type SimplePayment = { }; export type PaymentOrder = SimplePayment & { - type: PaymentOptionsFlag.PaymentOrder; + type: PaymentOptions.PaymentOrder; }; /** * Rozšírenie platobných údajov o údaje pre nastavenie trvalého príkazu. */ export type StandingOrder = SimplePayment & { - type: PaymentOptionsFlag.StandingOrder; + type: PaymentOptions.StandingOrder; /** * Deň platby vyplývajúci z opakovania (Periodicity). Deň v mesiaci je číslo * medzi 1 a 31. Deň v týždni je číslo medzi 1 a 7 (1 = pondelok, 2 =utorok, @@ -302,7 +302,7 @@ export type StandingOrder = SimplePayment & { * Rozšírenie platobných údajov o údaje pre nastavenie a identifikáciu inkasa. */ export type DirectDebit = SimplePayment & { - type: PaymentOptionsFlag.DirectDebit; + type: PaymentOptions.DirectDebit; directDebitScheme?: DirectDebitScheme; directDebitType?: DirectDebitType; /** From 75d77c5b605eac0cce53f8242cce2a4ce2529ff2 Mon Sep 17 00:00:00 2001 From: Daniel Derevjanik Date: Mon, 12 Aug 2024 19:44:07 +0200 Subject: [PATCH 3/6] refactor: improve error message for max compressed size --- src/encode.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/encode.ts b/src/encode.ts index 873c22a..1c0c451 100644 --- a/src/encode.ts +++ b/src/encode.ts @@ -69,8 +69,8 @@ export function headerBysquare( * combination with CRC32 in bytes. */ export function headerDataLength(length: number): Uint8Array { - if (length >= 131072 /** 2^17 */) { - throw new Error("The maximum compressed data size has been reached"); + if (length >= MAX_COMPRESSED_SIZE) { + throw new Error(`Data size ${length} exceeds limit of ${MAX_COMPRESSED_SIZE} bytes`); } const header = new ArrayBuffer(2); From 438713ff2a39f10c789ea7e90abb3e4e4a66cde6 Mon Sep 17 00:00:00 2001 From: Daniel Derevjanik Date: Mon, 12 Aug 2024 20:56:13 +0200 Subject: [PATCH 4/6] refactor: improve err messages --- src/decode.ts | 2 +- src/encode.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/decode.ts b/src/decode.ts index e717709..069beb2 100644 --- a/src/decode.ts +++ b/src/decode.ts @@ -215,7 +215,7 @@ export function decode(qr: string): DataModel { const decodedBysquareHeader = bysquareHeaderDecoder(bysquareHeader); if ((decodedBysquareHeader.version > Version["1.1.0"])) { throw new Error( - `Unsupported Bysquare version '${decodedBysquareHeader.version}' in header detected. Only '0' and '1' are supported`, + `Unsupported Bysquare version '${decodedBysquareHeader.version}' in header detected. Only '0' and '1' values are supported`, ); } diff --git a/src/encode.ts b/src/encode.ts index 1c0c451..dd09c53 100644 --- a/src/encode.ts +++ b/src/encode.ts @@ -36,16 +36,16 @@ export function headerBysquare( ], ): Uint8Array { if (header[0] < 0 || header[0] > 15) { - throw new Error("Invalid 'BySquareType' value in header, valid range <0,15>"); + throw new Error(`Invalid BySquareType value '${header[0]}' in header, valid range <0,15>`); } if (header[1] < 0 || header[1] > 15) { - throw new Error("Invalid 'Version' value in header, valid range <0,15>"); + throw new Error(`Invalid Version value '${header[1]}' in header, valid range <0,15>`); } if (header[2] < 0 || header[2] > 15) { - throw new Error("Invalid 'DocumentType' value in header, valid range <0,15>"); + throw new Error(`Invalid DocumentType value '${header[2]}' in header, valid range <0,15>`); } if (header[3] < 0 || header[3] > 15) { - throw new Error("Invalid 'Reserved' value in header, valid range <0,15>"); + throw new Error(`Invalid Reserved value '${header[3]}' in header, valid range <0,15>`); } const [ From fba7defa78431a94e27c700284d51c26c4816c66 Mon Sep 17 00:00:00 2001 From: Daniel Derevjanik Date: Tue, 13 Aug 2024 10:27:10 +0200 Subject: [PATCH 5/6] docs: move how it works section further --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8d72b2a..87ac11d 100644 --- a/README.md +++ b/README.md @@ -60,12 +60,6 @@ import { ``` -## How it works - -### Encoding sequence - -![logic](./docs/uml/logic.svg) - ## Usage ### Encode @@ -143,6 +137,12 @@ qrstring argument should be a valid QR code string. npx bysquare --decode ``` +## How it works + +### Encoding sequence + +![logic](./docs/uml/logic.svg) + ## Platform support I mainly focus on LTS versions of Node.js and try to use the most idiomatic From 249415d8451f595d390c9172c7d4b67513b8ecd2 Mon Sep 17 00:00:00 2001 From: Daniel Derevjanik Date: Fri, 16 Aug 2024 14:20:53 +0200 Subject: [PATCH 6/6] chore: minor docs changes --- README.md | 7 ++++--- src/types.ts | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 87ac11d..8efad26 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,10 @@ transformed into images depends on how you implement it. See ## Installation -**NOTE**: This package is native [ESM][mozzila-esm] and no longer provides a -CommonJS export. If your project uses CommonJS, you will have to convert to ESM -or use the dynamic [`import()`][mozzila-import] function. +> [!NOTE] +> This package is native [ESM][mozzila-esm] and no longer provides a +> CommonJS export. If your project uses CommonJS, you will have to convert to ESM +> or use the dynamic [`import()`][mozzila-import] function. [mozzila-esm]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules [mozzila-import]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import diff --git a/src/types.ts b/src/types.ts index 489d42e..029409f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -137,6 +137,8 @@ export type BankAccount = { * Formát [ISO 9362](https://en.wikipedia.org/wiki/ISO_9362) (swift) 8 or 11 characters long * * Pattern: `[A-Z]{4}[A-Z]{2}[A-Z\d]{2}([A-Z\d]{3})?` + * + * @example "TATRSKBX" */ bic?: string; }; @@ -333,7 +335,7 @@ export type DirectDebit = SimplePayment & { * Dátum platnosti inkasa. Platnosť inkasa zaníka dňom tohto dátumu. * * Maximálna dĺžka 8 - * Formát YYYYMMDD + * Formát `YYYYMMDD` */ validTillDate?: string; };