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 Node.js / JavaScript & TypeScript

Version publiée de @itzenata/efact-node

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-node

Initialisation

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

Method Signature
efact.invoices.create(input: CreateInvoiceRequest): Promise<InvoiceResponse>

Paramètres

Parameters
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

Complete Example
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é serveur

efact.invoices.retrieve(invoiceId)

Retrieve Invoice
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)

Retrieve Status
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 DGI

efact.invoices.list(params?)

List Invoices
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)

Update Invoice
// 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)

Cancel Invoice
// 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 :

Error Handling
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

ClasseCode HTTPQuand
EFactConfigError-Clé manquante ou option de configuration invalide
EFactAuthError401 / 403Clé API manquante ou invalide
EFactNotFoundError404Ressource introuvable (y compris hors tenant)
EFactValidationError400 / 422Payload invalide
EFactConflictError409Ex. modification/annulation d’une facture qui n’est plus PENDING
EFactApiError5xxErreur 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
// 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 :

StatutSignification
PENDINGCréée, modifiable, pas encore transmise
SIGNEDSignée (XAdES), en attente de transmission
TRANSMITTEDEnvoyée à la plateforme DGI, en attente de clairance
ACCEPTEDValidée par la DGI - référence et QR code disponibles
REJECTEDRejetée par la DGI - voir errorCode / errorMessage
FAILEDÉchec technique de traitement
CANCELLEDAnnulé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.