Tester votre intégration
La plateforme nationale DGI n'étant pas encore ouverte, EFact fonctionne comme un environnement de test grandeur nature : chaque facture traverse le pipeline réel (validation, UBL 2.1, signature XAdES) puis est clairée par notre simulateur DGI. Vous pouvez valider toute votre intégration aujourd'hui, sans aucune conséquence fiscale.
En bref
Le simulateur DGI d'EFact est le mode test par défaut : vos factures traversent le pipeline réel (validation, UBL 2.1, signature XAdES) sans transmission à la plateforme officielle. En juillet 2026, toute facture valide passe à ACCEPTEDenviron 5 secondes après transmission. Vous pouvez tester dès aujourd'hui les erreurs 400, 409, 401 et 429 ; le rejet DGI forcé est planifié.
Sandbox par conception : le simulateur reproduit le flux de clairance officiel de bout en bout - soumission, vérification de la signature, référence, QR code, callbacks. Le jour où la plateforme nationale ouvre, votre code ne change pas : seule la cible de transmission change, côté EFact. Voir Architecture - Environnements.
Le cycle nominal
Dans le sandbox, toute facture valide est acceptée : créez, suivez le statut jusqu'à ACCEPTED, récupérez la référence et le QR code.
import { EFact } from '@itzenata/efact-node'
const efact = new EFact(process.env.EFACT_SECRET_KEY!)
// 1. Créer : la facture revient déjà normalisée (UBL 2.1) et signée (XAdES)
const invoice = await efact.invoices.create({
invoiceNumber: `TEST-${Date.now()}`, // unique par organisation
invoiceDate: '2026-07-14',
currency: 'MAD',
paymentMethod: 'BANK_TRANSFER',
buyerIce: '002345678901234',
buyerName: 'Client de test SARL',
lines: [
{ lineNumber: 1, description: 'Test integration', quantity: 1, unitPrice: 100, vatRate: 20 },
],
})
console.log(invoice.status) // 'SIGNED'
// 2. Suivre la clairance : le simulateur répond en quelques secondes
let status = await efact.invoices.retrieveStatus(invoice.id)
while (status.status === 'SIGNED' || status.status === 'TRANSMITTED') {
await new Promise((r) => setTimeout(r, 2000))
status = await efact.invoices.retrieveStatus(invoice.id)
}
console.log(status.status) // 'ACCEPTED'
console.log(status.dgiReference) // référence de clairance
console.log(status.dgiQrPayload) // contenu du QR codeChronologie attendue
| Statut | Quand |
|---|---|
SIGNED | Immédiat : la réponse du POST est déjà signée |
TRANSMITTED | Quelques instants après la création (soumission au simulateur) |
ACCEPTED | Environ 5 secondes après la transmission (callback simulé). Si un incident transitoire survient, les planificateurs de rattrapage re-soumettent automatiquement (2 à 5 minutes) |
Un intervalle de polling de 2 à 5 secondes suffit. Restez sous la limite de 300 requêtes/minute ; le SDK gère automatiquement les 429.
Les cas d'erreur à couvrir
Tous ces comportements sont reproductibles à la demande dans le sandbox :
| Cas | Comment le déclencher | Résultat attendu |
|---|---|---|
| Payload invalide | ICE ≠ 15 chiffres, lines vide, devise invalide... | 400 VALIDATION_ERROR + détail par champ |
| Numéro dupliqué | Rejouer un create avec le même invoiceNumber | 409 INVOICE_NUMBER_EXISTS |
| Immuabilité | update ou cancel sur une facture signée | 409 INVOICE_NOT_EDITABLE |
| Authentification | Clé invalide ou absente | 401 API_KEY_INVALID / API_KEY_MISSING |
| Isolation tenant | UUID d'une facture d'une autre organisation | 404 INVOICE_NOT_FOUND (jamais 403) |
| Rate limiting | Plus de 300 requêtes/minute | 429 + Retry-After |
| Mentions légales | Créer une facture avant d'avoir complété ICE/IF/RC au dashboard | 409 MISSING_LEGAL_MENTIONS |
import { EFactValidationError, EFactConflictError } from '@itzenata/efact-node'
// ICE invalide (14 chiffres au lieu de 15) -> 400 VALIDATION_ERROR
try {
await efact.invoices.create({ ...input, buyerIce: '12345678901234' })
} catch (e) {
if (e instanceof EFactValidationError) {
console.log(e.errors) // [{ field: 'buyerIce', message: '...' }]
}
}
// Numéro de facture déjà utilisé -> 409 INVOICE_NUMBER_EXISTS
try {
await efact.invoices.create({ ...input, invoiceNumber: 'TEST-DUPLICATE' })
await efact.invoices.create({ ...input, invoiceNumber: 'TEST-DUPLICATE' })
} catch (e) {
if (e instanceof EFactConflictError) {
console.log(e.code) // 'INVOICE_NUMBER_EXISTS'
}
}
// Modification d'une facture déjà signée -> 409 INVOICE_NOT_EDITABLE
try {
await efact.invoices.update(invoice.id, { buyerName: 'Autre nom' })
} catch (e) {
if (e instanceof EFactConflictError) {
console.log(e.code) // 'INVOICE_NOT_EDITABLE'
}
}Gérer les rejets DGIPlanifié
Aujourd'hui, le sandbox accepte toute facture valide : il n'est pas encore possible de forcer un REJECTED à la demande (des valeurs de test dédiées, à la Stripe, sont planifiées). Écrivez néanmoins votre gestion de rejet dès maintenant - le contrat est stable et documenté :
// Écrivez ce handler dès maintenant : le contrat de REJECTED est stable,
// même si le sandbox ne produit pas encore de rejet à la demande.
const status = await efact.invoices.retrieveStatus(invoice.id)
switch (status.status) {
case 'ACCEPTED':
// Facture cleared : archiver dgiReference + QR, marquer payable
break
case 'REJECTED':
// Rejet de clairance : montrer dgiErrorCode / dgiErrorMessage
// à l'utilisateur, corriger la facture, en émettre une nouvelle
console.error(status.dgiErrorCode, status.dgiErrorMessage)
break
case 'FAILED':
// Erreur technique irrécupérable : alerter, contacter le support
break
}En cas de rejet, une facture n'est pas modifiable : corrigez les données et émettez une nouvelle facture avec un nouveau numéro.
Vérifier les artefacts
Après ACCEPTED, vérifiez que votre intégration récupère et archive les artefacts :
// Une fois ACCEPTED, les artefacts sont téléchargeables :
GET /v1/invoices/{id}/pdf # PDF de la facture
GET /v1/invoices/{id}/xml # XML UBL 2.1 signé (original légal)
GET /v1/invoices/{id}/qr # QR code de clairance (PNG)Bonnes pratiques de test
- • Numéros uniques : préfixez vos numéros de test (
TEST-...) et incluez un timestamp - leinvoiceNumberest unique par organisation, pour toujours. - • Ne rejouez jamais un create en aveugle : après un timeout réseau, rejouez avec le même numéro ; soit il réussit, soit le
409confirme que la facture existe déjà. - • Testez les statuts inattendus : votre code doit tolérer de voir
SIGNEDouTRANSMITTEDpendant plusieurs minutes (rattrapage automatique) sans paniquer. - • Une clé API dédiée aux tests : créez une clé séparée pour vos environnements de test et révoquez-la sans risque.
Étapes suivantes
Questions fréquentes
Comment tester l'API EFact sans envoyer de vraies factures à la DGI ?
EFact fonctionne avec un simulateur DGI qui est le mode test par défaut : vos factures traversent le pipeline réel (validation, UBL 2.1, signature XAdES) puis sont clairées par le simulateur, sans transmission à la plateforme officielle. Vous testez ainsi le cycle complet en toute sécurité, sans conséquence fiscale.
Quel résultat renvoie le simulateur DGI d'EFact ?
En juillet 2026, toute facture valide est acceptée (ACCEPTED) environ 5 secondes après la transmission via un callback simulé. Vous pouvez tester dès aujourd'hui les erreurs de validation (400), l'immutabilité (409), l'authentification (401) et les limites de débit (429). Le déclenchement forcé d'un rejet DGI est planifié.
À quelle fréquence interroger le statut d'une facture en test ?
Un intervalle de polling de 2 à 5 secondes suffit : le simulateur répond en quelques secondes. Restez sous la limite de 300 requêtes par minute pour éviter une réponse 429. En production, les webhooks remplaceront le polling pour être notifié du statut final sans interrogation répétée.