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.
Small doesn't mean simple to get right. Slovenian invoicing has real legal requirements, and every PDF that ships is a legal document.
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.
What's under the hood.
Ready to fix, build,
or scale?
30 minutes, with me personally. I'll read your system like a log file and tell you what I'd do first. No pitch deck, no sales funnel.
— Davor Majc, founder, Numen


