Aller au contenu

Module PRD+FSD - Facturation (BILL)

Papillon Collection Solution · V1 (MVP)

Auteur : ANALYST (Claude Code) · Date : 2026-04-30
Branche : docs/billing-prd · Module prefix : BILL
Pays cible V1 : Côte d'Ivoire (CI) uniquement
Mode : A - nouveau PRD (aucun PRD existant)

Source legal audit : docs/legal/audit/memo-conformite-CI-v2-audit.md (audité le 30/04/2026 - disposition LEGAL REVIEW REQUIRED). Mémo source : docs/legal/memo-conformite-CI-v2.md.

Legal status discipline (memo v2) - toute obligation INCERTAIN - avocat requis bloque l'implémentation des stories qui en dépendent et impose une note [LEGAL-REVIEW] dans la spec/story. Toute obligation PRÉLIMINAIRE déclenche un avertissement + [LEGAL-REVIEW]. La conformité ne se déduit jamais d'un statut INCERTAIN.

Note de périmètre pour BILL - le mémo memo-conformite-CI-v2 couvre la chaîne client → courtier (CIMA, données personnelles, paiement mobile money, preuve). Il ne couvre PAS la facturation plateforme → tenant (régime DGI / FNE Côte d'Ivoire pour les tenants CI ; régimes équivalents SN, BJ - cf. CLAUDE.md §Multi-Tenant Model). Ce périmètre relève d'un mémo de conformité distinct à produire avant V1 go-live (étape 0A du workflow). Tant qu'un audit légal FNE n'a pas été produit, toute story BILL qui touche au format de facture, à l'envoi à la DGI, ou au cycle de vie d'une facture certifiée doit porter [LEGAL-REVIEW] et appliquer la même discipline tri-état dès que ce mémo existera.


V0 Envelope Scope (ajout 2026-05-21)

BILL est entièrement HORS V0. Aucune sous-version de l'enveloppe V0 (v0.0.0 à v0.1.0) ne livre de code, de schéma ou d'UI du module Facturation. La résolution de la question ouverte V0-OQ-05 (docs/prd/prd-v0.md § 10) confirme cette mise hors périmètre. La ligne TENANT du § 4 du PRD V0 rappelle que la facturation plateforme -> tenant (et notamment l'intégration FNE pour les tenants CI) ne ship qu'avec V1.

Conséquences pratiques pour la phase V0 :

  • Aucune story BILL-NNN n'entre dans le backlog V0. Les sous-versions v0.0.0 à v0.1.0 n'en planifient aucune (cf. prd-v0.md § 8 : la colonne BILL du mapping n'existe pas).
  • Aucun placeholder, aucun stub : pas de table invoice, pas d'endpoint, pas de feature flag câblé dans le code V0. Le module reste inerte côté repo jusqu'à l'ouverture du chantier V1.
  • Le chiffrage commercial des tenants V0 est manuel / hors plateforme. Les contrats Basile (démo) et NSIA (pilote v0.0.4 -> v0.0.5) ne déclenchent aucun appel FNE / DGI depuis Papillon Collection Solution. La facturation effective de ces tenants pendant la phase V0 est faite via les outils existants du founder (hors scope produit).
  • Le mémo de conformité FNE / DGI (mentionné dans la note de périmètre ci-dessus) n'est pas un prérequis pour V0 go-live. Il devient bloquant uniquement quand le chantier V1 du module BILL démarre (étape 0A du workflow, fresh ANALYST + LEGAL_AUDITOR session).

Ce que V0 garantit malgré tout pour ne pas re-bloquer V1 :

  • La colonne country_code portée par tenant dès v0.0.0 (cf. CLAUDE.md Multi-Tenant Model + prd-v0.md § 5) suffit à router plus tard une intégration FNE conditionnelle (CI / SN / BJ) sans migration de schéma sur les tables tenant-scopées.
  • La table country_profile seedée CI only en v0.0.0 réserve les colonnes nécessaires à un futur einvoicing_provider + einvoicing_endpoint (la création de ces colonnes elle-même est V1 ; aucune valeur n'est exigée en V0).
  • Le pattern outbox d'AUDIT (spring-modulith-events-jdbc, anchor D4 + D7) accueillera naturellement les futurs événements INVOICE_ISSUED / INVOICE_FNE_ACCEPTED / INVOICE_FNE_REJECTED à V1 sans rework.

Statuts légaux : aucun statut INCERTAIN - avocat requis ou PRÉLIMINAIRE du futur mémo FNE n'est arbitré par cette mise hors V0. Le module reste intégralement à instruire (mémo + audit + PRD + arch + stories) au moment du démarrage V1.


Table des matières


Partie A - Exigences Produit

A1. Résumé exécutif

Problème adressé

La plateforme Papillon Collection Solution facture ses services aux cabinets de courtage (tenants) sur une base d'abonnement. En Côte d'Ivoire, toute facture B2B doit obligatoirement être certifiée par la Facture Normalisée Électronique (FNE) du système DGI avant d'être fiscalement opposable. Sans un module de facturation intégrant cette exigence, la plateforme ne peut pas légalement encaisser ses abonnements dans le respect du droit fiscal ivoirien.

Par ailleurs, les tenants (petits cabinets en phase pilote) doivent pouvoir consulter, payer et télécharger leurs factures en libre-service depuis leur back-office, sans dépendre d'échanges par email ou WhatsApp avec l'équipe de la plateforme - c'est un standard de toute application SaaS B2B moderne.

Valeur créée

Bénéficiaire Valeur
Plateforme (opérateur) Facturation traçable, certifiée DGI, encaissement en mobile money ou virement, audit complet de chaque transaction.
Tenant admin Tableau de bord de ses factures, paiement en un clic (mobile money), téléchargement de la facture certifiée DGI et de la quittance pour sa comptabilité.
Comptable tenant Factures au format standard CI (HT / TVA 18 % / TTC, NIF, numéro FNE, QR code DGI) directement téléchargeables.

Positionnement V1

Le module BILL est un module de contrôle (Control plane) consommé par l'administration de la plateforme. Il n'est pas visible des assurés finaux. Il opère en tandem avec le module TENANT (configuration du tenant) et réutilise l'abstraction de paiement du module PAY.


A2. Personas utilisateurs

Administrateur plateforme [ADMIN]

Employé de l'entreprise opératrice de Papillon. Seul acteur autorisé à configurer les paramètres de facturation d'un tenant, à générer des factures, à marquer un virement reçu, et à re-soumettre une facture en cas d'échec FNE.

Workflow typique : créer/modifier la configuration de facturation d'un tenant → générer une facture d'abonnement ou de frais d'installation → suivre le statut FNE → réconcilier les virements bancaires → consulter les tableaux de bord de paiement.

Administrateur tenant [OPERATOR]

Responsable du cabinet, accès au back-office opérateur. Consulte les factures de sa propre organisation, initie le paiement en mobile money ou prend note des coordonnées bancaires pour le virement. Télécharge les documents pour sa comptabilité.

Accès : restreint aux factures de son tenant uniquement.

Système (backend) [BACKEND]

Jobs planifiés et webhooks : soumission FNE asynchrone, détection des factures en retard, envoi des rappels automatiques, traitement des webhooks de confirmation de paiement mobile money.


A3. User Stories

BILL-US-001 - Configuration de facturation d'un tenant [ADMIN][V1]

En tant qu' administrateur plateforme, je veux configurer les paramètres de facturation d'un tenant afin de définir son plan tarifaire, son contact de facturation et ses coordonnées fiscales avant d'émettre la première facture.

# Étant donné Quand Alors
AC1 Un tenant existe sans configuration de facturation L'admin ouvre l'onglet "Facturation" sur la fiche tenant Il voit un formulaire vierge avec les champs : type de plan (MENSUEL/ANNUEL), montant HT (XOF), montant HT annuel (XOF, si annuel), NIF/CC du tenant, email de contact facturation, délai de paiement (défaut 15 j), frais d'installation HT (0 si exonéré), statut de facturation (ACTIF/PAUSÉ)
AC2 La config est sauvegardée Une facture est générée pour ce tenant La facture utilise les valeurs de la config au moment de la génération (snapshot, pas la config courante)
AC3 Une config existe déjà L'admin la modifie et sauvegarde Un événement audit TENANT_BILLING_CONFIG_UPDATED est créé avec l'ancien et le nouvel état des champs modifiés
AC4 L'email de contact facturation est absent L'admin tente de sauvegarder Le système bloque avec le message : « L'adresse email de contact facturation est obligatoire. »

BILL-US-002 - Génération manuelle d'une facture d'abonnement [ADMIN][V1]

En tant qu' administrateur plateforme, je veux générer manuellement une facture d'abonnement pour un tenant afin de le facturer pour la période en cours.

# Étant donné Quand Alors
AC1 Un tenant a une config de facturation ACTIVE L'admin clique "Générer une facture d'abonnement", renseigne la période (date début, date fin) et confirme Une facture est créée en statut DRAFT avec le numéro BILL-{AAAA}-{NNNN}, les montants HT/TVA/TTC calculés, et la date d'échéance = date de facture + 15 jours
AC2 La facture est créée - La soumission FNE est mise en file d'attente immédiatement ; un email "Nouvelle facture" est envoyé au contact de facturation du tenant
AC3 Deux demandes de génération pour le même tenant, même période et même type sont soumises simultanément - Une seule facture est créée (vérification d'idempotence sur tenant_id + period_start + period_end + invoice_type) ; la seconde requête retourne la facture existante
AC4 L'admin souhaite ajuster le montant (ex : pro-rata premier mois) Il modifie le montant HT avant de confirmer Le système accepte le montant HT saisi manuellement et recalcule TVA et TTC en conséquence ; la ligne de détail affiche la description saisie par l'admin

BILL-US-003 - Génération d'une facture de frais d'installation [ADMIN][V1]

En tant qu' administrateur plateforme, je veux générer une facture de frais d'installation pour un tenant afin de lui facturer la mise en place initiale ou toute prestation ponctuelle.

# Étant donné Quand Alors
AC1 La config tenant contient onboarding_fee_ht > 0 L'admin déclenche une facture de frais d'installation Une facture de type ONBOARDING_FEE est créée avec le montant de la config ; même flux FNE que l'abonnement
AC2 L'admin saisit un montant personnalisé (frais de dérogation) Il entre un montant HT différent et une description libre Le système crée la facture avec le montant saisi et la description personnalisée
AC3 Une facture de frais d'installation a déjà été payée pour ce tenant L'admin tente d'en générer une seconde Le système affiche un avertissement « Une facture de frais d'installation payée existe déjà pour ce tenant. Confirmer pour continuer ? » ; l'admin peut passer outre

BILL-US-004 - Réconciliation d'un virement bancaire [ADMIN][V1]

En tant qu' administrateur plateforme, je veux marquer une facture comme payée par virement bancaire afin de mettre à jour le statut et notifier le tenant.

# Étant donné Quand Alors
AC1 Une facture est en statut DRAFT, CERTIFIED ou OVERDUE L'admin saisit la référence bancaire et la date de paiement puis clique "Marquer payée" Le statut de la facture passe à PAID, un enregistrement BillingPayment est créé avec la référence et la date, et le contact de facturation reçoit un email de confirmation de paiement
AC2 La facture est déjà en statut PAID L'admin tente de la marquer payée Le système rejette l'action avec le message : « Cette facture est déjà marquée comme payée. »
AC3 La référence bancaire est absente L'admin tente de valider Le système bloque avec le message : « La référence de la transaction bancaire est obligatoire. »

BILL-US-005 - Tableau de bord facturation (admin plateforme) [ADMIN][V1]

En tant qu' administrateur plateforme, je veux consulter l'ensemble des factures de tous les tenants afin de suivre les encaissements et détecter les retards.

# Étant donné Quand Alors
AC1 Je suis sur le tableau de bord facturation - Je vois la liste de toutes les factures (tous tenants) avec les colonnes : nom du tenant, numéro de facture, type, période, montant TTC, date d'échéance, statut facture, statut FNE
AC2 Je filtre par statut = OVERDUE - Seules les factures en retard sont affichées
AC3 Je filtre par tenant - Seules les factures du tenant sélectionné sont affichées
AC4 Je clique sur une facture - J'accède au détail : lignes, FNE status, historique de paiement, bouton de réconciliation bancaire, bouton de re-soumission FNE (si fne_status = FAILED)

BILL-US-006 - Consultation des factures par le tenant admin [OPERATOR][V1]

En tant qu' administrateur tenant, je veux consulter mes factures plateforme en libre-service afin de suivre mes engagements financiers et télécharger les documents pour ma comptabilité.

# Étant donné Quand Alors
AC1 Je suis connecté en tant qu'admin tenant Je navigue vers "Mon abonnement" Je vois la liste de mes factures avec : numéro, type, période, montant TTC, statut, date d'échéance, boutons d'action
AC2 Une facture a fne_status = CERTIFIED - Un lien "Télécharger la facture certifiée DGI (PDF)" est visible
AC3 Une facture est en statut DRAFT et fne_status ≠ CERTIFIED - Un badge "Certification DGI en cours…" est affiché à côté du numéro de facture ; le bouton de téléchargement PDF certifié est absent
AC4 Une facture est en statut PAID - Un lien "Télécharger la quittance de paiement (PDF)" est visible

BILL-US-007 - Paiement d'une facture par mobile money [OPERATOR][V1]

En tant qu' administrateur tenant, je veux payer une facture directement en mobile money depuis le back-office afin de régler mon abonnement sans me déplacer ni faire un virement bancaire.

# Étant donné Quand Alors
AC1 Une facture est en statut DRAFT, CERTIFIED ou OVERDUE Je clique "Payer maintenant" Je vois l'écran de sélection du mode de paiement : Mobile money / Virement bancaire
AC2 Je sélectionne Mobile money et valide - Je suis redirigé vers le flux de paiement du prestataire (saisie numéro, confirmation USSD ou push)
AC3 Le prestataire confirme le paiement via webhook - Le statut de la facture passe à PAID, je reçois un email de confirmation avec la référence de transaction et un lien pour télécharger la quittance
AC4 Le paiement échoue ou expire - Je vois le message : « Le paiement a échoué. Vous pouvez réessayer. » avec le bouton "Réessayer" ; aucun double débit n'est possible grâce à la clé d'idempotence

BILL-US-008 - Paiement d'une facture par virement bancaire [OPERATOR][V1]

En tant qu' administrateur tenant, je veux obtenir les instructions de virement pour payer une facture par ma banque afin de régler avec mes outils bancaires habituels.

# Étant donné Quand Alors
AC1 Je sélectionne "Virement bancaire" sur l'écran de paiement - Je vois les coordonnées bancaires de la plateforme (banque, titulaire, IBAN/RIB) et la référence exacte à mentionner dans l'ordre de virement (numéro de facture BILL-AAAA-NNNN)
AC2 Je reviens consulter la facture avant que l'admin ait réconcilié - Le statut reste DRAFT / CERTIFIED / OVERDUE, avec le message : « Virement attendu. Référence : BILL-AAAA-NNNN. »
AC3 L'admin plateforme marque le virement reçu - Mon statut de facture passe à PAID et je reçois un email de confirmation

BILL-US-009 - Soumission FNE asynchrone [BACKEND][V1]

En tant que système, je veux soumettre chaque facture à la FNE en arrière-plan et mettre à jour le statut de certification afin que les factures soient certifiées DGI sans bloquer le flux de génération.

# Étant donné Quand Alors
AC1 Une facture est créée en statut DRAFT La soumission FNE réussit fne_status = CERTIFIED, fne_number et fne_qr_code_url sont enregistrés, le PDF certifié est généré et stocké dans MinIO
AC2 La soumission FNE échoue Les 5 tentatives de la politique de relance sont épuisées (~3 h) fne_status = FAILED et une alerte est envoyée à l'administrateur plateforme
AC3 fne_status = FAILED L'admin plateforme clique "Re-soumettre à la FNE" Une nouvelle tentative est déclenchée et le compteur de relances est réinitialisé
AC4 La FNE certifie une facture déjà payée (paiement reçu avant certification) - La facture reste PAID ; le fne_number et le PDF certifié sont mis à jour ; aucun statut ne régresse

BILL-US-010 - Détection automatique des factures en retard et rappel [BACKEND][V1]

En tant que système, je veux détecter les factures impayées au-delà de leur date d'échéance et envoyer un rappel unique afin d'alerter les tenants sans intervention manuelle.

# Étant donné Quand Alors
AC1 Une facture a status ∈ {DRAFT, CERTIFIED} et due_date < today Le job quotidien s'exécute Le statut passe à OVERDUE et l'événement audit INVOICE_OVERDUE est enregistré
AC2 Une facture vient de passer à OVERDUE et overdue_reminder_sent_at IS NULL - Un email de rappel unique est envoyé au contact de facturation ; overdue_reminder_sent_at est renseigné
AC3 overdue_reminder_sent_at IS NOT NULL - Aucun second rappel automatique n'est envoyé (pas de chaîne de relance en V1)
AC4 Une facture OVERDUE est payée - Le statut passe à PAID ; le badge "En retard" disparaît de l'interface tenant

A4. Exigences non fonctionnelles

# Catégorie Exigence
NFR-01 Performance Le tableau de bord facturation admin (tous tenants) doit se charger en < 2 s pour ≤ 500 factures (pagination après).
NFR-02 Disponibilité FNE Le module BILL ne doit pas être indisponible si la FNE est down : la génération de facture réussit en DRAFT ; la certification est asynchrone.
NFR-03 Idempotence paiements Chaque tentative de paiement mobile money doit porter une clé d'idempotence unique. En cas de double webhook, le paiement n'est enregistré qu'une fois.
NFR-04 Intégrité des montants Tous les montants sont stockés en XOF entiers (pas de virgule flottante). La TVA est calculée par ROUND(montant_ht × tva_rate) avec arrondi arithmétique (0,5 → 1).
NFR-05 Stockage PDF Les PDFs de factures (brouillon et certifié) sont stockés dans MinIO avec une rétention minimale conforme au droit fiscal CI (durée TV - voir C1).
NFR-06 Isolation tenant Un tenant admin ne voit jamais les factures d'un autre tenant. Chaque requête est scopée par tenant_id issu du contexte d'authentification Keycloak.
NFR-07 Audit Chaque événement du cycle de vie d'une facture produit une entrée dans le module AUDIT avec : event_type, timestamp, actor (admin user_id ou SYSTEM), tenant_id, invoice_id, result.
NFR-08 Localisation Toutes les interfaces et emails sont en français. Montants affichés en format XOF (espace comme séparateur de milliers, 0 décimale) : 59 000 XOF.
NFR-09 Sécurité emails Les emails de facturation ne contiennent jamais de lien de paiement actif en clair. Le tenant clique pour ouvrir son back-office Keycloak-authentifié.
NFR-10 Retry FNE Politique de relance exponentielle : 5 s → 30 s → 5 min → 30 min → 2 h (5 tentatives totales). Si toutes échouent : fne_status = FAILED + alerte admin.

A5. Périmètre MVP

V1 - Inclus

Fonctionnalité Notes
Génération manuelle de factures d'abonnement (mensuel ou annuel) Toutes les factures sont déclenchées manuellement par l'admin plateforme en V1. Pas de scheduler automatique.
Génération manuelle de factures de frais d'installation Montant configurable par tenant ; peut être 0 si exonéré.
Soumission FNE asynchrone + certification DGI Brouillon immédiatement visible ; PDF certifié disponible après certification.
Paiement en libre-service (mobile money) Via la même abstraction de prestataire de paiement que le module PAY.
Paiement par virement bancaire + réconciliation manuelle Admin plateforme marque la facture payée avec la référence bancaire.
TVA 18 % HT/TVA/TTC sur toutes les factures Taux configuré dans le profil pays CI ; champ TVA sur le tenant.
Tableau de bord tenant : liste factures, paiement, téléchargement PDF certifié + quittance Interface back-office opérateur.
Tableau de bord admin plateforme : liste globale, config tenant, réconciliation, re-soumission FNE Interface admin.
Rappel automatique unique à J+16 par email Job quotidien.
Audit complet (10 événements) Module AUDIT.
Configuration facturation par tenant (plan, prix, NIF, email contact) Module TENANT.

V2 - Différé

Fonctionnalité Justification
Scheduler de facturation automatique (récurrence mensuelle/annuelle) Trop peu de tenants en V1 pour justifier la complexité.
Avoirs (credit notes) / annulation de facture Cas rares en V1 ; gérés manuellement hors plateforme.
Dunning automatisé (relances multiples, escalade) Dépasse le cadre du pilot ; V2 avec la gestion des comptes structurés.
Suspension automatique du tenant en cas d'impayé V2 - nécessite une gouvernance claire et un préavis légal.
Switching plan mid-cycle avec calcul de prorata automatique V2 - nécessite un moteur de crédit/débit.
Self-service pricing (grille tarifaire publique, choix du plan par le tenant) V2 - modèle de vente assistée en V1.
Facturation multi-pays (FNE équivalents SN/BJ, TVA locale) V2+ - résolution via le profil pays du tenant.
Tableau de bord analytique des revenus récurrents (MRR, ARR, churn) V2/V3.
Exports comptables (CSV, connexion Sage/QuickBooks) V2.

V3 - Vision

  • Marketplace de facturation (multi-marque, white-label).
  • API de facturation exposée aux partenaires.
  • Prédiction de churn basée sur les comportements de paiement.

A6. Questions ouvertes

ID Question Impact Propriétaire
OQ-01 Quel est le numéro NIF/CC officiel de la société opératrice de la plateforme pour figurer sur les factures émises ? Bloque la génération du PDF de facture Fondateur
OQ-02 Quelle banque et quels coordonnées bancaires (IBAN/RIB) figurer sur les factures pour le paiement par virement ? Bloque la story BILL-US-008 Fondateur
OQ-03 Faut-il un numéro de commande (bon de commande / PO number) sur la facture pour certains tenants (grandes organisations) ? Ajout d'un champ optionnel sur la config tenant Fondateur
OQ-04 Quelle est la durée légale de rétention des factures et PDFs en CI (droit fiscal + ARTCI) ? Paramètre de rétention MinIO et politique d'archivage /legal-audit FNE
OQ-05 Est-ce que l'API FNE de la DGI est disponible en mode sandbox pour les tests d'intégration ? Bloquerait les tests BILL-US-009 en dev/staging Fondateur / DGI
OQ-06 Le format exact d'une facture FNE certifiée (champs obligatoires, positionnement QR code, signature DGI) est-il documenté dans une spécification DGI publique ? Gabarit PDF certifié /legal-audit FNE
OQ-07 Un avoir (credit note) est-il également soumis à FNE si jamais on en émet un en dehors de la plateforme ? Hors V1 mais à anticiper pour V2 /legal-audit FNE

A7. Temporairement validé (TV)

Les éléments suivants sont implémentés dans l'état actuel des connaissances mais doivent être confirmés par un /legal-audit FNE avant la mise en production de BILL.

ID Affirmation Source actuelle Risque si invalide
TV-01 La TVA applicable aux services SaaS B2B fournis en CI est de 18 %. Connaissance générale du code fiscal CI. Taux incorrect sur toutes les factures → redressement fiscal.
TV-02 Le format FNE exige : numéro de facture DGI, QR code, NIF émetteur + récepteur, date, montants HT/TVA/TTC, désignation des produits/services. Connaissance générale FNE / DGI CI. Factures rejetées par FNE ou non conformes à l'audit DGI.
TV-03 Un prestataire SaaS peut émettre une facture avec un numéro interne (BILL-AAAA-NNNN) avant la certification FNE, laquelle peut intervenir en arrière-plan. Pratique observée dans le marché CI. La DGI pourrait exiger que le numéro de facture définitif ne soit assigné qu'après certification.
TV-04 Le tenant peut légalement payer une facture avant que la certification FNE soit complète (paiement sur brouillon). Décision produit validée par le fondateur ; pratique observée. Risque fiscal pour le tenant (TVA non déductible sur facture non certifiée).
TV-05 La durée de rétention légale des factures en CI est d'au moins 10 ans (délai de prescription commerciale et fiscale). Connaissance générale du droit des affaires CI. MinIO configuré avec une durée inadéquate.
TV-06 Les emails transactionnels de facturation vers un administrateur tenant (personne morale) ne nécessitent pas de base légale ARTCI particulière au-delà de l'exécution du contrat. Application du principe contractuel ; loi n°2013-450 non vérifiée article par article. Risque ARTCI si une déclaration préalable est requise.

Partie B - Spécifications Fonctionnelles

B1. Règles fonctionnelles

Groupe BILL-R-100 : Numérotation et génération de factures

BILL-R-101 - Numérotation des factures

Format : BILL-{AAAA}-{NNNN}
  AAAA = année civile de la date de facture (ex : 2026)
  NNNN = séquence sur 4 chiffres, remise à zéro le 1er janvier de chaque année,
         globale (toutes tenants confondus)
Exemples : BILL-2026-0001, BILL-2026-0042

Règle : le numéro interne est assigné à la création de la facture (statut DRAFT).
        Le numéro FNE certifié (attribué par la DGI) est un champ distinct (fne_number).
        Les deux numéros figurent sur le PDF certifié.

Contrainte : UNIQUE sur (invoice_number) - jamais réutilisé même si la facture est annulée.

BILL-R-102 - Idempotence de génération

SI une facture existe avec (tenant_id, invoice_type, period_start, period_end)
ALORS retourner la facture existante sans en créer une nouvelle.

Exception : invoice_type = ONE_OFF → pas de contrainte d'unicité sur la période.

BILL-R-103 - Montant de la facture

Pour chaque ligne :
  total_ht = quantité × prix_unitaire_ht

montant_ht_facture = SUM(total_ht de toutes les lignes)
tva_amount = ROUND(montant_ht_facture × tva_rate, 0)  -- arrondi arithmétique, 0 décimale
montant_ttc = montant_ht_facture + tva_amount

Tous les montants sont en XOF entiers (INTEGER).
Pas de devise autre que XOF en V1.

BILL-R-104 - Données de snapshot à la génération

À la génération, les champs suivants sont copiés depuis TenantBillingConfig
dans l'entité Invoice (snapshot immuable) :
  - tva_rate
  - tenant_nif
  - billing_contact_email
  - payment_terms_days
Si TenantBillingConfig est modifié ultérieurement, les factures existantes ne sont pas affectées.

BILL-R-105 - Date d'échéance

due_date = invoice_date + payment_terms_days (défaut : 15 jours calendaires)

Groupe BILL-R-200 : FNE (Facture Normalisée Électronique)

BILL-R-201 - Soumission FNE à la création [TV-02][TV-03]

À chaque création de facture :
  1. Créer la facture avec fne_status = PENDING
  2. Mettre en file d'attente un job de soumission FNE (asynchrone)
  3. Le job envoie à l'API FNE : NIF émetteur (plateforme), NIF récepteur (tenant),
     numéro interne, date, lignes, montants HT/TVA/TTC
  4. SI FNE répond avec succès :
       fne_status = CERTIFIED
       fne_number = numéro retourné par DGI
       fne_qr_code_url = URL du QR code retourné
       fne_certified_at = now()
       → Générer le PDF certifié (voir BILL-R-203)
  5. SI FNE retourne une erreur :
       → Appliquer BILL-R-202 (politique de relance)

BILL-R-202 - Politique de relance FNE

Tentative | Délai avant la tentative
    1     | 5 secondes
    2     | 30 secondes
    3     | 5 minutes
    4     | 30 minutes
    5     | 2 heures

Après la 5e tentative échouée :
  fne_status = FAILED
  → Envoyer une alerte email à l'administrateur plateforme
  → Enregistrer l'événement audit INVOICE_FNE_FAILED

Re-soumission manuelle : l'admin clique "Re-soumettre à FNE"
  → Réinitialiser le compteur de tentatives
  → Retourner à l'étape 3 de BILL-R-201

BILL-R-203 - Génération du PDF certifié

Déclencheur : fne_status passe à CERTIFIED

Le PDF certifié doit contenir (TV-02) :
  En-tête émetteur :
    - Nom légal de la plateforme
    - Adresse, NIF/CC plateforme
  En-tête récepteur :
    - Nom légal du tenant
    - Adresse du tenant, NIF/CC tenant (snapshot)
  Identifiants :
    - Numéro de facture interne (BILL-AAAA-NNNN)
    - Numéro FNE certifié (fne_number)
    - QR code DGI (fne_qr_code_url)
  Dates :
    - Date de facturation (invoice_date)
    - Date d'échéance (due_date)
  Corps :
    - Lignes de détail : description, quantité, prix unitaire HT, total HT
    - Sous-total HT
    - TVA {tva_rate × 100} % : {tva_amount} XOF
    - Total TTC : {montant_ttc} XOF
  Pied de page :
    - Coordonnées bancaires plateforme (OQ-02)
    - Référence à indiquer au virement : {invoice_number}
    - Mention légale : « Papillon Software est un prestataire de services techniques agréé. Facture certifiée par la Direction Générale des Impôts - Côte d'Ivoire. »

Stockage : MinIO, chemin : invoices/{tenant_id}/{invoice_id}/certified.pdf
Rétention : TV-05 (10 ans minimum)

BILL-R-204 - Comportement si paiement reçu avant certification FNE [TV-03][TV-04]

SI invoice.status = PAID ET invoice.fne_status = PENDING ou SUBMITTED
ALORS
  Continuer la soumission FNE en arrière-plan
  Quand FNE certifie : fne_number et pdf_certified_url mis à jour sur la facture
  Le statut PAID n'est pas régressé
  Le PDF certifié devient disponible au téléchargement rétroactivement
SINON
  Flux normal

Groupe BILL-R-300 : Paiement par mobile money

BILL-R-301 - Initiation du paiement mobile money

Prérequis : invoice.status ∈ {DRAFT, CERTIFIED, OVERDUE}

1. Générer une clé d'idempotence : BILL-{invoice_id}-{attempt_number}
   (attempt_number = nombre de tentatives précédentes + 1)
2. Appeler l'abstraction PaymentProvider (module PAY) avec :
   - montant = invoice.montant_ttc
   - devise = XOF
   - référence = invoice_number
   - idempotency_key
   - description = « Facture {invoice_number} - Papillon Collection Solution »
3. Retourner au tenant l'URL ou le prompt de paiement du prestataire

BILL-R-302 - Confirmation de paiement mobile money (webhook)

À réception d'un webhook du prestataire de paiement :
  1. Vérifier la signature du webhook (même logique que module PAY)
  2. Trouver la facture via la référence
  3. SI status ≠ PAID (vérification anti-doublon) :
       invoice.status = PAID
       invoice.paid_at = now()
       Créer BillingPayment(payment_method = MOBILE_MONEY,
                            provider_reference = transaction_id_prestataire,
                            amount_ttc = montant_ttc,
                            paid_at = now(),
                            idempotency_key)
       Émettre événement audit INVOICE_PAID_MOBILE_MONEY
       Envoyer email de confirmation au contact de facturation
  4. SI status = PAID déjà → ignorer (idempotent)

BILL-R-303 - Échec ou timeout de paiement mobile money

SI le prestataire retourne une erreur ou si le webhook n'est pas reçu dans le délai attendu :
  Afficher au tenant : « Le paiement a échoué ou n'a pas pu être confirmé.
                        Vous pouvez réessayer. »
  Bouton "Réessayer" : déclenche BILL-R-301 avec attempt_number incrémenté
  La clé d'idempotence différente garantit qu'aucun double débit n'est possible
  si le prestataire a en réalité traité la première tentative.

Groupe BILL-R-400 : Paiement par virement bancaire

BILL-R-401 - Affichage des instructions de virement

Lorsque le tenant sélectionne "Virement bancaire" :
  Afficher :
    - Banque : {bank_name de la config plateforme}
    - Titulaire : {account_holder_name}
    - IBAN/RIB : {iban_rib} (OQ-02)
    - Référence obligatoire : {invoice_number}
    - Montant exact à virer : {montant_ttc} XOF
    - Message : « Mentionnez impérativement la référence {invoice_number}
                  dans le libellé de votre virement. »

BILL-R-402 - Réconciliation manuelle par l'admin plateforme

L'admin plateforme saisit :
  - bank_transaction_id (obligatoire, chaîne libre)
  - payment_date (obligatoire, date)
  - notes (optionnel)

Puis clique "Marquer payée" :
  SI invoice.status = PAID → erreur : « Facture déjà payée »
  SINON :
    invoice.status = PAID
    invoice.paid_at = payment_date
    Créer BillingPayment(payment_method = BANK_TRANSFER,
                         provider_reference = bank_transaction_id,
                         amount_ttc = montant_ttc,
                         paid_at = payment_date,
                         recorded_by = admin_user_id)
    Émettre audit INVOICE_PAID_BANK_TRANSFER
    Envoyer email de confirmation au contact de facturation

Groupe BILL-R-500 : Gestion des retards et rappels

BILL-R-501 - Détection des factures en retard

Job planifié : quotidien, heure fixe (ex : 08h00 UTC)

POUR CHAQUE facture WHERE status ∈ {DRAFT, CERTIFIED} AND due_date < CURRENT_DATE :
  invoice.status = OVERDUE
  Émettre audit INVOICE_OVERDUE

Note : une facture peut passer OVERDUE avec fne_status = PENDING si FNE est lente.
       Le passage à OVERDUE n'affecte pas le flux FNE.

BILL-R-502 - Rappel unique de retard

Immédiatement après BILL-R-501, pour chaque facture nouvellement OVERDUE :
  SI overdue_reminder_sent_at IS NULL :
    Envoyer email à billing_contact_email (snapshot) :
      Sujet : « Rappel : Facture {invoice_number} en retard de paiement »
      Corps : numéro, montant TTC, date d'échéance, lien vers le back-office tenant,
              message : « Cette facture était due le {due_date}.
                          Merci de procéder au règlement dès que possible. »
    invoice.overdue_reminder_sent_at = now()
    Émettre audit INVOICE_OVERDUE_REMINDER_SENT
  SINON :
    Ne pas envoyer (un seul rappel automatique en V1)

Groupe BILL-R-600 : Notifications email

BILL-R-601 - Email "Nouvelle facture émise"

Déclencheur : création d'une facture (statut DRAFT)
Destinataire : billing_contact_email (snapshot)
Canal : email direct (pas via le module NOTIF)

Contenu :
  Sujet : « Nouvelle facture Papillon Collection Solution - {invoice_number} »
  Corps :
    - Type de facture (Abonnement / Frais d'installation)
    - Période couverte
    - Montant TTC : {montant_ttc} XOF
    - Date d'échéance : {due_date}
    - Lien : « Accéder à votre espace facturation » → back-office tenant (authentification Keycloak)
    - Note : « La certification DGI (FNE) est en cours.
               Vous serez notifié dès que votre facture certifiée sera disponible au téléchargement. »

BILL-R-602 - Email "Paiement confirmé"

Déclencheur : invoice.status passe à PAID (mobile money ou virement)
Destinataire : billing_contact_email (snapshot)

Contenu :
  Sujet : « Paiement reçu - Facture {invoice_number} »
  Corps :
    - Numéro de facture
    - Montant réglé : {montant_ttc} XOF
    - Mode de paiement : Mobile money / Virement bancaire
    - Référence de transaction : {provider_reference}
    - Lien : « Télécharger votre quittance de paiement »

BILL-R-603 - Email de rappel de retard → voir BILL-R-502


Groupe BILL-R-700 : Événements d'audit

Chaque événement produit une entrée dans le module AUDIT avec les champs requis par le PRD V1 §18.

Code événement Déclencheur Actor
INVOICE_GENERATED Création d'une facture admin user_id
INVOICE_FNE_SUBMITTED Tentative de soumission FNE SYSTEM
INVOICE_FNE_CERTIFIED Retour de certification FNE SYSTEM
INVOICE_FNE_FAILED Toutes les relances FNE épuisées SYSTEM
INVOICE_FNE_RESUBMITTED Re-soumission manuelle déclenchée par admin admin user_id
INVOICE_PAID_MOBILE_MONEY Webhook de confirmation du prestataire SYSTEM
INVOICE_PAID_BANK_TRANSFER Admin marque le virement reçu admin user_id
INVOICE_OVERDUE Job quotidien détecte le retard SYSTEM
INVOICE_OVERDUE_REMINDER_SENT Email de rappel envoyé SYSTEM
TENANT_BILLING_CONFIG_UPDATED Admin modifie la config facturation admin user_id

Champs requis : event_id, event_type, occurred_at, actor_id, actor_type (USER/SYSTEM), tenant_id, invoice_id (nullable), payload (delta pour CONFIG_UPDATED).


B2. Machine à états - Cycle de vie d'une facture

stateDiagram-v2
    [*] --> DRAFT : Génération manuelle (BILL-R-101)

    DRAFT --> CERTIFIED : FNE certifie (BILL-R-201)
    DRAFT --> PAID : Paiement reçu avant FNE (BILL-R-302/402)
    DRAFT --> OVERDUE : due_date dépassée, job quotidien (BILL-R-501)

    CERTIFIED --> PAID : Paiement mobile money ou virement (BILL-R-302/402)
    CERTIFIED --> OVERDUE : due_date dépassée, job quotidien (BILL-R-501)

    OVERDUE --> PAID : Paiement reçu après retard (BILL-R-302/402)

    PAID --> [*] : Statut final

    note right of DRAFT
        fne_status : PENDING → SUBMITTED → CERTIFIED
                              ↘ FAILED (après 5 relances)
        Paiement autorisé dès DRAFT [TV-04]
    end note

    note right of PAID
        Si paiement reçu avant FNE :
        La certification FNE continue en arrière-plan (BILL-R-204)
    end note

Machine à états FNE (orthogonale au statut facture) :

stateDiagram-v2
    [*] --> PENDING : Facture créée
    PENDING --> SUBMITTED : Tentative de soumission envoyée
    SUBMITTED --> CERTIFIED : Réponse DGI positive
    SUBMITTED --> PENDING : Échec → relance (< 5 tentatives)
    SUBMITTED --> FAILED : Toutes les relances épuisées
    FAILED --> SUBMITTED : Re-soumission manuelle admin
    CERTIFIED --> [*]

B3. Tables de décision

Table 1 - Boutons d'action affichés sur une facture (vue tenant)

Statut facture fne_status Bouton "Payer maintenant" Lien "Facture certifiée PDF" Lien "Quittance de paiement"
DRAFT PENDING / SUBMITTED ✅ Visible + actif [TV-04]
DRAFT CERTIFIED ✅ Visible + actif
DRAFT FAILED ✅ Visible + actif
CERTIFIED CERTIFIED ✅ Visible + actif
OVERDUE CERTIFIED ✅ Visible + actif (avec badge "En retard")
OVERDUE PENDING ✅ Visible + actif (avec badge "En retard")
PAID CERTIFIED
PAID PENDING / SUBMITTED ❌ (en cours)
PAID FAILED

Table 2 - Actions disponibles pour l'admin plateforme sur une facture

Statut facture fne_status "Marquer payée" (virement) "Re-soumettre FNE" "Générer quittance PDF"
DRAFT PENDING / SUBMITTED
DRAFT CERTIFIED
DRAFT FAILED
CERTIFIED CERTIFIED
OVERDUE tout ✅ (si FAILED)
PAID tout ✅ (si FAILED)

B4. Modèle de données

Les entités suivantes s'ajoutent au modèle V1 PRD §27. Toutes les tables sont scopées par tenant_id.

tenant_billing_config

Colonne Type Contraintes Description
id UUID PK
tenant_id UUID FK → tenant, UNIQUE Un seul config par tenant
plan_type VARCHAR NOT NULL, CHECK ∈ {MONTHLY, ANNUAL}
price_ht INTEGER NOT NULL, > 0 XOF, mensuel
annual_price_ht INTEGER NULL XOF, annuel ; NULL si plan MONTHLY uniquement
tva_rate NUMERIC(5,4) NOT NULL, DEFAULT 0.1800
tenant_nif VARCHAR(50) NULL NIF/CC du tenant pour la facture
billing_contact_email VARCHAR(255) NOT NULL
payment_terms_days INTEGER NOT NULL, DEFAULT 15
onboarding_fee_ht INTEGER NOT NULL, DEFAULT 0 XOF
billing_status VARCHAR NOT NULL, DEFAULT 'ACTIVE', CHECK ∈ {ACTIVE, PAUSED} SUSPENDED = V2
notes TEXT NULL Notes internes admin plateforme
created_at TIMESTAMPTZ NOT NULL
updated_at TIMESTAMPTZ NOT NULL

invoice

Colonne Type Contraintes Description
id UUID PK
tenant_id UUID FK → tenant, NOT NULL
invoice_number VARCHAR(20) UNIQUE, NOT NULL BILL-AAAA-NNNN
invoice_type VARCHAR NOT NULL, CHECK ∈ {SUBSCRIPTION, ONBOARDING_FEE, ONE_OFF}
period_start DATE NULL Nul pour ONE_OFF
period_end DATE NULL Nul pour ONE_OFF
invoice_date DATE NOT NULL
due_date DATE NOT NULL
status VARCHAR NOT NULL, CHECK ∈ {DRAFT, CERTIFIED, PAID, OVERDUE}
fne_status VARCHAR NOT NULL, CHECK ∈ {PENDING, SUBMITTED, CERTIFIED, FAILED}
fne_number VARCHAR(100) NULL Numéro DGI
fne_qr_code_url TEXT NULL URL du QR code DGI
fne_submitted_at TIMESTAMPTZ NULL
fne_certified_at TIMESTAMPTZ NULL
fne_retry_count INTEGER NOT NULL, DEFAULT 0
amount_ht INTEGER NOT NULL XOF
tva_amount INTEGER NOT NULL XOF
amount_ttc INTEGER NOT NULL XOF
tva_rate_snapshot NUMERIC(5,4) NOT NULL Snapshot au moment de la génération
tenant_nif_snapshot VARCHAR(50) NULL Snapshot
billing_email_snapshot VARCHAR(255) NOT NULL Snapshot
payment_terms_days_snapshot INTEGER NOT NULL Snapshot
pdf_draft_url TEXT NULL MinIO path
pdf_certified_url TEXT NULL MinIO path, disponible après FNE
overdue_reminder_sent_at TIMESTAMPTZ NULL
paid_at TIMESTAMPTZ NULL
created_by UUID FK → user Admin plateforme
created_at TIMESTAMPTZ NOT NULL
updated_at TIMESTAMPTZ NOT NULL

Contrainte d'idempotence :

UNIQUE (tenant_id, invoice_type, period_start, period_end)
  WHERE invoice_type IN ('SUBSCRIPTION', 'ONBOARDING_FEE')

invoice_line_item

Colonne Type Contraintes Description
id UUID PK
invoice_id UUID FK → invoice, NOT NULL
description VARCHAR(500) NOT NULL
quantity INTEGER NOT NULL, DEFAULT 1
unit_price_ht INTEGER NOT NULL XOF
total_ht INTEGER NOT NULL = quantity × unit_price_ht
sort_order INTEGER NOT NULL, DEFAULT 0
created_at TIMESTAMPTZ NOT NULL

billing_payment

Colonne Type Contraintes Description
id UUID PK
invoice_id UUID FK → invoice, NOT NULL
payment_method VARCHAR NOT NULL, CHECK ∈ {MOBILE_MONEY, BANK_TRANSFER}
provider_reference VARCHAR(255) NOT NULL ID transaction prestataire ou référence virement
amount_ttc INTEGER NOT NULL XOF
paid_at TIMESTAMPTZ NOT NULL
recorded_by UUID FK → user, NULL NULL = automatique (webhook)
idempotency_key VARCHAR(255) NULL Clé d'idempotence mobile money
created_at TIMESTAMPTZ NOT NULL

Contrainte : UNIQUE (idempotency_key) WHERE idempotency_key IS NOT NULL


B5. Implications multi-tenant

Isolation des données

  • Toutes les requêtes sur invoice, invoice_line_item, billing_payment sont scopées par tenant_id résolu depuis le contexte d'authentification Keycloak.
  • Un administrateur tenant ne voit que ses propres factures. La résolution du tenant_id est faite côté serveur, jamais fourni par le client.
  • L'administrateur plateforme voit toutes les factures (rôle PLATFORM_ADMIN), mais chaque ligne affiche le nom du tenant pour identification.

Profil pays et TVA

  • Le taux TVA par défaut (tva_rate = 0.1800) est défini dans le profil pays CI.
  • En V1, tous les tenants sont CI → taux fixe 18 %.
  • En V2, le profil pays du tenant déterminera le taux applicable (ex : CI → 18 %, CM → 19,25 %, etc.).
  • Le champ tva_rate_snapshot dans invoice garantit qu'une modification du profil pays n'affecte pas les factures déjà émises.

Config par tenant vs config globale

  • TenantBillingConfig est une entité par tenant : le prix, le NIF, le contact de facturation et les frais d'installation sont tous propres au tenant.
  • Les coordonnées bancaires de la plateforme (émetteur des factures) sont une configuration globale (non scopée par tenant), stockée dans la configuration applicative (non dans la DB).

Profils DB V1 (shared + BYO-DB)

  • Le module BILL fonctionne identiquement sur les deux profils DB V1 (shared-schema et BYO-DB).
  • Sur BYO-DB : les tables tenant_billing_config, invoice, invoice_line_item, billing_payment sont créées dans la base du tenant comme toutes les autres tables scopées tenant.
  • Note : invoice_number (BILL-AAAA-NNNN) doit rester globalement unique sur l'ensemble de la plateforme → sur BYO-DB, la séquence globale doit être maintenue dans la base partagée de la plateforme, non dans la base du tenant. À préciser par l'ARCHITECT.

B6. Dépendances inter-modules

Modules consommés par BILL

Module Dépendance Détail
TENANT Lecture tenant_id, nom légal, adresse, country_code pour les factures. TenantBillingConfig est créée/éditée via l'interface TENANT admin.
AUTH Authz Rôle PLATFORM_ADMIN pour les actions admin ; rôle TENANT_ADMIN pour la vue tenant. Keycloak fournit le token avec les claims nécessaires.
PAY Abstraction paiement BILL consomme l'abstraction PaymentProvider du module PAY pour les paiements mobile money. Le prestataire concret (agrégateur ou direct) est résolu par le profil pays CI via PAY. BILL ne contient pas son propre client prestataire.
AUDIT Écriture Tous les événements BILL-R-700 sont émis vers le module AUDIT.

API / événements exposés par BILL

Endpoint / Événement Consommateur Description
GET /api/billing/invoices Tenant admin (front) Liste des factures du tenant authentifié
GET /api/billing/invoices/{id} Tenant admin (front) Détail d'une facture
POST /api/billing/invoices/{id}/pay/mobile-money Tenant admin (front) Initier le paiement mobile money
GET /api/billing/invoices/{id}/pdf/certified Tenant admin (front) Télécharger le PDF certifié (302 redirect MinIO presigned URL)
GET /api/billing/invoices/{id}/pdf/receipt Tenant admin (front) Télécharger la quittance de paiement
POST /api/v1/internal/billing/webhook/payment Prestataire de paiement Webhook de confirmation de paiement
GET /api/admin/billing/invoices Admin plateforme (front) Liste globale de toutes les factures
POST /api/admin/billing/invoices Admin plateforme (front) Générer une nouvelle facture
POST /api/admin/billing/invoices/{id}/mark-paid Admin plateforme (front) Réconciliation virement bancaire
POST /api/admin/billing/invoices/{id}/resubmit-fne Admin plateforme (front) Re-soumettre à FNE
GET /api/admin/billing/tenants/{id}/config Admin plateforme (front) Lire la config de facturation d'un tenant
PUT /api/admin/billing/tenants/{id}/config Admin plateforme (front) Mettre à jour la config de facturation

Événements internes émis

Événement Spring (ApplicationEvent) Consommateur Déclencheur
InvoiceGenerated AUDIT Création facture
InvoicePaid AUDIT Paiement confirmé
InvoiceOverdue AUDIT Job quotidien
FneCertified AUDIT, PDF generation service Certification DGI
FneFailed AUDIT, email admin Toutes relances épuisées

Partie C - Contraintes et Limites

C1. Conformité réglementaire

FNE - Facture Normalisée Électronique (CI) [TV-01 à TV-06]

⚠️ Temporairement validé - l'ensemble des règles FNE ci-dessous est basé sur la connaissance publique du dispositif DGI CI. Un /legal-audit FNE doit être conduit avant la mise en production de BILL-001.

  • Obligation : toute facture B2B émise par une entreprise domiciliée en CI ou facturant un client en CI doit être certifiée par le système FNE de la Direction Générale des Impôts.
  • Certification : la DGI attribue un numéro de facture normalisée (fne_number) et un QR code permettant à l'administration fiscale de retrouver la facture dans sa base. Ce numéro doit figurer sur le PDF remis au client.
  • TVA : les services SaaS B2B sont assujettis à la TVA au taux de 18 % (TV-01). Les montants HT, TVA et TTC doivent figurer en clair sur la facture.
  • Rétention : les factures et leurs PDFs doivent être conservés au minimum 10 ans (TV-05 - à confirmer par /legal-audit FNE).
  • Paiement avant certification : autorisé par décision produit (TV-04) - à valider légalement.

TVA et obligations fiscales du prestataire

  • La plateforme opératrice est l'émetteur des factures. Son NIF/CC doit figurer sur chaque facture (OQ-01 - à renseigner avant BILL-001).
  • Le module BILL ne calcule pas de déclaration TVA ; il fournit les données nécessaires à la comptabilité de la plateforme (export CSV en V2).

ARTCI / Loi n°2013-450 (CI)

  • Les emails transactionnels de facturation (invoice issued, payment confirmed, overdue) sont adressés à l'administrateur du tenant (personne morale B2B), sur la base de l'exécution du contrat de service. La base légale ARTCI est présumée contractuelle (TV-06 - à confirmer).
  • Les données personnelles traitées par BILL sont limitées à : email de contact facturation, NIF/CC du tenant (donnée de la personne morale, pas de la personne physique). Minimisation respectée.

C2. Localisation

Dimension Règle
Langue Français uniquement en V1 pour toutes les interfaces BILL (back-office, emails, PDFs).
Monnaie XOF uniquement. Affichage : espace comme séparateur de milliers, 0 décimale. Ex : 59 000 XOF.
Dates Format DD/MM/AAAA. Ex : 01/05/2026.
Taux TVA Toujours affiché sous la forme TVA 18 %.
Emails Envoyés depuis une adresse noreply@[domaine-plateforme] avec un nom d'expéditeur en français.
PDF Gabarit en français, police lisible, compatible impression A4.
Numéro de téléphone Non utilisé dans le module BILL (email uniquement).

C3. Sécurité

Règle Détail
Isolation tenant Toute requête d'un tenant admin est scopée par tenant_id résolu depuis le token Keycloak. Aucune donnée de facturation d'un autre tenant n'est jamais exposée.
Contrôle d'accès Les endpoints /admin/billing/* exigent le rôle PLATFORM_ADMIN. Les endpoints /billing/* (vue tenant) exigent TENANT_ADMIN. Authz sur chaque endpoint.
Vérification des webhooks de paiement BILL vérifie la signature HMAC de chaque webhook reçu du prestataire de paiement avant tout traitement (même logique que PAY).
Idempotence des webhooks Un webhook de paiement déjà traité est ignoré silencieusement (vérification sur idempotency_key).
PDFs en MinIO Les PDFs de factures et quittances sont stockés en MinIO avec une URL pré-signée à durée limitée (ex : 15 min) lors du téléchargement. Jamais d'URL permanente publique.
Pas de données de carte en clair BILL ne stocke aucune donnée de carte bancaire. Seule la référence de transaction du prestataire est stockée.
Montants non modifiables Une fois une facture créée, ses montants (amount_ht, tva_amount, amount_ttc) sont immuables. Toute correction nécessite une annulation hors plateforme + nouvelle facture (V1).
Chiffrement en transit Toutes les communications BILL → FNE, BILL → prestataire paiement, BILL → serveur email se font en HTTPS/TLS.
Audit log immuable Tous les événements BILL alimentent le module AUDIT (append-only).

C4. Connectivité

Le module BILL est un module opérateur-side (back-office) et admin-side uniquement. Il n'est pas exposé aux assurés finaux. Les contraintes de connectivité basse bande passante du module CP (portail client) ne s'appliquent pas ici.

Exigences spécifiques :

Scénario Comportement attendu
FNE indisponible La génération de facture réussit (DRAFT). FNE est soumise en arrière-plan avec la politique de relance BILL-R-202. L'admin plateforme est alerté si FNE reste indisponible > 3 h.
Prestataire de paiement indisponible Le bouton "Payer maintenant" affiche un message d'erreur clair ; le tenant peut réessayer plus tard. Aucune donnée de paiement n'est créée jusqu'à confirmation du prestataire.
Webhook de paiement retardé La facture reste dans son statut courant (DRAFT/CERTIFIED/OVERDUE) jusqu'à réception du webhook. Une requête de statut polling peut être implémentée en V2. En V1, la réconciliation manuelle (virement) reste disponible en fallback si le webhook ne revient pas.
Serveur email indisponible L'échec d'envoi d'un email transactionnel est loggé (événement audit) mais ne bloque pas la génération ou la confirmation de facture. L'email peut être re-déclenché manuellement par l'admin en V2.

C5. Hors périmètre

Les fonctionnalités suivantes sont explicitement hors périmètre V1 afin de prévenir le scope creep :

Hors périmètre Version cible Justification
Scheduler de récurrence automatique (génération mensuelle/annuelle sans action admin) V2 Trop peu de tenants pilotes pour justifier la complexité.
Avoirs (credit notes) et annulations de factures dans la plateforme V2 Cas rares en phase pilote ; gérés manuellement hors plateforme.
Dunning automatisé (chaîne de relances, escalades, suspension) V2 Nécessite une gouvernance contractuelle claire et un préavis légal.
Suspension automatique d'accès tenant en cas d'impayé V2 Idem ; impact opérationnel fort.
Switching de plan mid-cycle avec crédit automatique V2 Nécessite un moteur de crédit/débit.
Self-service pricing et activation de plan par le tenant V2 Modèle de vente assistée en V1.
Facturation multi-pays (SN, BJ, CM, GA - autres régimes e-invoicing) V2/V3 V1 = CI uniquement ; la résolution via le profil pays est déjà architecturée.
Exports comptables (CSV, Sage, QuickBooks) V2 Non demandé en V1.
Calcul déclaration TVA ou liasse fiscale Hors périmètre produit Responsabilité de la comptabilité de la plateforme.
Paiement par carte bancaire (CB/Visa/Mastercard) V2 Selon disponibilité du prestataire et KYC B2B ; non prioritaire en V1.
API de facturation exposée aux partenaires V3 Architecture API-first = V3.
Tableau de bord MRR/ARR/churn V2/V3 Analytics = V2 minimum.
Quittance tenant téléchargeable avant FNE si fne_status = FAILED V1 La quittance de paiement (preuve de transaction) est disponible ; seul le PDF certifié DGI est bloqué sur fne_status ≠ CERTIFIED.

Fin du document - billing-prd.md - v1.0 - 2026-04-30