Retour au blog
Développeurs 7 min de lecture

Facture encaissée d'avance : l'émettre acquittée

Commande en ligne, abonnement, acompte : l'argent est souvent encaissé avant que la facture existe. Dans ce cas, la facture doit naître réglée. Émettre une facture impayée puis enregistrer le paiement une seconde plus tard produit un original Factur-X qui dit « À RÉGLER » sur une facture payée, et cet original ne change jamais. Depuis la version 2.3 de l'API et des SDKs, la finalisation accepte l'encaissement dans son corps : numérotation et paiement sont appliqués dans la même transaction.

Le problème : deux appels en course

La séquence habituelle enchaîne POST /v1/invoices/{id}/finalize puis POST /v1/invoices/{id}/payments. La finalisation attribue le numéro et lance le rendu de l'original ; le paiement arrive moins d'une seconde après. Selon la latence du travailleur de rendu, l'original voit ou non le paiement. Deux factures de la même série, émises de la même façon, peuvent sortir l'une « À RÉGLER » et l'autre « PAYÉE ». Le document dépend d'une course, et le Factur-X, original légal figé à l'émission, garde ce résultat pour toujours.

Avant : finalize lance le rendu, payments.create arrive 0,6 seconde plus tard, le rendu terminé a vu ou non le paiement, deux factures de la même série peuvent dire À RÉGLER ou PAYÉE. Après : finalize avec le corps payment applique numéro et encaissement dans la même transaction, l'original Factur-X et le PDF sont rendus sur une facture déjà réglée, dates.paidAt porte la date du règlement
Deux appels en course contre une seule transaction.

La solution : finaliser avec l'encaissement

POST /v1/invoices/{id}/finalize accepte un corps facultatif { "payment": { … } }. payment est exactement l'objet de POST /v1/invoices/{id}/payments : amount en centimes entiers, method parmi transfer, card, check, cash, direct_debit, sepa, paypal et other, une reference libre, et paidAt, la date réelle du règlement.

L'encaissement est appliqué dans la même transaction que la numérotation. La facture est émise acquittée : l'original PDF et Factur-X est rendu sur une facture réglée, mention « PAYÉE » et montants payé et restant dû renseignés (BT-113, BT-115). Le paiement créé est indiscernable d'un paiement enregistré après coup ; il apparaît dans GET /v1/invoices/{id}/payments avec sa référence. Sans corps, la finalisation garde son comportement d'origine.

curl -X POST https://facturino.com/api/v1/invoices/inv_7f3a…/finalize \
  -H "Authorization: Bearer fac_test_…" \
  -H "Idempotency-Key: finalize-order-4711" \
  -H "Content-Type: application/json" \
  -d '{
    "payment": {
      "amount": 3480,
      "method": "card",
      "reference": "dec_9c1e…",
      "paidAt": "2026-09-15"
    }
  }'

La réponse est la facture émise : number attribué, paymentStatus à paid, totals.amountDue à zéro, et dates.paidAt à la date du règlement.

Tout ou rien

Un encaissement supérieur au restant dû est refusé en 422 avec le code payment_exceeds_amount_due et un tableau error.issues qui pointe payment.amount. La finalisation entière est refusée : aucun numéro n'est brûlé, la facture reste un brouillon, rien n'est à annuler. Un encaissement partiel est accepté : la facture est émise partially_paid, sans dates.paidAt tant qu'un solde reste.

Dans les quatre SDKs

Node.js

const invoice = await facturino.invoices.finalize(draft.id, {
  payment: { amount: 3480, method: 'card',
             reference: decision.id, paidAt: '2026-09-15' },
}, { idempotencyKey: 'finalize-order-4711' });

La méthode est surchargée, pas réordonnée : finalize(id) et finalize(id, options) gardent leur sens ; seul un objet portant payment est lu comme corps.

Python

invoice = client.invoices.finalize(
    draft["id"],
    payment={"amount": 3480, "method": "card",
             "reference": decision["id"], "paid_at": "2026-09-15"},
)

PHP

$invoice = Invoice::finalize($draft['id'], [
    'amount' => 3480,
    'method' => 'card',
    'reference' => $decision['id'],
    'paidAt' => '2026-09-15',
]);

Go

invoice, err := client.Invoices.FinalizeWithPayment(draft.ID, &facturino.PaymentParams{
    Amount:         3480,
    Method:         "card",
    Reference:      decision.ID,
    PaidAt:         "2026-09-15",
    IdempotencyKey: "finalize-order-4711",
})

Go n'a pas de paramètre optionnel : Finalize(id) est inchangé et n'envoie aucun corps ; le nouveau comportement est une nouvelle méthode.

Clé d'idempotence : une nouvelle demande, une nouvelle clé

Une finalisation avec payment est une demande différente d'une finalisation sans corps. Donnez-lui sa propre Idempotency-Key. Rejouer une clé déjà servie renvoie la réponse d'origine ; rejouer la même clé avec un corps différent est refusé. Une relance réseau avec la même clé et le même corps est sûre : elle ne crée ni second paiement ni second numéro.

Original figé, copie qui suit

Le Factur-X est l'original : figé à l'émission, jamais réécrit. La copie PDF de présentation, elle, suit les encaissements. Après un paiement enregistré plus tard, une annulation ou un remboursement, GET /v1/invoices/{id}/pdf répond 202 avec un job de rendu au lieu de servir une copie périmée, puis 200 avec l'URL signée. Un job peut finir en superseded : le rendu a été produit pour une révision du grand livre qu'un mouvement a dépassée depuis. Ce n'est pas un échec ; redemandez le PDF.

Quand garder payments.create

Situation Appel
Paiement reçu avant l'émission, montant connu finalize avec payment
Acompte à déduire d'un solde finalize avec le paiement intégral du montant décidé
Facture à échéance, virement reçu plus tard payments.create, puis le PDF suit
Règlement partiel puis solde finalize avec le premier montant, payments.create pour le reste

Facturino : la facture naît dans son vrai état

La finalisation avec encaissement est disponible dans l'API, contrat 2026-09-01, et dans les SDKs Node.js, Python, PHP et Go à partir de la version 2.3. La documentation des factures détaille le corps payment, les codes d'erreur et le cycle des copies ; le guide d'architecture replace cet appel dans les cinq scénarios d'intégration.

Prêt à passer à la facturation électronique ?

Créez votre compte Facturino gratuitement et commencez à émettre des factures conformes en quelques minutes.