import fs from "fs"; import axios from "axios"; import os from "os"; import path from "path"; import crypto from "crypto"; import { formatDate } from "./utils"; const QR_GENERATOR_URL = 'https://api.paylibo.com/paylibo/generator/image'; const COUNTRY_CODE = 'CZ'; const CURRENCY_CODE = 'CZK'; const QR_PIXEL_SIZE = 256; const tmpDir = os.tmpdir(); /** * Převede číslo účtu z BBAN do IBAN. Automaticky dopočítá kontrolní číslice. * * @param bankAccountNumber číslo účtu ve formátu BBAN (123456-0123456789/0100) */ function convertBbanToIban(bankAccountNumber: string): string { // TODO validovat číslo účtu stejně jako na klientovi, pro případ že sem někdo pošle nesmysl let prefix: string = ''; let accountNumber: string = bankAccountNumber; if (bankAccountNumber.indexOf('-') >= 0) { const split = bankAccountNumber.split('-'); prefix = split[0]; accountNumber = split[1]; } prefix = prefix.padStart(6, '0'); const split = accountNumber.split('/'); accountNumber = split[0].padStart(10, '0'); const bankCode = split[1].padStart(4, '0'); let iban = `${bankCode}${prefix}${accountNumber}${COUNTRY_CODE}00`; // Zatím napevno, nemá smysl řešit nic jiného než CZ iban = iban.replace('C', '12').replace('Z', '35'); const remainder = BigInt(iban) % BigInt(97); const checkDigits = BigInt(98) - remainder; iban = `${COUNTRY_CODE}${checkDigits.toString()}${bankCode}${prefix}${accountNumber}`; if (iban.length !== 24) { throw Error("Neplatná délka sestaveného IBAN: " + iban.length + ", očekáváno 24"); } return iban; } function createNameHash(customerName: string): string { return crypto.createHash('md5').update(customerName).digest('hex'); } function createFilePath(nameHash: string): string { const fileName = `${formatDate(new Date())}_${nameHash}.png`; return path.join(tmpDir, fileName); } /** * Vygeneruje, uloží a vrátí unikátní ID obrázku platebního QR kódu s danými parametry. * * @param customerName jméno uživatele, pro kterého je QR kód generován * @param bankAccountNumber číslo cílového bankovního účtu ve formátu BBAN * @param bankAccountHolder jméno držitele cílového bankovního účtu * @param amount částka v Kč * @param message zpráva pro příjemce * @returns hash, pomocí kterého lze následně získat vygenerovaný obrázek */ export async function generateQr(customerName: string, bankAccountNumber: string, bankAccountHolder: string, amount: number, message: string): Promise { // Zpráva pro příjemce nesmí dle standardu obsahovat '*' a být delší než 60 znaků if (message.indexOf('*') >= 0) { message = message.replace('*', ''); } if (message.length > 60) { message = message.substring(0, 60); } const payload = { iban: convertBbanToIban(bankAccountNumber), amount, currency: CURRENCY_CODE, message, recipientName: bankAccountHolder, branding: false, compress: false, size: QR_PIXEL_SIZE, } const response = await axios.get(QR_GENERATOR_URL, { responseType: 'stream', params: { ...payload } }); // Použijeme hash, abychom nemuseli řešit nepovolené znaky ve jménu uživatele const nameHash = createNameHash(customerName); const imgPath = createFilePath(nameHash); response.data.pipe(fs.createWriteStream(imgPath)); return nameHash; } /** * Vrátí obrázek s QR kódem, pokud existuje. * * @param customerName jméno uživatele * @returns data obrázku */ export function getQr(customerName: string): Buffer { const imgPath = createFilePath(createNameHash(customerName)); return fs.readFileSync(imgPath); }