Skip to content

Architecture Scell.io

Version : 1.0 Derniere mise a jour : 2026-03-03 Audience : Developpeurs integrateurs, contributeurs internes, CTO/DSI evaluateurs


Table des matieres


1. Vue d'ensemble

Scell.io est une plateforme B2B micro-SaaS de facturation electronique (Factur-X/UBL/CII) et de signature electronique simple (eIDAS EU-SES). L'architecture suit trois principes fondamentaux : API-first, multi-tenant et event-driven.

1.1 Structure Monorepo

Le projet est organise en monorepo avec trois composants principaux :

scell.io/
  backend/           -> Laravel 12 API (PHP 8.2+)
  frontend/          -> React 19 + TypeScript SPA
  superpdp-agent/    -> MCP Server Agent (Node.js)
  assets/            -> Brand assets (logos, icones)
  docs/              -> Documentation technique
  nginx.conf.example -> Configuration reverse proxy

1.2 Diagramme d'architecture globale

mermaid
graph TB
    subgraph Clients
        SPA["React 19 SPA<br/>scell.io"]
        SDK["SDK / API directe<br/>Developpeurs tiers"]
        WIDGET["Widget Onboarding<br/>Sites partenaires"]
        MCP_CLIENT["Agents IA<br/>MCP Protocol"]
    end

    subgraph "Scell.io Platform"
        subgraph "Backend Laravel 12"
            API["API REST<br/>api.scell.io"]
            HORIZON["Laravel Horizon<br/>Queue Worker"]
            SCHEDULER["Task Scheduler<br/>Cron Jobs"]
            MCP_SERVER["MCP Server<br/>54 outils"]
        end

        subgraph "Data Layer"
            PG["PostgreSQL 17<br/>Base principale"]
            REDIS["Redis 7.4<br/>Cache / Sessions / Queues"]
            S3["Scaleway S3<br/>Documents / PDF"]
        end
    end

    subgraph "Services Externes"
        OPENAPI["OpenAPI.com<br/>Signatures eIDAS"]
        SUPERPDP["SuperPDP<br/>Facturation PDP"]
        BULKGATE["BulkGate<br/>SMS OTP"]
        STRIPE["Stripe<br/>Paiements"]
        INSEE["INSEE Sirene<br/>Verification SIRET"]
        VIES["VIES UE<br/>Verification TVA"]
        TSA["FreeTSA<br/>Horodatage RFC3161"]
    end

    SPA --> API
    SDK --> API
    WIDGET --> API
    MCP_CLIENT --> MCP_SERVER

    API --> PG
    API --> REDIS
    API --> S3
    HORIZON --> PG
    HORIZON --> REDIS

    API --> OPENAPI
    API --> SUPERPDP
    API --> BULKGATE
    API --> STRIPE
    API --> INSEE
    API --> VIES
    HORIZON --> TSA

    OPENAPI -.->|Webhooks| API
    SUPERPDP -.->|Webhooks| API
    STRIPE -.->|Webhooks| API

1.3 Stack technique

CoucheTechnologieVersionRole
BackendLaravel12Framework API REST
ORMEloquentBuilt-inCouche d'acces donnees
DatabasePostgreSQL17Stockage principal (UUID natif, JSONB)
Cache/QueueRedis7.4Sessions, cache, file de taches
Queue WorkerLaravel HorizonLatestTraitement asynchrone
AuthSanctum + Spatie PermissionLatestAuthentification multi-mode
FrontendReact19Interface utilisateur SPA
BuildVite7Bundler et dev server
Admin UIRefinev5Framework CRUD admin
ComposantsAnt Design5Bibliotheque de composants
Routage clientReact Router7Navigation SPA
API DocsL5-SwaggerLatestGeneration OpenAPI
MCPAnthropic MCP2024-11-05Integration agents IA

1.4 Principes architecturaux

  1. API-first : toute fonctionnalite est d'abord exposee via l'API REST. Le frontend et les SDK sont des consommateurs au meme titre que les clients externes.
  2. Multi-tenant : isolation stricte des donnees par tenant_id avec verification a chaque couche (middleware, scope Eloquent, policies).
  3. Event-driven : les operations longues (generation Factur-X, appels signature, webhooks) sont traitees de maniere asynchrone via Laravel Horizon.
  4. Immutabilite fiscale : conformite NF525 avec un grand livre append-only, chaine de hachage SHA256, et horodatage RFC3161.
  5. Sandbox-first : chaque fonctionnalite dispose d'un mode sandbox avec des services mock pour les tests sans cout.

2. Architecture Backend

2.1 Couches applicatives

Le backend suit une architecture en couches classique avec un pattern Service Layer pour isoler la logique metier des controleurs.

mermaid
graph TB
    subgraph "Couche HTTP"
        MW["Middleware<br/>(18 middleware custom)"]
        CTRL["Controllers<br/>(28 controllers)"]
        REQ["Form Requests<br/>(Validation)"]
        RES["API Resources<br/>(Serialisation)"]
    end

    subgraph "Couche Metier"
        SVC["Services<br/>(Logique metier)"]
        OBS["Observers<br/>(Evenements modele)"]
        JOBS["Jobs<br/>(Traitement async)"]
        NOTIF["Notifications<br/>(Email, SMS)"]
    end

    subgraph "Couche Donnees"
        MDL["Models Eloquent<br/>(41 modeles)"]
        TRAITS["Traits<br/>(HasTenantScope, HasUuids)"]
        SCOPES["Scopes<br/>(forTenant, active)"]
    end

    subgraph "Couche Persistance"
        PG["PostgreSQL 17"]
        REDIS["Redis 7.4"]
        S3["S3 Storage"]
    end

    MW --> CTRL
    CTRL --> REQ
    CTRL --> SVC
    CTRL --> RES
    SVC --> MDL
    SVC --> JOBS
    OBS --> SVC
    MDL --> TRAITS
    MDL --> SCOPES
    MDL --> PG
    JOBS --> REDIS
    SVC --> S3

2.2 Pattern Service Layer

La logique metier est encapsulee dans des services dedies, jamais dans les controleurs. Chaque service a une responsabilite unique.

Services principaux :

ServiceResponsabilite
InvoiceNumberingServiceNumerotation sequentielle par tenant/sub-tenant
HorstekoFacturXGeneratorGeneration Factur-X / UBL / CII
FiscalLedgerServiceGrand livre fiscal immutable
FiscalAnchoringServiceHorodatage RFC3161 TSA
FiscalClosingServiceClotures quotidiennes/mensuelles/annuelles
FecExportServiceExport FEC (Fichier des Ecritures Comptables)
OnboardingServiceWorkflow d'onboarding B2B
TenantBillingServiceFacturation consolidee des tenants
OpenAPIClientIntegration OpenAPI.com (signatures)
SuperPDPServiceIntegration SuperPDP (facturation)
SmsServiceIntegration BulkGate (SMS OTP)

Exemple de pattern Service Layer :

php
// Controller : delegation au service
class TenantInvoiceController extends Controller
{
    public function store(StoreInvoiceRequest $request, string $subTenantId)
    {
        $invoice = app(InvoiceNumberingService::class)
            ->getNextNumber($request->tenant->id, $subTenantId);

        // Le controleur orchestre, le service execute
        return new TenantInvoiceResource($invoice);
    }
}

// Service : logique metier isolee
class InvoiceNumberingService
{
    public function getNextNumber(
        string $tenantId,
        ?string $subTenantId,
        ?int $year = null,
        ?int $month = null
    ): string {
        // Gestion atomique de la sequence
        // Formats: YYYYNNNNN, NNNNNN, PREFIXNNNNNNSUFFIX, YYMMx{N}
    }
}

2.3 Pattern Observer

Les observers Eloquent interceptent les evenements du cycle de vie des modeles pour appliquer les regles metier de maniere transversale.

ObserverModeleResponsabilite
InvoiceObserverInvoiceImmutabilite fiscale, enregistrement audit, creation entree fiscale
CreditNoteObserverCreditNoteImmutabilite fiscale, blocage suppression si non-brouillon

Mecanisme d'immutabilite :

php
// Dans Invoice::updating()
// Les champs fiscaux sont verrouilles une fois le statut != 'draft'
const IMMUTABLE_FISCAL_FIELDS = [
    'total_ht', 'total_ttc', 'total_tax', 'invoice_number',
    'issue_date', 'seller_name', 'seller_siret', 'seller_address',
    'buyer_name', 'buyer_siret', 'buyer_address', 'currency',
    'user_id', 'company_id',
];

// Toute tentative de modification est :
// 1. Bloquee (RuntimeException)
// 2. Journalisee dans fiscal_audit_log

2.4 Pattern Circuit Breaker

Le service SuperPDP implemente un circuit breaker pour prevenir les defaillances en cascade lors d'indisponibilite du service externe.

mermaid
stateDiagram-v2
    [*] --> CLOSED
    CLOSED --> OPEN: Seuil d'echecs atteint
    OPEN --> HALF_OPEN: Timeout de recuperation expire
    HALF_OPEN --> CLOSED: Appel reussi
    HALF_OPEN --> OPEN: Appel echoue

    CLOSED: Fonctionnement normal
    CLOSED: Compteur d'echecs actif
    OPEN: Appels bloques
    OPEN: Reponse fallback immediate
    HALF_OPEN: Test de recuperation
    HALF_OPEN: 1 seul appel autorise

Configuration :

php
// config/superpdp.php
'circuit_breaker' => [
    'failure_threshold' => 5,      // Nombre d'echecs avant ouverture
    'recovery_timeout'  => 60,     // Secondes avant test de recuperation
],

2.5 Systeme de queues (Horizon)

Laravel Horizon supervise le traitement asynchrone des taches.

Jobs principaux :

JobDeclencheurTimeoutDescription
ProcessInvoiceCreation facture120sGeneration Factur-X, audit trail, entree fiscale, webhooks
ProcessSignatureCreation signature120sAppel OpenAPI.com, audit trail, webhooks
SendWebhookEvenement metier30sEnvoi webhook avec retry exponentiel
ProcessSuperPDPWebhookWebhook entrant120sMise a jour statut facture/avoir
ProcessIncomingInvoiceWebhookWebhook entrant120sReception facture fournisseur
SyncIncomingInvoicesJobCron quotidien300sSynchronisation factures entrantes

Commandes planifiees :

CommandeFrequenceDescription
fiscal:daily-closingQuotidien 00:05 UTCCloture fiscale journaliere
fiscal:monthly-closingMensuelCloture fiscale mensuelle
fiscal:integrity-checkQuotidien 02:00 UTCVerification integrite chaine
fiscal:check-version-changeQuotidien 03:00 UTCDetection changement version logiciel
invoices:generate-monthly1er du mois 01:00 UTCFacturation tenant consolidee
superpdp:sync-statusesToutes les 30 minSynchronisation statuts SuperPDP

2.6 Middleware Stack

18 middleware personnalises organisent le pipeline de traitement des requetes.

mermaid
graph LR
    subgraph "Authentification"
        A1["ValidateApiKey<br/>X-API-Key"]
        A2["TenantApiKeyMiddleware<br/>X-Tenant-Key"]
        A3["ValidatePartnerKey<br/>X-API-Key"]
        A4["VerifySuperPDPSignature<br/>HMAC-SHA256"]
    end

    subgraph "Isolation Tenant"
        B1["EnsureTenantIsolation"]
        B2["SubTenantMiddleware"]
        B3["ResolveDatabaseConnection"]
    end

    subgraph "Balance & Rate Limit"
        C1["CheckBalance"]
        C2["TenantBalanceMiddleware"]
        C3["ApiRateLimiter"]
        C4["TenantRateLimiter"]
        C5["OnboardingRateLimiter"]
        C6["McpRateLimiter"]
    end

    subgraph "Fiscal NF525"
        D1["FiscalKillSwitchGuard"]
        D2["FiscalPeriodGuard"]
        D3["FiscalScopeGuard<br/>fiscal:read/write/admin"]
        D4["FiscalAccessLogger"]
    end

    A2 --> B1 --> B2 --> C2 --> D1 --> D2 --> D4

3. Architecture Frontend

3.1 Structure des composants

Le frontend est une SPA React 19 utilisant Refine v5 pour les fonctionnalites CRUD admin et Ant Design 5 comme bibliotheque de composants.

mermaid
graph TB
    subgraph "Application React 19"
        APP["App.tsx<br/>ConfigProvider + Refine"]

        subgraph "Pages Publiques"
            HOME["Home"]
            PRICING["Pricing"]
            DOCS["Documentation"]
            FAQ["FAQ"]
            SDK["SDK"]
            LEGAL["Pages legales<br/>CGU, CGV, RGPD"]
        end

        subgraph "Pages Authentifiees"
            DASH["Dashboard"]
            COMP["Companies"]
            KEYS["API Keys"]
            BAL["Balance"]
            WH["Webhooks"]
        end

        subgraph "Pages Admin"
            ADMIN["AdminDashboard"]
            USERS["UsersList"]
            CREDITS["CreditStats"]
            OA_SETTINGS["OpenAPISettings"]
        end

        subgraph "Pages Tenant"
            T_DASH["TenantDashboard"]
            T_BILL["TenantBilling"]
            T_SUB["SubTenantsList"]
            T_KYB["KybDocuments"]
        end

        subgraph "Onboarding"
            ONB_START["Start"]
            ONB_VERIFY["Verify SIRET/TVA"]
            ONB_DOCS["Documents KYB"]
            ONB_DONE["Complete"]
        end
    end

    subgraph "Providers"
        AUTH["authProvider<br/>Sanctum Bearer Token"]
        DATA["dataProvider<br/>REST API"]
        T_AUTH["tenantAuthProvider<br/>X-Tenant-Key"]
        T_DATA["tenantDataProvider<br/>REST API Tenant"]
    end

    subgraph "Contexts"
        CTX_THEME["ThemeContext<br/>Dark/Light"]
        CTX_ENV["EnvironmentContext<br/>Sandbox/Production"]
        CTX_TENANT["TenantContext<br/>Tenant courant"]
        CTX_ONB["OnboardingContext<br/>Etat du flux"]
    end

    APP --> AUTH
    APP --> DATA
    APP --> T_AUTH
    APP --> T_DATA
    APP --> CTX_THEME
    APP --> CTX_ENV

3.2 Integration Refine v5

Refine v5 fournit les hooks CRUD et la gestion de l'authentification :

  • dataProvider : traduit les operations CRUD en appels REST vers api.scell.io/api/v1
  • authProvider : gere login/logout/register via Sanctum (Bearer Token)
  • tenantDataProvider : meme pattern mais avec header X-Tenant-Key
  • Resources declarees : companies, invoices, signatures, webhooks, api-keys, balance

3.3 Structure du routage

/                          -> Home (landing)
/pricing                   -> Pricing
/documentation             -> Documentation API
/sdk                       -> SDK info
/faq                       -> FAQ
/contact                   -> Contact
/about                     -> A propos
/changelog                 -> Notes de version
/legal/*                   -> Pages legales

/login                     -> Connexion
/register                  -> Inscription

/dashboard                 -> Dashboard utilisateur
/dashboard/companies       -> Gestion entreprises
/dashboard/api-keys        -> Gestion cles API
/dashboard/balance         -> Solde et transactions
/dashboard/webhooks        -> Gestion webhooks

/admin/*                   -> Pages administration

/tenant/*                  -> Pages tenant multi-tenant

/onboarding/*              -> Flux d'onboarding partenaire

3.4 Gestion d'etat

L'application utilise les Context React pour la gestion d'etat globale, sans bibliotheque externe (pas de Redux, Zustand, etc.) :

ContextPerimetreDonnees
ThemeContextGlobalMode dark/light, variables CSS
EnvironmentContextGlobalSandbox ou production
TenantContextTenantTenant courant, liste tenants, switch
OnboardingContextOnboardingSession, progression, erreurs

3.5 Internationalisation

Le frontend supporte le francais et l'anglais via i18next et react-i18next :

src/i18n/locales/
  en/ -> common.json, dashboard.json, landing.json, pricing.json, tenant.json
  fr/ -> common.json, dashboard.json, landing.json, pricing.json, tenant.json

3.6 Widget d'onboarding embarquable

Un widget autonome (frontend/packages/onboarding-widget/) permet aux partenaires d'integrer le flux d'onboarding dans leurs propres sites :

  • Technologie : TypeScript pur (pas de React), bundle Vite
  • Etapes : Start -> SIRET -> Verification TVA -> Documents KYB -> Complete
  • Integration : <script src="scell-onboarding.js"></script> + new ScellOnboarding({...})

4. Architecture Multi-Tenant

4.1 Modele de donnees hierarchique

L'architecture multi-tenant suit une hierarchie a 4 niveaux :

mermaid
graph TB
    PARTNER["Partner<br/>Partenaire integrateur<br/>(ex: cabinet comptable)"]
    TENANT["Tenant<br/>Entreprise cliente<br/>(ex: PME)"]
    SUBTENANT["SubTenant<br/>Sous-entite<br/>(ex: filiale, client du tenant)"]
    COMPANY["Company<br/>Entite juridique<br/>(SIRET, TVA)"]
    INVOICE["Invoice / CreditNote<br/>Documents"]

    PARTNER -->|1:N| TENANT
    TENANT -->|1:N| SUBTENANT
    TENANT -->|1:N| COMPANY
    SUBTENANT -->|1:N| COMPANY
    COMPANY -->|1:N| INVOICE

Relations :

  • Un Partner (cabinet comptable, ERP) onboarde plusieurs Tenants
  • Un Tenant gere ses SubTenants (ses propres clients) et ses Companies
  • Chaque Company possede des Invoices, CreditNotes et Signatures

4.2 Strategies d'isolation

L'isolation des donnees est appliquee a trois niveaux :

Niveau 1 -- Middleware : chaque requete est authentifiee et le tenant est resolu avant d'atteindre le controleur.

mermaid
sequenceDiagram
    participant Client
    participant TenantApiKeyMiddleware
    participant EnsureTenantIsolation
    participant SubTenantMiddleware
    participant Controller

    Client->>TenantApiKeyMiddleware: X-Tenant-Key: sk_live_xxx
    TenantApiKeyMiddleware->>TenantApiKeyMiddleware: Verifier cle, tenant actif, KYB verifie
    TenantApiKeyMiddleware->>EnsureTenantIsolation: Request->tenant = Tenant
    EnsureTenantIsolation->>EnsureTenantIsolation: Verifier que l'entite demandee<br/>appartient au tenant
    EnsureTenantIsolation->>SubTenantMiddleware: Passer si valide
    SubTenantMiddleware->>SubTenantMiddleware: Valider sub-tenant<br/>appartient au tenant
    SubTenantMiddleware->>Controller: Request enrichie<br/>(tenant + subTenant)

Niveau 2 -- Scopes Eloquent : le trait HasTenantScope filtre automatiquement les requetes par tenant_id.

php
// Trait HasTenantScope
trait HasTenantScope
{
    public function scopeForTenant($query, ?string $tenantId)
    {
        return $query->where('tenant_id', $tenantId);
    }
}

Niveau 3 -- Row-Level Security (RLS) : des policies PostgreSQL ajoutent une couche de protection au niveau de la base de donnees pour les tables fiscales sensibles.

4.3 Modes d'authentification

ModeHeaderCibleMiddleware
Bearer TokenAuthorization: Bearer {token}Dashboard utilisateurauth:sanctum
Secret KeyX-Tenant-Key: sk_live_xxxAPI externetenant.key
Tenant KeyX-Tenant-Key: sk_live_xxxAPI multi-tenanttenant.key
Publishable KeyX-Publishable-Key: pk_live_xxxAPI onboardingpublishable.key

4.4 Resolution de connexion base de donnees

Le middleware ResolveDatabaseConnection permet de resoudre la connexion base de donnees par tenant. Actuellement, tous les tenants partagent la meme base avec isolation par colonne (tenant_id). L'architecture est prete pour une evolution vers une isolation par base de donnees si necessaire.


5. Architecture Fiscale (NF525)

Le module fiscal assure la conformite a la norme NF525 (certification des logiciels de caisse) et aux exigences ISCA (Inalterable, Securise, Conserve, Archive).

5.1 Flux fiscal complet

mermaid
graph TB
    subgraph "Declenchement"
        INV["Facture creee"]
        CN["Avoir cree"]
        PAY["Paiement enregistre"]
    end

    subgraph "Enregistrement"
        FE["FiscalEntry<br/>Entree immutable<br/>data_snapshot + data_hash"]
        SEQ["FiscalSequence<br/>Numero sequence atomique<br/>(verrouillage optimiste)"]
        CHAIN["Chaine de hachage<br/>previous_hash -> chain_hash<br/>SHA256"]
    end

    subgraph "Cloture"
        DAILY["Cloture quotidienne<br/>00:05 UTC"]
        MONTHLY["Cloture mensuelle"]
        YEARLY["Cloture annuelle"]
        FEC["Export FEC<br/>Fichier des Ecritures<br/>Comptables"]
    end

    subgraph "Attestation"
        ATT["Attestation annuelle<br/>PDF signe"]
        ANCHOR["Ancrage TSA<br/>RFC3161<br/>(FreeTSA)"]
    end

    subgraph "Integrite"
        CHECK["Verification quotidienne<br/>02:00 UTC"]
        AUDIT["Fiscal Audit Log<br/>Tentatives de modification<br/>Violations d'immutabilite"]
        KS["Kill Switch<br/>Arret d'urgence"]
    end

    INV --> FE
    CN --> FE
    PAY --> FE
    FE --> SEQ
    SEQ --> CHAIN

    CHAIN --> DAILY
    DAILY --> MONTHLY
    MONTHLY --> YEARLY
    DAILY --> FEC

    YEARLY --> ATT
    FE --> ANCHOR

    CHECK --> CHAIN
    FE -.->|Violation| AUDIT
    KS -.->|Bloque| FE

5.2 Chaine de hachage

Chaque FiscalEntry est chainee cryptographiquement a la precedente via SHA256 :

Entry N-1: chain_hash = SHA256(data_hash + previous_hash + sequence_number)
Entry N  : previous_hash = Entry(N-1).chain_hash
           chain_hash = SHA256(data_hash + previous_hash + sequence_number)
Entry N+1: previous_hash = Entry(N).chain_hash
           ...

Garanties :

  • Inalterable : les champs fiscaux sont verrouilles apres soumission (traits LogsImmutabilityViolation)
  • Securise : chaque modification tentee est journalisee dans fiscal_audit_log
  • Conserve : retention minimale de 6 ans (configurable)
  • Archive : ancrage TSA RFC3161 pour preuve juridique

5.3 Separation des roles (ISCA S-10)

Le middleware FiscalScopeGuard applique une separation stricte des roles :

ScopeOperations autoriseesExemple
fiscal:readConsultation grand livre, closures, FEC, attestationsGET /tenant/fiscal/*
fiscal:writeClotures manuelles, creation/modification reglesPOST /tenant/fiscal/closings/daily
fiscal:adminKill-switch (activation/desactivation)POST /tenant/fiscal/kill-switch/activate

5.4 Tables fiscales

TableRoleParticularite
fiscal_entriesGrand livre immutableAppend-only, pas de timestamps updatable
fiscal_sequencesCompteur de sequenceVerrouillage optimiste (lock_version)
fiscal_closingsClotures periodiquesQuotidiennes, mensuelles, annuelles
fiscal_attestationsAttestations annuellesPDF signe, informations editeur
fiscal_anchorsHorodatage TSARFC3161 via FreeTSA
fiscal_kill_switchesArret d'urgenceUn seul par tenant
fiscal_integrity_checksRapports d'integriteQuotidien a 02:00 UTC
fiscal_audit_logJournal d'auditViolations, acces, modifications tentees
fiscal_rulesRegles de conformiteConfigurables par tenant
fiscal_rule_applicationsJournal d'application des reglesResultat par execution

6. Integrations Externes

6.1 OpenAPI.com (Signatures eIDAS)

Integration pour la signature electronique simple (EU-SES) conforme eIDAS.

mermaid
sequenceDiagram
    participant Client
    participant Scell API
    participant Horizon
    participant OpenAPI.com

    Client->>Scell API: POST /v1/signatures<br/>{file, signers, auth_method}
    Scell API->>Scell API: Verifier solde (1.20 EUR)
    Scell API->>Horizon: Dispatch ProcessSignature
    Scell API-->>Client: 201 {id, status: "waiting_signers"}

    Horizon->>OpenAPI.com: POST /signatures<br/>(OAuth2 Bearer Token)
    OpenAPI.com-->>Horizon: {openapi_id, signing_urls}
    Horizon->>Scell API: Mise a jour Signature (signing_urls)

    Note over OpenAPI.com: Signataire signe le document

    OpenAPI.com->>Scell API: POST /webhooks/openapi/signature<br/>{event: "completed"}
    Scell API->>Scell API: Mise a jour statut
    Scell API->>Client: Webhook: signature.completed

Authentification : OAuth2 avec refresh token automatique (OpenAPIOAuthService)

Methodes d'authentification signataire : SMS OTP (via BulkGate), email, biometrique

6.2 SuperPDP (Facturation electronique)

Integration avec la plateforme PDP pour la transmission de factures electroniques aux formats Factur-X, UBL et CII.

mermaid
sequenceDiagram
    participant Client
    participant Scell API
    participant Horizon
    participant SuperPDP
    participant CircuitBreaker

    Client->>Scell API: POST /v1/invoices<br/>{seller, buyer, lines}
    Scell API->>Scell API: Verifier solde (0.04 EUR)
    Scell API->>Horizon: Dispatch ProcessInvoice
    Scell API-->>Client: 201 {id, status: "draft"}

    Client->>Scell API: POST /v1/invoices/{id}/submit
    Scell API->>CircuitBreaker: Verifier etat
    alt Circuit CLOSED (normal)
        Scell API->>SuperPDP: POST /documents<br/>(Factur-X/UBL/CII)
        SuperPDP-->>Scell API: {superpdp_id, status: "processing"}
    else Circuit OPEN (fallback)
        Scell API-->>Client: 503 Service temporairement indisponible
    end

    Note over SuperPDP: Traitement du document

    SuperPDP->>Scell API: POST /webhooks/superpdp/invoice<br/>{status: "accepted"}
    Scell API->>Scell API: Mise a jour statut
    Scell API->>Client: Webhook: invoice.submitted

Formats supportes : Factur-X (MINIMUM, BASIC_WL, BASIC, EN16931, EXTENDED), UBL 2.1, CII

Profils : Minimum, Basic WL, Basic, EN16931, Extended

6.3 BulkGate (SMS OTP)

Envoi de codes SMS pour l'authentification des signataires.

  • Rate limit : 1 SMS par signataire par heure
  • Suivi : table sms_logs pour audit
  • Verification : code OTP a 6 chiffres, expiration configurable

6.4 Stripe (Paiements)

Gestion des paiements et recharges de solde.

  • Recharge : POST /v1/tenant/billing/top-up cree un PaymentIntent Stripe
  • Confirmation : POST /v1/tenant/billing/top-up/confirm finalise le paiement
  • Facturation : generation mensuelle automatique (GenerateMonthlyInvoices)

6.5 INSEE Sirene et VIES

  • SIRET : verification aupres du registre INSEE avec cache 24h
  • TVA : verification intracommunautaire via VIES avec cache 24h

7. Decisions d'Architecture (ADR)

ADR-001 : UUID pour toutes les cles primaires

Statut : Accepte

Contexte : le systeme est multi-tenant et distribue. Les identifiants auto-incrementes exposent l'ordre de creation et le volume de donnees. De plus, la generation d'ID cote client est necessaire pour les operations asynchrones.

Decision : utiliser UUID v4 pour toutes les cles primaires via le trait Eloquent HasUuids. PostgreSQL 17 offre un support natif performant du type uuid.

Consequences :

  • Positif : pas de collision, generation cote client possible, securite renforcee
  • Negatif : index plus larges (16 octets vs 4/8), jointures legerement plus lentes
  • Attenuation : les performances restent excellentes avec les index PostgreSQL natifs

ADR-002 : Multi-tenant avec isolation middleware (colonne) vs base separee

Statut : Accepte

Contexte : deux strategies possibles -- isolation par base de donnees (chaque tenant a sa propre DB) ou isolation par colonne (tenant_id sur chaque table).

Decision : isolation par colonne avec tenant_id et verification a trois niveaux (middleware, scope Eloquent, RLS PostgreSQL).

Consequences :

  • Positif : simplicite de deploiement, migrations uniques, requetes cross-tenant pour admin
  • Negatif : risque de fuite de donnees si oubli de filtre
  • Attenuation : le trait HasTenantScope, le middleware EnsureTenantIsolation et les RLS policies PostgreSQL forment un triple filet de securite

ADR-003 : Sanctum vs Passport pour authentification API

Statut : Accepte

Contexte : Laravel propose Sanctum (tokens simples) et Passport (OAuth2 complet). Scell.io utilise 4 modes d'authentification distincts.

Decision : Sanctum pour l'authentification utilisateur (Bearer Token) combine avec des middleware custom pour les 3 autres modes (Secret Key, Tenant Key, Publishable Key).

Consequences :

  • Positif : legerete de Sanctum, flexibilite des middleware custom, pas de surcharge OAuth2
  • Negatif : pas de scopes OAuth2 natifs (compense par Spatie Permission)

ADR-004 : Chaine de hachage SHA256 pour conformite NF525

Statut : Accepte

Contexte : la norme NF525 exige l'inalterabilite des donnees fiscales. Plusieurs approches possibles : blockchain, chaine de hachage, ou base de donnees append-only.

Decision : chaine de hachage SHA256 avec numero de sequence atomique par tenant. Chaque FiscalEntry inclut le hash de l'entree precedente, creant une chaine inviolable.

Consequences :

  • Positif : performance elevee, verification rapide, pas de dependance externe
  • Negatif : la verification de chaine complete est O(n) sur le nombre d'entrees
  • Attenuation : verification par lots et ancrage TSA periodique pour preuve externe

ADR-005 : Horizon vs database driver pour les queues

Statut : Accepte

Contexte : Laravel supporte plusieurs drivers de queue (database, Redis, SQS). Horizon offre un dashboard de supervision pour Redis.

Decision : Laravel Horizon avec Redis 7.4 comme backend de queue.

Consequences :

  • Positif : dashboard de monitoring, metriques temps reel, auto-scaling des workers
  • Negatif : dependance a Redis (deja requis pour cache/sessions)

ADR-006 : MCP Server en Laravel integre vs Node.js externe

Statut : Accepte

Contexte : le protocole MCP (Model Context Protocol) permet aux agents IA d'interagir avec l'API. L'implementation peut etre integree au backend Laravel ou deployee separement.

Decision : MCP server integre au backend Laravel (app/MCP/) avec 54 outils, utilisant un transport HTTP+SSE (HttpStreamableTransport). Un agent SuperPDP supplementaire en Node.js (superpdp-agent/) sert de pont specialise.

Consequences :

  • Positif : acces direct aux services et modeles Laravel, authentification unifiee
  • Negatif : charge supplementaire sur le backend
  • Attenuation : rate limiting dedie (McpRateLimiter), sessions Redis avec TTL

ADR-007 : Factur-X via Horsteko vs implementation custom

Statut : Accepte

Contexte : la generation de factures Factur-X necessite l'integration de XML dans des PDF/A-3. Plusieurs bibliotheques PHP existent.

Decision : utiliser la bibliotheque Horsteko (HorstekoFacturXGenerator) pour la generation Factur-X, UBL et CII.

Consequences :

  • Positif : support de tous les profils Factur-X, conformite EN16931 validee
  • Negatif : dependance a une bibliotheque tierce

ADR-008 : Soft deletes desactivees pour credit notes (NF525)

Statut : Accepte

Contexte : la norme NF525 interdit la suppression de documents fiscaux. Les soft deletes Laravel (SoftDeletes) marquent les enregistrements comme supprimes sans les effacer physiquement, ce qui pourrait creer une ambiguite juridique.

Decision : desactiver les soft deletes sur la table credit_notes. La suppression n'est autorisee que pour les brouillons (status = draft). Une migration dediee (remove_soft_deletes_from_credit_notes) a retire la colonne deleted_at.

Consequences :

  • Positif : conformite NF525 stricte, pas d'ambiguite sur les enregistrements fiscaux
  • Negatif : aucune possibilite de suppression apres emission
  • Attenuation : les avoirs permettent d'annuler economiquement une facture sans la supprimer

Documentation Scell.io