Technique

Déployer un validateur Factur-X : KoSIT vs Mustangproject vs API SaaS (checklist technique)

17 min de lecture Par FacturX API

Comparatif technique pour déployer un validateur Factur-X : KoSIT Validator, Mustangproject/ZUV, pipeline custom, API SaaS. Installation, code, CI/CD, matrice de décision.

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 :

  1. 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).
  2. É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
CouvertureXSD + Schematron uniquement (étapes 3-5). Pas de vérification PDF/A-3.
EntréeXML seul — il faut extraire le XML du PDF en amont.
ConfigurationScénarios XML puissants mais verbeux. Pas de scénario Factur-X officiel.
MaintenanceLes artefacts de validation (XSD, Schematron) doivent être mis à jour manuellement quand le CEN publie une nouvelle version.
LangagesJava 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
CouverturePipeline complet : PDF/A-3 (via verapdf) + XSD + Schematron (étapes 1-5).
EntréePDF Factur-X directement — pas besoin d’extraire le XML manuellement.
ConfigurationMinimale. Les profils sont détectés automatiquement depuis le XML.
MaintenanceLes 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.
LangagesJava uniquement (pas de mode daemon HTTP natif).
PoidsLourd 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 :

ArtefactSourceFréquence de mise à jour
XSD CII D22BPack conformité FNFE-MPE ou UN/CEFACTRarement (stabilisé)
Schematron EN16931ConnectingEurope/eInvoicing-EN16931~2-3 fois/an (correctifs, nouvelles versions)
Schematron Factur-X profilsPack conformité FNFE-MPEÀ chaque version Factur-X
Profils verapdfDistribution verapdfSuit 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
CouvertureComplète si tous les composants sont assemblés (étapes 1-5).
EntréeFlexible — chaque composant peut être invoqué séparément.
ConfigurationTotale. Vous contrôlez chaque étape.
MaintenanceCharge maximale : surveiller 4+ dépôts, recompiler les Schematron, tester les régressions.
LangagesAgnostique. 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
CouverturePipeline complet (étapes 1-5). Artefacts toujours à jour.
EntréePDF Factur-X directement via HTTP POST.
ConfigurationAucune. Obtenir une clé API, envoyer le fichier.
MaintenanceZéro côté client. Les artefacts de validation sont mis à jour côté serveur.
LangagesTout langage capable de faire un HTTP POST (tous).
LatenceDé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ûtGratuit 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èreKoSIT ValidatorMustangproject/ZUVPipeline customAPI SaaS
PDF/A-3NonOui (verapdf)Oui (verapdf)Oui
XSDOuiOuiOui (lxml/Saxon)Oui
Schematron EN16931OuiOuiOui (Saxon)Oui
Schematron profilsVia scénariosOuiVia configOui
Langage requisJava 11+Java 11+Au choixAucun (HTTP)
EntréeXML seulPDF completAu choixPDF complet
Setup initialÉlevé (scénarios)Faible (1 dépendance)Élevé (assemblage)Minimal (clé API)
Maintenance artefactsManuelleVia version libManuelleAutomatique
Mode daemon/HTTPOui (natif)Non (à développer)Non (à développer)Natif
Personnalisation règlesTotaleLimitéeTotaleNon
CoûtGratuit + infraGratuit + infraGratuit + infra + tempsGratuit / payant selon volume
OfflineOuiOuiOuiNon

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

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.

#validation #kosit #mustangproject #verapdf #factur-x #en16931 #ci-cd