feat: add SPAYD QR payment code to invoice PDF

Generates QR code in SVG format using the SPAYD payment standard,
matching the PHP implementation. Contains: IBAN, amount, currency,
variable symbol, constant symbol, and invoice reference.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-03-23 10:37:43 +01:00
parent 47fb4dc8ac
commit 7ef25a077b
3 changed files with 243 additions and 11 deletions

View File

@@ -1,4 +1,5 @@
import { FastifyInstance } from 'fastify';
import QRCode from 'qrcode';
import prisma from '../../config/database';
import { requirePermission } from '../../middleware/auth';
@@ -289,6 +290,29 @@ export default async function invoicesPdfRoutes(fastify: FastifyInstance): Promi
}
const totalToPay = subtotal + totalVat;
// QR code - SPAYD payment format
let qrSvg = '';
try {
const spaydParts = [
'SPD*1.0',
'ACC:' + (invoice.bank_iban || '').replace(/ /g, ''),
'AM:' + totalToPay.toFixed(2),
'CC:' + currency,
'X-VS:' + (invoice.invoice_number || ''),
'X-KS:' + (invoice.constant_symbol || '0308'),
'MSG:' + t.title + ' ' + (invoice.invoice_number || ''),
];
const spaydString = spaydParts.join('*');
qrSvg = await QRCode.toString(spaydString, {
type: 'svg',
errorCorrectionLevel: 'M',
margin: 1,
width: 200,
});
} catch {
// QR generation failed — leave empty
}
// VAT recapitulation (always in CZK)
const isForeign = currency.toUpperCase() !== 'CZK';
const cnbRate = 1.0; // Skip CNB rate conversion
@@ -911,7 +935,7 @@ ${indentCSS}
<!-- DPH rekapitulace + QR -->
<div class="recap-section">
<div class="qr">
<!-- QR code placeholder (requires QR library) -->
${qrSvg}
</div>
<table>
<thead>