Zum Inhalt springen

E-Rechnung in Laravel erstellen: Schritt für Schritt

In etwa 15 Minuten von einer leeren Laravel-App zur ersten gültigen E-Rechnung. Mit echten Codebeispielen aus der Dokumentation.

Anleitung15.10.2026 · ca. 12 Minuten

Kurz gesagt

Installiere quittio/quittio, trage deine Firma einmal in config/quittio.php ein und baue Rechnungen mit Quittio::invoice(). render() rechnet Summen und Steuer selbst, prüft die Rechnung, wählt das passende Format (für Unternehmen ZUGFeRD, für Behörden XRechnung) und liefert PDF und XML.

Voraussetzungen

  • PHP 8.3 oder neuer mit den Erweiterungen ctype, dom, gd, libxml, mbstring, xmlwriter und zlib; intl wird empfohlen.
  • Laravel 13. Ohne Laravel geht es auch, siehe E-Rechnungen mit PHP, ganz ohne Laravel.
  • Optional für die offiziellen Prüfungen: Java für den KoSIT-Validator und Docker oder eine lokale Installation für veraPDF. Für den Einstieg brauchst du beides nicht.

Schritt 1: Installieren

Das Paket quittio/quittio bringt alles mit, was eine Laravel-App braucht: den Kern, die Laravel-Integration und das Länderpaket Deutschland mit den Pflichtangaben nach UStG.

Terminal
composer require quittio/quittio          # Core, Laravel-Integration und Länderpaket Deutschland
php artisan quittio:install               # veröffentlicht config/quittio.php

Schritt 2: Deine Firma eintragen

Jede Rechnung nennt den Verkäufer, also deine Firma. Du trägst sie einmal in config/quittio.php ein, am besten über Umgebungsvariablen. Quittio::invoice() setzt sie dann in jede Rechnung ein.

config/quittio.php
// config/quittio.php (Auszug)
'seller' => [
    'name' => env('QUITTIO_SELLER_NAME'),               // Muster GmbH
    'vat_id' => env('QUITTIO_SELLER_VAT_ID'),           // DE123456789
    'tax_number' => env('QUITTIO_SELLER_TAX_NUMBER'),   // 12/345/67890
    'address' => ['line1' => 'Hauptstraße 1', 'postcode' => '12345', 'city' => 'Musterstadt', 'country' => 'DE'],
    'contact' => ['name' => 'Max Muster', 'phone' => '+49 123 456789', 'email' => '[email protected]'],
    'bank_accounts' => [
        ['iban' => 'DE02120300000000202051', 'bic' => 'BYLADEM1001', 'name' => 'Muster GmbH'],
    ],
],
SchlüsselNötigWarum
name, addressimmerDeutsche Rechnungen nennen den Verkäufer mit vollständiger Anschrift
vat_id oder tax_numberimmerEine der beiden muss auf der Rechnung stehen; die USt-IdNr. ist vorzuziehen
contact, bank_accountsempfohlenSteht auf der Rechnung; für XRechnung an Behörden Pflicht
small_businessnur Kleinunternehmertrue kennzeichnet jede Rechnung als Kleinunternehmer-Rechnung ohne Umsatzsteuer

Ohne USt-IdNr.?

Wer nur eine Steuernummer hat, braucht zusätzlich eine Handelsregisternummer (legal_registration) oder einen anderen Identifikator (identifiers). Das verlangt die EN 16931 mit der Regel BR-CO-26.

Wie die Rechnung mit Beispieldaten aussieht, zeigt dir die Vorschau:

php artisan quittio:preview --scenario=simple

Schritt 3: Die erste Rechnung bauen

Eine Rechnung beginnt mit Quittio::invoice(). Du ergänzt, was zu dieser Rechnung gehört, und beendest die Kette mit render():

InvoiceController.php
use Quittio\Core\Builder\Party;
use Quittio\Core\Enums\UnitCode;
use Quittio\Country\DE\Tax\VatPreset;
use Quittio\Laravel\Facades\Quittio;

$result = Quittio::invoice()                             // deine Firma aus config/quittio.php
    ->number('RE-2026-0042')
    ->deliveryDate('2026-09-30')                         // Leistungsdatum, in Deutschland Pflicht
    ->buyer(Party::make('Kunde AG')
        ->vatId('DE987654321')
        ->address('Bahnhofstraße 5', '54321', 'Beispielstadt', 'DE'))
    ->line('Web development', netPrice: '95.00', quantity: 24, unit: UnitCode::Hour,
        description: 'Customer portal, sprint 3')
    ->line('Hosting', netPrice: '49.00', unit: UnitCode::Month)
    ->line('Handbook of VAT law', netPrice: '39.00', vat: VatPreset::Reduced7)   // 7 % statt 19 %
    ->dueInDays(14)->cashDiscount(days: 7, percent: '2.00')
    ->render();

return $result->download();                              // oder $result->save('invoices', '2026/')
  • number() ist deine Rechnungsnummer. Das Rechnungsdatum ist heute, außer du setzt issueDate(). Für lückenlose Nummern sorgt deine App oder Quittio Managed.
  • deliveryDate() ist das Leistungsdatum. Deutsche Rechnungen müssen es nennen, auch wenn es dem Rechnungsdatum entspricht.
  • buyer() ist dein Kunde: Name, Anschrift und bei Unternehmen die USt-IdNr.
  • line() fügt eine Position hinzu. netPrice ist der Nettopreis pro Einheit. Ohne quantity ist die Menge 1, ohne vat gilt der Standardsatz deines Landes (19 % in Deutschland).
  • dueInDays() setzt das Zahlungsziel, cashDiscount() ein Skonto.

Beträge als Text

Beträge und Mengen übergibst du als Strings ('95.00'), nie als Float. Gleitkommazahlen können nicht jeden Betrag exakt speichern; Quittio lehnt sie ab. Summen und Umsatzsteuer berechnet Quittio immer selbst, nach den Rundungsregeln der EN 16931.

Schritt 4: Ausgeben, speichern, versenden

Das Ergebnis von render() enthält alles, was erzeugt wurde. Es lässt sich herunterladen, speichern oder direkt an eine Mail hängen:

InvoiceController.php
$result->pdf();            // PDF/A-3 mit eingebettetem XML
$result->xml();            // XML
$result->filename();       // Rechnung_RE-2026-0042.pdf
$result->validation();     // Befunde der Prüfung, auch Warnungen

$result->save('invoices', '2026/');                    // auf eine Storage-Disk
return $result->download();                             // ->stream() / return $result
Mail::to($customer)->send(new YourInvoiceMail($result)); // das Ergebnis ist Attachable

Große Mengen renderst du in der Queue: Quittio::make($invoice)->queue()->save('invoices', '2026/').

Schritt 5: Das richtige Format

Meist musst du nichts tun: Ohne Formatangabe wählt Quittio pro Rechnung. Für einen deutschen Verkäufer gilt:

KundeFormatWarum
Unternehmen in DeutschlandZUGFeRD (Profil EN 16931)E-Rechnung, die zugleich ein lesbares PDF ist
Behörde (Leitweg-ID als Käuferreferenz)XRechnung (CII)von öffentlichen Auftraggebern verlangt
VerbraucherPDFkeine E-Rechnungspflicht
Kleinbetrag bis 250 €, mit ->smallAmount() markiertPDFKleinbetragsrechnungen sind ausgenommen
Unternehmen im EU-AuslandZUGFeRD (EN 16931)lesbares PDF mit Standard-XML
Kunde außerhalb der EUPDFkeine EU-Vorgaben

Verlangt ein Kunde ein bestimmtes Format, nennst du es. Für Behörden brauchst du die Leitweg-ID:

InvoiceController.php
$result = Quittio::invoice()
    ->buyer($authority)
    ->buyerReference('04011000-12345-03')       // Leitweg-ID
    ->line('Consulting', netPrice: '120.00')
    ->render('xrechnung-cii');

Mehr dazu im Beitrag ZUGFeRD, XRechnung oder Peppol.

Schritt 6: Eigene Modelle anbinden

Statt Käuferdaten bei jeder Rechnung zu wiederholen, liefern deine Modelle sie selbst. Dafür gibt es drei Verträge: ProvidesParty (Kunde), ProvidesInvoiceLine (Positionen) und ProvidesInvoiceData (eine ganze Rechnung, z. B. aus einer Bestellung).

app/Models/User.php
use Quittio\Core\Builder\PartyBuilder;
use Quittio\Core\Contracts\ProvidesParty;

class User extends Authenticatable implements ProvidesParty
{
    public function fillParty(PartyBuilder $party): void
    {
        $party->name($this->company ?? $this->name)
            ->vatId($this->vat_id)
            ->address($this->street, $this->postcode, $this->city, $this->country)
            ->electronicAddress($this->email, 'EM');
    }
}

$invoice = Quittio::invoice()
    ->buyer($user)                    // ProvidesParty
    ->lines($order->items)            // ProvidesInvoiceLine
    ->build();

Die Rechnung behält eine Kopie der Werte. Ändert sich das Modell später, bleiben bereits erzeugte Rechnungen unverändert.

Schritt 7: Fehlermeldungen verstehen

Jede Rechnung wird vor dem Rendern geprüft. Ist sie ungültig, wirft render() eine ValidationException mit Regelcode, Feld und einem Hinweis zur Lösung, zum Beispiel bei einer XRechnung ohne Leitweg-ID:

ValidationException
The invoice is not valid (2 errors):
[QUITTIO-PROFILE-REQUIRED] BT-10: The field buyerReference (BT-10) is mandatory in profile XRechnung 3.0.
    Provide the field or choose a profile that does not require it.
[BR-DE-15] BT-10: The buyer reference (BT-10) must be provided.
    Set the Leitweg-ID with ->buyerReference('04011000-12345-03').

Der Präfix verrät die Herkunft der Regel: BR- aus der EN 16931, BR-DE- aus XRechnung, PEPPOL- aus Peppol, DE-USTG- aus dem Umsatzsteuergesetz und QUITTIO- aus Quittio selbst. Mit check() statt render() siehst du alle Befunde auf einmal, ohne dass etwas geworfen wird.

Schritt 8: Testen

Der häufigste Test prüft, dass dein Code eine Rechnung erzeugt. Quittio::fake() ersetzt das Rendern durch eine schnelle Attrappe, die jeden Aufruf aufzeichnet:

tests/Feature/OrderInvoiceTest.php
use Quittio\Laravel\Facades\Quittio;
use Quittio\Laravel\Rendering\RenderedInvoice;

it('renders the invoice of an order', function () {
    $fake = Quittio::fake();

    $this->post('/orders/42/invoice')->assertOk();

    Quittio::assertRenderedCount(1);
    Quittio::assertRendered(fn (RenderedInvoice $r) => $r->format() === 'zugferd'
        && $r->data()->totals->amountDue->isEqualTo('1234.56'));
});

Ob die Rechnung auch eine gültige E-Rechnung ist, prüfst du mit assertValidEInvoice() und den offiziellen Validatoren. Wie das in der CI läuft, steht im Beitrag E-Rechnungen validieren.

Schritt 9: Stornieren

Eine ausgestellte Rechnung wird nie geändert oder gelöscht. Um sie aufzuheben, stellst du eine Stornorechnung aus: eine neue Rechnung mit eigener Nummer, die auf das Original verweist.

InvoiceController.php
$cancellation = $result->cancel('ST-2026-0001', reason: 'Charged twice');

Teilstornos, Vorzeichen und Korrekturen erklärt der Beitrag Storno, Teilstorno, Korrektur.

Wie es weitergeht

  • Stellt deine App Rechnungen selbst aus, brauchst du lückenlose Nummern, ein Archiv und Unveränderbarkeit. Das übernimmt Quittio Managed; Hintergründe im Beitrag Lückenlose Rechnungsnummern.
  • Logo, Farben und Texte passt du in der Konfiguration an; eigene Vorlagen sind möglich.
  • Eingehende E-Rechnungen liest du mit Quittio::read().

Häufige Fragen

Wie lange dauert die Einrichtung?

Etwa 15 Minuten: Paket installieren, Firmendaten eintragen, erste Rechnung bauen. Die offiziellen Validatoren für die CI kommen optional dazu.

Muss ich XML selbst schreiben?

Nein. Du beschreibst die Rechnung mit dem Builder; Quittio erzeugt CII oder UBL, rechnet Summen und Steuer und bettet das XML bei ZUGFeRD in ein PDF/A-3 ein.

Woher kommt die Rechnungsnummer?

Du setzt sie mit number(). Quittio Open Source vergibt keine Nummern; lückenlose Nummernkreise übernimmt Quittio Managed.

Kann ich das Design anpassen?

Ja, in drei Stufen: Logo, Farben und Texte per Konfiguration, einzelne Teile der Vorlage überschreiben oder eine eigene Vorlage verwenden.

Funktioniert das auch mit älteren Laravel-Versionen?

Quittio unterstützt die jeweils aktuelle Laravel-Hauptversion, derzeit Laravel 13. Der Kern läuft auch ohne Laravel.

Bereit für die erste Rechnung?

Die vollständige Anleitung mit allen Optionen steht in der Dokumentation.