This guide covers the specific requirements for generating UBL invoices that comply with the Belgian EN 16931 standard.
Belgium uses the standard PEPPOL BIS Billing 3.0 with additional national requirements according to EN 16931.
// Required: PEPPOL reference
$ubl->addAdditionalDocumentReference('PEPPOL', 'PEPPOLInvoice');
Use correct BTCC values:
// Standard rate (21%)
'tax_category_name' => 'Taux standard'
// Zero rate (0%)
'tax_category_name' => 'Taux zéro'
For Belgian compliance, TaxTotal is omitted at InvoiceLine level.
use Darvis\UblPeppol\UblBeBis3Service;
$ubl = new UblBeBis3Service();
$ubl->createDocument();
// 1. Invoice header
$ubl->addInvoiceHeader('BE-INV-2024-001', '2024-01-15', '2024-02-14');
// 2. Required references
$ubl->addBuyerReference('CLIENT-001');
$ubl->addOrderReference('ORDER-2024-001');
// 3. PEPPOL reference (ubl-BE-01)
$ubl->addAdditionalDocumentReference('PEPPOL', 'PEPPOLInvoice');
// 4. Supplier (Belgian company)
$ubl->addAccountingSupplierParty(
'0123456789', // VAT number WITHOUT "BE" prefix as endpoint
'0208', // Belgian VAT scheme (0208)
'BE0123456789', // Party ID
'My Belgian Company BV',
'Grote Markt 1',
'1000',
'Brussel',
'BE',
'BE0123456789' // VAT number (with prefix)
);
// 5. Customer
$ubl->addAccountingCustomerParty(
'0987654321', // VAT number WITHOUT "BE" prefix as endpoint
'0208', // Belgian VAT scheme (0208)
'BE0987654321', // Party ID
'Customer Company NV',
'Kerkstraat 123',
'2000',
'Antwerpen',
'BE'
);
// 6. Invoice lines
$ubl->addInvoiceLine([
'id' => '1',
'quantity' => 2,
'unit_code' => 'C62',
'price_amount' => 100.00,
'line_extension_amount' => 200.00, // Optional; defaults to quantity * price when omitted
'currency' => 'EUR',
'name' => 'Consultancy services',
'description' => 'IT consultancy - 2 days',
'tax_category_id' => 'S',
'tax_percent' => 21.0,
'tax_scheme_id' => 'VAT'
]);
// 7. Taxes (ubl-BE-10 compliance)
$ubl->addTaxTotal([
[
'taxable_amount' => '200.00',
'tax_amount' => '42.00',
'currency' => 'EUR',
'tax_category_id' => 'S',
'tax_category_name' => 'Taux standard', // Belgian BTCC value
'tax_percent' => 21.0,
'tax_scheme_id' => 'VAT'
]
]);
// 8. Totals
$ubl->addLegalMonetaryTotal([
'line_extension_amount' => 200.00,
'tax_exclusive_amount' => 200.00,
'tax_inclusive_amount' => 242.00,
'charge_total_amount' => 0.00,
'payable_amount' => 242.00
], 'EUR');
// 9. Payment information
$ubl->addPaymentMeans(
'30', // Credit transfer
'Credit transfer',
'BE-PAY-2024-001',
'BE12 3456 7890 1234', // Belgian IBAN
'My Belgian Company BV',
'BBRUBEBB', // BIC code
null, // Channel code not used in Belgium
null // Due date handled at invoice level
);
$ubl->addPaymentTerms('Payment within 30 days', null, null, null);
// 10. Generate XML
$xml = $ubl->generateXml();
| Rate | Percentage | Tax Category ID | BTCC Name |
|---|---|---|---|
| Standard | 21% | S | Taux standard |
| Reduced | 6% | S | Taux réduit |
| Zero | 0% | Z | Taux zéro |
| Exempt | 0% | E | Exonéré |
Format: BE0123456789 (BE + 10 digits)
// Validation
if (!preg_match('/^BE[0-9]{10}$/', $vatNumber)) {
throw new InvalidArgumentException('Invalid Belgian VAT number');
}
Important: For Belgium, the EndpointID should be the VAT number WITHOUT the “BE” prefix, not the KBO number.
Format: 10 digits (VAT number without country code)
// Example VAT number: BE0999000228
// EndpointID should be: 0999000228 (without "BE")
// Scheme ID: 0208 (Belgian VAT)
// Extract EndpointID from VAT number
$vatNumber = 'BE0999000228';
$endpointId = preg_replace('/^BE/i', '', $vatNumber); // Result: 0999000228
$endpointId = preg_replace('/[^0-9]/', '', $endpointId); // Remove non-numeric
// Validation
if (strlen($endpointId) !== 10 || !ctype_digit($endpointId)) {
throw new InvalidArgumentException('Invalid Belgian VAT number for EndpointID');
}
KBO numbers use mod97 checksum validation. The last 2 digits are: 97 - (first 8 digits mod 97)
// Example: 0681845662
// Basis: 06818456
// Checksum: 97 - (6818456 % 97) = 97 - 35 = 62 ✓
function isValidKboNumber(string $kbo): bool {
if (strlen($kbo) !== 10 || !ctype_digit($kbo)) {
return false;
}
$basis = (int) substr($kbo, 0, 8);
$checksum = (int) substr($kbo, 8, 2);
return $checksum === (97 - ($basis % 97));
}
For minimum order amounts, use AllowanceCharge instead of invoice lines:
// Minimum order surcharge as AllowanceCharge
$ubl->addAllowanceCharge(
true, // isCharge (true = surcharge)
74.63, // amount
'Minimum order surcharge', // reason
'S', // taxCategoryId
21.0, // taxPercent
'EUR' // currency
);
// Totals must reflect the charge
$ubl->addLegalMonetaryTotal([
'line_extension_amount' => 0.37, // Sum of invoice lines only
'tax_exclusive_amount' => 75.00, // Lines + charges
'tax_inclusive_amount' => 90.75,
'charge_total_amount' => 74.63, // Sum of charges
'payable_amount' => 90.75
], 'EUR');
Formula: TaxExclusiveAmount = LineExtensionAmount + ChargeTotalAmount
Test your invoices at: https://ecosio.com/en/peppol-and-xml-document-validator/
0208 - VAT number (most used)0096 - DUNS number0088 - EAN/GLN number✅ Successful:
✓ ubl-BE-01: PEPPOL reference present
✓ ubl-BE-10: Correct BTCC values used
✓ ubl-BE-14: TaxTotal correctly positioned
✓ XSD validation passed
❌ Error:
✗ ubl-BE-10: Tax category name "Standard rate" not allowed
Use: "Taux standard"