Peppol Koppeling UBL PEPPOL invoices for PHP and Laravel

darvis/ubl-peppol

Electronic invoices the PEPPOL network accepts

A PHP package that builds UBL 2.1 invoices and credit notes following PEPPOL BIS Billing 3.0 and EN 16931. With Dutch and Belgian rules, a VAT number check through VIES and an optional Laravel integration.

$ composer require darvis/ubl-peppol

What is Peppol Koppeling

You already have your invoice data, in a database, an ERP or a web shop. This package turns it into an XML document a PEPPOL receiver accepts. You map your data to the builder, the package writes the XML.

New to Peppol? First read what Peppol is and how the network works, or get in touch if you want help with your integration.

What it does

  • Dutch invoices and credit notes with UblNlBis3Service
  • Belgian invoices and credit notes with UblBeBis3Service
  • Checks with validate() before sending, naming the rule that fails
  • VAT numbers checked through VIES, and the format of KvK, KBO, RCS, SIREN and Handelsregister numbers
  • In Laravel: sending to your access point provider, with logging and a cleanup command

What it does not do

  • It is not an access point. To send, you need a contract with a provider.
  • It does not calculate your invoice. You pass the amounts, VAT and totals.
  • It does not create PDFs and does not receive invoices.

Highlights

Version 1.11: VAT categories as a knowledge base

An enum with all nine VAT categories PEPPOL allows, explaining when to use which and which exemption reason belongs to it. validate() now also checks what each category requires.

Online PEPPOL validator

Upload or paste a UBL invoice or credit note and check it against the official EN 16931 and PEPPOL BIS Billing 3.0 rules. Everything runs in your browser; nothing is uploaded.

Credit notes with the Dutch builder

Since version 1.10 the Dutch builder also creates credit notes, with a reference to the invoice you correct.

All changes

How to build an invoice

Create a builder, add the header, the parties, the payment, the VAT, the totals and the lines, and let it write the XML. With validateFirst: true you know before sending whether anything is missing.

use Darvis\UblPeppol\UblNlBis3Service;

$ubl = new UblNlBis3Service();

$ubl->createDocument();
$ubl->addInvoiceHeader('INV-2026-001', '2026-01-15', '2026-02-14');
$ubl->addBuyerReference('CLIENT-001');

$ubl->addAccountingSupplierParty(/* your company */);
$ubl->addAccountingCustomerParty(/* your customer */);
$ubl->addPaymentMeans('30', 'Credit transfer', 'INV-2026-001', 'NL91ABNA0417164300');

$ubl->addTaxTotal([/* VAT per category */]);
$ubl->addLegalMonetaryTotal([/* totals */], 'EUR');
$ubl->addInvoiceLine([/* invoice line */]);

// Throws an InvalidArgumentException when validate() finds an error.
$xml = $ubl->generateXml(validateFirst: true);

The full example, line by line

Why now

Belgium

Mandatory since 2026

Since 1 January 2026, Belgian companies must invoice each other electronically. Peppol is the default channel for this.

Netherlands

Mandatory from 2030

The Dutch government wants to make electronic invoicing between businesses mandatory from 1 July 2030. Connect now and you are ready in time.

Europe

VAT in the Digital Age

ViDA brings digital VAT reporting and electronic invoices for trade between businesses across borders.

Frequently asked questions

Does this package send my invoices over the PEPPOL network?

No. The package is not an access point. PEPPOL is a closed network you reach through an access point provider you have a contract with. In Laravel, PeppolService posts the XML to that provider's API, using PEPPOL_URL, PEPPOL_USERNAME and PEPPOL_PASSWORD from your .env.

What is the difference between the Dutch and the Belgian builder?

Pick the builder by the receiver's country. UblNlBis3Service puts the elements in the right order itself and checks the Dutch rules. UblBeBis3Service adds up the lines for you with calculateTotals() and checks that the amounts add up. Both build credit notes.

Is a passing validate() enough?

No. validate() changes nothing in your document and checks the rules the package knows. A receiver checks more. So run a document through an official validator before you go live, for example the online validator in the documentation.

Which PHP and Laravel version do I need?

PHP 8.2 or newer with the dom and libxml extensions. You need bcmath to check an IBAN and the soap extension for VIES. Laravel 11, 12 or 13 is only needed for the optional Laravel layer.

Is the package free?

Yes. The package is free and released under the MIT license. You may use it in commercial projects.

More questions in the documentation