amazscript / laravel-einvoicing
French e-invoicing for Laravel: receive and send invoices through an accredited platform (Plateforme Agréée).
Requires
- php: ^8.3
- guzzlehttp/guzzle: ^7.8|^8.0
- illuminate/contracts: ^12.0|^13.0
- illuminate/database: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/queue: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
- phpstan/phpstan: ^2.0
README
Recevoir et émettre les factures électroniques françaises dans une application Laravel, via une Plateforme Agréée.
Prérequis : un compte Iopole. C'est aujourd'hui le seul driver disponible. Si votre Plateforme Agréée est une autre, ce package ne vous servira pas en l'état — voir écrire un driver.
Maturité. Version
0.2.0: le package est fonctionnel et couvert par 318 tests, mais il n'a pas encore d'usage en production connu. Il a été vérifié contre une sandbox réelle, pas contre des flux de facturation d'entreprise. L'API publique peut évoluer d'ici la1.0.Il est publié avant l'échéance du 1er septembre 2026 parce qu'un package disponible et perfectible sert mieux qu'un package parfait et absent. Les remontées d'usage sont les bienvenues.
Le problème
Depuis le 1er septembre 2026, toute entreprise assujettie à la TVA doit être capable de recevoir des factures électroniques. Le PPF n'étant plus une plateforme d'échange, tout flux transite par une Plateforme Agréée.
Se raccorder suppose d'écrire la plomberie : un webhook signé en HMAC dont le calcul diffère selon le type de contenu, le routage vers le bon dossier client alors qu'une seule URL de rappel dessert tout le parc, la déduplication de livraisons répétées par conception, le stockage des documents.
Ce package écrit cette plomberie une fois pour toutes.
Installation
composer require amazscript/laravel-einvoicing php artisan einvoicing:install php artisan migrate php artisan einvoicing:secret
Puis, dans .env, les identifiants fournis par votre Plateforme Agréée :
IOPOLE_BASE_URL=https://api.ppd.iopole.fr IOPOLE_TOKEN_URL=https://auth.preprod.iopole.fr/realms/iopole/protocol/openid-connect/token IOPOLE_CLIENT_ID= IOPOLE_CLIENT_SECRET= IOPOLE_CUSTOMER_ID= EINVOICING_WEBHOOK_SECRET=
Le traitement passe par une file dédiée. Sans worker qui l'écoute, tout paraît sain — la route répond, les livraisons sont enregistrées — et aucune facture n'est traitée :
php artisan queue:work --queue=einvoicing
Enfin, vérifiez le raccordement :
php artisan einvoicing:doctor
Recevoir une facture
namespace App\Listeners; use AmazScript\Einvoicing\Events\InboundInvoiceReceived; final class EnregistrerFactureFournisseur { public function handle(InboundInvoiceReceived $event): void { $facture = $event->invoice; Achat::create([ 'fournisseur' => $facture->sender_name, 'siren' => $facture->sender_siren, 'numero' => $facture->invoice_number, 'date' => $facture->invoice_date, 'montant_ttc' => $facture->amount_total, 'montant_tva' => $facture->amount_tax, 'devise' => $facture->currency, ]); } }
C'est tout. Le webhook, la vérification de signature, le routage multi-tenant, la déduplication et le téléchargement des documents ont déjà eu lieu.
Les montants sont des chaînes, pas des flottants : un centime perdu dans un arrondi binaire est une
écriture fausse. Pour les calculer, employez bcsub() ou une bibliothèque décimale.
Consulter les factures
use AmazScript\Einvoicing\Facades\Einvoicing; // Ce que le package détient Einvoicing::for($tenant)->invoices()->local()->get(); // Ce que la plateforme n'a pas vu acquitté Einvoicing::for($tenant)->invoices()->remoteNotSeen(); // Recherche, parcourue paresseusement Einvoicing::for($tenant)->invoices() ->search(['invoice.direction' => 'INBOUND', 'invoice.state' => 'NOT_DELIVERED']) ->take(20); // Une facture précise Einvoicing::for($tenant)->invoice($id)->markAsSeen(); Einvoicing::for($tenant)->invoice($id)->readablePdf(); Einvoicing::for($tenant)->invoice($id)->attachments();
Events
| Event | Déclencheur |
|---|---|
InboundInvoiceReceived |
une facture fournisseur est arrivée et a été consignée |
InvoiceStatusUpdated |
un statut de cycle de vie a été reçu |
InboundInvoiceInvalid |
une facture entrante a été refusée par la plateforme |
OutboundInvoiceSent |
la plateforme a pris une facture émise et l'a nommée |
OutboundInvoiceFailed |
elle l'a refusée d'emblée — rien n'est parti |
OutboundInvoiceNotDelivered |
une facture émise n'a pas atteint son destinataire |
TenantResolutionFailed |
aucun dossier ne correspond au destinataire — à surveiller |
WebhookSignatureRejected |
signature invalide — à surveiller de près |
Ce que le package ne fait pas
- Il ne génère aucun format de facture : ni Factur-X, ni UBL, ni CII, ni PDF/A-3. À l'émission, il transporte un document que votre application a déjà produit.
- Il n'exécute aucune validation Schematron.
- Il ne remplace pas un compte chez une Plateforme Agréée : il en consomme l'API.
- Il ne parle qu'à Iopole. Les huit contrats internes sont neutres et un second driver est prévu, mais aucun n'est écrit à ce jour : rien ne serait vérifiable sans un compte pour l'éprouver. Voir écrire un driver si vous en avez un.
- Il ne conduit pas le rattachement d'une entreprise à votre compte (KYB).
Le package est un Opérateur de Dématérialisation. Il ne certifie rien et n'apporte aucune garantie de conformité : seule la Plateforme Agréée est agréée.
Configuration
Le fichier config/einvoicing.php couvre le driver, le webhook, le stockage, la file d'attente, la
rétention des événements et le résolveur de tenant. Voir la
documentation de configuration.
Commandes
| Commande | Rôle |
|---|---|
einvoicing:doctor |
diagnostique la configuration et le raccordement |
einvoicing:install |
publie la configuration et les migrations |
einvoicing:secret |
génère un secret HMAC |
einvoicing:poll |
récupère ce qu'un webhook aurait manqué |
einvoicing:webhooks:sync |
compare la déclaration du webhook à la configuration locale |
einvoicing:retry:sync |
affiche la stratégie de relance de la plateforme |
einvoicing:events:prune |
purge les événements déjà traités |
einvoicing:events:retry |
rejoue les événements non routés ou en échec |
Sécurité
Le secret du webhook doit faire au moins 32 octets et n'apparaître que dans le .env. Une signature
invalide, un horodatage hors tolérance ou un secret absent font répondre 401 sans rien écrire.
Le customer-id est chiffré au repos. Ni les jetons, ni les secrets, ni les identifiants d'entreprise
n'apparaissent dans les messages d'erreur ou les journaux.
Pour signaler une vulnérabilité : contact@amazscript.com.
Documentation
Installation · Configuration · Webhooks · Multi-tenant · Events · Commandes · Entreprises · Émission · Réponses · E-reporting · Drivers · Dépannage
Licence
MIT. Voir LICENSE.
Support commercial et accompagnement à l'intégration : contact@amazscript.com.