Architecture d'intégration idéale : 5 scénarios
Une intégration de facturation électronique se juge sur l'ordre des opérations, pas sur le nombre d'endpoints appelés. La TVA et le montant à encaisser doivent être arrêtés avant qu'un centime soit prélevé ; la facture doit naître dans l'état où elle est vraiment, réglée ou non ; la transmission à la Plateforme Agréée et le e-reporting suivent, sans que votre code ait à en décider. Cet article décrit l'architecture qui respecte cet ordre, puis la déroule sur cinq scénarios courants avec les appels API dans l'ordre exact.
Trois principes qui structurent tout le reste
1. La décision fiscale précède l'encaissement
POST /v1/tax-decisions reçoit l'opération commerciale, client, lignes, date d'effet, mode de prix, et renvoie une décision immuable : la TVA ligne par ligne, le montant exact à encaisser (amountToCharge), le canal de facturation (invoiceChannel) et les obligations de e-reporting. Votre code ne recalcule jamais la TVA. Une clé d'idempotence métier est obligatoire sur cette ressource : la même clé avec le même corps renvoie la même décision, la même clé avec un corps différent est refusée.
2. La facture naît dans son vrai état
POST /v1/invoices crée un brouillon adossé à la décision (taxDecisionId et decisionLines) ; POST /v1/invoices/{id}/finalize attribue le numéro et fige l'original Factur-X. Si l'encaissement est déjà connu, la finalisation le porte dans son corps payment : numérotation et encaissement sont appliqués dans la même transaction, et l'original est rendu sur une facture réglée. Si l'encaissement viendra plus tard, POST /v1/invoices/{id}/payments l'enregistrera, et la copie PDF de présentation suivra.
3. Trois axes de statut, indépendants
Une facture porte trois statuts qui ne se mélangent pas : documentStatus (brouillon, finalisée…), transmissionStatus (dépôt, transmission, réception, approbation ou refus par la plateforme du client, ou not_applicable hors canal e-invoicing) et paymentStatus (impayée, partiellement payée, payée). Une facture réglée peut encore être en cours de transmission ; une facture approuvée peut être impayée. Vos écrans et vos règles métier lisent l'axe qui les concerne.
Scénario 1 : abonnement SaaS payé avant la facture
Le cas le plus fréquent en ligne : le client règle au moment de la commande, la facture est produite ensuite. Le montant débité doit être celui de la décision, et la facture doit sortir « PAYÉE » dès l'émission.
- Décider.
POST /v1/tax-decisionsavec la ligne d'abonnement, la catégorie de service et la date d'effet. La décision renvoieamountToCharge. - Encaisser. Prélevez exactement ce montant, par votre prestataire ou par virement, en plaçant l'identifiant de décision dans la référence du paiement.
- Vérifier.
GET /v1/tax-decisions/{id}: la décision est finale, non expirée, et le montant reçu correspond. - Créer puis émettre acquittée.
POST /v1/invoicesadossée à la décision, puisPOST /v1/invoices/{id}/finalizeavec{ payment: { amount, method, reference, paidAt } }. - Transmettre.
POST /v1/invoices/{id}/sendseulement siinvoiceChannelvauteinvoicing; hors de ce canal, l'obligation éventuelle passe par le e-reporting, sans action de votre part.
const decision = await facturino.taxDecisions.create({
customerId: customer.id,
effectiveAt: '2026-09-15',
currency: 'eur',
priceMode: 'tax_exclusive',
lines: [{ reference: 'abo-pro', description: 'Abonnement Pro',
category: 'electronically_supplied_services',
rateCategory: 'standard', unitAmount: 2900, quantity: '1' }],
}, { idempotencyKey: `order-${order.id}` });
// … encaissement de decision.amountToCharge, référence = decision.id …
const draft = await facturino.invoices.create({
customerId: customer.id,
taxDecisionId: decision.id,
decisionLines: [{ taxLineRef: 'abo-pro', unit: 'month' }],
}, { idempotencyKey: `invoice-${order.id}` });
const invoice = await facturino.invoices.finalize(draft.id, {
payment: { amount: decision.amountToCharge, method: 'card',
reference: decision.id, paidAt: '2026-09-15' },
}, { idempotencyKey: `finalize-${order.id}` });
// invoice.paymentStatus === 'paid', invoice.dates.paidAt === '2026-09-15' Scénario 2 : prestation avec acompte, puis solde
Un acompte n'est déductible du solde que s'il a été encaissé : une facture d'acompte simplement émise a été facturée, pas payée. L'ordre est donc le point.
- Décider l'acompte avec une ligne de catégorie
depositqui nomme la prestation principale qu'elle précède. - Émettre l'acompte acquitté : brouillon de type
deposit(facture 386), puis finalisation avec le paiement intégral du montant décidé. L'acompte n'existe jamais impayé. - Décider puis émettre le solde en déduisant l'acompte réglé (BT-113) et, si besoin, un échéancier ; le serveur valide la déduction et l'échéancier contre le montant restant dû.
- Encaisser le solde à l'échéance :
POST /v1/invoices/{id}/paymentsfait passerpaymentStatusàpaidet posedates.paidAt.
Scénario 3 : facture à échéance, encaissement postérieur
Le client paie à trente jours. La facture est émise impayée, transmise, puis réglée plus tard. Ici, ce sont les webhooks qui portent l'intégration.
- Décider, créer, finaliser sans corps : la facture est émise « À RÉGLER », son original Factur-X est figé à cet état, ce qui est exact.
- Transmettre sur le canal décidé ; suivez
invoice.deposited,invoice.transmitted,invoice.received,invoice.approvedouinvoice.refused. - Encaisser quand le virement arrive :
POST /v1/invoices/{id}/payments. Vous recevezpayment.createdpuisinvoice.paidouinvoice.partially_paid;invoice.overduesignale un retard. - Servir le document à jour : après un encaissement,
GET /v1/invoices/{id}/pdfrépond202avec un job de rendu tant que la copie n'est pas redessinée, puis200avec l'URL. Le Factur-X, lui, ne change jamais.
Les webhooks sont livrés au moins une fois : dédupliquez par identifiant d'événement, et traitez payment.created comme le déclencheur de vos écritures comptables, jamais la réponse HTTP d'un appel.
Scénario 4 : vente à un particulier dans l'Union européenne
Un service vendu à un particulier établi dans un autre État membre relève de la TVA du pays de consommation au-delà du seuil annuel, ou du régime OSS si vous y avez opté. La décision fiscale règle la question, à condition de lui donner les preuves de localisation.
- Décider avec les preuves : nature du client (particulier), pays déclaré, code postal, et les indices de localisation disponibles (adresse de facturation, pays de l'adresse IP, pays de la banque). Une preuve résolue au niveau du pays est acceptée sans code postal.
- Lire la décision : la TVA appliquée,
euB2cDestination, et la part du seuil annuel consommée par l'opération dans le registreGET /v1/eu-threshold-ledgers/{year}. - Encaisser et émettre acquittée comme au scénario 1. Si le registre de l'année est en revue, la décision est refusée avec une raison explicite plutôt que de sous-estimer le seuil.
Scénario 5 : recevoir les factures de vos fournisseurs
La réception est obligatoire pour toutes les entreprises depuis le 1er septembre 2026. Avec une Plateforme Agréée connectée, les factures reçues arrivent dans Facturino sans développement de votre part ; votre intégration décide ensuite.
- Recevoir :
invoice.incoming.receivedvous notifie ;GET /v1/received-invoices/{id}donne le document, son Factur-X et son historique. - Décider du sort :
POST /v1/received-invoices/{id}/approve,/refuseou/suspend. Les statuts du cycle de vie remontent à la plateforme de l'émetteur. - Enregistrer le règlement :
POST /v1/received-invoices/{id}/record-paymentquand vous avez payé, pour un e-reporting des paiements exact quand il vous incombe.
Ce que cette architecture évite
| Erreur fréquente | Ce qui la remplace |
|---|---|
| Recalculer la TVA dans l'application, puis découvrir un écart à la facture | Une décision fiscale prise avant l'encaissement, immuable, rejouable par clé d'idempotence |
| Finaliser puis enregistrer le paiement une seconde plus tard, et rendre un original « À RÉGLER » sur une facture payée | La finalisation avec payment, une seule transaction |
| Déduire un acompte facturé mais non encaissé | L'acompte émis acquitté, puis déduit comme prépayé |
Appeler send sur toute facture | Le canal décidé : send seulement si invoiceChannel vaut einvoicing |
| Considérer un webhook comme livré une seule fois | Déduplication par identifiant d'événement |
Pour aller plus loin
Les cinq scénarios sont implémentés, dans le même ordre, dans les démos publiques Node.js, Go, Python, PHP et HTTP sans SDK, avec une trame partagée qui les décrit étape par étape. La référence des ressources citées est dans la documentation des factures, celle des décisions fiscales et celle des webhooks. L'article suivant détaille le premier scénario : émettre acquittée une facture encaissée d'avance.
Facturino : l'API qui respecte l'ordre
Décision fiscale immuable, facture émise dans son vrai état, transmission par la Plateforme Agréée de votre choix, e-reporting déclaré sans code de votre part. Le démarrage rapide vous fait émettre une première facture en sandbox en quelques minutes, sans carte bancaire.
Prêt à passer à la facturation électronique ?
Créez votre compte Facturino gratuitement et commencez à émettre des factures conformes en quelques minutes.