SDK React - @itzenata/efact-react

Composants React prêts à l'emploi pour afficher le statut d'une facture électronique en temps réel.

Installation

Installation
npm install @itzenata/efact-react

Peer dependencies

(doivent être installées séparément) :

Peer Dependencies
npm install react react-dom

Versions supportées : React 18+.

Configuration du Provider

Envelopper l'application (ou la partie concernée) avec ItzenataProvider :

app/layout.tsx
// app/layout.tsx (Next.js App Router)
import { ItzenataProvider } from '@itzenata/efact-react'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ItzenataProvider publishableKey={process.env.NEXT_PUBLIC_EFACT_PUBLISHABLE_KEY}>
          {children}
        </ItzenataProvider>
      </body>
    </html>
  )
}

La clé publiable est la seule information sensible nécessaire côté frontend - elle est sûre à exposer dans le navigateur.

Composant <InvoiceStatus />

Le composant principal. Affiche le cycle de vie d'une facture en temps réel via SSE.

Usage minimal

Minimal Usage
import { InvoiceStatus } from '@itzenata/efact-react'

export function InvoicePage({ clientSecret }: { clientSecret: string }) {
  return <InvoiceStatus clientSecret={clientSecret} />
}

Props

PropTypeRequisDescription
clientSecretstringOuiclient_secret retourné par invoices.create()
onStatusChange(status: InvoiceStatus) => voidNonCallback appelé à chaque changement de statut
onAccepted(data: InvoiceAcceptedData) => voidNonCallback quand la DGI accepte la facture
onRejected(data: InvoiceRejectedData) => voidNonCallback quand la DGI rejette la facture
locale'fr' | 'ar'NonLangue d'affichage - défaut : 'fr'
showDownloadLinksbooleanNonAfficher les liens PDF/XML - défaut : true
showQRCodebooleanNonAfficher le QR code DGI - défaut : true
showRetryButtonbooleanNonAfficher "Renvoyer" si rejeté - défaut : true
classNamestringNonClasse CSS personnalisée sur le container

Exemple avec callbacks

Callbacks Example
import { InvoiceStatus } from '@itzenata/efact-react'
import { useRouter } from 'next/navigation'

export function InvoicePage({ clientSecret, invoiceId }) {
  const router = useRouter()

  return (
    <InvoiceStatus
      clientSecret={clientSecret}
      onAccepted={({ dgiId, pdfUrl, xmlUrl }) => {
        console.log('Facture acceptée par la DGI, ID :', dgiId)
        // Rediriger vers la page de confirmation
        router.push(`/invoices/${invoiceId}/confirmed`)
      }}
      onRejected={({ rejectionReason }) => {
        console.error('Facture rejetée :', rejectionReason)
      }}
      onStatusChange={(status) => {
        // Tracking analytics
        analytics.track('invoice_status_changed', { status })
      }}
      locale="fr"
    />
  )
}

Ce qu'affiche <InvoiceStatus />

États et leur rendu

StatutAffichage
pendingIndicateur de chargement + "Traitement en cours..."
signingIcône signature + "Signature électronique..."
transmittedIcône envoi + "Transmis à la DGI..."
acceptedIcône succès vert + QR code + liens téléchargement
rejectedIcône erreur rouge + motif de rejet + bouton "Renvoyer"

Rendu état accepted

Facture acceptée par la DGI
────────────────────────────────
[QR Code DGI] ID DGI : DGI-2026-0042-XYZABC

Télécharger PDF Télécharger XML signé

Hook useInvoice

Pour un contrôle total sur le rendu, utiliser le hook bas niveau :

Custom Hook Usage
import { useInvoice } from '@itzenata/efact-react'

export function CustomInvoiceStatus({ clientSecret }) {
  const { invoice, isLoading, error } = useInvoice(clientSecret)

  if (isLoading) return <Spinner />
  if (error) return <ErrorMessage message={error.message} />

  return (
    <div>
      <p>Statut : {invoice.status}</p>

      {invoice.status === 'accepted' && (
        <div>
          <p>ID DGI : {invoice.dgiId}</p>
          <a href={invoice.pdfUrl} download>Télécharger PDF</a>
        </div>
      )}

      {invoice.status === 'rejected' && (
        <p className="text-red-500">{invoice.rejectionReason}</p>
      )}
    </div>
  )
}

Valeur retournée par useInvoice

Hook Interface
interface UseInvoiceResult {
  invoice: Invoice | null
  isLoading: boolean
  error: Error | null
  retry: () => Promise<void>   // forcer un renvoi si status === 'rejected'
}

Personnalisation du style

Via className

Custom ClassName
<InvoiceStatus
  clientSecret={clientSecret}
  className="my-custom-invoice-status"
/>
Custom CSS
.my-custom-invoice-status {
  border: 2px solid #e2e8f0;
  border-radius: 8px;
  padding: 24px;
}

Variables CSS

Le composant expose des variables CSS pour la personnalisation des couleurs :

CSS Variables
:root {
  --efact-color-pending: #6b7280;
  --efact-color-signing: #f59e0b;
  --efact-color-transmitted: #3b82f6;
  --efact-color-accepted: #10b981;
  --efact-color-rejected: #ef4444;
  --efact-font-family: inherit;
  --efact-border-radius: 8px;
}

Intégration complète - Exemple Next.js

app/invoices/[id]/page.tsx
// app/invoices/[id]/page.tsx
import { getInvoiceClientSecret } from '@/lib/invoices'
import { InvoiceStatus } from '@itzenata/efact-react'

export default async function InvoicePage({ params }) {
  // Récupérer le clientSecret depuis votre base de données
  const { clientSecret } = await getInvoiceClientSecret(params.id)

  return (
    <main className="max-w-2xl mx-auto p-8">
      <h1 className="text-2xl font-bold mb-6">Statut de votre facture</h1>
      <InvoiceStatus
        clientSecret={clientSecret}
        showQRCode={true}
        showDownloadLinks={true}
      />
    </main>
  )
}

Compatibilité

EnvironnementSupport
Next.js 14+ (App Router)Supporté
Next.js 13 (Pages Router)Supporté
Vite + ReactSupporté
Create React AppSupporté
RemixSupporté
Server Components (RSC)Client component uniquement - utiliser 'use client'