API
Routes HTTP du module de facturation et fonctions serveur importables depuis le projet hôte.
Fonctions serveur
Un composant serveur, un template ou une page du projet hôte lit la base directement, jamais par un appel HTTP à ses propres routes.
import {
getInvoiceById,
getInvoiceByToken,
listInvoices,
getInvoiceStats,
getClientById,
getClientByUserId,
listClients,
listTransactions,
listInvoiceTransactions,
computeBalance,
computeInvoiceTotals,
formatCents,
getBillingConfig,
isBillingEnabled,
} from '@lrvo/module-billing/server';getClientByUserId(userId) est le point d'accroche d'un portail client : elle résout le compte connecté vers sa fiche de facturation, dont l'identifiant sert ensuite à filtrer listInvoices.
Routes HTTP
Toutes sous /lrvo/api/billing/. Les routes d'admin demandent une session administrateur, plus la permission indiquée. Le routeur applique la limite de débit, la protection CSRF, l'authentification et la permission avant d'atteindre le module.
Configuration
| Méthode | Chemin | Permission |
|---|---|---|
GET | /admin/config | billing.view |
Renvoie ce dont l'admin a besoin pour dessiner ses formulaires : taxesEnabled, defaultTaxProfile, gstRatePpm, qstRatePpm et currency. C'est une lecture de l'environnement, pas un réglage : il n'y a pas d'écriture correspondante.
Clients
| Méthode | Chemin | Permission |
|---|---|---|
GET | /admin/clients | billing.view |
POST | /admin/clients | billing.manage |
GET | /admin/clients/:id | billing.view |
PUT | /admin/clients/:id | billing.manage |
DELETE | /admin/clients/:id | billing.manage |
POST | /admin/clients/:id/interac | billing.manage |
GET /admin/clients?options=true renvoie la liste compacte des clients actifs, pour un sélecteur.
Catégories et articles
| Méthode | Chemin | Permission |
|---|---|---|
GET / POST | /admin/categories | billing.view / billing.manage |
GET / PUT / DELETE | /admin/categories/:id | billing.view / billing.manage |
GET / POST | /admin/items | billing.view / billing.manage |
GET / PUT / DELETE | /admin/items/:id | billing.view / billing.manage |
?options=true fonctionne aussi sur ces deux ressources. Sur les catégories, elle renvoie l'arbre aplati avec une profondeur, pour une liste indentée.
La suppression d'un article référencé par une facture le désactive au lieu de le supprimer, et la réponse le dit.
Factures
| Méthode | Chemin | Permission |
|---|---|---|
GET | /admin/invoices | billing.view |
POST | /admin/invoices | billing.manage |
GET | /admin/invoices/stats | billing.view |
GET | /admin/invoices/:id | billing.view |
PUT | /admin/invoices/:id | billing.manage |
DELETE | /admin/invoices/:id | billing.manage |
POST | /admin/invoices/:id/issue | billing.manage |
POST | /admin/invoices/:id/resend | billing.manage |
POST | /admin/invoices/:id/cancel | billing.manage |
POST | /admin/invoices/:id/duplicate | billing.manage |
GET | /admin/invoices/:id/pdf | billing.view |
GET / POST | /admin/invoices/:id/transactions | billing.view / billing.manage |
Filtres de la liste : status, clientId, overdue=true, from, to, search, page, limit, sortBy, sortOrder.
sortBy passe par une liste blanche : une valeur inconnue retombe sur le tri par défaut, elle n'atteint jamais la requête.
overdue=true n'est pas un statut mais une clause de recherche : échéance passée et solde positif.
Transactions
| Méthode | Chemin | Permission |
|---|---|---|
GET | /admin/transactions | billing.view |
GET | /admin/transactions/:id | billing.view |
PUT | /admin/transactions/:id | billing.manage |
DELETE | /admin/transactions/:id | billing.manage |
POST | /admin/transactions/:id/refund | billing.manage |
GET | /admin/transactions/:id/receipt | billing.view |
PUT et DELETE répondent 409 sur une transaction Stripe : le registre doit refléter ce que le processeur a fait, et l'annulation d'un encaissement Stripe passe par un remboursement.
Récurrences
| Méthode | Chemin | Permission |
|---|---|---|
GET / POST | /admin/recurrences | billing.view / billing.manage |
GET / PUT / DELETE | /admin/recurrences/:id | billing.view / billing.manage |
POST | /admin/recurrences/:id/run | billing.manage |
Tâches planifiées
| Méthode | Chemin | Permission |
|---|---|---|
GET | /admin/jobs | billing.view |
POST | /admin/jobs/:name/run | billing.manage |
POST .../run répond 409 quand la tâche est déjà en cours : le déclenchement manuel passe par le même verrou que le déclenchement planifié.
Routes publiques
| Méthode | Chemin | Rôle |
|---|---|---|
GET | /public/:token | Charge publique de la facture. |
POST | /public/:token/checkout | Ouvre une session de paiement pour le solde. |
GET | /public/:token/pdf | PDF de la facture. |
GET | /public/:token/receipt | Reçu, une fois la facture soldée. |
Le jeton est obligatoire sur chacune. Son format, 64 caractères hexadécimaux, est vérifié avant toute requête en base, et la recherche exclut les brouillons. Aucun chemin ne résout une facture par son identifiant numérique.
La charge publique est réduite : ni identifiant numérique, ni notes internes, ni adresse du client.
POST /public/:token/checkout ne prend aucun corps. La facture et le montant sont dérivés du jeton côté serveur, donc il n'y a rien à falsifier.
Les deux routes de document répondent en Content-Disposition: inline : le navigateur affiche le PDF au lieu de le télécharger, ce qui permet aux pages /lrvo/billing/<jeton>/pdf et /recu de le présenter dans une visionneuse. Le document est rendu à chaque requête, jamais relu depuis le stockage. /public/:token/receipt répond 409 tant que le solde n'est pas nul.
Codes d'erreur
| Situation | Code |
|---|---|
| Identifiant ou corps invalide | 400 |
| Permission manquante | 403 |
| Facture, client ou article introuvable | 404 |
| Facture déjà émise, lignes figées, transaction Stripe, tâche en cours | 409 |
Notes
- Les PDF sont servis en pièce jointe. Le routeur du core impose
Content-Disposition: attachmentpour tout ce qui n'est pas une image, et poseX-Frame-Options: DENY: un PDF ne peut donc pas s'afficher dans un cadre. - Le webhook Stripe n'est pas dans ce tableau : il appartient au core, sur
/lrvo/api/payments/stripe/webhook, et le module s'y abonne. Voir Installation. - Les pages d'admin appellent ces routes avec
credentials: 'include'. Sans le cookie de session, toute route d'admin répond 401.