Simulateur DGI: la plateforme nationale d'e-facturation de la DGI n'est pas encore ouverte aux intégrations. La clairance (statuts ACCEPTED / REJECTED, référence, QR code) est aujourd'hui assurée par notre simulateur, qui reproduit le flux officiel. Le basculement vers la plateforme officielle se fera sans changement côté client. En savoir plus
SDK officiel EFact pour Node.js. Fonctionne aussi bien en JavaScript pur (ESM ou CommonJS) qu'en TypeScript natif - les types sont inclus dans les deux cas. Toutes les opérations sur les factures sont exposées via efact.invoices.*.
En bref
Le SDK Node.js officiel d'EFact (@itzenata/efact-node), écrit en TypeScript natif, permet de créer et suivre des factures électroniques conformes DGI depuis Node.js. Installez-le via npm, initialisez-le avec votre clé secrète efact_sk_, puis appelez efact.invoices.* : le SDK gère la génération UBL 2.1, la signature XAdES et la transmission côté serveur.
Installation
npm install @itzenata/efact-nodeInitialisation
Basique
import { EFact } from '@itzenata/efact-node'
// La clé secrète identifie ET authentifie votre organisation.
// Ne la committez jamais : chargez-la depuis l'environnement.
const efact = new EFact(process.env.EFACT_SECRET_KEY!)efact.invoices.create(input)
Signature
efact.invoices.create(input: CreateInvoiceRequest): Promise<InvoiceResponse>Paramètres
interface CreateInvoiceLineRequest {
lineNumber: number // Requis, positif, unique dans la facture
description: string // Requis, <= 512 caractères
quantity: number // Requis, positif
unitPrice: number // Requis, >= 0
vatRate: number // Requis, >= 0 (pourcentage)
unit?: string // Optionnel, <= 32 caractères, unité de mesure libre (ex. "HOUR")
}
interface CreateInvoiceRequest {
invoiceNumber: string // Requis, <= 64 caractères
invoiceDate: string // Requis, format YYYY-MM-DD
currency: string // Requis, ISO 3 lettres majuscules (ex. "MAD")
paymentMethod: CreateInvoiceRequestPaymentMethodEnum
// Requis : 'BANK_TRANSFER' | 'CASH' | 'CHECK' | 'CARD' | 'OTHER'
buyerIce?: string // Optionnel, exactement 15 chiffres si fourni
buyerName?: string // Optionnel, raison sociale de l'acheteur
buyerAddress?: string // Optionnel, adresse de l'acheteur
dueDate?: string // Optionnel, format YYYY-MM-DD
lines: CreateInvoiceLineRequest[] // Non vide
}Exemple complet
import { EFact, CreateInvoiceRequestPaymentMethodEnum } from '@itzenata/efact-node'
const efact = new EFact(process.env.EFACT_SECRET_KEY!)
const invoice = await efact.invoices.create({
invoiceNumber: 'INV-2026-043',
invoiceDate: '2026-06-19',
currency: 'MAD',
paymentMethod: CreateInvoiceRequestPaymentMethodEnum.BankTransfer,
buyerIce: '002345678901234',
buyerName: 'Client SARL',
buyerAddress: '12 Rue Allal Ben Abdellah, Rabat',
lines: [
{
lineNumber: 1,
description: 'Licence logiciel annuelle',
quantity: 2,
unitPrice: 100,
vatRate: 20,
},
{
lineNumber: 2,
description: 'Formation (exonérée TVA)',
quantity: 1,
unitPrice: 40,
vatRate: 0,
},
],
})
console.log(invoice.id) // identifiant interne
console.log(invoice.status) // 'SIGNED' (normalisée et signée à la création)
console.log(invoice.amountTtc) // montant total TTC, calculé côté serveurefact.invoices.retrieve(invoiceId)
const invoice = await efact.invoices.retrieve('550e8400-e29b-41d4-a716-446655440000')
if (invoice.status === InvoiceResponseStatusEnum.Accepted) {
console.log('Référence DGI :', invoice.dgiReference)
console.log('QR code DGI :', invoice.dgiQrCode)
}
if (invoice.status === InvoiceResponseStatusEnum.Rejected) {
console.log('Code erreur :', invoice.errorCode)
console.log('Message :', invoice.errorMessage)
}Lève EFactNotFoundError (HTTP 404) si la facture n’existe pas ou n’appartient pas au tenant de la clé API utilisée.
efact.invoices.retrieveStatus(invoiceId)
const result = await efact.invoices.retrieveStatus('550e8400-e29b-41d4-a716-446655440000')
console.log(result.status) // InvoiceStatusResponseStatusEnum
console.log(result.dgiReference) // référence de clairance DGI, si disponible
console.log(result.dgiQrPayload) // contenu du QR code, si disponible
console.log(result.dgiErrorCode) // code d'erreur DGI, si rejeté
console.log(result.dgiErrorMessage) // message d'erreur DGI, si rejeté
console.log(result.dgiSubmittedAt) // date de soumission à la DGI
console.log(result.dgiValidatedAt) // date de validation par la DGIefact.invoices.list(params?)
import { ListStatusEnum } from '@itzenata/efact-node'
const page = await efact.invoices.list({
status: ListStatusEnum.Accepted,
page: 0,
size: 50,
sort: 'createdAt,desc',
})
for (const invoice of page.content) {
console.log(invoice.id, invoice.invoiceNumber)
}Pagination par page/size, pas par curseur. La réponse expose content: InvoiceResponse[] ainsi que les métadonnées de pagination du backend. Filtrage par statut via ListStatusEnum.
efact.invoices.update(invoiceId, input)
// Uniquement pour une facture au statut PENDING - sinon 409 (INVOICE_NOT_EDITABLE)
// Tous les champs sont optionnels - seuls les champs fournis sont appliqués.
const updated = await efact.invoices.update('550e8400-e29b-41d4-a716-446655440000', {
dueDate: '2026-07-31',
buyerAddress: 'Nouvelle adresse, Casablanca',
})efact.invoices.cancel(invoiceId)
// Uniquement pour une facture au statut PENDING
const cancelled = await efact.invoices.cancel('550e8400-e29b-41d4-a716-446655440000')
console.log(cancelled.status) // 'CANCELLED'Gestion des erreurs
Le SDK lève des erreurs typées, toutes héritées de EFactError. Toujours envelopper les appels dans un try/catch :
import {
EFact,
EFactError,
EFactConfigError,
EFactConnectionError,
EFactTimeoutError,
EFactApiError,
EFactAuthError,
EFactValidationError,
EFactNotFoundError,
EFactConflictError,
} from '@itzenata/efact-node'
try {
const invoice = await efact.invoices.create({ /* ... */ })
} catch (error) {
if (error instanceof EFactAuthError) {
// Clé API manquante ou invalide - HTTP 401/403
console.error('Authentification :', error.message)
} else if (error instanceof EFactValidationError) {
// Payload invalide - HTTP 400/422
console.error('Validation :', error.code, error.errors)
} else if (error instanceof EFactNotFoundError) {
// Facture introuvable ou hors tenant - HTTP 404
console.error('Introuvable :', error.message)
} else if (error instanceof EFactConflictError) {
// Ex. modification d'une facture qui n'est plus PENDING - HTTP 409
console.error('Conflit :', error.message)
} else if (error instanceof EFactTimeoutError) {
console.error('Timeout après', error.timeoutMs, 'ms')
} else if (error instanceof EFactConnectionError) {
console.error('Réseau indisponible :', error.cause)
} else if (error instanceof EFactApiError) {
// Toute autre erreur applicative (5xx, etc.)
console.error('Erreur API :', error.code, error.httpStatus)
} else {
throw error
}
}Hiérarchie des erreurs
| Classe | Code HTTP | Quand |
|---|---|---|
EFactConfigError | - | Clé manquante ou option de configuration invalide |
EFactAuthError | 401 / 403 | Clé API manquante ou invalide |
EFactNotFoundError | 404 | Ressource introuvable (y compris hors tenant) |
EFactValidationError | 400 / 422 | Payload invalide |
EFactConflictError | 409 | Ex. modification/annulation d’une facture qui n’est plus PENDING |
EFactApiError | 5xx | Erreur côté EFact non classifiée plus précisément |
EFactTimeoutError | - | Délai dépassé avant réponse |
EFactConnectionError | - | Échec réseau (DNS, connexion refusée, TLS...) |
Les requêtes GET, HEAD, PUT et DELETE sont rejouées automatiquement (back-off exponentiel avec jitter) sur timeout, erreur réseau, ou statuts HTTP 408/425/429/500/502/503/504, jusqu’à maxRetries fois. POST (création) n’est jamais rejoué.
Intégration Next.js App Router
// app/api/invoices/route.ts
import { EFact, CreateInvoiceRequestPaymentMethodEnum } from '@itzenata/efact-node'
import { NextRequest, NextResponse } from 'next/server'
const efact = new EFact(process.env.EFACT_SECRET_KEY!)
export async function POST(req: NextRequest) {
const body = await req.json()
const invoice = await efact.invoices.create({
invoiceNumber: body.invoiceNumber,
invoiceDate: body.invoiceDate,
currency: 'MAD',
paymentMethod: CreateInvoiceRequestPaymentMethodEnum.BankTransfer,
buyerIce: body.buyerIce,
buyerName: body.buyerName,
buyerAddress: body.buyerAddress,
lines: body.lines,
})
return NextResponse.json({ id: invoice.id, status: invoice.status })
}Statuts de facture
Le statut reflète le cycle de vie complet, aligné avec le backend :
| Statut | Signification |
|---|---|
PENDING | Créée, modifiable, pas encore transmise |
SIGNED | Signée (XAdES), en attente de transmission |
TRANSMITTED | Envoyée à la plateforme DGI, en attente de clairance |
ACCEPTED | Validée par la DGI - référence et QR code disponibles |
REJECTED | Rejetée par la DGI - voir errorCode / errorMessage |
FAILED | Échec technique de traitement |
CANCELLED | Annulée (uniquement possible depuis PENDING) |
Questions fréquentes
Le SDK Node.js d'EFact fonctionne-t-il en JavaScript pur, sans TypeScript ?
Oui. Le SDK @itzenata/efact-node fonctionne à l'identique en JavaScript pur : les exemples restent valables, il suffit de retirer les annotations de types. Les deux systèmes de modules sont supportés, en ESM (import) comme en CommonJS (require). Même en JavaScript, votre éditeur bénéficie de l'autocomplétion grâce aux types embarqués dans le paquet.
Comment installer le SDK Node.js d'EFact ?
Installez le paquet officiel @itzenata/efact-node depuis npm (npm install @itzenata/efact-node, ou l'équivalent pnpm/yarn). Le SDK est écrit en TypeScript natif et fournit ses propres types, mais s'utilise aussi bien en JavaScript pur. Initialisez-le avec votre clé secrète : new EFact(process.env.EFACT_SECRET_KEY). Toutes les opérations sur les factures sont exposées via efact.invoices.*.
Comment gérer les erreurs avec le SDK Node.js EFact ?
Le SDK lève des erreurs typées héritant toutes de EFactError : EFactAuthError (401/403), EFactValidationError (400/422), EFactNotFoundError (404), EFactConflictError (409), EFactTimeoutError et EFactConnectionError. Enveloppez vos appels dans un try/catch et testez le type d'erreur avec instanceof pour réagir précisément à chaque cas.
Le SDK Node.js réessaie-t-il automatiquement les requêtes ?
Oui, mais uniquement les requêtes idempotentes (GET, HEAD, PUT, DELETE), rejouées avec un back-off exponentiel et du jitter sur timeout, erreur réseau ou statuts 408/425/429/500/502/503/504, jusqu'à maxRetries fois. La création de facture (POST) n'est jamais rejouée automatiquement, pour éviter tout doublon.