In progress · 2026
Invoicing API with hash-chained records
A central invoicing service: issues, calculates, renders the PDF and keeps a tamper-evident audit trail with a Verifactu-style hash chain.
- Role
- Design and development
Problem
Every application that invoices ends up reimplementing the same things: series, numbering, tax calculation, PDF and a record of what happened. Doing it five times means five places where the rounding can disagree.
Decision
One service where invoices are issued. Applications ask for them; they do not compute them.
An issued invoice is never updated or deleted: it is corrected by issuing a credit note. Each record stores the hash of the previous one, so the whole chain is verifiable and tampering directly with the database shows up.
Implementation
Amounts are stored as integer cents, never floating point. Rounding is applied per aggregated tax rate rather than line by line — the criterion the Spanish tax agency uses, and where the one-cent discrepancies appear as soon as an invoice has twenty lines.
Numbering is sequential and gapless per series and year, with the counter incremented inside a locking transaction. If PDF generation fails, the invoice already exists: the PDF is retried, the number is never handed back.
The PDF is rendered as HTML and converted in a separate Gotenberg container; the result goes to S3-compatible storage and is served through a signed, expiring URL.
The service is not exposed to the internet: it is reachable only from the private Docker network, so the component holding the tax records has no public door.