Skip to content

Depannage et FAQ Technique

Guide de resolution des problemes courants rencontres lors du developpement, de l'integration et de l'exploitation de Scell.io. Chaque section suit le format : symptome, diagnostic, solution.


Erreurs d'Installation

Composer install echoue (extensions PHP manquantes)

Symptome : composer install echoue avec des erreurs du type ext-pgsql is missing ou ext-redis is missing.

Diagnostic : Laravel 12 et les dependances Scell.io requierent PHP 8.2+ avec plusieurs extensions.

Solution :

bash
# Verifier la version PHP
php -v  # Doit afficher 8.2+

# Installer les extensions requises (Ubuntu/Debian)
sudo apt install php8.2-pgsql php8.2-redis php8.2-xml php8.2-curl php8.2-mbstring php8.2-zip php8.2-gd php8.2-bcmath

# macOS (via Homebrew)
pecl install redis
pecl install pgsql

# Verifier les extensions chargees
php -m | grep -E "pgsql|redis|xml|curl|mbstring|zip|gd|bcmath"

Si horstoeko/zugferd (generation Factur-X) echoue, verifier que ext-xml et ext-dom sont presents.


npm install echoue (version Node.js)

Symptome : Erreurs lors de npm install dans /frontend ou /superpdp-agent.

Diagnostic : Le frontend (Vite 7, React 19) necessite Node.js 18+. Le superpdp-agent necessite egalement Node.js 18+.

Solution :

bash
# Verifier la version Node
node -v  # Doit afficher 18+

# Installer via nvm (recommande)
nvm install 20
nvm use 20

# Relancer l'installation
cd frontend && rm -rf node_modules package-lock.json && npm install
cd superpdp-agent && rm -rf node_modules package-lock.json && npm install

Migration echoue (connexion PostgreSQL)

Symptome : php artisan migrate retourne SQLSTATE[08006] could not connect to server ou FATAL: password authentication failed.

Diagnostic : Les parametres de connexion dans .env ne correspondent pas a la configuration PostgreSQL.

Solution :

bash
# 1. Verifier que PostgreSQL tourne
sudo systemctl status postgresql

# 2. Verifier la connexion manuellement
psql -h 127.0.0.1 -U scell -d scell_dev

# 3. Verifier le .env
cat backend/.env | grep DB_
# Attendu :
#   DB_CONNECTION=pgsql
#   DB_HOST=127.0.0.1
#   DB_PORT=5432
#   DB_DATABASE=scell_dev
#   DB_USERNAME=scell
#   DB_PASSWORD=votre_mot_de_passe

# 4. Creer la base si elle n'existe pas
sudo -u postgres psql -c "CREATE DATABASE scell_dev OWNER scell;"

# 5. Pour le sandbox (base separee)
sudo -u postgres psql -c "CREATE DATABASE scell_sandbox OWNER scell;"

Si les migrations fiscales echouent (tables fiscal_entries, fiscal_sequences), verifier que l'extension pgcrypto est activee :

sql
CREATE EXTENSION IF NOT EXISTS pgcrypto;

Redis non disponible

Symptome : Erreurs Connection refused sur les operations de cache, session ou queue. Les headers X-RateLimit-* ne s'affichent pas.

Diagnostic : Redis est utilise pour le cache (CACHE_STORE=redis), les sessions (SESSION_DRIVER=redis), les queues (QUEUE_CONNECTION=redis) et Horizon.

Solution :

bash
# Verifier que Redis tourne
redis-cli ping  # Doit retourner PONG

# Demarrer Redis
sudo systemctl start redis

# Verifier la connexion depuis PHP
php -r "echo (new Redis())->connect('127.0.0.1', 6379) ? 'OK' : 'ERREUR';"

# Verifier le .env
# REDIS_HOST=127.0.0.1
# REDIS_PORT=6379
# REDIS_PASSWORD=null  (ou votre mot de passe)

# Fallback temporaire (dev uniquement - pas de Horizon)
# SESSION_DRIVER=file
# CACHE_STORE=file
# QUEUE_CONNECTION=sync

Erreurs d'Authentification

401 Unauthorized - Token expire ou cle API invalide

Symptome : L'API retourne 401 avec API_KEY_MISSING, API_KEY_INVALID ou Unauthenticated.

Diagnostic : Deux modes d'auth coexistent sur Scell.io :

  1. Token Sanctum (dashboard) : Authorization: Bearer {token} -- expire apres la duree configuree dans SESSION_LIFETIME.
  2. Cle API (API externe) : X-API-Key: {cle} -- doit etre une cle active et non expiree.
  3. Cle Tenant (multi-tenant) : X-API-Key: sk_live_xxx ou X-API-Key: sk_test_xxx.

Solution :

bash
# Pour les cles API classiques
curl -H "X-API-Key: sk_live_xxxxxxxxxxxx" https://api.scell.io/api/v1/invoices

# Pour l'auth token Sanctum (dashboard)
curl -H "Authorization: Bearer 12|abc123token..." https://api.scell.io/api/v1/auth/me

# Pour les tenants
curl -H "X-API-Key: sk_live_YOUR_KEY" \
     https://api.scell.io/api/v1/tenant/me

Verifier que :

  • La cle n'a pas ete revoquee (is_active = true dans la table api_keys ou tenants).
  • La cle n'est pas expiree (expires_at IS NULL ou expires_at > NOW()).
  • L'environnement correspond : cle sandbox pour /sandbox/*, cle production pour les autres routes.

403 Forbidden - Permissions insuffisantes ou tenant non autorise

Symptome : 403 avec KYC_REQUIRED, KYB_REQUIRED, TENANT_DISABLED ou API_KEY_ENVIRONMENT_MISMATCH.

Diagnostic : Plusieurs cas possibles :

  • KYC/KYB non complete : Les routes production requierent une verification terminee.
  • Environnement incorrect : Cle sandbox sur route production (ou inversement).
  • Tenant desactive : Le champ is_active est false.
  • Permissions fiscales : Les scopes fiscal:read, fiscal:write, fiscal:admin ne sont pas configures.

Solution :

bash
# Verifier le statut KYB d'un tenant
# En base : SELECT kyb_status, is_active FROM tenants WHERE id = 'xxx';

# Pour tester en sandbox (pas de KYB requis)
# Utiliser une cle sk_test_xxx au lieu de sk_live_xxx

# Verifier l'environnement de la cle
# Le prefixe determine l'environnement :
#   sk_live_ / sk_live_ -> production (KYC/KYB requis)
#   sk_test_ / sk_test_ -> sandbox (pas de KYC/KYB)

Pour les permissions fiscales, verifier settings.fiscal_permissions du tenant :

php
// Par defaut (null) = acces complet
// Sinon, tableau de scopes autorises : ['fiscal:read', 'fiscal:write']
$tenant->settings['fiscal_permissions'] ?? null;

Token Sanctum ne fonctionne pas

Symptome : Login reussit mais les requetes suivantes retournent Unauthenticated.

Diagnostic : Probleme de configuration Sanctum ou de domaine.

Solution :

bash
# Verifier la config Sanctum
php artisan config:show sanctum

# Verifier que le domaine frontend est dans SANCTUM_STATEFUL_DOMAINS
# .env :
SANCTUM_STATEFUL_DOMAINS=localhost,localhost:5173,scell.io

# Vider le cache de config
cd backend && php artisan config:clear && php artisan cache:clear

Erreurs API

422 Validation Error - Champs manquants ou format incorrect

Symptome : 422 Unprocessable Entity avec un objet errors detaillant les champs invalides.

Diagnostic : Les Form Requests Laravel valident strictement les donnees entrantes. Les regles sont definies dans app/Http/Requests/.

Exemples courants :

json
// Creation de facture - champs obligatoires
{
    "company_id": "uuid-requis",
    "invoice_number": "FACT-2026-001",
    "issue_date": "2026-01-15",       // format: YYYY-MM-DD
    "due_date": "2026-02-15",
    "currency": "EUR",
    "total_ht": 1000.00,              // decimal, pas de string
    "total_tax": 200.00,
    "total_ttc": 1200.00,
    "seller_siret": "12345678901234", // 14 chiffres exactement
    "seller_name": "Ma Societe",
    "buyer_siret": "98765432101234",
    "buyer_name": "Client SARL"
}

// Erreur typique : seller_siret trop court
{
    "errors": {
        "seller_siret": ["Le champ seller_siret doit contenir exactement 14 caracteres."]
    }
}

Solution : Consulter les FormRequest correspondantes dans app/Http/Requests/ pour connaitre les regles exactes. Utiliser le sandbox pour tester les payloads.


429 Too Many Requests - Rate limiting

Symptome : 429 avec RATE_LIMIT_EXCEEDED et un header Retry-After.

Diagnostic : Les limites par defaut sont :

  • API classique : 60 requetes/minute par cle API ou utilisateur.
  • Tenant : Configurable via TenantRateLimiter.
  • Sandbox : 1000 requetes/minute (relaxe pour les tests).
  • Onboarding : 5 echanges de code/minute par IP.

Solution :

bash
# Lire les headers de rate limiting dans la reponse
# X-RateLimit-Limit: 60
# X-RateLimit-Remaining: 0
# Retry-After: 42  (secondes avant reset)

# Attendre le temps indique par Retry-After
# Implementer un backoff exponentiel dans votre client

# Si vous avez besoin de limites plus elevees, contacter le support
# Les overrides sont stockes dans la table rate_limit_overrides

500 Internal Server Error - Debugging

Symptome : 500 avec un message generique. Les details ne sont pas exposes au client en production.

Diagnostic : L'erreur est loguee dans backend/storage/logs/laravel.log.

Solution :

bash
# Lire les derniers logs
tail -100 backend/storage/logs/laravel.log

# En developpement, activer le mode debug
# .env : APP_DEBUG=true (JAMAIS en production)

# Verifier les logs avec un filtre
grep "ERROR" backend/storage/logs/laravel.log | tail -20

# En production, utiliser Laravel Pail pour le streaming
cd backend && php artisan pail

Causes frequentes de 500 :

  • Variable d'environnement manquante (cle API externe, config S3).
  • Service externe indisponible (OpenAPI.com, SuperPDP, INSEE).
  • Timeout base de donnees sur des requetes lourdes.
  • Extension PHP manquante apres un deploiement.

Timeout sur les appels SuperPDP

Symptome : Requetes de soumission de facture qui prennent plus de 30 secondes et finissent en timeout.

Diagnostic : Le service SuperPDP est externe avec un circuit breaker configure.

Solution :

bash
# Verifier les parametres dans .env
SUPERPDP_TIMEOUT=30              # secondes
SUPERPDP_RETRY_TIMES=3           # nombre de tentatives
SUPERPDP_RETRY_SLEEP=1000        # ms entre les tentatives
SUPERPDP_CB_FAILURE_THRESHOLD=5  # seuil avant ouverture du circuit
SUPERPDP_CB_RECOVERY_TIMEOUT=60  # secondes avant re-essai

# Verifier l'etat du circuit breaker
# Le circuit s'ouvre apres 5 echecs consecutifs, puis re-essaie apres 60s

# En sandbox, les appels sont simules (MockSuperPDPService)
# Verifier : SUPERPDP_SANDBOX=true

Si le circuit breaker est ouvert, les appels retournent immediatement une erreur 503. Attendre le recovery_timeout ou redemarrer l'application.


Erreurs de Facturation

"Solde insuffisant" lors de la creation

Symptome : 402 Payment Required avec INSUFFICIENT_BALANCE.

Diagnostic : Chaque operation a un cout :

  • Facture : 0.04 EUR (Invoice::PRICE_PER_INVOICE)
  • Avoir : 0.04 EUR
  • Signature : 1.20 EUR (Signature::PRICE_PER_SIGNATURE)

Le middleware CheckBalance (API classique) ou TenantBalanceMiddleware (multi-tenant) verifie le solde avant l'operation.

Solution :

bash
# Verifier le solde actuel
curl -H "X-API-Key: sk_live_xxx" https://api.scell.io/api/v1/tenant/balance

# Recharger via l'endpoint de top-up
curl -X POST -H "X-API-Key: sk_live_xxx" \
     -H "Content-Type: application/json" \
     -d '{"amount": 50.00}' \
     https://api.scell.io/api/v1/tenant/billing/top-up

# En sandbox, la verification de balance est desactivee
# Utiliser une cle sk_test_xxx pour tester sans frais

Format Factur-X invalide

Symptome : Erreurs de validation lors de la generation du XML embarque dans le PDF.

Diagnostic : La generation Factur-X utilise horstoeko/zugferd. Les erreurs viennent generalement de donnees manquantes ou incorrectes.

Solution :

Champs obligatoires pour un Factur-X valide (profil BASIC) :

  • seller_siret : 14 chiffres
  • seller_name et seller_address : non vides
  • buyer_name et buyer_siret (ou buyer_address)
  • total_ht, total_tax, total_ttc : coherents (total_ht + total_tax == total_ttc)
  • issue_date : format ISO 8601
  • currency : code ISO 4217 (EUR, USD...)
php
// Verifier la coherence des montants
if (abs($total_ht + $total_tax - $total_ttc) > 0.01) {
    // Erreur : les montants ne sont pas coherents
}

SIRET/TVA non reconnu

Symptome : Le SIRET ou numero de TVA est rejete lors de la verification.

Diagnostic : La verification utilise l'API INSEE (SIRET) avec authentification mTLS et la base VIES (TVA).

Solution :

bash
# Verifier la configuration INSEE
# .env :
INSEE_API_URL=https://api.insee.fr/entreprises/sirene/V3.11
INSEE_CLIENT_ID=votre_client_id
INSEE_CERT_PATH=storage/certs/insee-client.pem
INSEE_KEY_PATH=storage/certs/insee-client.key

# Verifier que les certificats sont valides
openssl x509 -in backend/storage/certs/insee-client.pem -noout -dates

# En sandbox, utiliser les SIRET/TVA de test (config/sandbox.php)
# SIRET valides : 00000000000001, 00000000000002, 12345678901234
# Tout SIRET commencant par "000" est accepte
# TVA valides : FR00000000000, FR00123456789, DE000000000

Facture bloquee en statut "pending"

Symptome : La facture reste en statut pending ou submitted indefiniment.

Diagnostic : Apres soumission, la facture est traitee de maniere asynchrone via un job (ProcessInvoice). Le changement de statut depend du webhook SuperPDP.

Solution :

bash
# 1. Verifier l'etat des jobs
cd backend && php artisan horizon:status

# 2. Verifier les jobs echoues
php artisan queue:failed

# 3. Relancer un job echoue
php artisan queue:retry {job_id}

# 4. Verifier que le webhook SuperPDP est configure
# Le webhook doit pointer vers : https://api.scell.io/api/webhooks/superpdp

# 5. En sandbox, la progression est simulee automatiquement :
#    submitted (2s) -> processing (3s) -> validated (5s) -> transmitted

Erreurs de Signature

Document trop volumineux

Symptome : Erreur lors de l'upload du document a signer.

Diagnostic : Les limites sont definies au niveau PHP, Nginx et de l'application.

Solution :

bash
# Verifier les limites PHP (php.ini)
php -i | grep -E "upload_max_filesize|post_max_size"
# Recommande : upload_max_filesize = 20M, post_max_size = 25M

# Verifier la limite Nginx (nginx.conf)
# client_max_body_size 25m;

# Verifier la validation Laravel (StoreSignatureRequest)
# Le fichier doit etre un PDF de moins de 20 Mo

Signataire ne recoit pas le SMS OTP

Symptome : Le signataire ne recoit pas le code OTP par SMS.

Diagnostic : Les SMS sont envoyes via BulkGate. En sandbox, le code est toujours 123456.

Solution :

bash
# Verifier la config BulkGate
# .env :
BULKGATE_APPLICATION_ID=votre_app_id
BULKGATE_APPLICATION_TOKEN=votre_token
BULKGATE_SENDER_ID=Scell.io

# Verifier le format du numero de telephone
# Format international requis : +33612345678 (pas 0612345678)

# Verifier les logs SMS
# Table sms_logs : phone, otp_code, sent_at, attempt_count

# En sandbox, le code OTP est toujours 123456
# Pas d'envoi SMS reel en sandbox

Signature expiree

Symptome : Le signataire ne peut plus signer. L'API retourne signature_expired.

Diagnostic : Chaque demande de signature a une date d'expiration (expires_at). Passe ce delai, la signature est automatiquement marquee comme expiree.

Solution :

bash
# Verifier la date d'expiration
curl -H "X-API-Key: sk_live_xxx" \
     https://api.scell.io/api/v1/signatures/{id}
# Regarder le champ "expires_at"

# Creer une nouvelle demande de signature avec un delai plus long
# Le champ expires_at est optionnel ; par defaut, le systeme applique un delai standard

Erreur OpenAPI.com

Symptome : Erreurs 401, 403 ou 500 lors de la creation de signature.

Diagnostic : L'integration utilise OAuth2 avec OpenAPI.com. Les tokens expirent et doivent etre renouveles.

Solution :

bash
# Verifier la configuration OAuth
# .env :
OPENAPI_BASE_URL=https://esignature.openapi.com
OPENAPI_SANDBOX_URL=https://test.esignature.openapi.com
OPENAPI_BEARER_TOKEN=votre_token

# Verifier les tokens dans la table openapi_tokens
# Regarder expires_at et rafraichir si necessaire

# En cas d'erreur persistante, verifier la config admin :
# Dashboard > Admin > OpenAPI Settings > Verify Config

Erreurs Fiscales (NF525)

Kill switch active par erreur

Symptome : 503 Service Unavailable avec fiscal_kill_switch_active sur toutes les operations d'ecriture fiscale.

Diagnostic : Le kill switch est un mecanisme d'urgence NF525 qui bloque toutes les ecritures fiscales (factures, avoirs, clotures). Les lectures restent accessibles.

Solution :

bash
# Verifier le statut du kill switch
curl -H "X-API-Key: sk_live_xxx" \
     https://api.scell.io/api/v1/tenant/fiscal/kill-switch/status

# Desactiver le kill switch (necessite le scope fiscal:admin)
curl -X POST -H "X-API-Key: sk_live_xxx" \
     -H "Content-Type: application/json" \
     -d '{"reason": "Fausse alerte, desactivation apres verification"}' \
     https://api.scell.io/api/v1/tenant/fiscal/kill-switch/deactivate

# L'activation/desactivation est tracee dans fiscal_audit_log
# Verifier qui l'a active : table fiscal_kill_switches, champs activated_by_*

Important : La desactivation du kill switch requiert le scope fiscal:admin. Si votre cle API n'a que fiscal:read ou fiscal:write, vous recevrez un 403.


Cloture journaliere echouee

Symptome : La commande fiscal:daily-closing echoue ou ne s'execute pas.

Diagnostic : La cloture journaliere s'execute automatiquement via le scheduler Laravel (configurable via fiscal.daily_closing_time, par defaut 00:05). Elle produit une entree dans fiscal_closings.

Solution :

bash
# Lancer manuellement une cloture journaliere
cd backend && php artisan fiscal:daily-closing

# Verifier que le scheduler fonctionne
php artisan schedule:list

# Verifier le cron
crontab -l
# Doit contenir : * * * * * cd /path/to/backend && php artisan schedule:run >> /dev/null 2>&1

# Via l'API (scope fiscal:write requis)
curl -X POST -H "X-API-Key: sk_live_xxx" \
     https://api.scell.io/api/v1/tenant/fiscal/closings/daily

Integrite de la chaine compromise

Symptome : La verification d'integrite signale des anomalies (anomalies_found > 0).

Diagnostic : Les ecritures fiscales forment une chaine cryptographique (SHA-256). Chaque entree contient un chain_hash qui depend du previous_hash. Si un maillon est altere, la chaine est rompue.

Solution :

bash
# Lancer une verification d'integrite
curl -H "X-API-Key: sk_live_xxx" \
     https://api.scell.io/api/v1/tenant/fiscal/integrity

# Verifier l'historique des anomalies
curl -H "X-API-Key: sk_live_xxx" \
     https://api.scell.io/api/v1/tenant/fiscal/integrity/history

# Lancer la commande artisan de verification approfondie
cd backend && php artisan fiscal:integrity-check

# L'integrite est verifiee automatiquement chaque jour a 02:00 UTC
# Configurable dans config/fiscal.php : integrity_check.schedule_time

Si des anomalies sont detectees, NE JAMAIS modifier les ecritures fiscales directement en base. Les tentatives de modification sont bloquees par les traits d'immutabilite (LogsImmutabilityViolation) et enregistrees dans fiscal_audit_log.


Export FEC incomplet

Symptome : L'export FEC (Fichier des Ecritures Comptables) ne contient pas toutes les lignes attendues.

Diagnostic : L'export FEC filtre les ecritures par tenant et par periode. Le format par defaut est pipe (separateur |), encodage UTF-8.

Solution :

bash
# Generer un export FEC
curl -H "X-API-Key: sk_live_xxx" \
     "https://api.scell.io/api/v1/tenant/fiscal/fec?start_date=2026-01-01&end_date=2026-12-31" \
     -o export-fec.txt

# Verifier que la periode couvre bien les ecritures souhaitees
# Les clotures creent des ecritures supplementaires (type "closing")

# Export forensique (plus detaille, pour audit)
curl -H "X-API-Key: sk_live_xxx" \
     "https://api.scell.io/api/v1/tenant/fiscal/forensic-export" \
     -o forensic-export.json

Erreurs Multi-Tenant

Tenant non trouve

Symptome : 401 avec INVALID_API_KEY lors des appels sur /v1/tenant/*.

Diagnostic : Le middleware TenantApiKeyMiddleware valide le format de la cle (sk_live_xxx ou sk_test_xxx, 32 caracteres alphanumeriques apres le prefixe) puis recherche le tenant.

Solution :

bash
# Verifier le format de la cle
# Regex attendue : ^tk_(live|test)_[a-zA-Z0-9]{32}$

# Verifier que la cle est envoyee dans le bon header
curl -H "X-API-Key: sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
     https://api.scell.io/api/v1/tenant/me

# Le header X-Tenant-Key est aussi accepte (alias)

Isolation violation

Symptome : Impossible d'acceder aux ressources d'un sub-tenant, ou les donnees d'un autre tenant apparaissent.

Diagnostic : L'isolation est assuree par :

  1. Le trait HasTenantScope sur les modeles fiscaux.
  2. Le middleware EnsureTenantIsolation (variable PostgreSQL app.current_tenant_id).
  3. Les scopes Eloquent forTenant() et forSubTenant().

Solution :

bash
# Verifier que le tenant_id est bien propage
# Dans les controllers tenant, le tenant est injecte via la requete :
# $tenant = $request->attributes->get('tenant');

# Pour debugger, activer le query log
DB::enableQueryLog();
// ... votre requete
dd(DB::getQueryLog());
// Verifier que WHERE tenant_id = 'xxx' est present

Sub-tenant non autorise

Symptome : 403 ou 404 lors de l'acces a un sub-tenant.

Diagnostic : Le middleware SubTenantMiddleware verifie que le sub-tenant appartient bien au tenant authentifie.

Solution :

bash
# Verifier l'appartenance du sub-tenant
curl -H "X-API-Key: sk_live_xxx" \
     https://api.scell.io/api/v1/tenant/sub-tenants

# Le sub-tenant doit avoir tenant_id = le tenant authentifie
# Rechercher par external_id si necessaire :
curl -H "X-API-Key: sk_live_xxx" \
     https://api.scell.io/api/v1/tenant/sub-tenants/by-external-id/EXT-001

Onboarding bloque

Symptome : La session d'onboarding reste en statut in_progress ou documents_requested.

Diagnostic : Le flux d'onboarding necessite : verification SIRET -> verification TVA -> upload documents KYB -> completion.

Solution :

bash
# Verifier le statut de la session
curl -H "X-API-Key: pk_xxx" \
     https://api.scell.io/api/v1/onboarding/sessions/{session_id}

# Les etapes doivent etre completees dans l'ordre :
# 1. POST /v1/onboarding/verify-siret
# 2. POST /v1/onboarding/verify-vat
# 3. POST /v1/onboarding/documents (upload KYB)
# 4. POST /v1/onboarding/complete

# Verifier les timestamps dans la session :
# siret_verified_at, vat_verified_at, documents_submitted_at, completed_at

# Le code de verification expire (verification_code_expires_at)
# Si expire, il faut relancer une session

Erreurs de Queue/Jobs

Horizon ne demarre pas

Symptome : php artisan horizon echoue ou se termine immediatement.

Diagnostic : Horizon necessite Redis et l'extension pcntl.

Solution :

bash
# Verifier Redis
redis-cli ping

# Verifier l'extension pcntl
php -m | grep pcntl
# Si absent : cette extension est dispo uniquement en CLI, pas en FPM

# Demarrer Horizon
cd backend && php artisan horizon

# En production (supervisor ou systemd)
# Creer un fichier /etc/supervisor/conf.d/horizon.conf :
# [program:horizon]
# command=php /home/ploi/api.scell.io/backend/artisan horizon
# user=ploi
# autostart=true
# autorestart=true
# redirect_stderr=true
# stdout_logfile=/home/ploi/api.scell.io/backend/storage/logs/horizon.log

# Verifier le dashboard Horizon
# URL : https://api.scell.io/horizon (necessite auth admin)

Jobs en echec (failed_jobs)

Symptome : Les jobs s'accumulent dans failed_jobs. Les factures/signatures ne sont pas traitees.

Diagnostic : Les jobs principaux sont ProcessInvoice, ProcessSignature, SendWebhook, ProcessIncomingInvoiceWebhook, ProcessSuperPDPWebhook.

Solution :

bash
# Lister les jobs echoues
cd backend && php artisan queue:failed

# Voir le detail d'un job echoue
php artisan queue:failed --id={uuid}

# Relancer un job specifique
php artisan queue:retry {uuid}

# Relancer tous les jobs echoues
php artisan queue:retry all

# Purger les jobs echoues (avec prudence)
php artisan queue:flush

# Causes frequentes d'echec :
# - Service externe indisponible (OpenAPI.com, SuperPDP)
# - Timeout depassé
# - Donnees invalides (payload corrompu)
# - Erreur de serialisation (modele supprime entre-temps)

Webhooks non delivres

Symptome : Vos endpoints webhook ne recoivent pas les notifications.

Diagnostic : Les webhooks sortants sont envoyes via le job SendWebhook. Chaque tentative est loguee dans webhook_logs.

Solution :

bash
# Verifier la configuration du webhook
curl -H "Authorization: Bearer {token}" \
     https://api.scell.io/api/v1/webhooks

# Verifier les logs de livraison
curl -H "Authorization: Bearer {token}" \
     https://api.scell.io/api/v1/webhooks/{id}/logs

# Tester manuellement la livraison
curl -X POST -H "Authorization: Bearer {token}" \
     https://api.scell.io/api/v1/webhooks/{id}/test

# Verifier que votre endpoint :
# 1. Repond en HTTP 200 dans les 5 secondes
# 2. Est accessible publiquement (pas de firewall bloquant)
# 3. Accepte le Content-Type: application/json
# 4. Valide la signature HMAC (header X-Webhook-Signature)

# Le systeme effectue des retries avec backoff exponentiel :
# Configurable par webhook : retries, max_retries, backoff_multiplier

Debug Tips

Activer le mode debug Laravel

bash
# En developpement UNIQUEMENT
# .env : APP_DEBUG=true

# Recharger la config
cd backend && php artisan config:clear

# Les erreurs s'affichent maintenant en detail dans les reponses JSON
# ATTENTION : NE JAMAIS activer en production (fuite d'informations sensibles)

Consulter les logs

bash
# Logs Laravel (erreurs, requetes, jobs)
tail -f backend/storage/logs/laravel.log

# Streaming en temps reel avec Pail
cd backend && php artisan pail

# Filtrer par niveau
php artisan pail --filter="ERROR"

# Logs Horizon (queue, workers)
tail -f backend/storage/logs/horizon.log

Horizon dashboard

Accessible a https://api.scell.io/horizon (authentification admin requise). Permet de voir :

  • Les workers actifs et leur charge.
  • Les jobs en attente, en cours, echoues.
  • Les metriques de throughput et de latence.
  • L'etat des queues par priorite.

Tester avec le sandbox

Le mode sandbox permet de tester l'integration sans frais et sans services externes reels.

bash
# Utiliser une cle sandbox
# Format : sk_test_xxx (API classique) ou sk_test_xxx (tenant)

# Specifites du sandbox :
# - Pas de verification de balance
# - SIRET/TVA de test acceptes (voir config/sandbox.php)
# - Code OTP toujours 123456
# - Signatures auto-completees apres 10 secondes
# - Progression de statut simulee pour les factures
# - Rate limits relaxes (1000 req/min)
# - Pas d'appels reels vers OpenAPI.com ou SuperPDP

FAQ Technique

Comment regenerer une cle API ?

bash
# Pour un utilisateur (via le dashboard)
curl -X DELETE -H "Authorization: Bearer {token}" \
     https://api.scell.io/api/v1/api-keys/{key_id}
curl -X POST -H "Authorization: Bearer {token}" \
     -H "Content-Type: application/json" \
     -d '{"name": "Ma nouvelle cle", "environment": "production"}' \
     https://api.scell.io/api/v1/api-keys

# Pour un tenant
curl -X POST -H "X-API-Key: sk_live_xxx" \
     https://api.scell.io/api/v1/tenant/regenerate-key
# ATTENTION : l'ancienne cle est immediatement invalidee.
# Sauvegarder la nouvelle cle retournee dans la reponse.

Comment forcer une cloture fiscale ?

bash
# Cloture journaliere manuelle (via API, scope fiscal:write)
curl -X POST -H "X-API-Key: sk_live_xxx" \
     https://api.scell.io/api/v1/tenant/fiscal/closings/daily

# Cloture journaliere manuelle (via artisan)
cd backend && php artisan fiscal:daily-closing

# Cloture mensuelle
cd backend && php artisan fiscal:monthly-closing

# Les clotures sont idempotentes : relancer une cloture pour une date deja traitee
# ne cree pas de doublon.

Comment migrer du sandbox vers la production ?

  1. Completer le KYB : Soumettre les documents requis (identite, K-bis, justificatif de domicile).
  2. Attendre la validation : Le statut KYB passe de pending a verified.
  3. Generer une cle production : Creer une cle sk_live_xxx ou sk_live_xxx.
  4. Mettre a jour votre integration : Remplacer la cle sandbox par la cle production.
  5. Recharger le solde : En production, chaque operation est facturee.
bash
# Verifier le statut KYB
curl -H "X-API-Key: sk_test_xxx" \
     https://api.scell.io/api/v1/tenant/kyb/status

# Les donnees sandbox et production sont isolees.
# Les factures creees en sandbox ne migrent PAS vers la production.

Comment debugger un webhook ?

bash
# 1. Verifier la configuration
curl -H "Authorization: Bearer {token}" \
     https://api.scell.io/api/v1/webhooks/{id}

# 2. Consulter les logs de livraison
curl -H "Authorization: Bearer {token}" \
     https://api.scell.io/api/v1/webhooks/{id}/logs
# Chaque log contient : event_name, payload, status_code, response, attempt

# 3. Envoyer un test
curl -X POST -H "Authorization: Bearer {token}" \
     https://api.scell.io/api/v1/webhooks/{id}/test

# 4. Verifier la signature HMAC cote recepteur
# Le header X-Webhook-Signature contient un HMAC SHA-256 du body
# Signe avec le secret du webhook

# Exemple de verification (Node.js) :
# const crypto = require('crypto');
# const signature = crypto.createHmac('sha256', webhookSecret)
#   .update(rawBody)
#   .digest('hex');
# if (signature !== receivedSignature) throw new Error('Invalid signature');

# 5. Regenerer le secret si compromis
curl -X POST -H "Authorization: Bearer {token}" \
     https://api.scell.io/api/v1/webhooks/{id}/regenerate-secret

Contacts et Escalade

ProblemeCanal
Bug APIGitHub Issues du projet
Urgence productionEquipe DevOps via Slack
Probleme facturationsupport@scell.io
Conformite fiscalelegal@scell.io

Documentation Scell.io