Guide d'intégration · PHP

Générer un Factur-X en PHP

Beaucoup d'applications de facturation françaises tournent en PHP, et produisent aujourd'hui un PDF classique via une bibliothèque de rendu. Passer à Factur-X ne consiste pas à changer de gabarit : il faut produire un PDF/A-3 qui embarque un XML CII valide au profil EN 16931. Voici la voie la plus courte, en cURL, sans dépendance Composer.

Ce qui ne suffit pas

Trois raccourcis souvent tentés, et pourquoi ils échouent :

RaccourciPourquoi ça ne passe pas
Joindre le XML au PDF avec une bibliothèque de rendu classiqueLe fichier obtenu est un PDF ordinaire, pas un PDF/A-3 : l'archivage et la déclaration de la pièce jointe ne sont pas conformes
Écrire le CII « à la main » avec du templatingLe XSD impose un ordre d'éléments et des cardinalités stricts ; le moindre écart est un rejet
S'arrêter au XSDLes règles métier (totaux, ventilation de TVA, mentions légales) relèvent du Schematron, qui exige un moteur XSLT 2.0+ — hors de portée de l'extension XSL de PHP

Générer une facture

Seule l'extension ext-curl est requise, présente par défaut sur la plupart des distributions PHP.

<?php

const CONFACTURA_API = 'https://confactura.fr';
const CONFACTURA_KEY = 'df_live_…';   // POST /v1/signup pour en obtenir une

function confacturaGenerate(array $invoice): string
{
    $ch = curl_init(CONFACTURA_API . '/v1/invoices/facturx');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            'X-API-Key: ' . CONFACTURA_KEY,
        ],
        CURLOPT_POSTFIELDS => json_encode($invoice, JSON_UNESCAPED_UNICODE),
    ]);
    $body   = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($status === 422) {
        // Facture non conforme : rapport détaillé, aucun PDF produit.
        $report = json_decode($body, true);
        throw new DomainException(json_encode($report['errors'], JSON_UNESCAPED_UNICODE));
    }
    if ($status >= 400) {
        throw new RuntimeException("Confactura API error {$status}: {$body}");
    }
    return $body;   // PDF/A-3b brut, XML CII embarqué
}

$invoice = [
    'number'     => '2026-0042',
    'issue_date' => '2026-09-01',
    'due_date'   => '2026-09-30',
    'seller' => [
        'name'              => 'Ma Société',
        'address_line'      => '1 rue X',
        'postcode'          => '75001',
        'city'              => 'Paris',
        'siren'             => '123456789',
        'vat_id'            => 'FR32123456789',
        'electronic_address'=> '[email protected]',
    ],
    'buyer' => [
        'name'              => 'Client SARL',
        'address_line'      => '2 av. Y',
        'postcode'          => '69001',
        'city'              => 'Lyon',
        'electronic_address'=> '[email protected]',
    ],
    'lines' => [
        ['description' => 'Presta dev', 'quantity' => 5, 'unit_price' => '450.00'],
    ],
];

file_put_contents('facture-2026-0042.pdf', confacturaGenerate($invoice));

Le 422 est une réponse métier, pas une panne

Une facture qui ne passe pas XSD + Schematron n'est jamais livrée. Traitez le 422 comme un cas normal — à remonter à l'utilisateur qui a saisi la facture — et non comme une erreur serveur à retenter. Le corps de la réponse liste les règles en échec, une par une.

Les montants en chaînes, pas en float

'unit_price' => '450.00' et non 450.00 : la représentation flottante fait dériver les totaux au centime, et le Schematron vérifie l'égalité exacte entre la somme des lignes et le total du document (BR-CO-10). C'est la première cause de rejet en PHP comme ailleurs.

Les champs que la réforme française rend obligatoires

Les mentions légales 2026 sont ajoutées côté serveur : voir BR-FR-05.

Vérifier le résultat

Déposez le PDF produit sur le validateur Factur-X en ligne, ou automatisez-le — /v1/validate est gratuit, anonyme et ne consomme aucun quota :

<?php

$ch = curl_init(CONFACTURA_API . '/v1/validate');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POSTFIELDS     => [
        'file' => new CURLFile('facture-2026-0042.pdf', 'application/pdf'),
    ],
]);
$report = json_decode(curl_exec($ch), true);
curl_close($ch);

var_dump($report['valid'], $report['errors']);

Essayer maintenant

20 documents par mois gratuits, sans carte bancaire. La clé API est créée en une requête.

Obtenir ma clé API Valider un PDF existant