Valider une facture Factur-X ne se résume pas à vérifier qu’un fichier XML est bien formé. Le pipeline complet couvre cinq étapes distinctes : conformité PDF/A-3 du conteneur, extraction du XML embarqué, validation XSD de la structure CII, validation Schematron des règles EN16931, et validation Schematron des contraintes spécifiques au profil Factur-X. Chacune de ces étapes peut échouer indépendamment, et chacune nécessite ses propres artefacts de validation.
La question n’est donc pas “faut-il valider ?” — c’est une obligation si vous émettez des factures vers une PDP. La question est : avec quel outil, déployé comment, et maintenu par qui ?
Cet article compare quatre approches concrètes : le KoSIT Validator (référence de l’administration allemande), Mustangproject/ZUV (bibliothèque Java tout-en-un), un pipeline assemblé manuellement, et une API SaaS. Pour chacune, vous trouverez les étapes d’installation, les limites réelles, et les patterns d’intégration CI/CD.
Pour le contexte global de la réforme et le pipeline de validation, voir Facturation électronique 2026 : le guide technique complet pour développeurs. Pour le détail des deux niveaux XSD et Schematron, voir Valider EN16931/Factur-X : XSD vs Schematron, erreurs BR-*.
Le pipeline de validation complet
Avant de comparer les outils, il faut comprendre ce que signifie “valider complètement” une facture Factur-X. Ce modèle en 5 passes est une approche pédagogique courante. En pratique, l’ordre et le nombre de validations dépendent de l’outil utilisé, mais les couches logiques restent les mêmes :
PDF/A-3 check → XML extraction → XSD validation → Schematron EN16931 → Schematron Factur-X profil
│ │ │ │ │
ISO 19005-3 factur-x.xml CII D22B schema Règles BR-* Règles profil
(verapdf) (.xsd) (CEN TC 434) (FNFE-MPE)
Étape 1 — PDF/A-3 : le conteneur PDF doit être conforme à ISO 19005-3. Cela inclut les polices embarquées, les profils ICC, les métadonnées XMP, et l’absence de dépendances externes. Détails dans PDF/A-3 pour Factur-X : checklist de conformité.
Étape 2 — Extraction XML : le fichier factur-x.xml (ou zugferd-invoice.xml pour ZUGFeRD) doit être présent comme pièce jointe du PDF avec le bon AFRelationship.
Étape 3 — XSD : le XML extrait doit être valide selon le schéma CII. Factur-X 1.08 / ZUGFeRD 2.4 utilisent CII D22B (CrossIndustryInvoice_100pD22B.xsd). Le binding CEN officiel (EN16931-3-5) est basé sur D16B ; D22B est une extension rétrocompatible adoptée par Factur-X et ZUGFeRD.
Étape 4 — Schematron EN16931 : les règles métier BR-* publiées par le CEN TC 434 sur le dépôt ConnectingEurope/eInvoicing-EN16931. Ces règles vérifient la cohérence arithmétique, les listes de codes, les contraintes conditionnelles. Voir le catalogue des erreurs BR-* pour le détail.
Étape 5 — Schematron profil Factur-X : les contraintes spécifiques au profil déclaré (Minimum, Basic WL, Basic, EN16931, Extended). Un champ autorisé en Extended peut être interdit en Basic.
Un outil qui ne couvre qu’une partie de ce pipeline laisse passer des factures invalides. C’est le critère principal de comparaison.
Option 1 : KoSIT Validator
Ce qu’il fait
Le KoSIT Validator (itplr-kosit/validator) est développé par la Koordinierungsstelle für IT-Standards, l’organisme allemand chargé de la standardisation IT pour l’administration fédérale. C’est le validateur de référence pour XRechnung (la CIUS allemande d’EN16931).
Le KoSIT Validator est un moteur de validation générique. Il ne contient pas lui-même les règles XSD ou Schematron : il exécute des scénarios de validation configurés via des fichiers XML. Un scénario définit la séquence de validations à appliquer pour un type de document donné — quel XSD utiliser, quels fichiers Schematron appliquer, dans quel ordre.
Ce découplage entre le moteur et les règles est sa force architecturale : le même validateur peut traiter XRechnung, Factur-X, UBL, ou n’importe quel format XML, à condition d’avoir le scénario correspondant.
Installation et setup
Prérequis : Java 11+ (testé avec Java 17 LTS).
# Télécharger le validateur (remplacer par la version courante)
wget https://github.com/itplr-kosit/validator/releases/download/v1.5.0/validator-1.5.0-distribution.zip
unzip validator-1.5.0-distribution.zip -d kosit-validator
# Télécharger les scénarios de validation
# Pour XRechnung (Allemagne) — les scénarios officiels les mieux maintenus :
wget https://github.com/itplr-kosit/validator-configuration-xrechnung/releases/download/release-2024-06-20/validator-configuration-xrechnung_3.0.2_2024-06-20.zip
unzip validator-configuration-xrechnung_3.0.2_2024-06-20.zip -d scenarios
Pour Factur-X spécifiquement, il n’existe pas de scénario KoSIT officiel maintenu par la FNFE-MPE. Deux options :
- Utiliser les scénarios XRechnung si votre facture est aussi destinée au marché allemand (les règles EN16931 core sont les mêmes, mais les extensions nationales
BR-DE-*s’appliqueront). - Écrire votre propre scénario en pointant vers les XSD CII D22B et les Schematron EN16931 du dépôt CEN, plus les Schematron Factur-X du pack FNFE-MPE.
Un scénario personnalisé Factur-X ressemblerait à :
<!-- scenarios.xml — Configuration KoSIT pour Factur-X EN16931 -->
<scenarios xmlns="http://www.xoev.de/de/validator/framework/1/scenarios">
<scenario>
<name>Factur-X EN16931 Profile (CII D22B)</name>
<match>
/rsm:CrossIndustryInvoice[
rsm:ExchangedDocumentContext/ram:GuidelineSpecifiedDocumentContextParameter/
ram:ID = 'urn:cen.eu:en16931:2017'
]
</match>
<validateWithXmlSchema>
<resource>
<name>CII D22B Schema</name>
<location>xsd/CrossIndustryInvoice_100pD22B.xsd</location>
</resource>
</validateWithXmlSchema>
<validateWithSchematron>
<resource>
<name>EN16931 CII rules</name>
<location>schematron/EN16931-CII-validation.xslt</location>
</resource>
</validateWithSchematron>
<validateWithSchematron>
<resource>
<name>Factur-X EN16931 profile rules</name>
<location>schematron/FACTUR-X_EN16931.xslt</location>
</resource>
</validateWithSchematron>
</scenario>
</scenarios>
Dans cette configuration, les fichiers Schematron sont référencés sous forme de XSLT pré-compilées. Le KoSIT Validator peut aussi être configuré avec des scénarios Schematron directement, selon la version et la configuration utilisées, mais l’approche XSLT pré-compilée (via Saxon ou un outil de compilation Schematron) est la plus courante et la plus performante.
Exécution
# Mode CLI
java -jar kosit-validator/validator-1.5.0.jar \
--scenarios scenarios/scenarios.xml \
--input facture.xml
# Mode daemon (HTTP server pour intégration continue)
java -jar kosit-validator/validator-1.5.0.jar \
--scenarios scenarios/scenarios.xml \
--daemon --port 8080
En mode daemon, le validateur expose un endpoint HTTP qui accepte les fichiers XML en POST et retourne un rapport de validation XML structuré.
# Appel au daemon
curl -X POST http://localhost:8080 \
-H "Content-Type: application/xml" \
-d @facture.xml
Le rapport retourné est un document XML contenant le résultat de chaque étape de validation (XSD, chaque Schematron), avec les assertions échouées, les messages et les niveaux de sévérité.
Intégration Java (embarqué)
Pour une intégration directe dans une application Java :
<!-- pom.xml -->
<dependency>
<groupId>de.kosit</groupId>
<artifactId>validationtool</artifactId>
<version>1.5.0</version>
</dependency>
import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.api.Input;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.api.Result;
import de.kosit.validationtool.impl.DefaultCheck;
import java.net.URI;
import java.nio.file.Path;
// Charger la configuration des scénarios
Configuration config = Configuration.load(
URI.create("file:///path/to/scenarios/scenarios.xml")
).build();
DefaultCheck validator = new DefaultCheck(config);
// Valider un fichier
Input input = InputFactory.read(Path.of("facture.xml"));
Result result = validator.checkInput(input);
System.out.println("Valide: " + result.isAcceptable());
result.getSchematronResult().forEach(sr -> {
sr.getFailedAsserts().forEach(fa -> {
System.out.println(fa.getId() + ": " + fa.getText());
});
});
Bilan KoSIT
| Critère | Évaluation |
|---|---|
| Couverture | XSD + Schematron uniquement (étapes 3-5). Pas de vérification PDF/A-3. |
| Entrée | XML seul — il faut extraire le XML du PDF en amont. |
| Configuration | Scénarios XML puissants mais verbeux. Pas de scénario Factur-X officiel. |
| Maintenance | Les artefacts de validation (XSD, Schematron) doivent être mis à jour manuellement quand le CEN publie une nouvelle version. |
| Langages | Java natif. Accessible depuis d’autres langages via le mode daemon HTTP. |
| Production | Éprouvé en production par l’administration allemande. Robuste, bien testé. |
Le KoSIT Validator est le bon choix si vous êtes déjà dans un écosystème Java, si vous avez besoin de contrôler précisément les règles appliquées, et si la validation PDF/A-3 est gérée séparément. Il demande un investissement initial significatif en configuration.
Option 2 : Mustangproject / ZUV
Ce qu’il fait
Mustangproject (ZUGFeRD/mustangproject) est une bibliothèque Java open source pour créer, lire et valider les factures ZUGFeRD et Factur-X. Son composant de validation, ZUV (ZUGFeRD Validation), couvre un périmètre plus large que le KoSIT Validator : il peut valider à la fois le conteneur PDF/A-3 et le contenu XML.
ZUV intègre en interne :
- verapdf pour la validation PDF/A-3 (ISO 19005-3)
- Des validateurs XSD pour CII
- Des validateurs Schematron pour EN16931 et les profils Factur-X/ZUGFeRD
C’est l’approche “tout-en-un” : un seul outil qui prend un fichier PDF Factur-X en entrée et vérifie l’ensemble du pipeline.
Installation
Prérequis : Java 11+ et Maven ou Gradle.
<!-- pom.xml -->
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>validator</artifactId>
<version>2.15.0</version>
</dependency>
Mustangproject tire transitivement verapdf et ses dépendances. Le classpath résultant est conséquent (plusieurs dizaines de Mo de JARs), car verapdf embarque les profils ICC, les validateurs de polices, et les parsers PDF bas niveau.
Usage en ligne de commande
Mustangproject fournit un JAR exécutable :
# Télécharger le JAR standalone
wget https://github.com/ZUGFeRD/mustangproject/releases/download/v2.15.0/Mustang-CLI-2.15.0.jar
# Valider un fichier Factur-X complet (PDF + XML)
java -jar Mustang-CLI-2.15.0.jar --action validate --source facture.pdf
Le rapport de sortie indique les résultats pour chaque couche : PDF/A-3, XML structure, Schematron.
Usage programmatique
import org.mustangproject.validator.ZUGFeRDValidator;
ZUGFeRDValidator validator = new ZUGFeRDValidator();
validator.validate("facture.pdf");
// Résultats
System.out.println("PDF/A valide: " + validator.isPDFAValid());
System.out.println("XML valide: " + validator.isXMLValid());
System.out.println("Profil détecté: " + validator.getProfile());
// Erreurs détaillées
for (String error : validator.getValidationErrors()) {
System.out.println("Erreur: " + error);
}
Bilan Mustangproject/ZUV
| Critère | Évaluation |
|---|---|
| Couverture | Pipeline complet : PDF/A-3 (via verapdf) + XSD + Schematron (étapes 1-5). |
| Entrée | PDF Factur-X directement — pas besoin d’extraire le XML manuellement. |
| Configuration | Minimale. Les profils sont détectés automatiquement depuis le XML. |
| Maintenance | Les artefacts sont embarqués dans le JAR. Mise à jour = nouvelle version de Mustangproject. Délai entre la publication CEN et l’intégration dans Mustangproject. |
| Langages | Java uniquement (pas de mode daemon HTTP natif). |
| Poids | Lourd en dépendances (~60+ Mo avec verapdf et les profils ICC). |
Mustangproject est le bon choix si vous voulez une solution Java clé en main, si vous n’avez pas besoin de personnaliser finement les scénarios de validation, et si le poids des dépendances n’est pas un problème. C’est la solution la plus rapide à mettre en place pour un développeur Java.
Option 3 : Pipeline assemblé manuellement
Le principe
Plutôt que d’utiliser un outil monolithique, vous pouvez assembler votre propre pipeline en combinant des outils spécialisés pour chaque étape. Cette approche offre le plus de flexibilité et permet de choisir le langage et les outils pour chaque couche.
Composants
PDF/A-3 : verapdf
verapdf est l’implémentation de référence pour la validation PDF/A, développée par le PDF Association et la Digital Preservation Coalition. C’est le validateur que la plupart des autres outils (dont Mustangproject) intègrent en interne.
# Installation via le script officiel (Linux/macOS)
wget https://software.verapdf.org/releases/1.26/verapdf-installer.zip
unzip verapdf-installer.zip
./verapdf-install/verapdf-install
# Validation PDF/A-3
verapdf --flavour 3b facture.pdf
verapdf est un outil Java, mais il s’utilise en ligne de commande et retourne du XML ou du JSON. Il est donc intégrable depuis n’importe quel langage via un appel subprocess.
Extraction XML : bibliothèque PDF au choix
# Python avec pypdf
from pypdf import PdfReader
reader = PdfReader("facture.pdf")
for name, data_list in reader.attachments.items():
if name.lower() in ("factur-x.xml", "zugferd-invoice.xml"):
xml_content = data_list[0]
break
// Node.js avec pdf-lib
import { PDFDocument } from 'pdf-lib';
import { readFileSync } from 'fs';
const pdfBytes = readFileSync('facture.pdf');
const pdfDoc = await PDFDocument.load(pdfBytes);
const rawAttachments = pdfDoc.catalog.lookup(
pdfDoc.catalog.get(PDFName.of('Names'))
);
// Extraction selon la structure Names/EmbeddedFiles
XSD : lxml (Python) ou Saxon (Java/CLI)
# Python avec lxml
from lxml import etree
xsd_tree = etree.parse("CrossIndustryInvoice_100pD22B.xsd")
xsd_schema = etree.XMLSchema(xsd_tree)
xml_tree = etree.parse("factur-x.xml")
is_valid = xsd_schema.validate(xml_tree)
if not is_valid:
for error in xsd_schema.error_log:
print(f"Ligne {error.line}: {error.message}")
Schematron : Saxon + compilation XSLT
La validation Schematron est un processus en deux étapes : d’abord compiler le fichier .sch en XSLT, puis appliquer cette XSLT au document à valider. Saxon est le processeur XSLT le plus utilisé pour cette tâche.
# Étape 1 : Compiler le Schematron en XSLT (une seule fois)
# Utilise les feuilles de style ISO Schematron (schematron.com)
java -jar saxon-he.jar \
-s:EN16931-CII-validation.sch \
-xsl:iso_svrl_for_xslt2.xsl \
-o:EN16931-CII-validation.xslt
# Étape 2 : Valider le document
java -jar saxon-he.jar \
-s:factur-x.xml \
-xsl:EN16931-CII-validation.xslt \
-o:rapport-svrl.xml
Le résultat est un rapport SVRL (Schematron Validation Report Language) contenant les assertions échouées :
<svrl:failed-assert test="..." location="/rsm:CrossIndustryInvoice/..." id="BR-CO-14">
<svrl:text>[BR-CO-14] Invoice total VAT amount (BT-110) = ...</svrl:text>
</svrl:failed-assert>
En Python, vous pouvez aussi utiliser la bibliothèque lxml qui supporte Schematron nativement (avec certaines limitations par rapport à Saxon pour les Schematron complexes utilisant XSLT 2.0) :
from lxml import etree
from lxml.isoschematron import Schematron
# Note : lxml supporte Schematron basé sur XSLT 1.0.
# Les Schematron EN16931 utilisent des fonctions XPath 2.0 (ex: matches(), tokenize())
# qui nécessitent Saxon. Vérifier la compatibilité avant d'utiliser lxml.
schematron_tree = etree.parse("EN16931-CII-validation.sch")
schematron = Schematron(schematron_tree)
xml_tree = etree.parse("factur-x.xml")
is_valid = schematron.validate(xml_tree)
if not is_valid:
report = schematron.validation_report
print(etree.tostring(report, pretty_print=True).decode())
Pour plus de détails sur l’intégration Python, voir Python et Factur-X : générer et valider.
Gestion des artefacts de validation
Le point critique de cette approche est la maintenance des artefacts. Voici les sources canoniques :
| Artefact | Source | Fréquence de mise à jour |
|---|---|---|
| XSD CII D22B | Pack conformité FNFE-MPE ou UN/CEFACT | Rarement (stabilisé) |
| Schematron EN16931 | ConnectingEurope/eInvoicing-EN16931 | ~2-3 fois/an (correctifs, nouvelles versions) |
| Schematron Factur-X profils | Pack conformité FNFE-MPE | À chaque version Factur-X |
| Profils verapdf | Distribution verapdf | Suit les mises à jour ISO |
Chaque mise à jour CEN peut modifier le comportement des règles BR-*. Un artefact obsolète peut accepter des factures que la PDP rejettera, ou l’inverse. Vous devez surveiller ces dépôts et mettre à jour vos artefacts régulièrement.
Bilan pipeline custom
| Critère | Évaluation |
|---|---|
| Couverture | Complète si tous les composants sont assemblés (étapes 1-5). |
| Entrée | Flexible — chaque composant peut être invoqué séparément. |
| Configuration | Totale. Vous contrôlez chaque étape. |
| Maintenance | Charge maximale : surveiller 4+ dépôts, recompiler les Schematron, tester les régressions. |
| Langages | Agnostique. Python, Node, Go, Ruby — chaque composant est appelable en subprocess. |
| Complexité | Élevée. Chaque composant a ses propres conventions d’erreur, formats de rapport, et modes de fonctionnement. |
Le pipeline custom est le bon choix si vous avez des contraintes de langage fortes (pas de Java), si vous avez besoin d’un contrôle total sur chaque étape, ou si vous intégrez la validation dans un système existant avec des exigences spécifiques. Le coût est la charge de maintenance continue.
Option 4 : API SaaS (FacturX API)
Le principe
Plutôt que de déployer et maintenir l’infrastructure de validation, vous déléguez l’exécution du pipeline complet à un service HTTP. Vous envoyez un fichier PDF Factur-X, vous recevez un rapport de validation structuré.
Comment ça fonctionne
# Validation d'un fichier Factur-X
curl -X POST https://facturxapi.com/api/v1/validate \
-H "X-API-Key: YOUR_API_KEY" \
-F "[email protected]"
Réponse :
{
"valid": false,
"pdf_a3": { "valid": true, "errors": [] },
"xml_extraction": { "valid": true, "filename": "factur-x.xml" },
"xsd": { "valid": true, "errors": [] },
"schematron": {
"valid": false,
"errors": [
{
"id": "BR-CO-14",
"severity": "fatal",
"message": "[BR-CO-14] Invoice total VAT amount...",
"location": "/rsm:CrossIndustryInvoice/..."
}
]
},
"profile": "EN16931"
}
Le rapport détaille chaque étape du pipeline séparément. Si le PDF/A-3 échoue, vous le savez indépendamment des résultats XML. Chaque erreur Schematron inclut le code BR-*, le message, la sévérité et le chemin XPath.
Intégration par langage
Python :
import httpx
def validate_facturx(pdf_path: str, api_key: str) -> dict:
with open(pdf_path, "rb") as f:
response = httpx.post(
"https://facturxapi.com/api/v1/validate",
headers={"X-API-Key": api_key},
files={"file": f},
timeout=30,
)
response.raise_for_status()
return response.json()
result = validate_facturx("facture.pdf", "sk_live_...")
if not result["valid"]:
for step in ("pdf_a3", "xsd", "schematron"):
for error in result[step]["errors"]:
print(f"[{step}] {error['id']}: {error['message']}")
Node.js :
import { readFileSync } from 'fs';
async function validateFacturX(pdfPath, apiKey) {
const formData = new FormData();
formData.append('file', new Blob([readFileSync(pdfPath)]), 'facture.pdf');
const response = await fetch('https://facturxapi.com/api/v1/validate', {
method: 'POST',
headers: { 'X-API-Key': apiKey },
body: formData,
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
PHP :
function validateFacturX(string $pdfPath, string $apiKey): array {
$ch = curl_init('https://facturxapi.com/api/v1/validate');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $apiKey"],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($pdfPath, 'application/pdf', 'facture.pdf'),
],
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new RuntimeException("Validation failed: HTTP $httpCode");
}
return json_decode($response, true);
}
Bilan API SaaS
| Critère | Évaluation |
|---|---|
| Couverture | Pipeline complet (étapes 1-5). Artefacts toujours à jour. |
| Entrée | PDF Factur-X directement via HTTP POST. |
| Configuration | Aucune. Obtenir une clé API, envoyer le fichier. |
| Maintenance | Zéro côté client. Les artefacts de validation sont mis à jour côté serveur. |
| Langages | Tout langage capable de faire un HTTP POST (tous). |
| Latence | Dépend du réseau. Quelques secondes par fichier. |
| Disponibilité | Dépend du service. Pas de validation possible si le service est down ou si le réseau est coupé. |
| Coût | Gratuit pour les volumes faibles. Tarification à l’usage pour les volumes de production. |
L’API SaaS est le bon choix si vous ne voulez pas gérer d’infrastructure de validation, si vous travaillez dans un langage non-Java, si vous avez besoin d’intégrer la validation en CI/CD rapidement, ou si le volume de factures ne justifie pas le déploiement d’un serveur dédié.
Matrice de comparaison
| Critère | KoSIT Validator | Mustangproject/ZUV | Pipeline custom | API SaaS |
|---|---|---|---|---|
| PDF/A-3 | Non | Oui (verapdf) | Oui (verapdf) | Oui |
| XSD | Oui | Oui | Oui (lxml/Saxon) | Oui |
| Schematron EN16931 | Oui | Oui | Oui (Saxon) | Oui |
| Schematron profils | Via scénarios | Oui | Via config | Oui |
| Langage requis | Java 11+ | Java 11+ | Au choix | Aucun (HTTP) |
| Entrée | XML seul | PDF complet | Au choix | PDF complet |
| Setup initial | Élevé (scénarios) | Faible (1 dépendance) | Élevé (assemblage) | Minimal (clé API) |
| Maintenance artefacts | Manuelle | Via version lib | Manuelle | Automatique |
| Mode daemon/HTTP | Oui (natif) | Non (à développer) | Non (à développer) | Natif |
| Personnalisation règles | Totale | Limitée | Totale | Non |
| Coût | Gratuit + infra | Gratuit + infra | Gratuit + infra + temps | Gratuit / payant selon volume |
| Offline | Oui | Oui | Oui | Non |
Framework de décision
Le choix dépend de trois variables principales : votre stack technique, votre volume de validation, et votre capacité de maintenance.
Vous êtes en Java et vous voulez du clé en main
Mustangproject/ZUV. Une dépendance Maven, un appel de méthode. La couverture est complète (PDF/A-3 inclus). Le compromis : vous êtes lié au rythme de publication de Mustangproject pour les mises à jour d’artefacts, et le poids des dépendances est significatif.
Vous êtes en Java et vous avez besoin de contrôle fin sur les règles
KoSIT Validator. Les scénarios XML permettent de composer exactement le jeu de règles que vous voulez : EN16931 core uniquement, avec extensions nationales françaises, avec règles custom. Le compromis : pas de validation PDF/A-3, configuration initiale conséquente, et pas de scénario Factur-X officiel à utiliser directement.
Vous n’êtes pas en Java
API SaaS ou Pipeline custom. La question se résume à : voulez-vous gérer l’infrastructure ?
Si non : API SaaS. Un POST HTTP depuis n’importe quel langage, résultats structurés, aucune maintenance.
Si oui (contraintes de sécurité, air-gapped, volume très élevé) : pipeline custom avec verapdf en subprocess pour le PDF/A-3, lxml ou Saxon pour le XSD, et Saxon pour le Schematron. Prévoyez un mécanisme de mise à jour des artefacts CEN.
Vous faites de la CI/CD
API SaaS comme premier choix. Un curl dans un job GitHub Actions ou GitLab CI suffit. Pas de JDK à installer dans l’image de build, pas d’artefacts à cacher.
Si la latence réseau est un problème ou si vous avez besoin de valider offline dans la CI, un conteneur Docker avec Mustangproject ou le KoSIT Validator en mode daemon est l’alternative.
Vous traitez un très gros volume (>10 000 factures/jour)
À ce volume, la latence réseau et le coût par appel d’une API SaaS peuvent devenir significatifs. Mustangproject ou un pipeline custom déployé localement sera plus économique. Le KoSIT Validator en mode daemon, derrière un load balancer, encaisse bien la charge.
Vous avez besoin de validation offline / air-gapped
API SaaS exclue par définition. KoSIT ou Mustangproject sont vos seules options sans connexion réseau.
Patterns d’intégration CI/CD
GitHub Actions avec API SaaS
# .github/workflows/validate-facturx.yml
name: Validate Factur-X templates
on:
push:
paths:
- 'templates/**'
- 'src/invoice/**'
pull_request:
paths:
- 'templates/**'
- 'src/invoice/**'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate test invoices
run: |
# Votre script de génération de factures de test
python scripts/generate_test_invoices.py
- name: Validate all generated invoices
run: |
FAILED=0
for pdf in output/*.pdf; do
RESULT=$(curl -s -w "\n%{http_code}" -X POST \
"${{ vars.FACTURX_API_URL }}/validate" \
-H "X-API-Key: ${{ secrets.FACTURX_API_KEY }}" \
-F "file=@$pdf")
HTTP_CODE=$(echo "$RESULT" | tail -1)
BODY=$(echo "$RESULT" | head -n -1)
if [ "$HTTP_CODE" != "200" ]; then
echo "::error::HTTP $HTTP_CODE for $pdf"
FAILED=1
continue
fi
VALID=$(echo "$BODY" | jq -r '.valid')
if [ "$VALID" != "true" ]; then
echo "::error::INVALID: $pdf"
echo "$BODY" | jq '.schematron.errors[] | " \(.id): \(.message)"' -r
FAILED=1
else
echo "::notice::VALID: $pdf"
fi
done
exit $FAILED
GitLab CI avec API SaaS
# .gitlab-ci.yml
validate-invoices:
stage: test
image: curlimages/curl:latest
script:
- |
FAILED=0
for pdf in output/*.pdf; do
RESULT=$(curl -s -X POST "$FACTURX_API_URL/validate" \
-H "X-API-Key: $FACTURX_API_KEY" \
-F "file=@$pdf")
VALID=$(echo "$RESULT" | jq -r '.valid')
if [ "$VALID" != "true" ]; then
echo "FAIL: $pdf"
echo "$RESULT" | jq '.schematron.errors'
FAILED=1
else
echo "PASS: $pdf"
fi
done
exit $FAILED
variables:
FACTURX_API_URL: https://facturxapi.com/api/v1
FACTURX_API_KEY: $FACTURX_API_KEY
rules:
- changes:
- templates/**/*
- src/invoice/**/*
GitHub Actions avec KoSIT Validator (offline)
# .github/workflows/validate-kosit.yml
name: Validate with KoSIT (offline)
on: [push]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: Cache KoSIT validator and scenarios
uses: actions/cache@v4
with:
path: |
kosit-validator/
scenarios/
key: kosit-v1.5.0-scenarios-2024-06
- name: Download KoSIT validator
run: |
if [ ! -d "kosit-validator" ]; then
wget -q https://github.com/itplr-kosit/validator/releases/download/v1.5.0/validator-1.5.0-distribution.zip
unzip -q validator-1.5.0-distribution.zip -d kosit-validator
fi
- name: Validate XML files
run: |
FAILED=0
for xml in output/*.xml; do
java -jar kosit-validator/validator-1.5.0.jar \
--scenarios scenarios/scenarios.xml \
--input "$xml" \
--output reports/
# Analyser le rapport XML de sortie
ACCEPTABLE=$(xmllint --xpath "string(//rep:assessment/rep:accept)" \
"reports/$(basename $xml .xml)-report.xml" 2>/dev/null)
if [ "$ACCEPTABLE" != "true" ]; then
echo "FAIL: $xml"
FAILED=1
else
echo "PASS: $xml"
fi
done
exit $FAILED
Docker Compose avec Mustangproject (self-hosted)
Pour une validation locale permanente, vous pouvez encapsuler Mustangproject dans un service HTTP minimal :
# Dockerfile.validator
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY Mustang-CLI-2.15.0.jar validator.jar
COPY validator-server.sh /app/
RUN chmod +x /app/validator-server.sh
EXPOSE 8080
CMD ["java", "-jar", "validator.jar", "--action", "validate"]
# docker-compose.yml
services:
facturx-validator:
build:
context: .
dockerfile: Dockerfile.validator
ports:
- "8080:8080"
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
Ce setup nécessite d’écrire un wrapper HTTP mince autour de la CLI Mustangproject (un simple servlet Java ou un micro-service Spring Boot qui appelle ZUGFeRDValidator en interne).
Ce que le pipeline custom ne vous dit pas
Quelle que soit l’option choisie, quelques réalités pratiques méritent d’être anticipées.
Les Schematron EN16931 évoluent. Le dépôt CEN ConnectingEurope/eInvoicing-EN16931 publie des mises à jour correctives régulièrement. Une règle BR-* peut changer de sévérité (warning vers fatal), un pattern XPath peut être corrigé. Si vos artefacts sont figés, votre validateur diverge silencieusement de ce que les PDP appliquent.
verapdf est gourmand en mémoire. La validation PDF/A-3 d’un fichier volumineux (factures avec pièces jointes lourdes) peut consommer plusieurs centaines de Mo de heap Java. En CI/CD, prévoyez un runner avec suffisamment de RAM si vous utilisez verapdf en local.
Le Schematron EN16931 pour CII utilise XPath 2.0. Des fonctions comme matches(), tokenize(), string-join() ne sont pas supportées par les processeurs XSLT 1.0. Si vous utilisez lxml en Python (qui ne supporte que XSLT 1.0), la compilation ou l’exécution des Schematron EN16931 échouera silencieusement ou produira des faux positifs. Saxon (Java) ou un processeur XSLT 2.0+ est requis pour une validation Schematron fiable.
Le test “valide” d’aujourd’hui peut devenir “invalide” demain. Une PDP qui met à jour ses artefacts de validation peut rejeter une facture qui passait la semaine précédente. La seule parade : valider en continu, pas uniquement au moment du développement.
Ressources
- Facturation électronique 2026 : le guide technique complet — le contexte réglementaire et technique complet
- Valider EN16931/Factur-X : XSD vs Schematron, erreurs BR-* — le détail des deux niveaux de validation et le workflow de debug
- PDF/A-3 pour Factur-X : checklist de conformité — tout sur le conteneur PDF/A-3 et les pièges courants
- Catalogue des erreurs BR-* — référence des erreurs Schematron les plus fréquentes
- Python et Factur-X : générer et valider — intégration Python avec lxml, factur-x library, et API
Chaque approche a un coût réel : soit en temps d’infrastructure (KoSIT, Mustangproject, pipeline custom), soit en dépendance réseau (API SaaS). Le choix n’est pas technique — il est organisationnel. Qui, dans votre équipe, maintiendra les artefacts de validation à jour quand le CEN publiera une correction ? Si la réponse est “personne”, une API gérée est probablement la voie la plus fiable. Si la réponse est “notre équipe infra Java”, KoSIT ou Mustangproject feront le travail.