Générer un PDF en PHP, tout le monde sait faire. Générer une Factur-X conforme, c'est autre chose : il faut produire le bon XML (CII), l'embarquer dans un PDF/A-3 et y injecter les métadonnées XMP attendues. Rater une de ces trois étapes, et la facture est techniquement invalide — même si le PDF s'affiche parfaitement.
Bonne nouvelle : une librairie gère la partie la plus délicate. Voici le chemin complet, du XML au fichier validé.
🔎 À la fin, vérifiez toujours votre fichier dans le Testeur de factures Factur-X — c'est le réflexe qui évite les rejets en production.
Le principe : deux couches, un seul fichier
facture.pdf (Factur-X)
├── 📄 Couche visuelle → un PDF/A-3 lisible
└── 🤖 Couche données → factur-x.xml (CII) embarqué
Vous fournissez deux choses : le PDF visuel de votre facture (celui que vous générez déjà) et le XML Factur-X. La librairie assemble les deux en un PDF/A-3 conforme. Pour le contexte complet du format, voir le guide Factur-X : comprendre le format.
Étape 1 — Installer la librairie
La référence en PHP est atgp/factur-x (PHP 7.4+) :
composer require atgp/factur-x
Elle s'appuie sur setasign/fpdf, setasign/fpdi et smalot/pdfparser, et sait générer, extraire et valider le XML embarqué.
Étape 2 — Construire le XML Factur-X (CII)
C'est votre responsabilité : la librairie n'invente pas vos données. Voici un squelette profil MINIMUM (le plus simple — sans lignes de détail) à adapter :
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:factur-x.eu:1p0:minimum</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>FA-2026-0042</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20260627</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:ApplicableHeaderTradeAgreement>
<ram:SellerTradeParty>
<ram:Name>Ma Société SARL</ram:Name>
<ram:SpecifiedLegalOrganization>
<ram:ID schemeID="0002">123456789</ram:ID>
</ram:SpecifiedLegalOrganization>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:Name>Client SAS</ram:Name>
</ram:BuyerTradeParty>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeDelivery/>
<ram:ApplicableHeaderTradeSettlement>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:TaxBasisTotalAmount>1000.00</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">200.00</ram:TaxTotalAmount>
<ram:GrandTotalAmount>1200.00</ram:GrandTotalAmount>
<ram:DuePayableAmount>1200.00</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>
Points de vigilance : la date est au format 102 (AAAAMMJJ), la devise suit ISO 4217, et TaxBasis + TaxTotal = GrandTotal (sinon rejet — voir les 20 erreurs). Pour un profil EN 16931 (COMFORT) avec les lignes de détail, le XML est plus riche ; on l'assemble souvent par gabarit ou via une librairie d'écriture dédiée plutôt qu'à la main.
Étape 3 — Embarquer le XML dans le PDF/A-3
C'est là que atgp/factur-x fait le travail difficile (PDF/A-3 + XMP) :
<?php
require 'vendor/autoload.php';
use Atgp\FacturX\Facturx;
$pdfVisuel = file_get_contents('facture.pdf'); // votre PDF existant
$xml = file_get_contents('factur-x.xml'); // le XML de l'étape 2
$facturx = new Facturx();
// Assemble un PDF/A-3 conforme contenant le XML embarqué
$facturxPdf = $facturx->generateFacturxFromFiles($pdfVisuel, $xml);
file_put_contents('facture-facturx.pdf', $facturxPdf);
La librairie nomme automatiquement la pièce jointe factur-x.xml, pose la bonne relation de fichier et écrit les métadonnées XMP — les erreurs n°1 à 4 de la checklist sont ainsi éliminées d'office.
Étape 4 — Valider le résultat
Ne jamais mettre en production sans valider. D'abord le schéma, en local :
$isValid = $facturx->checkFacturxXsd($xml); // true / exception si non conforme
Puis une validation complète (schéma + règles métier EN 16931 + cohérence PDF) :
🔎 Test rapide — le Testeur Factur-X de SynapxLab : présence du XML, PDF/A-3, profil, montants, mentions. Gratuit, aucune donnée conservée.
🏛️ Validation faisant autorité — le validateur du FNFE-MPE, avant toute mise en production.
Lire / extraire le XML d'une Factur-X existante
L'opération inverse est tout aussi simple :
$pdf = file_get_contents('facture-facturx.pdf');
$xml = $facturx->getFacturxXmlFromPdf($pdf); // récupère le XML embarqué
À détailler dans un prochain article du cluster : comment lire et exploiter le XML embarqué.
Et après ? Générer ≠ transmettre
Produire un fichier conforme n'est qu'une étape. La réforme impose ensuite de le transmettre via une plateforme agréée (PA), de gérer les statuts (reçue, acceptée, encaissée) et l'e-reporting. Une librairie traite le format ; elle ne fait ni la transmission, ni les statuts, ni l'e-reporting.
💡 Pour un flux complet sans tout recoder, un ERP/CRM génère la Factur-X et la transmet via une PA, avec suivi du cycle de vie. Le cadre complet est détaillé dans notre guide de la réforme.
❓ FAQ
Quelle librairie PHP pour générer une Factur-X ?
atgp/factur-x : elle embarque le XML dans un PDF/A-3 conforme, et sait aussi l'extraire et le valider.
La librairie génère-t-elle le XML à ma place ? Non : vous fournissez le PDF et le XML (CII). La librairie gère l'embarquement conforme et les métadonnées XMP.
Comment vérifier que ma Factur-X est valide ?
checkFacturxXsd() en local pour le schéma, puis le testeur Factur-X ou le FNFE-MPE pour une validation complète.
Ai-je besoin de Ghostscript ? Pas pour l'embarquement de base. Mais normaliser des PDF d'origines variées en PDF/A-3 strict peut nécessiter un outil de conversion ou de contrôle (Ghostscript, veraPDF).
Pour aller plus loin
- 🔎 Testeur de factures Factur-X — validez votre fichier généré, gratuitement.
- 🛠️ Les 20 erreurs Factur-X les plus fréquentes — la checklist à garder ouverte pendant le dev.
- 🧩 Pourquoi ma facture Factur-X est invalide ? — les grandes familles de causes.
- 📘 Factur-X : comprendre le format et valider ses factures — le guide complet.
- 🏛️ Librairie atgp/factur-x sur GitHub — code source, README et exemples.