Invoicing System for Slovenian Small Businesses
A self-hosted PHP invoicing tool built for Slovenian small businesses — UPN QR codes, DDV/VAT compliance, dual PDF engines, and email delivery. Battle-tested across real books and client deployments.
Overview
I built this invoicing system for small Slovenian businesses — the kind that issue a few dozen invoices per year, need to stay compliant with Slovenian DDV/VAT rules, and want a self-hosted tool that doesn’t phone home or lock them into a SaaS subscription.
It’s been running in production since 2024, covering real invoicing cycles. The scope is deliberately narrow: create an invoice, generate a compliant PDF with a UPN QR code the client’s bank can scan, and send it by email. Everything is configurable through a web UI with no framework overhead — raw PHP 8, flat JSON storage, and a REST API that also lets me issue invoices from the CLI or from other tools.
The hardest part wasn’t the UI — it was getting the UPN QR code exactly right. The standard mandates a specific 19-field payload in ISO-8859-2 encoding, a GDSV purpose code, an SI00 payment reference derived from the invoice number, and a 3-digit checksum that counts all field lengths plus the newline characters between them. Every Slovenian banking app validates this strictly. I also implemented the sibling UPN-QR microservice as a standalone Node.js/Express QR generator that follows the same standard — useful for other integrations.
Architecture
Reading the diagram: Invoices enter via the browser UI or the REST API (API-key authenticated). The InvoiceGenerator class handles the full data model — dates, DDV-exempt notes, per-item discounts, and UPN QR payload generation. PDFs are rendered by TCPDF (primary) with automatic fallback to mPDF; the QR code is embedded directly. Completed invoices are stored as JSON flat-files in a directory outside the web root, and email delivery goes through PHPMailer 6 supporting three sending methods. Everything deploys via GitHub → cPanel SSH.
Invoice mockup
Stylized mockup of the generated PDF — three-column header, line-item table with per-item discounts, DDV-exempt footnote, UPN QR code (scanned by any Slovenian banking app), and SI00 payment reference derived from the invoice number.
Six things shipped,
three hard ones solved.
Key contributions
- Designed and implemented the full invoicing system from scratch — web UI, REST API, PDF pipeline, email delivery, and data layer.
- Implemented the Slovenian UPN QR standard: 19-field ISO-8859-2 payload, SI00 payment reference, GDSV purpose code, and 3-digit checksum.
- Built a dual-engine PDF pipeline (TCPDF + mPDF) with automatic fallback and separate flex-free mPDF template for compatibility.
- Wired DDV/VAT compliance for two regimes: domestic small-business exemption (94. člen ZDDV-1) and EU reverse-charge (Article 44, Directive 2006/112/ES).
- Shipped configurable email delivery — php_mail, sendmail, and full SMTP via PHPMailer 6 — with Markdown-templated messages and PDF attachment.
- Set up CI/CD from GitHub to cPanel VPS via SSH deploy keys; data files live outside web root and async-backup to a sibling private repo.
Challenges solved
- The Slovenian UPN QR standard is strict: 19 newline-separated fields, amounts in cents zero-padded to 11 digits, content in ISO-8859-2, and a 3-digit checksum that counts all newline characters. One byte off and every banking app rejects it.
- TCPDF and mPDF behave differently with flexbox HTML — mPDF simply ignores it. Maintaining two template variants without duplicating business logic required a clean separation between data rendering and layout.
- cPanel shared-hosting imposes tight constraints: no background workers, no Redis, no writable system temp by default. Async backup uses proc_open with bypass_shell on both Windows (dev) and Linux (prod) paths.
Small doesn't mean simple to get right. Slovenian invoicing has real legal requirements, and every PDF that ships is a legal document.
What's under the hood.
¿Listo para arreglar, construir
o escalar?
30 minutos, conmigo personalmente. Leo tu sistema como un archivo de logs y te digo qué haría primero. Sin presentaciones, sin embudo de ventas.
— Davor Majc, fundador, Numen