98 lines
3.7 KiB
TypeScript
98 lines
3.7 KiB
TypeScript
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<string> {
|
|
// 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);
|
|
} |