Duticotac React
Composants React (Tailwind/Shadcn) pour integrer les paiements Duticotac dans vos applications.
Installation
pnpm add @applite/duticotac-reactC’est tout ! Le SDK core (@applite/duticotac) est inclus et re-exporte automatiquement. Pas besoin de l’installer separement.
Prerequis : React >= 18, Tailwind CSS configure, tokens de couleur Shadcn (primary, border, etc.).
Demarrage rapide
La maniere recommandee d’utiliser ce package est le hook useDuticotac :
import { DuticotacSDK, useDuticotac } from "@applite/duticotac-react";
const sdk = new DuticotacSDK({ apiKey: "your_api_key" });
function CheckoutPage() {
const { pay, Dialog } = useDuticotac({
sdk,
getTransactionId: async () => "unique-txn-id",
});
return (
<>
<button onClick={() => pay({
amount: 5000,
onSuccess: (tx) => console.log("Confirme:", tx),
})}>
Acheter
</button>
{Dialog}
</>
);
}Hook useDuticotac (recommande)
Le hook fournit un dialog responsive integre : modal centre sur desktop, bottom sheet sur mobile. Aucune configuration de modal necessaire.
Exemple complet
import { DuticotacSDK } from "@applite/duticotac";
import { useDuticotac } from "@applite/duticotac-react";
const sdk = new DuticotacSDK({ apiKey: "your_api_key" });
function CheckoutPage() {
const { pay, Dialog, isOpen, close } = useDuticotac({
sdk,
providers: ["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"],
getTransactionId: async () => {
const res = await fetch("/api/init", { method: "POST" });
return (await res.json()).transactionId;
},
name: "Jean Dupont",
email: "jean@example.com",
});
return (
<>
<button onClick={() => pay({
amount: 5000,
productReference: "premium-monthly",
onSuccess: (tx) => console.log("Confirme:", tx),
})}>
Acheter — 5 000 FCFA
</button>
{Dialog}
</>
);
}Comportement responsive
| Ecran | Comportement |
|---|---|
| Desktop (> 640px) | Modal centre, max-width 440px, animation d’echelle |
Mobile (<= 640px) | Bottom sheet, slide-up depuis le bas, coins arrondis, poignee de drag |
Inclus : fermeture par backdrop click, touche Escape, verrouillage du scroll, transitions CSS fluides.
Composants
Pour la plupart des cas, utilisez le hook
useDuticotacplutot que les composants directement.
DuticotacPaymentModal
Modal complet : selection provider, telephone, OTP, polling, resultat.
Exemple
<DuticotacPaymentModal
open={open}
onClose={() => setOpen(false)}
sdk={sdk}
amount={5000}
providers={["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"]}
getTransactionId={getTransactionId}
productReference="product-ref"
name="Jean Dupont"
email="jean@example.com"
onSuccess={(tx) => console.log(tx)}
/>Integration Shadcn Dialog
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
<DuticotacPaymentModal
open={open}
onClose={() => setOpen(false)}
sdk={sdk}
amount={5000}
getTransactionId={getTransactionId}
renderModal={({ open, onClose, title, children }) => (
<Dialog open={open} onOpenChange={(v) => { if (!v) onClose(); }}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
</DialogHeader>
{children}
</DialogContent>
</Dialog>
)}
/>Integration Shadcn Drawer (mobile)
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from "@/components/ui/drawer";
<DuticotacPaymentModal
open={open}
onClose={() => setOpen(false)}
sdk={sdk}
amount={5000}
getTransactionId={getTransactionId}
renderModal={({ open, onClose, title, children }) => (
<Drawer open={open} onOpenChange={(v) => { if (!v) onClose(); }}>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>{title}</DrawerTitle>
</DrawerHeader>
<div className="px-4 pb-6">{children}</div>
</DrawerContent>
</Drawer>
)}
/>ResponsiveDialog
Dialog responsive : modal centre sur desktop, bottom sheet sur mobile. Utilise par useDuticotac.
Exemple
import { ResponsiveDialog } from "@applite/duticotac-react";
<ResponsiveDialog
open={isOpen}
onClose={() => setOpen(false)}
title="Mon dialog"
>
<p>Contenu</p>
</ResponsiveDialog>ProviderSelector
Grille de selection de provider avec logos.
Exemple
import { ProviderSelector } from "@applite/duticotac-react";
<ProviderSelector
providers={["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"]}
value={provider}
onChange={setProvider}
/>PhoneInput
Champ telephone avec prefixe +225 (Cote d’Ivoire).
Exemple
import { PhoneInput } from "@applite/duticotac-react";
<PhoneInput
value={phone}
onChange={setPhone}
error={phoneError}
autoFocus
/>OtpInput
Saisie de code OTP a 4 chiffres (Orange Money). Auto-focus, collage, navigation clavier.
Exemple
import { OtpInput } from "@applite/duticotac-react";
<OtpInput value={otp} onChange={setOtp} />TransactionStatus
Affichage du statut : polling (spinner + compteur), succes, ou erreur.
Exemple
import { TransactionStatus } from "@applite/duticotac-react";
// Polling
<TransactionStatus status="polling" elapsedSeconds={42} />
// Succes
<TransactionStatus status="success" message="500 credits ajoutes." onClose={close} />
// Erreur
<TransactionStatus status="error" errorMessage="Echec." onRetry={retry} onClose={close} />Providers supportes
| Code | Nom | OTP | Prefixe | Particularite |
|---|---|---|---|---|
OM_CI | Orange Money | Oui (4 chiffres) | 07 | #144*82# pour le code OTP |
MTN_CI | MTN MoMo | Non | 05 | Confirmation sur telephone |
MOOV_CI | Moov (Flooz) | Non | 01 | Confirmation sur telephone |
WAVE_CI | Wave | Non | Tous | Ouvre un onglet navigateur |
CREDIT_CARD | Carte de credit | Non | — | A venir |
CASH | Especes | Non | — | Paiement en main propre |
IAP | Achat Integre | Non | — | In-App Purchase |
Utilitaires
import {
validatePhone, // Validation telephone par provider
normalizePhone, // "+2250701020304"
getPhoneRegex, // Regex par provider
needsOtp, // true pour OM_CI
formatCFA, // 5000 → "5 000"
cn, // Merge classes Tailwind
} from "@applite/duticotac-react";Comportement du polling
- Backoff exponentiel : 1s → 2s → 3s → 5s → 8s
- Timeout : 90s par defaut (configurable)
- Tab-aware : Pause quand l’onglet est cache, reprend au retour
- Compteur : “Verification en cours… 42s”
- Wave : Ouvre automatiquement l’URL de paiement dans un nouvel onglet
Gestion des erreurs
Les erreurs du SDK sont automatiquement affichees en francais dans le modal :
| Code | Message |
|---|---|
otp-required | Code OTP requis pour les paiements Orange Money |
payment-failed | Echec du paiement |
payment-cancelled | Paiement annule |
polling-timeout | Temps d’attente de la transaction expire |
phone-required | Numero de telephone requis |
unknown-error | Une erreur inconnue est survenue |