@applite/duticotac-react 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +628 -0
- package/dist/index.d.mts +102 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.js +720 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +677 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
# @applite/duticotac-react
|
|
2
|
+
|
|
3
|
+
Composants React pour integrer les paiements Duticotac dans vos applications. Construit avec Tailwind CSS et compatible avec les tokens Shadcn UI.
|
|
4
|
+
|
|
5
|
+
Ce package fournit une experience de paiement complete : selection du provider, saisie du numero de telephone, code OTP, suivi en temps reel de la transaction, et affichage du resultat.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table des matieres
|
|
10
|
+
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Prerequis](#prerequis)
|
|
13
|
+
- [Demarrage rapide](#demarrage-rapide)
|
|
14
|
+
- [Flux de paiement](#flux-de-paiement)
|
|
15
|
+
- [Composants](#composants)
|
|
16
|
+
- [DuticotacPaymentModal](#duticotacpaymentmodal)
|
|
17
|
+
- [ProviderSelector](#providerselector)
|
|
18
|
+
- [PhoneInput](#phoneinput)
|
|
19
|
+
- [OtpInput](#otpinput)
|
|
20
|
+
- [TransactionStatus](#transactionstatus)
|
|
21
|
+
- [Utilitaires](#utilitaires)
|
|
22
|
+
- [Integration avec Shadcn UI](#integration-avec-shadcn-ui)
|
|
23
|
+
- [Providers supportes](#providers-supportes)
|
|
24
|
+
- [Validation du telephone](#validation-du-telephone)
|
|
25
|
+
- [Gestion des erreurs](#gestion-des-erreurs)
|
|
26
|
+
- [Comportement du polling](#comportement-du-polling)
|
|
27
|
+
- [API Reference complete](#api-reference-complete)
|
|
28
|
+
- [Exemples avances](#exemples-avances)
|
|
29
|
+
- [Scripts](#scripts)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pnpm add @applite/duticotac @applite/duticotac-react
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Les deux packages sont necessaires : `@applite/duticotac` contient le SDK (logique API, types, models) et `@applite/duticotac-react` contient les composants UI.
|
|
40
|
+
|
|
41
|
+
## Prerequis
|
|
42
|
+
|
|
43
|
+
- **React** >= 18
|
|
44
|
+
- **Tailwind CSS** configure dans votre projet
|
|
45
|
+
- Les composants utilisent les tokens de couleur Shadcn (`primary`, `muted-foreground`, `border`, `destructive`, etc.). Si vous utilisez Shadcn UI, tout fonctionne directement. Sinon, definissez ces variables CSS dans votre theme Tailwind.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Demarrage rapide
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { useState } from "react";
|
|
53
|
+
import { DuticotacSDK } from "@applite/duticotac";
|
|
54
|
+
import { DuticotacPaymentModal } from "@applite/duticotac-react";
|
|
55
|
+
|
|
56
|
+
// Creez l'instance SDK une seule fois (en dehors du composant)
|
|
57
|
+
const sdk = new DuticotacSDK({
|
|
58
|
+
apiKey: "your_api_key",
|
|
59
|
+
appId: "your_app_id", // optionnel
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
function CheckoutPage() {
|
|
63
|
+
const [paymentOpen, setPaymentOpen] = useState(false);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
<button onClick={() => setPaymentOpen(true)}>
|
|
68
|
+
Acheter — 5 000 FCFA
|
|
69
|
+
</button>
|
|
70
|
+
|
|
71
|
+
<DuticotacPaymentModal
|
|
72
|
+
open={paymentOpen}
|
|
73
|
+
onClose={() => setPaymentOpen(false)}
|
|
74
|
+
sdk={sdk}
|
|
75
|
+
amount={5000}
|
|
76
|
+
providers={["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"]}
|
|
77
|
+
getTransactionId={async () => {
|
|
78
|
+
// Appelez votre backend pour generer un ID unique
|
|
79
|
+
const res = await fetch("/api/create-transaction", { method: "POST" });
|
|
80
|
+
const data = await res.json();
|
|
81
|
+
return data.transactionId;
|
|
82
|
+
}}
|
|
83
|
+
productReference="premium-monthly"
|
|
84
|
+
name="Jean Dupont"
|
|
85
|
+
email="jean@example.com"
|
|
86
|
+
onSuccess={(transaction) => {
|
|
87
|
+
console.log("Paiement confirme :", transaction);
|
|
88
|
+
// Rafraichir le solde, rediriger, etc.
|
|
89
|
+
}}
|
|
90
|
+
/>
|
|
91
|
+
</>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Flux de paiement
|
|
99
|
+
|
|
100
|
+
Le `DuticotacPaymentModal` gere automatiquement les 4 etapes du paiement :
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
1. FORMULAIRE 2. CONFIRMATION 3. SUCCES / ERREUR
|
|
104
|
+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
105
|
+
│ [OM] [MTN] │ │ Spinner │ │ [OK] │
|
|
106
|
+
│ [MOOV] [WAVE]│ ───> │ │ ────> │ Paiement │
|
|
107
|
+
│ │ │ Confirmez │ │ reussi ! │
|
|
108
|
+
│ Tel: 07... │ │ sur votre │ │ │
|
|
109
|
+
│ OTP: ____ │ │ telephone... │ │ [Fermer] │
|
|
110
|
+
│ │ │ │ │ │
|
|
111
|
+
│ [Payer 5000] │ │ 42s... │ │ — ou — │
|
|
112
|
+
└──────────────┘ └──────────────┘ │ │
|
|
113
|
+
│ [X] │
|
|
114
|
+
│ Echec du │
|
|
115
|
+
│ paiement │
|
|
116
|
+
│ [Reessayer] │
|
|
117
|
+
└──────────────┘
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Etape 1 — Formulaire** : L'utilisateur choisit un provider, entre son numero de telephone, et son code OTP (Orange Money uniquement).
|
|
121
|
+
|
|
122
|
+
**Etape 2 — Confirmation** : Le SDK appelle `mobileMoney.cashout()` puis `transaction.poll()`. Si le provider retourne une `paymentUrl` (Wave), un nouvel onglet s'ouvre. Le polling est intelligent : il pause quand l'onglet est cache et reprend instantanement quand l'utilisateur revient.
|
|
123
|
+
|
|
124
|
+
**Etape 3 — Resultat** : Succes (avec callback `onSuccess`) ou erreur (avec bouton "Reessayer").
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Composants
|
|
129
|
+
|
|
130
|
+
### `DuticotacPaymentModal`
|
|
131
|
+
|
|
132
|
+
Le composant principal qui orchestre tout le flux de paiement.
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { DuticotacPaymentModal } from "@applite/duticotac-react";
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### Props
|
|
139
|
+
|
|
140
|
+
| Prop | Type | Requis | Defaut | Description |
|
|
141
|
+
|------|------|--------|--------|-------------|
|
|
142
|
+
| `open` | `boolean` | Oui | — | Controle la visibilite du modal |
|
|
143
|
+
| `onClose` | `() => void` | Oui | — | Appele quand le modal se ferme |
|
|
144
|
+
| `sdk` | `DuticotacSDK` | Oui | — | Instance du SDK Duticotac |
|
|
145
|
+
| `amount` | `number` | Oui | — | Montant du paiement en FCFA |
|
|
146
|
+
| `getTransactionId` | `() => Promise<string>` | Oui | — | Fonction async retournant un ID de transaction unique |
|
|
147
|
+
| `providers` | `PaymentProvider[]` | Non | `["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"]` | Providers de paiement disponibles |
|
|
148
|
+
| `productReference` | `string` | Non | — | Reference du produit/offre |
|
|
149
|
+
| `onSuccess` | `(tx: TransactionModel) => void` | Non | — | Appele quand le paiement est confirme |
|
|
150
|
+
| `initialPhone` | `string` | Non | `""` | Pre-remplir le numero de telephone |
|
|
151
|
+
| `name` | `string` | Non | `"Duticotac App"` | Nom du client |
|
|
152
|
+
| `email` | `string` | Non | `""` | Email du client |
|
|
153
|
+
| `customerId` | `string` | Non | — | ID du client |
|
|
154
|
+
| `kolaboReference` | `string` | Non | — | Reference Kolabo partenaire |
|
|
155
|
+
| `app` | `CoreApp` | Non | — | Application source (`"XORAIA"`, `"MONSMSPRO"`, `"FREE"`) |
|
|
156
|
+
| `platform` | `PlatformType` | Non | — | Plateforme (`"STORE"`, `"DUTICOTAC"`, etc.) |
|
|
157
|
+
| `title` | `string` | Non | `"Paiement"` | Titre du modal |
|
|
158
|
+
| `successMessage` | `string` | Non | `"Le paiement a ete effectue avec succes."` | Message affiche apres succes |
|
|
159
|
+
| `pollTimeout` | `number` | Non | `90000` | Timeout du polling en ms |
|
|
160
|
+
| `className` | `string` | Non | — | Classes CSS additionnelles |
|
|
161
|
+
| `renderModal` | `(props) => ReactNode` | Non | — | Render prop pour utiliser votre propre modal |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
### `ProviderSelector`
|
|
166
|
+
|
|
167
|
+
Grille de selection des providers de paiement avec logos et indicateur de selection.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import { ProviderSelector } from "@applite/duticotac-react";
|
|
171
|
+
|
|
172
|
+
function MyForm() {
|
|
173
|
+
const [provider, setProvider] = useState<PaymentProvider>("OM_CI");
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<ProviderSelector
|
|
177
|
+
providers={["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"]}
|
|
178
|
+
value={provider}
|
|
179
|
+
onChange={setProvider}
|
|
180
|
+
disabled={isLoading}
|
|
181
|
+
className="my-4"
|
|
182
|
+
/>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Props
|
|
188
|
+
|
|
189
|
+
| Prop | Type | Requis | Description |
|
|
190
|
+
|------|------|--------|-------------|
|
|
191
|
+
| `providers` | `PaymentProvider[]` | Oui | Liste des providers a afficher |
|
|
192
|
+
| `value` | `PaymentProvider` | Oui | Provider actuellement selectionne |
|
|
193
|
+
| `onChange` | `(provider: PaymentProvider) => void` | Oui | Callback de changement |
|
|
194
|
+
| `disabled` | `boolean` | Non | Desactiver la selection |
|
|
195
|
+
| `className` | `string` | Non | Classes CSS additionnelles |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### `PhoneInput`
|
|
200
|
+
|
|
201
|
+
Champ de saisie de numero de telephone avec prefixe `+225` (Cote d'Ivoire).
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
import { PhoneInput } from "@applite/duticotac-react";
|
|
205
|
+
|
|
206
|
+
<PhoneInput
|
|
207
|
+
value={phone}
|
|
208
|
+
onChange={setPhone}
|
|
209
|
+
label="Numero de telephone"
|
|
210
|
+
placeholder="07 01 02 03 04"
|
|
211
|
+
error={phoneError}
|
|
212
|
+
disabled={isLoading}
|
|
213
|
+
autoFocus
|
|
214
|
+
/>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### Props
|
|
218
|
+
|
|
219
|
+
| Prop | Type | Requis | Defaut | Description |
|
|
220
|
+
|------|------|--------|--------|-------------|
|
|
221
|
+
| `value` | `string` | Oui | — | Valeur du champ (sans prefixe +225) |
|
|
222
|
+
| `onChange` | `(value: string) => void` | Oui | — | Callback de changement |
|
|
223
|
+
| `label` | `string` | Non | `"Numero de telephone"` | Label du champ |
|
|
224
|
+
| `placeholder` | `string` | Non | `"07 01 02 03 04"` | Placeholder |
|
|
225
|
+
| `error` | `string \| null` | Non | — | Message d'erreur a afficher |
|
|
226
|
+
| `disabled` | `boolean` | Non | — | Desactiver le champ |
|
|
227
|
+
| `autoFocus` | `boolean` | Non | — | Focus automatique |
|
|
228
|
+
| `className` | `string` | Non | — | Classes CSS additionnelles |
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
### `OtpInput`
|
|
233
|
+
|
|
234
|
+
Saisie de code OTP a 4 chiffres. Utilise pour Orange Money (`OM_CI`). Gere automatiquement le focus entre les champs, le collage, et la navigation au clavier.
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
import { OtpInput } from "@applite/duticotac-react";
|
|
238
|
+
|
|
239
|
+
<OtpInput
|
|
240
|
+
value={otp}
|
|
241
|
+
onChange={setOtp}
|
|
242
|
+
length={4}
|
|
243
|
+
disabled={isLoading}
|
|
244
|
+
/>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### Props
|
|
248
|
+
|
|
249
|
+
| Prop | Type | Requis | Defaut | Description |
|
|
250
|
+
|------|------|--------|--------|-------------|
|
|
251
|
+
| `value` | `string` | Oui | — | Code OTP saisi |
|
|
252
|
+
| `onChange` | `(value: string) => void` | Oui | — | Callback de changement |
|
|
253
|
+
| `length` | `number` | Non | `4` | Nombre de chiffres |
|
|
254
|
+
| `disabled` | `boolean` | Non | — | Desactiver la saisie |
|
|
255
|
+
| `className` | `string` | Non | — | Classes CSS additionnelles |
|
|
256
|
+
|
|
257
|
+
**Fonctionnalites :**
|
|
258
|
+
- Auto-focus vers le champ suivant apres saisie d'un chiffre
|
|
259
|
+
- Retour au champ precedent sur Backspace
|
|
260
|
+
- Navigation avec les fleches gauche/droite
|
|
261
|
+
- Support du copier-coller (colle automatiquement les 4 chiffres)
|
|
262
|
+
- Instruction integree : "Faites **#144*82#** pour recevoir le code"
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
### `TransactionStatus`
|
|
267
|
+
|
|
268
|
+
Affiche l'etat de la transaction : spinner de polling, succes, ou erreur. Utilise en interne par `DuticotacPaymentModal`, mais peut etre utilise independamment.
|
|
269
|
+
|
|
270
|
+
```tsx
|
|
271
|
+
import { TransactionStatus } from "@applite/duticotac-react";
|
|
272
|
+
|
|
273
|
+
// Polling en cours
|
|
274
|
+
<TransactionStatus
|
|
275
|
+
status="polling"
|
|
276
|
+
elapsedSeconds={42}
|
|
277
|
+
paymentUrl="https://wave.com/pay/..."
|
|
278
|
+
onOpenPaymentUrl={() => window.open(url, "_blank")}
|
|
279
|
+
onClose={handleClose}
|
|
280
|
+
/>
|
|
281
|
+
|
|
282
|
+
// Succes
|
|
283
|
+
<TransactionStatus
|
|
284
|
+
status="success"
|
|
285
|
+
message="500 credits ajoutes a votre compte."
|
|
286
|
+
onClose={handleClose}
|
|
287
|
+
/>
|
|
288
|
+
|
|
289
|
+
// Erreur
|
|
290
|
+
<TransactionStatus
|
|
291
|
+
status="error"
|
|
292
|
+
errorMessage="Le delai de confirmation a expire."
|
|
293
|
+
onRetry={handleRetry}
|
|
294
|
+
onClose={handleClose}
|
|
295
|
+
/>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
#### Props
|
|
299
|
+
|
|
300
|
+
| Prop | Type | Requis | Description |
|
|
301
|
+
|------|------|--------|-------------|
|
|
302
|
+
| `status` | `"polling" \| "success" \| "error"` | Oui | Etat actuel |
|
|
303
|
+
| `elapsedSeconds` | `number` | Non | Secondes ecoulees (affiche pendant le polling) |
|
|
304
|
+
| `message` | `string` | Non | Message de succes |
|
|
305
|
+
| `errorMessage` | `string` | Non | Message d'erreur |
|
|
306
|
+
| `paymentUrl` | `string \| null` | Non | URL de paiement externe (Wave) |
|
|
307
|
+
| `onRetry` | `() => void` | Non | Callback du bouton "Reessayer" |
|
|
308
|
+
| `onClose` | `() => void` | Non | Callback du bouton "Fermer" |
|
|
309
|
+
| `onOpenPaymentUrl` | `() => void` | Non | Callback pour ouvrir l'URL de paiement |
|
|
310
|
+
| `className` | `string` | Non | Classes CSS additionnelles |
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Utilitaires
|
|
315
|
+
|
|
316
|
+
### Validation du telephone
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
import { validatePhone, normalizePhone, getPhoneRegex, needsOtp } from "@applite/duticotac-react";
|
|
320
|
+
|
|
321
|
+
// Valider un numero pour un provider specifique
|
|
322
|
+
const error = validatePhone("0701020304", "OM_CI");
|
|
323
|
+
// null si valide, message d'erreur sinon
|
|
324
|
+
|
|
325
|
+
// Normaliser en format international
|
|
326
|
+
normalizePhone("0701020304"); // "+2250701020304"
|
|
327
|
+
|
|
328
|
+
// Obtenir la regex de validation
|
|
329
|
+
getPhoneRegex("OM_CI"); // /^(\+225)(07)[0-9]{8}$/
|
|
330
|
+
getPhoneRegex("MTN_CI"); // /^(\+225)(05)[0-9]{8}$/
|
|
331
|
+
getPhoneRegex("MOOV_CI");// /^(\+225)(01)[0-9]{8}$/
|
|
332
|
+
|
|
333
|
+
// Verifier si l'OTP est requis
|
|
334
|
+
needsOtp("OM_CI"); // true
|
|
335
|
+
needsOtp("MTN_CI"); // false
|
|
336
|
+
needsOtp("WAVE_CI"); // false
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Formatage
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
import { formatCFA } from "@applite/duticotac-react";
|
|
343
|
+
|
|
344
|
+
formatCFA(5000); // "5 000"
|
|
345
|
+
formatCFA(1500000); // "1 500 000"
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Classes CSS
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
import { cn } from "@applite/duticotac-react";
|
|
352
|
+
|
|
353
|
+
cn("px-4 py-2", isActive && "bg-primary", className);
|
|
354
|
+
// Merge intelligent des classes Tailwind (via clsx + tailwind-merge)
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Integration avec Shadcn UI
|
|
360
|
+
|
|
361
|
+
### Avec Shadcn Dialog
|
|
362
|
+
|
|
363
|
+
```tsx
|
|
364
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
|
365
|
+
import { DuticotacPaymentModal } from "@applite/duticotac-react";
|
|
366
|
+
|
|
367
|
+
<DuticotacPaymentModal
|
|
368
|
+
open={open}
|
|
369
|
+
onClose={() => setOpen(false)}
|
|
370
|
+
sdk={sdk}
|
|
371
|
+
amount={5000}
|
|
372
|
+
getTransactionId={getTransactionId}
|
|
373
|
+
renderModal={({ open, onClose, title, children }) => (
|
|
374
|
+
<Dialog open={open} onOpenChange={(v) => { if (!v) onClose(); }}>
|
|
375
|
+
<DialogContent className="sm:max-w-md">
|
|
376
|
+
<DialogHeader>
|
|
377
|
+
<DialogTitle>{title}</DialogTitle>
|
|
378
|
+
</DialogHeader>
|
|
379
|
+
{children}
|
|
380
|
+
</DialogContent>
|
|
381
|
+
</Dialog>
|
|
382
|
+
)}
|
|
383
|
+
/>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Avec Shadcn Drawer (mobile)
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from "@/components/ui/drawer";
|
|
390
|
+
|
|
391
|
+
<DuticotacPaymentModal
|
|
392
|
+
open={open}
|
|
393
|
+
onClose={() => setOpen(false)}
|
|
394
|
+
sdk={sdk}
|
|
395
|
+
amount={5000}
|
|
396
|
+
getTransactionId={getTransactionId}
|
|
397
|
+
renderModal={({ open, onClose, title, children }) => (
|
|
398
|
+
<Drawer open={open} onOpenChange={(v) => { if (!v) onClose(); }}>
|
|
399
|
+
<DrawerContent>
|
|
400
|
+
<DrawerHeader>
|
|
401
|
+
<DrawerTitle>{title}</DrawerTitle>
|
|
402
|
+
</DrawerHeader>
|
|
403
|
+
<div className="px-4 pb-6">{children}</div>
|
|
404
|
+
</DrawerContent>
|
|
405
|
+
</Drawer>
|
|
406
|
+
)}
|
|
407
|
+
/>
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Sans Shadcn (modal integre)
|
|
411
|
+
|
|
412
|
+
Si vous ne passez pas de `renderModal`, un modal overlay simple est utilise automatiquement. Aucune dependance externe requise.
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## Providers supportes
|
|
417
|
+
|
|
418
|
+
| Code | Nom | OTP requis | Prefixe telephone | Particularite |
|
|
419
|
+
|------|-----|------------|-------------------|---------------|
|
|
420
|
+
| `OM_CI` | Orange Money | Oui (4 chiffres) | `07` | L'utilisateur fait `#144*82#` pour obtenir le code OTP |
|
|
421
|
+
| `MTN_CI` | MTN MoMo | Non | `05` | Confirmation sur le telephone |
|
|
422
|
+
| `MOOV_CI` | Moov (Flooz) | Non | `01` | Confirmation sur le telephone |
|
|
423
|
+
| `WAVE_CI` | Wave | Non | `07`, `05`, `01` | Ouvre un onglet navigateur pour le paiement |
|
|
424
|
+
| `CREDIT_CARD` | Carte de credit | Non | — | A venir |
|
|
425
|
+
| `CASH` | Especes | Non | — | Paiement en main propre |
|
|
426
|
+
| `IAP` | Achat Integre | Non | — | In-App Purchase (mobile) |
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## Validation du telephone
|
|
431
|
+
|
|
432
|
+
Chaque provider a des regles de validation specifiques basees sur les prefixes telephoniques de Cote d'Ivoire :
|
|
433
|
+
|
|
434
|
+
| Provider | Regex | Exemples valides |
|
|
435
|
+
|----------|-------|------------------|
|
|
436
|
+
| Orange Money (`OM_CI`) | `+225 07 XX XX XX XX` | +225 07 01 02 03 04 |
|
|
437
|
+
| MTN (`MTN_CI`) | `+225 05 XX XX XX XX` | +225 05 01 02 03 04 |
|
|
438
|
+
| Moov (`MOOV_CI`) | `+225 01 XX XX XX XX` | +225 01 01 02 03 04 |
|
|
439
|
+
| Wave (`WAVE_CI`) | `+225 (07\|05\|01) XX XX XX XX` | Accepte tous les prefixes |
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Gestion des erreurs
|
|
444
|
+
|
|
445
|
+
Le modal gere automatiquement les erreurs du SDK et affiche des messages en francais :
|
|
446
|
+
|
|
447
|
+
| Code erreur | Message affiche |
|
|
448
|
+
|-------------|-----------------|
|
|
449
|
+
| `otp-required` | Code OTP requis pour les paiements Orange Money |
|
|
450
|
+
| `ref-or-idFromClient-required` | Reference ou IDFromClient requis |
|
|
451
|
+
| `payment-failed` | Echec du paiement |
|
|
452
|
+
| `payment-cancelled` | Paiement annule |
|
|
453
|
+
| `polling-timeout` | Temps d'attente de la transaction expire |
|
|
454
|
+
| `phone-required` | Numero de telephone requis |
|
|
455
|
+
| `no-api-key` | Cle API non fournie |
|
|
456
|
+
| `app-not-found` | Application non trouvee dans AppLite UI |
|
|
457
|
+
| `unknown-error` | Une erreur inconnue est survenue |
|
|
458
|
+
|
|
459
|
+
En cas d'erreur, l'utilisateur peut cliquer sur **"Reessayer"** pour revenir au formulaire.
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Comportement du polling
|
|
464
|
+
|
|
465
|
+
Apres l'appel `cashout`, le composant suit le statut de la transaction via `sdk.transaction.poll()` :
|
|
466
|
+
|
|
467
|
+
- **Backoff exponentiel** : 1s → 2s → 3s → 5s → 8s (meme strategie que le SDK Dart)
|
|
468
|
+
- **Timeout** : 90 secondes par defaut (configurable via `pollTimeout`)
|
|
469
|
+
- **Tab-aware** : Le polling pause quand l'onglet est cache (utilisateur sur Wave ou sur son telephone) et reprend immediatement quand l'onglet redevient visible
|
|
470
|
+
- **Compteur** : Un indicateur "Verification en cours... 42s" est affiche
|
|
471
|
+
- **Wave** : Si le provider retourne une `paymentUrl`, un nouvel onglet s'ouvre automatiquement avec un bouton de fallback si le popup est bloque
|
|
472
|
+
- **Etats terminaux** :
|
|
473
|
+
- `CONFIRMED` → ecran de succes + callback `onSuccess`
|
|
474
|
+
- `CANCELLED` → ecran d'erreur ("Paiement annule")
|
|
475
|
+
- `FAILED` → ecran d'erreur ("Echec du paiement")
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## API Reference complete
|
|
480
|
+
|
|
481
|
+
### Types reimportes de `@applite/duticotac`
|
|
482
|
+
|
|
483
|
+
```ts
|
|
484
|
+
import type {
|
|
485
|
+
PaymentProvider, // "OM_CI" | "MTN_CI" | "MOOV_CI" | "WAVE_CI" | "CREDIT_CARD" | "CASH" | "IAP"
|
|
486
|
+
PlatformType, // "STORE" | "TRANSPORT" | "RESTAURATION" | "MULTI_SERVICE" | "E_LEARNING" | "DUTICOTAC"
|
|
487
|
+
CoreApp, // "XORAIA" | "MONSMSPRO" | "FREE"
|
|
488
|
+
TransactionModel, // Objet transaction complet
|
|
489
|
+
TransactionStatus, // "PENDING" | "CONFIRMED" | "CANCELLED" | "FAILED"
|
|
490
|
+
} from "@applite/duticotac";
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Exports de `@applite/duticotac-react`
|
|
494
|
+
|
|
495
|
+
```ts
|
|
496
|
+
// Composants
|
|
497
|
+
export { DuticotacPaymentModal } from "@applite/duticotac-react";
|
|
498
|
+
export { ProviderSelector } from "@applite/duticotac-react";
|
|
499
|
+
export { PhoneInput } from "@applite/duticotac-react";
|
|
500
|
+
export { OtpInput } from "@applite/duticotac-react";
|
|
501
|
+
export { TransactionStatus } from "@applite/duticotac-react";
|
|
502
|
+
|
|
503
|
+
// Types des props
|
|
504
|
+
export type { DuticotacPaymentModalProps } from "@applite/duticotac-react";
|
|
505
|
+
export type { ProviderSelectorProps } from "@applite/duticotac-react";
|
|
506
|
+
export type { PhoneInputProps } from "@applite/duticotac-react";
|
|
507
|
+
export type { OtpInputProps } from "@applite/duticotac-react";
|
|
508
|
+
export type { TransactionStatusProps } from "@applite/duticotac-react";
|
|
509
|
+
|
|
510
|
+
// Utilitaires
|
|
511
|
+
export { cn, formatCFA, validatePhone, normalizePhone, getPhoneRegex, needsOtp } from "@applite/duticotac-react";
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## Exemples avances
|
|
517
|
+
|
|
518
|
+
### Avec un backend personnalise
|
|
519
|
+
|
|
520
|
+
```tsx
|
|
521
|
+
function PaymentPage({ offer, apiKey }: { offer: Offer; apiKey: string }) {
|
|
522
|
+
const [open, setOpen] = useState(false);
|
|
523
|
+
const sdk = useMemo(() => new DuticotacSDK({ apiKey }), [apiKey]);
|
|
524
|
+
|
|
525
|
+
const getTransactionId = async () => {
|
|
526
|
+
const res = await fetch("/api/payments/init", {
|
|
527
|
+
method: "POST",
|
|
528
|
+
headers: { "Content-Type": "application/json" },
|
|
529
|
+
body: JSON.stringify({ offerId: offer.id }),
|
|
530
|
+
});
|
|
531
|
+
const { transactionId } = await res.json();
|
|
532
|
+
return transactionId;
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
return (
|
|
536
|
+
<DuticotacPaymentModal
|
|
537
|
+
open={open}
|
|
538
|
+
onClose={() => setOpen(false)}
|
|
539
|
+
sdk={sdk}
|
|
540
|
+
amount={offer.price}
|
|
541
|
+
getTransactionId={getTransactionId}
|
|
542
|
+
productReference={offer.ref}
|
|
543
|
+
providers={["OM_CI", "MTN_CI", "WAVE_CI"]}
|
|
544
|
+
pollTimeout={120_000}
|
|
545
|
+
onSuccess={(tx) => {
|
|
546
|
+
toast.success(`Paiement confirme ! Ref: ${tx.ref}`);
|
|
547
|
+
router.push("/dashboard");
|
|
548
|
+
}}
|
|
549
|
+
/>
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Composants individuels (sans le modal)
|
|
555
|
+
|
|
556
|
+
Vous pouvez utiliser les composants independamment pour construire votre propre flux :
|
|
557
|
+
|
|
558
|
+
```tsx
|
|
559
|
+
import {
|
|
560
|
+
ProviderSelector,
|
|
561
|
+
PhoneInput,
|
|
562
|
+
OtpInput,
|
|
563
|
+
TransactionStatus,
|
|
564
|
+
needsOtp,
|
|
565
|
+
validatePhone,
|
|
566
|
+
} from "@applite/duticotac-react";
|
|
567
|
+
|
|
568
|
+
function CustomPaymentForm() {
|
|
569
|
+
const [provider, setProvider] = useState<PaymentProvider>("OM_CI");
|
|
570
|
+
const [phone, setPhone] = useState("");
|
|
571
|
+
const [otp, setOtp] = useState("");
|
|
572
|
+
|
|
573
|
+
return (
|
|
574
|
+
<form>
|
|
575
|
+
<ProviderSelector
|
|
576
|
+
providers={["OM_CI", "MTN_CI", "WAVE_CI"]}
|
|
577
|
+
value={provider}
|
|
578
|
+
onChange={(p) => {
|
|
579
|
+
setProvider(p);
|
|
580
|
+
if (!needsOtp(p)) setOtp("");
|
|
581
|
+
}}
|
|
582
|
+
/>
|
|
583
|
+
|
|
584
|
+
<PhoneInput
|
|
585
|
+
value={phone}
|
|
586
|
+
onChange={setPhone}
|
|
587
|
+
error={validatePhone(phone, provider)}
|
|
588
|
+
/>
|
|
589
|
+
|
|
590
|
+
{needsOtp(provider) && (
|
|
591
|
+
<OtpInput value={otp} onChange={setOtp} />
|
|
592
|
+
)}
|
|
593
|
+
|
|
594
|
+
<button type="submit">Payer</button>
|
|
595
|
+
</form>
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### Personnalisation du style
|
|
601
|
+
|
|
602
|
+
Tous les composants acceptent une prop `className` pour ajouter des classes Tailwind :
|
|
603
|
+
|
|
604
|
+
```tsx
|
|
605
|
+
<ProviderSelector
|
|
606
|
+
providers={providers}
|
|
607
|
+
value={selected}
|
|
608
|
+
onChange={setSelected}
|
|
609
|
+
className="gap-4 grid-cols-2" // Override la grille
|
|
610
|
+
/>
|
|
611
|
+
|
|
612
|
+
<PhoneInput
|
|
613
|
+
value={phone}
|
|
614
|
+
onChange={setPhone}
|
|
615
|
+
className="mb-6" // Espacement custom
|
|
616
|
+
/>
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## Scripts
|
|
622
|
+
|
|
623
|
+
| Commande | Description |
|
|
624
|
+
|----------|-------------|
|
|
625
|
+
| `pnpm build` | Build CJS + ESM avec declarations de types |
|
|
626
|
+
| `pnpm dev` | Mode watch pour le developpement |
|
|
627
|
+
| `pnpm type-check` | Verification des types TypeScript |
|
|
628
|
+
| `pnpm lint` | Lint du code source |
|