Skip to content

Conformite Fiscale ISCA (autocertification) ​

Version applicative: 1.2.1 Derniere mise a jour: 2026-06-05 Garanties: ISCA (Inalterabilite, Securisation, Conservation, Archivage) Base legale: Article 286-I-3 bis du Code General des Impots (Loi de Finances 2026)

Statut juridique : Scell.io est autocertifie sur la base d'une attestation interne (modele CNIL / DGFiP). La plateforme n'est pas certifiee par un organisme tiers. Le terme "ISCA" designe le concept technique de chaine de hachage immuable ; "autocertification" designe le statut juridique.


Table des matieres ​

  1. Introduction
  2. Architecture Fiscale
  3. Immutabilite
  4. Integrite de la Chaine de Hachage
  5. Clotures
  6. Ancrage (Timestamping)
  7. Kill Switch
  8. Export FEC
  9. Regles Fiscales
  10. API Fiscale
  11. Seuils Auto-Entrepreneur
  12. Scellement des Devis (PAdES + Bitcoin)

1. Introduction ​

Qu'est-ce que ISCA ? ​

ISCA (Inalterabilite, Securisation, Conservation, Archivage) regroupe les exigences de conformite imposees aux logiciels de caisse et de facturation en France. Quatre garanties fondamentales :

ExigenceDescriptionImplementation Scell.io
InalterabiliteLes donnees fiscales ne peuvent etre modifiees apres validationTriggers SQL, immutabilite Eloquent, audit log
SécurisationLes donnees sont protegees contre toute alterationChaine SHA-256, ancrage OpenTimestamps / Bitcoin (provider RFC 3161 optionnel, voir ADR-0009)
ConservationLes donnees sont conservees pendant la duree legaleRetention 6 ans minimum, archivage S3
ArchivageLes donnees peuvent etre restituees a l'administrationExport FEC conforme, export forensique

Pourquoi c'est obligatoire en France ? ​

Depuis la Loi de Finances 2018 (renforcee en 2026), tout logiciel de facturation utilise par une entreprise assujettie a la TVA doit etre certifie conforme par un organisme tiers ou produire une auto-attestation de conformite de son editeur (modele retabli par l'article 125 de la LF 2026). Le non-respect expose a une amende de 7 500 EUR par logiciel non conforme. Scell.io retient la voie de l'autocertification.

Ce que Scell.io implemente ​

Scell.io implemente l'integralite des exigences ISCA au niveau applicatif et base de donnees :

  • Grand livre fiscal (fiscal ledger) avec chaine de hachage SHA-256
  • Triggers PostgreSQL pour l'immutabilite des ecritures
  • Clotures journalieres et mensuelles automatiques
  • Ancrage RFC 3161 TSA pour preuve d'anteriorite
  • Export FEC conforme a l'article A.47 A-1 du LPF
  • Kill switch pour arret d'urgence
  • Piste d'audit complete de toute tentative de modification

2. Architecture Fiscale ​

Diagramme du flux complet ​

mermaid
graph TD
    subgraph "Evenements Source"
        INV["Facture creee/validee/transmise"]
        CN["Avoir cree/envoye"]
        PAY["Paiement recu"]
    end

    subgraph "Grand Livre Fiscal"
        FE["FiscalEntry<br/>Ecriture fiscale"]
        FS["FiscalSequence<br/>Compteur atomique"]
        HASH["Chaine SHA-256<br/>data_hash + previous_hash = chain_hash"]
    end

    subgraph "Clotures"
        DC["Cloture journaliere<br/>(00:05 UTC)"]
        MC["Cloture mensuelle<br/>(1er du mois)"]
        ATT["Attestation annuelle"]
    end

    subgraph "Preuves"
        TSA["Ancrage RFC 3161 TSA<br/>(Timestamp Authority)"]
        FEC["Export FEC<br/>(DGFIP)"]
        FOR["Export forensique"]
    end

    subgraph "Securite"
        KS["Kill Switch<br/>(arret d'urgence)"]
        IC["Verification d'integrite<br/>(02:00 UTC)"]
        AL["Audit Log<br/>(tentatives de modification)"]
    end

    INV --> FE
    CN --> FE
    PAY --> FE
    FE --> FS
    FS --> HASH
    HASH --> DC
    DC --> MC
    MC --> ATT
    DC --> TSA
    MC --> TSA
    FE --> IC
    IC --> AL
    FE --> FEC
    FE --> FOR
    KS -.->|"bloque"| FE

Modeles de donnees ​

Le systeme fiscal repose sur 9 modeles interconnectes :

ModeleTableRole
FiscalEntryfiscal_entriesEcriture fiscale unitaire (append-only)
FiscalSequencefiscal_sequencesCompteur de sequence atomique par tenant
FiscalClosingfiscal_closingsCloture journaliere/mensuelle/annuelle
FiscalAttestationfiscal_attestationsAttestation de conformite annuelle
FiscalAnchorfiscal_anchorsAncrage TSA (preuve d'anteriorite)
FiscalKillSwitchfiscal_kill_switchesHistorique des arrets d'urgence
FiscalIntegrityCheckfiscal_integrity_checksResultats des verifications d'integrite
FiscalRulefiscal_rulesRegles fiscales configurables par tenant
FiscalRuleApplicationfiscal_rule_applicationsJournal d'application des regles

Services ​

ServiceFichierResponsabilite
FiscalLedgerServiceServices/Fiscal/FiscalLedgerService.phpGrand livre, enregistrement, verification
FiscalAnchoringServiceServices/Fiscal/FiscalAnchoringService.phpAncrage TSA, attestations
FecExportServiceServices/Fiscal/FecExportService.phpExport FEC conforme DGFIP
FiscalForensicExportServiceServices/Fiscal/FiscalForensicExportService.phpExport forensique complet
FiscalVersionChangeDetectorServices/Fiscal/FiscalVersionChangeDetector.phpDetection de changement de version logicielle

3. Immutabilite ​

L'immutabilite est la pierre angulaire de la conformite ISCA. Scell.io l'implemente a trois niveaux.

Niveau 1 : Modele Eloquent ​

Les modeles Invoice et CreditNote definissent des champs fiscaux immutables :

php
// Invoice.php
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',
];

Des qu'une facture quitte le statut draft, toute tentative de modification de ces champs :

  1. Est bloquee par un RuntimeException
  2. Est enregistree dans FiscalAuditLog avec la severite critical
  3. Inclut l'identifiant de l'acteur, l'adresse IP et le user-agent
php
// Protection dans le modele Invoice (booting)
protected static function booting(): void
{
    static::updating(function (Invoice $invoice) {
        if ($invoice->status !== 'draft') {
            $changedFiscalFields = array_intersect(
                array_keys($invoice->getDirty()),
                self::IMMUTABLE_FISCAL_FIELDS
            );
            if (!empty($changedFiscalFields)) {
                FiscalAuditLog::record([
                    'event_type' => 'modification_attempt',
                    'target_table' => 'invoices',
                    'target_id' => $invoice->id,
                    'severity' => 'critical',
                    'details' => ['fields' => $changedFiscalFields],
                ]);
                throw new \RuntimeException(
                    'Modification interdite : champs fiscaux immutables.'
                );
            }
        }
    });
}

Niveau 2 : Triggers PostgreSQL ​

La migration 2026_02_10_000005_add_fiscal_immutability_triggers installe des triggers SQL qui protegent les donnees directement au niveau de la base de donnees, meme en cas de contournement de l'application :

sql
-- Empecher la modification des ecritures fiscales
CREATE OR REPLACE FUNCTION prevent_fiscal_entry_update()
RETURNS TRIGGER AS $$
BEGIN
    RAISE EXCEPTION 'Les ecritures fiscales sont immutables (ISCA)';
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tr_fiscal_entries_immutable
BEFORE UPDATE ON fiscal_entries
FOR EACH ROW
EXECUTE FUNCTION prevent_fiscal_entry_update();

-- Empecher la suppression des ecritures fiscales
CREATE TRIGGER tr_fiscal_entries_no_delete
BEFORE DELETE ON fiscal_entries
FOR EACH ROW
EXECUTE FUNCTION prevent_fiscal_entry_delete();

Niveau 3 : Journal d'audit ​

Le trait LogsImmutabilityViolation est applique aux modeles fiscaux. Toute tentative de modification est tracee :

php
FiscalAuditLog::record([
    'event_type' => 'modification_attempt',  // ou deletion_attempt, deletion_blocked
    'event_source' => 'eloquent',            // ou sql_trigger, api
    'target_table' => 'fiscal_entries',
    'target_id' => $entry->id,
    'severity' => 'critical',
    'actor_type' => 'user',                  // ou api_key, system
    'actor_id' => auth()->id(),
    'ip_address' => request()->ip(),
    'user_agent' => request()->userAgent(),
    'details' => ['attempted_changes' => $dirty],
]);

Suppression interdite ​

Les factures et avoirs valides (statut != draft) ne peuvent pas etre supprimes. Les soft deletes sont desactivees sur les ecritures fiscales (FiscalEntry n'utilise pas SoftDeletes). Le grand livre est strictement en mode append-only.


4. Integrite de la Chaine de Hachage ​

Principe ​

Chaque ecriture fiscale (FiscalEntry) est liee a la precedente par un hash cryptographique SHA-256, formant une chaine inalterable :

Entry #1: chain_hash = SHA256(data_hash_1 + "0000...0000")
Entry #2: chain_hash = SHA256(data_hash_2 + chain_hash_1)
Entry #3: chain_hash = SHA256(data_hash_3 + chain_hash_2)
...
Entry #N: chain_hash = SHA256(data_hash_N + chain_hash_N-1)

Calcul du hash ​

php
// FiscalEntry::record() - methode statique, seul point de creation
public static function record(
    ?string $tenantId,
    string $entryType,
    string $entityType,
    string $entityId,
    string $fiscalDate,
    array $dataSnapshot,
    string $environment,
    string $legalStatus,
    string $actorType,
    ?string $actorId,
    ?string $ipAddress,
    ?string $userAgent,
): self {
    return DB::transaction(function () use (...) {
        // 1. Obtenir le prochain numero de sequence (atomique)
        $sequence = FiscalSequence::lockForUpdate()
            ->where('tenant_id', $tenantId)
            ->first();
        $sequenceNumber = $sequence->last_sequence_number + 1;

        // 2. Calculer le hash des donnees
        $dataHash = hash('sha256', json_encode($dataSnapshot, JSON_UNESCAPED_UNICODE));

        // 3. Recuperer le hash precedent
        $previousEntry = self::where('tenant_id', $tenantId)
            ->orderBy('sequence_number', 'desc')
            ->first();
        $previousHash = $previousEntry?->chain_hash ?? str_repeat('0', 64);

        // 4. Calculer le hash de chaine
        $chainHash = hash('sha256', $dataHash . $previousHash);

        // 5. Creer l'ecriture (append-only)
        $entry = self::create([
            'tenant_id' => $tenantId,
            'sequence_number' => $sequenceNumber,
            'entry_type' => $entryType,
            'data_hash' => $dataHash,
            'previous_hash' => $previousHash,
            'chain_hash' => $chainHash,
            'data_snapshot' => $dataSnapshot,
            // ...
        ]);

        // 6. Mettre a jour la sequence
        $sequence->increment('last_sequence_number');

        return $entry;
    });
}

Verification d'integrite ​

Le FiscalLedgerService::verifyChainIntegrity() rejoue l'integralite de la chaine pour detecter toute falsification :

php
public function verifyChainIntegrity(Tenant $tenant, ?Carbon $from, ?Carbon $to): array
{
    $query = FiscalEntry::forTenant($tenant->id)
        ->orderBy('sequence_number', 'asc');

    if ($from) $query->where('fiscal_date', '>=', $from);
    if ($to) $query->where('fiscal_date', '<=', $to);

    $entries = $query->get();
    $discrepancies = [];
    $previousHash = str_repeat('0', 64);

    foreach ($entries as $entry) {
        // Recalculer le hash des donnees
        $expectedDataHash = hash('sha256', json_encode($entry->data_snapshot));
        if ($expectedDataHash !== $entry->data_hash) {
            $discrepancies[] = [
                'type' => 'data_hash_mismatch',
                'sequence' => $entry->sequence_number,
            ];
        }

        // Verifier la chaine
        $expectedChainHash = hash('sha256', $entry->data_hash . $previousHash);
        if ($expectedChainHash !== $entry->chain_hash) {
            $discrepancies[] = [
                'type' => 'chain_break',
                'sequence' => $entry->sequence_number,
            ];
        }

        $previousHash = $entry->chain_hash;
    }

    return [
        'result' => empty($discrepancies) ? 'valid' : 'invalid',
        'entries_checked' => $entries->count(),
        'discrepancies' => $discrepancies,
    ];
}

Verification automatique ​

La commande FiscalIntegrityCheckCommand s'execute automatiquement chaque jour a 02:00 UTC :

bash
# Configuration dans le scheduler Laravel
$schedule->command('fiscal:integrity-check')->dailyAt('02:00');

Le resultat est enregistre dans la table fiscal_integrity_checks avec le nombre d'entrees verifiees, le nombre d'anomalies trouvees et le chemin du rapport.


5. Clotures ​

Cloture journaliere ​

La cloture journaliere s'execute automatiquement a 00:05 UTC via la commande FiscalDailyClosingCommand :

bash
php artisan fiscal:daily-closing

Elle produit un enregistrement FiscalClosing contenant :

ChampDescription
closing_dateDate cloturee
closing_typedaily
entries_countNombre d'ecritures de la journee
opening_balanceSolde d'ouverture
closing_balanceSolde de cloture
total_operationsSomme des operations de la journee
fec_export_pathChemin du FEC journalier (optionnel)
closed_atHorodatage de la cloture

La cloture est elle-meme enregistree comme une ecriture fiscale dans le grand livre (entry_type: closing), garantissant son inclusion dans la chaine de hachage.

Cloture mensuelle ​

La cloture mensuelle s'execute le 1er de chaque mois via FiscalMonthlyClosingCommand :

bash
php artisan fiscal:monthly-closing

Elle agrege les clotures journalieres du mois et genere un enregistrement FiscalClosing de type monthly.

Attestation annuelle ​

A la fin de chaque exercice fiscal, une FiscalAttestation est generee automatiquement :

php
FiscalAttestation::create([
    'tenant_id' => $tenant->id,
    'year' => 2025,
    'attestation_type' => 'annual',
    'software_name' => 'Scell.io',                     // config('fiscal.attestation.software_name')
    'software_version' => '1.2.1',                      // config('fiscal.attestation.software_version')
    'editor_name' => 'QR Communication SAS',            // config('fiscal.attestation.editor')
    'editor_siret' => '...',                             // config('fiscal.attestation.editor_siret')
    'license_number' => '...',                           // config('fiscal.attestation.license_number')
    'market_release_date' => '2026-01-01',
    'attestation_date' => now(),
]);

L'attestation est disponible en PDF via l'API : GET /v1/tenant/fiscal/attestation/{year}/download.


6. Ancrage (Preuve d'anteriorite cryptographique) ​

Strategie : OpenTimestamps / Bitcoin (mecanisme principal) ​

Scell.io utilise OpenTimestamps comme mecanisme principal et par defaut de preuve d'anteriorite. Le hash de chaque cloture journaliere est ancre dans la blockchain Bitcoin via 3 calendar servers publics gratuits (alice, bob, finney).

Voir ADR-0009 pour la justification complete du choix Bitcoin vs TSA RFC 3161.

Garantie : preuve d'anteriorite irrefutable — l'altération d'un hash ancré dans Bitcoin est cryptographiquement impossible (preuve de travail, 600+ EH/s de hashrate). Le receipt .ots est autoportant et vérifiable par tout le monde sans tiers de confiance.

Flux d'ancrage OpenTimestamps ​

mermaid
sequenceDiagram
    participant S as Scell.io
    participant C1 as Calendar alice.btc
    participant C2 as Calendar bob.btc
    participant C3 as Calendar finney
    participant BTC as Blockchain Bitcoin

    S->>S: Cloture journaliere generee (closing_hash SHA-256)
    S->>C1: POST /digest (hash binaire 32 bytes)
    S->>C2: POST /digest (hash binaire 32 bytes)
    S->>C3: POST /digest (hash binaire 32 bytes)
    C1-->>S: Partial receipt (pending)
    C2-->>S: Partial receipt (pending)
    C3-->>S: Partial receipt (pending)
    Note over C1,C3: ~1-6h plus tard : ancrage OP_RETURN dans un bloc Bitcoin
    S->>C1: GET /timestamp/{hash} (upgrade)
    C1-->>S: Receipt complete (merkle path + block height)
    S->>S: Stockage receipt .ots + verification blockstream.info

Implementation ​

php
// config/fiscal.php
'anchoring' => [
    'provider' => env('FISCAL_ANCHOR_PROVIDER', 'none'),  // TSA désactivé par défaut
    'auto_anchor' => env('FISCAL_AUTO_ANCHOR', false),
    'tsa_url' => env('FISCAL_TSA_URL', 'https://freetsa.org/tsr'),
    'tsa_cert_path' => env('FISCAL_TSA_CERT_PATH'),
],
'opentimestamps' => [
    'enabled' => env('FISCAL_OTS_ENABLED', true),         // OTS activé par défaut
    'calendars' => [
        'https://alice.btc.calendar.opentimestamps.org',
        'https://bob.btc.calendar.opentimestamps.org',
        'https://finney.calendar.eternitywall.com',
    ],
],

Verification ​

Le receipt .ots peut etre verifie :

  1. En ligne : via l'API des calendars (upgrade du partial vers proof complete)
  2. Hors-ligne : avec opentimestamps-client (Python) sur une copie de la blockchain
  3. Via blockstream.info : le service Scell.io valide le block height + block hash

Le receipt est integre en attachment PDF/A-3 dans les certificats de conformite (TenantComplianceCertificateRenderer).

Provider RFC 3161 TSA (optionnel, non active par defaut) ​

Le provider Rfc3161TsaProvider est disponible mais desactive par defaut (FISCAL_ANCHOR_PROVIDER=none). Il peut etre active en complement d'OpenTimestamps si un client exige un double ancrage TSA + Bitcoin. Ce n'est pas requis pour l'autocertification ISCA.

Verification d'ancrage ​

php
// Verifier qu'un ancrage est valide
$isValid = $anchoringService->verifyAnchor($anchor);
// Rejoue la requete TSA et compare avec le token stocke

7. Kill Switch ​

Mecanisme d'arret d'urgence ​

Le kill switch fiscal est un mecanisme de securite extreme permettant de bloquer toutes les operations fiscales d'un tenant en cas d'urgence (fraude detectee, faille de securite, incoherence comptable grave).

Fonctionnement ​

mermaid
stateDiagram-v2
    [*] --> Inactif
    Inactif --> Actif: activate_kill_switch<br/>(raison obligatoire)
    Actif --> Inactif: deactivate_kill_switch<br/>(raison obligatoire)

    state Actif {
        [*] --> Bloquer_Factures
        [*] --> Bloquer_Avoirs
        [*] --> Bloquer_Clotures
    }

Lorsque le kill switch est actif :

  • Creation de factures : bloquee (HTTP 423 Locked)
  • Creation d'avoirs : bloquee
  • Clotures : bloquees
  • Consultation : autorisee (lecture seule)
  • Export FEC : autorise

Middleware FiscalKillSwitchGuard ​

Le middleware FiscalKillSwitchGuard intercepte les operations d'ecriture fiscale :

php
// Applique aux routes : tenant.balance:invoice, tenant.balance:credit_note
// Verifie le statut du kill switch avant toute creation
public function handle(Request $request, Closure $next): Response
{
    $tenant = $request->attributes->get('tenant');

    $killSwitch = FiscalKillSwitch::where('tenant_id', $tenant->id)
        ->where('status', 'active')
        ->first();

    if ($killSwitch) {
        return response()->json([
            'error' => 'Operations fiscales suspendues',
            'code' => 'FISCAL_KILL_SWITCH_ACTIVE',
            'reason' => $killSwitch->activation_reason,
            'activated_at' => $killSwitch->activated_at,
        ], 423);
    }

    return $next($request);
}

API Kill Switch ​

EndpointMethodeScope requisDescription
/v1/tenant/fiscal/kill-switch/statusGETfiscal:readStatut actuel
/v1/tenant/fiscal/kill-switch/activatePOSTfiscal:adminActiver
/v1/tenant/fiscal/kill-switch/deactivatePOSTfiscal:adminDesactiver

Durcissement (2026-06) — activate / deactivate :

  • Scope fiscal:admin fail-closed : doit etre EXPLICITEMENT present dans tenant.settings.fiscal_permissions, sinon 403 FISCAL_SCOPE_ADMIN_NOT_GRANTED.
  • reason obligatoire >= 20 caracteres.
  • Step-up hors-bande en production : le 1er appel envoie un token a l'email de contact du tenant et retourne 403 OOB_CONFIRMATION_REQUIRED ; re-soumettre avec confirmation_token. Une fuite de cle sk_* ne suffit donc plus.

Exemple (production, 2 phases) :

bash
# Phase 1 : declenche l'envoi du token (403)
curl -X POST https://api.scell.io/api/v1/tenant/fiscal/kill-switch/activate \
  -H "X-API-Key: sk_live_..." -H "Content-Type: application/json" \
  -d '{"reason":"Maintenance critique suite a incident de facturation"}'
# -> 403 {"code":"OOB_CONFIRMATION_REQUIRED", ...}

# Phase 2 : confirmer avec le token recu par email
curl -X POST https://api.scell.io/api/v1/tenant/fiscal/kill-switch/activate \
  -H "X-API-Key: sk_live_..." -H "Content-Type: application/json" \
  -d '{"reason":"Maintenance critique suite a incident de facturation","confirmation_token":"<token-email>"}'

Chaque activation/desactivation est enregistree dans :

  • Le modele FiscalKillSwitch (historique complet)
  • Le FiscalAuditLog (piste d'audit)
  • Le grand-livre fiscal (FiscalEntry, evenement kill_switch_activated/deactivated)

Voir aussi : backend/docs/runbooks/kill-switch-step-up.md et le point de durcissement docs/dev/security-hardening-2026-06.md.


8. Export FEC ​

Format FEC (Fichier des Ecritures Comptables) ​

Le FEC est le format standardise par la DGFIP pour le controle fiscal informatise. Scell.io genere un fichier conforme a l'article A.47 A-1 du Livre des Procedures Fiscales.

Structure du fichier ​

Le fichier FEC contient 18 colonnes obligatoires, separees par un pipe (|) ou une tabulation :

#ColonneDescriptionExemple
1JournalCodeCode du journalVE (ventes)
2JournalLibLibelle du journalJournal des ventes
3EcritureNumNumero de l'ecritureVE-2026-001
4EcritureDateDate de l'ecriture20260303
5CompteNumNumero de compte411000
6CompteLibLibelle du compteClients
7CompAuxNumCompte auxiliaireCLI-ACME
8CompAuxLibLibelle auxiliaireACME SAS
9PieceRefReference de la pieceFA-2026-001
10PieceDateDate de la piece20260303
11EcritureLibLibelle de l'ecritureFacture ACME SAS
12DebitMontant debit600.00
13CreditMontant credit0.00
14EcritureLetLettrage
15DateLetDate de lettrage
16ValidDateDate de validation20260303
17MontantdeviseMontant en devise600.00
18IdeviseCode deviseEUR

Comptabilisation en partie double ​

Le FecExportService genere les ecritures en partie double :

Facture (500 EUR HT, TVA 20%) :

CompteNumCompteLibDebitCredit
411000Clients600.000.00
701000Ventes de produits0.00500.00
44571TVA collectee0.00100.00

Avoir (extourne) :

CompteNumCompteLibDebitCredit
411000Clients0.00600.00
701000Ventes de produits500.000.00
44571TVA collectee100.000.00

Nommage du fichier ​

Le fichier FEC suit la convention de nommage DGFIP :

{SIREN}FEC{AAAAMMJJ}.txt

Exemple : 123456789FEC20261231.txt

API d'export ​

bash
# Export FEC pour l'exercice 2025
curl -X GET "https://api.scell.io/api/v1/tenant/fiscal/fec?year=2025&format=pipe" \
  -H "X-API-Key: sk_live_..." \
  -o 123456789FEC20251231.txt

Export forensique ​

L'export forensique est un export complet incluant :

  • Toutes les ecritures fiscales avec snapshots
  • Les clotures et attestations
  • Le journal d'audit complet
  • Les resultats de verification d'integrite
bash
curl -X GET "https://api.scell.io/api/v1/tenant/fiscal/forensic-export?date_from=2025-01-01&date_to=2025-12-31" \
  -H "X-API-Key: sk_live_..."

9. Regles Fiscales ​

Regles configurables par tenant ​

Le systeme de regles fiscales permet aux tenants de definir des alertes et validations personnalisees :

Type de regleDescriptionExemple
alertNotification sans blocage"Alerte si facture > 10 000 EUR"
validationBlocage si condition non remplie"Refuser si SIRET invalide"
correctionApplication automatique"Arrondir a 2 decimales"

Structure d'une regle ​

json
{
  "rule_key": "max_invoice_amount",
  "rule_name": "Montant maximum de facture",
  "rule_type": "alert",
  "condition_json": {
    "field": "total_ttc",
    "operator": "greater_than",
    "value": 10000
  },
  "action_json": {
    "type": "notify",
    "channel": "email",
    "message": "Facture de montant eleve detectee"
  },
  "enabled": true
}

API Regles ​

EndpointMethodeDescription
/v1/tenant/fiscal/rulesGETLister les regles
/v1/tenant/fiscal/rulesPOSTCreer une regle
/v1/tenant/fiscal/rules/{id}PUTModifier une regle
/v1/tenant/fiscal/rules/{key}GETDetail d'une regle
/v1/tenant/fiscal/rules/{key}/historyGETHistorique d'application
/v1/tenant/fiscal/rules/replayPOSTRejouer les regles
/v1/tenant/fiscal/rules/exportGETExporter en JSON

10. API Fiscale ​

Endpoints complets ​

Tous les endpoints fiscaux sont sous le prefixe /api/v1/tenant/fiscal/ et necessitent une cle secrete tenant (X-API-Key: sk_live_* / sk_test_*).

EndpointMethodeScopeDescription
/fiscal/complianceGETfiscal:readStatut de conformite
/fiscal/entriesGETfiscal:readEcritures fiscales
/fiscal/closingsGETfiscal:readListe des clotures
/fiscal/closings/dailyPOSTfiscal:writeCloture journaliere manuelle
/fiscal/integrityGETfiscal:readVerification d'integrite
/fiscal/integrity/historyGETfiscal:readHistorique des verifications
/fiscal/integrity/{date}GETfiscal:readVerification pour une date
/fiscal/attestation/{year}GETfiscal:readAttestation annuelle
/fiscal/attestation/{year}/downloadGETfiscal:readTelecharger PDF
/fiscal/fecGETfiscal:readExport FEC
/fiscal/forensic-exportGETfiscal:readExport forensique
/fiscal/anchorsGETfiscal:readListe des ancrages TSA
/fiscal/rulesGETfiscal:readRegles fiscales
/fiscal/rulesPOSTfiscal:writeCreer une regle
/fiscal/rules/{id}PUTfiscal:writeModifier une regle
/fiscal/rules/replayPOSTfiscal:writeRejouer les regles
/fiscal/kill-switch/statusGETfiscal:readStatut kill switch
/fiscal/kill-switch/activatePOSTfiscal:adminActiver
/fiscal/kill-switch/deactivatePOSTfiscal:adminDesactiver

Scopes de permission ​

Le systeme fiscal utilise 3 niveaux de permission (configurable par tenant dans settings.fiscal_permissions) :

ScopePermissions
fiscal:readConsultation du grand livre, clotures, FEC, attestation, integrite, regles
fiscal:writeDeclenchement de clotures, creation/modification de regles
fiscal:adminActivation/desactivation du kill switch

Configuration ​

env
# .env
FISCAL_COMPLIANCE_ENABLED=true
FISCAL_EDITOR_NAME="QR Communication SAS"
FISCAL_EDITOR_SIRET="12345678900001"
FISCAL_LICENSE_NUMBER="ISCA-AUTO-2026-001"
FISCAL_MARKET_RELEASE_DATE="2026-01-01"
FISCAL_ANCHOR_PROVIDER=rfc3161_tsa       # none | rfc3161_tsa
FISCAL_AUTO_ANCHOR=false
FISCAL_TSA_URL=https://freetsa.org/tsr
FISCAL_RETENTION_YEARS=6

Commandes Artisan ​

bash
# Cloture journaliere (normalement automatique a 00:05 UTC)
php artisan fiscal:daily-closing

# Cloture mensuelle (normalement automatique le 1er du mois)
php artisan fiscal:monthly-closing

# Verification d'integrite (normalement automatique a 02:00 UTC)
php artisan fiscal:integrity-check

# Detection de changement de version logicielle
php artisan fiscal:check-version-change

11. Seuils Auto-Entrepreneur (Loi 2025-1044) ​

Scell.io suit, par sous-tenant auto-entrepreneur, les seuils legaux de franchise en base de TVA et de plafond du micro-regime, definis par la loi 2025-1044. Ces seuils sont des regles fiscales datees (FiscalRule), pas des constantes en dur : ils peuvent etre rejoues a une date passee et evolueront sans redeploiement.

SeuilBaseMajore
Franchise en base de TVA37 500 €41 250 €
Plafond micro-regime (CA)83 600 €91 900 €

Le CA HT cumule est suivi par categorie de revenu. Chaque jauge expose un niveau d'alerte (warning_80 … micro_ceiling_exceeded) et une date de franchissement projetee.

Information, pas conseil fiscal. Les reponses incluent un champ disclaimer : le suivi est purement informatif et ne constitue pas un conseil fiscal.

Endpoints ​

MethodeEndpointDescription
GET/api/v1/sub-tenants/{id}/thresholdsJauges de seuils (lecture)
POST/api/v1/sub-tenants/{id}/thresholds/simulateSimulateur pre-emission (lecture seule)
PATCH/api/v1/sub-tenants/{id}/fiscal-statusStatut fiscal declare

Simulateur pre-emission. POST .../thresholds/simulate projette les jauges comme si une facture hypothetique de amount (HT) en categorie category etait emise — sans rien enregistrer. Cela permet d'alerter l'utilisateur AVANT d'emettre s'il franchit un seuil.

Statut fiscal declare. PATCH .../fiscal-status met a jour le regime, le vat_status, le type d'activite, la date de debut et le numero de TVA. Basculer vat_status a liable fait que Scell.io facture la TVA sur les factures suivantes et retire la mention art. 293 B ; un vat_number devient alors requis.

bash
curl https://api.scell.io/api/v1/sub-tenants/{id}/thresholds \
  -H "X-API-Key: sk_live_xxx"

Les notifications asynchrones de franchissement sont produites par la commande planifiee ae:monitor-thresholds (distincte du GET, qui est a la demande).


12. Scellement des Devis (PAdES + Bitcoin) ​

A l'acceptation d'un devis signe, Scell.io scelle le PDF en deux temps, dans la continuite de l'approche ISCA (chaine de hachage + ancrage OpenTimestamps deja utilises pour les clotures fiscales — voir section 6) :

  1. Signature PAdES : le PDF du devis est signe electroniquement au format PAdES (PDF Advanced Electronic Signatures).
  2. Ancrage Bitcoin : l'empreinte SHA-256 du PDF scelle est soumise aux calendriers OpenTimestamps, qui l'ancrent dans un bloc Bitcoin — preuve d'integrite et d'anteriorite infalsifiable et verifiable publiquement.

L'objet sealing est expose sur la ressource Devis une fois le sceau pose.

Champ sealing.*TypeDescription
is_sealedbooltrue une fois le devis scelle (seul champ non nullable)
pades_signed_atdatetimeDate de la signature PAdES
signed_pdf_sha256stringEmpreinte SHA-256 du PDF scelle (= valeur ancree Bitcoin)
ots_statusenumpending → confirmed (ou failed)
ots_submitted_atdatetimeSoumission aux calendriers OpenTimestamps
ots_bitcoin_confirmed_atdatetimeConfirmation de l'ancrage dans un bloc Bitcoin
bitcoin_block_heightintHauteur du bloc Bitcoin d'ancrage
ots_proof_base64stringReceipt OpenTimestamps .ots (BYTEA → base64)

Cycle de vie du statut d'ancrage :

null (pas encore scelle) → pending (soumis a OTS) → confirmed (bloc Bitcoin) 
                                                  ↘ failed (a rejouer)

Le receipt .ots (ots_proof_base64) suit le meme pattern d'accesseur BYTEA → base64 que FiscalClosing.ots_proof. Un integrateur avance peut le verifier hors-ligne avec le client ots verify.

Documentation Scell.io