PHP

eInvoicing in PHP

The eInvoice – or "E-Rechnung" in German terminology – is a thing.
I didn’t know until recently, when I had to start implementing a process here for a PHP backend.

Context

The eInvoice should already be used by businesses as we speak.
As of January 1, 2025, electronic invoicing are mandatory in the B2B sector in Germany.
Starting in January 2026, all EU businesses will be required to use e-invoicing for B2B transactions.
All invoices submitted between businesses must be electronic.

The legal requirements here in normal words:
An invoice is electronic, if

  • it is issued, transmitted, and received in a structured electronic format, and
  • this format enables the automated and electronic processing of the invoice.

For details read EN 16931 compliance.

So you can see that there isn’t too much time left here to fix up a maybe outdated approach.

Concept

The eInvoice consists primarily of an XML file. That’s the single source of truth.
While a PDF is optional, it sure helps to make it visible for the human eye.

Now to keep things simple, a PDF can actually get such an XML attached, so that it is still the "same"
single PDF, but now contains as payload the eInvoice information standardized to be processed by tooling.

The XML is using the CrossIndustryInvoice (CII) format. It was designed to support different business processes
and their invoicing requirements. The different processes are technically expressed in "profiles" (BASIC, BASIC-WL, EN16931, EXTENDED).

Building the PDF

There are a few different formats. I so far used ZUGFeRD / FacturX data format, or XRechnung specifically for Germany.

For PHP we can use an existing library to create those files, e.g.

  • easybill/zugferd-php
  • horstoeko/zugferd

and others. I personally used the latter so far.

I assume that a PDF building is already in place.
If not, that’s a 30min thing with dompdf or mpdf etc.

You chose the profile that fits and

$documentBuilder = ZugferdDocumentBuilder::createNew(ZugferdProfiles::PROFILE_EN16931);

And set the information coming from an array or DTO:

$documentBuilder
    ->setDocumentInformation(
        $this->invoiceDto->invoiceN