Concepts

Crédits & facturation

Modèle hybride : on déduit toujours, on alerte sur seuil, on bloque seulement si tu actives le soft-limit. Pas de hard cut-off par défaut — un client compromis ne tombe jamais brutalement sans alerte préalable.

Coût par opération

Opération Coût (crédits) Endpoint
Ouverture de session 1 POST /v1/sessions
Attachement d'artifact 1 POST /v1/sessions/{id}/artifacts
Génération bundle ASiC-E 5 POST /v1/proofs

Les actions internes (validate OTP, record action, close) sont gratuites — elles font partie d'une session déjà payée. Idem refus (POST /refuse).

Champs organisation

Champ Type Default Description
credit_balance integer 0 Solde courant. Peut devenir négatif si soft_limit_enforced=false.
credit_alert_threshold integer 0-100 80 Pourcentage de conso à partir duquel une alerte est levée (V1 = observation manuelle dans usage_event, V2 = webhook).
soft_limit_enforced boolean false Si true, toute opération qui ferait passer le solde < 0 est refusée avec HTTP 402.

Cycle de vie d'une déduction

POST /v1/sessions                          (organisation.credit_balance = 100)
  └─ OpenSessionUseCase
     └─ DeductCreditsUseCase.execute(organisation, 'session_open', 1)
        ├─ if (softLimit && balance - 1 < 0) → throw InsufficientCreditsException
        ├─ organisation.deductCredits(1)
        ├─ organisationRepo.save(organisation)            → balance = 99
        └─ usageRepo.recordEvent(            credit_deduction, qty=1,
            { reason: 'session_open', balance_after: 99, alert_threshold: 80 })

Réponse HTTP :
  201 Created (succès)
  402 Payment Required (si soft-limit ON et insuffisant) :
    { "error": "INSUFFICIENT_CREDITS", "message": "Organisation X has insufficient credits...",
      "balance": 0, "required": 1 }
        

Recharger une organisation

Via l'admin UI (V1) :

http://proof.test/admin/organisations/{id}     → bloc "Crédits"
                                          → champ amount + bouton "Topup"
                                          → POST /admin/organisations/{id}/credits/topup (CSRF)
        

Via console (script ops) :

php bin/console doctrine:dbal:sql \
  "UPDATE organisation SET credit_balance = credit_balance + 1000 WHERE id = '01900000-...'"

V2 (en backlog) : webhook Stripe → topup automatique après paiement réussi. Pour V1, c'est saisie manuelle (modèle compatible avec une facturation post-payée si tu préfères ne jamais activer le soft-limit).

Observer la consommation

Tous les événements de déduction sont enregistrés dans la table usage_event avec leur metadata complète. Pour le reporting :

-- Conso du mois courant par type d'opération
SELECT
    metadata->>'reason' AS operation,
    COUNT(*)            AS occurrences,
    SUM(quantity)       AS total_credits
FROM usage_event
WHERE organisation_id = '01900000-...'
  AND event_type = 'credit_deduction'
  AND occurred_at >= date_trunc('month', now())
GROUP BY 1
ORDER BY 3 DESC;

Le dashboard admin /admin/usage consolide ces données par organisation + période.

Recommandation prod : laisse soft_limit_enforced à false sur tes organisations payants (postpayé), et active-le uniquement sur les comptes d'essai / freemium pour cap au solde initial.