Skip to content

Create e-invoices with plain PHP, no Laravel needed

Quittio is known as an e-invoicing library for Laravel. But its core is plain PHP and runs in any project with PHP 8.3, even without a framework.

How-to15 Oct 2026 · about 6 minutes

In short

For e-invoices without Laravel, install quittio/core and the country pack quittio/country-de. Instead of the facade, create one instance with Quittio::create() and build invoices with InvoiceBuilder::make(). ZUGFeRD, XRechnung, Peppol, validation and reading incoming invoices work exactly as in Laravel.

Which packages you need

PackageWithout LaravelContent
quittio/corerequiredData model, calculation, CII and UBL, ZUGFeRD/Factur-X, validation, reading
quittio/country-derecommendedGerman mandatory fields, VAT rates, XRechnung rules, DIN 5008
quittio/laravelnot neededFacade, configuration, Blade, Artisan
quittio/quittionot neededComplete package for Laravel projects

You need PHP 8.3 or newer with the extensions ctype, dom, gd, libxml, mbstring, xmlwriter and zlib. Laravel or any other framework is not required.

Installation

One command, no config file:

composer require quittio/core quittio/country-de

A complete script

The script creates a ZUGFeRD invoice (PDF/A-3 with embedded XML), saves it and reads it back as a check. Run it with php invoice.php.

invoice.php
<?php
require __DIR__.'/vendor/autoload.php';

use Quittio\Core\Builder\InvoiceBuilder;
use Quittio\Core\Builder\Party;
use Quittio\Core\Enums\PaymentMeansCode;
use Quittio\Core\Enums\UnitCode;
use Quittio\Core\Quittio;
use Quittio\Core\Tax\CountryDefaultVat;
use Quittio\Country\DE\CountryDE;

$quittio = Quittio::create([], [new CountryDE]);
InvoiceBuilder::useDefaultVat(new CountryDefaultVat($quittio->countries()));   // lines without vat(): 19 %

$invoice = InvoiceBuilder::make()
    ->number('RE-2026-0120')
    ->issueDate('2026-10-14')
    ->deliveryDate('2026-10-14')
    ->seller(Party::make('Muster GmbH')->vatId('DE123456789')->address('Hauptstraße 1', '12345', 'Musterstadt', 'DE'))
    ->buyer(Party::make('Kunde AG')->vatId('DE987654321')->address('Bahnhofstraße 5', '54321', 'Beispielstadt', 'DE'))
    ->line('Consulting', netPrice: '120.00', quantity: 4, unit: UnitCode::Hour)
    ->payment(PaymentMeansCode::SepaCreditTransfer, iban: 'DE02120300000000202051')
    ->dueInDays(14)
    ->build();

$result = $quittio->make($invoice)->render();                        // ZUGFeRD: PDF/A-3 with XML
$path = $result->save(__DIR__.'/invoices/'.$result->filename());   // invoices/Rechnung_RE-2026-0120.pdf

// Read it back, as the recipient would
$read = $quittio->readFile($path);
echo $read->data->totals->amountDue;                               // EUR 571.20
  • Without Laravel there is no config file, so seller and bank account are set in code.
  • useDefaultVat() gives lines without their own VAT the default rate of the country pack, 19 % in Germany. Without it, build() fails for such lines.
  • render() validates the invoice. If it is invalid, an exception with all findings is thrown before any file is written.

What is different from Laravel

TopicLaravelPlain PHP
Entry pointFacade Quittio::invoice()Instance from Quittio::create(), invoice with InvoiceBuilder::make()
Configurationconfig/quittio.phpArray or Config object passed to Quittio::create()
Seller and bankfrom the configurationin code: ->seller(…) and ->payment(…)
Default VATautomaticcall InvoiceBuilder::useDefaultVat(…) once at start-up
Country packdiscovered automaticallypass new CountryDE to Quittio::create()
TemplatesBladePlain PHP templates; Twig etc. via your own TemplateEngine
SavingStorage disk$result->save($path) or file_put_contents()

Pass settings as an array, for example for the validation levels:

bootstrap.php
$quittio = Quittio::create([
    'validation' => ['mode' => 'error', 'levels' => ['L1', 'L2', 'L3', 'L4', 'L7']],
    'pdf' => ['b2c_embed_xml' => false],
], [new CountryDE]);

XRechnung and validation

Switching format is one line. check() validates an invoice without creating a PDF:

invoice.php
// XRechnung instead of ZUGFeRD
$result = $quittio->make($invoice)->format('xrechnung-cii')->render();

// Validate only, no PDF; never throws, returns a report
$report = $quittio->make($invoice)->format('xrechnung-cii')->check();
$report->isValid();
$report->errors();

Cancellations work without Laravel too:

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

Reading incoming invoices

Since 2025 every business in Germany must be able to receive e-invoices. The instance reads CII, UBL and ZUGFeRD and can check them on all levels:

incoming.php
$read = $quittio->read($uploadedContent);   // content of an XML or PDF file
$read = $quittio->readFile('/path/to/invoice.pdf');

$read->data->number;                        // "RE-2026-0101"
$read->data->seller->name;                  // the supplier
$read->profile;                             // e.g. "en16931" or "xrechnung"

$report = $quittio->validateFile($uploadedContent);   // all validation levels on an incoming invoice

Wiring it into your project

Create the instance once at start-up and reuse it everywhere. In Symfony, Slim or another framework, register it as a service; without a framework, a small factory is enough:

InvoiceFactory.php
final class InvoiceFactory
{
    private static ?Quittio $quittio = null;

    public static function quittio(): Quittio
    {
        if (self::$quittio === null) {
            self::$quittio = Quittio::create([], [new CountryDE]);
            InvoiceBuilder::useDefaultVat(new CountryDefaultVat(self::$quittio->countries()));
        }

        return self::$quittio;
    }
}

What only works with Laravel

  • Facade, config file and Artisan commands
  • Blade templates, queue, events, mail and storage integration
  • Quittio::fake() and the test assertions (factories and scenarios also work without Laravel)
  • Quittio Managed (quittio/managed) with database, numbering and archive requires Laravel 13

Everything that makes a valid e-invoice lives in the core. The complete reference for use without Laravel is in the documentation.

Frequently asked questions

Do I need Laravel for Quittio?

No. quittio/core has no dependency on Laravel and runs in any application with PHP 8.3 or newer. You only need Laravel for the quittio/laravel integration and for Quittio Managed.

Does Quittio work with Symfony, Slim, WordPress or Shopware?

Yes, with anything that can load Composer packages. Create the Quittio instance once at start-up and register it the way your framework manages services.

Which package do I install?

quittio/core and, for Germany, quittio/country-de. The quittio/quittio package is meant for Laravel projects because it includes the Laravel integration.

Which PHP extensions do I need?

ctype, dom, gd, libxml, mbstring, xmlwriter and zlib; intl is recommended. For the official KoSIT validator (level L5) also Java 17 or Docker, for veraPDF (L6) veraPDF or Docker.

Are invoices created without Laravel just as valid?

Yes. Data model, calculation, formats and validation all live in the core. The Laravel integration is just a convenient wrapper around it.

Try it in five minutes.

One Composer command, one script, one valid e-invoice.