@applite/duticotac-react 0.0.1 → 0.0.3
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 +163 -29
- package/dist/index.d.mts +64 -1
- package/dist/index.d.ts +64 -1
- package/dist/index.js +260 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +258 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -11,9 +11,11 @@ Ce package fournit une experience de paiement complete : selection du provider,
|
|
|
11
11
|
- [Installation](#installation)
|
|
12
12
|
- [Prerequis](#prerequis)
|
|
13
13
|
- [Demarrage rapide](#demarrage-rapide)
|
|
14
|
+
- [Hook useDuticotac](#hook-useduticotac)
|
|
14
15
|
- [Flux de paiement](#flux-de-paiement)
|
|
15
16
|
- [Composants](#composants)
|
|
16
17
|
- [DuticotacPaymentModal](#duticotacpaymentmodal)
|
|
18
|
+
- [ResponsiveDialog](#responsivedialog)
|
|
17
19
|
- [ProviderSelector](#providerselector)
|
|
18
20
|
- [PhoneInput](#phoneinput)
|
|
19
21
|
- [OtpInput](#otpinput)
|
|
@@ -48,51 +50,140 @@ Les deux packages sont necessaires : `@applite/duticotac` contient le SDK (logiq
|
|
|
48
50
|
|
|
49
51
|
## Demarrage rapide
|
|
50
52
|
|
|
53
|
+
La maniere recommandee d'utiliser ce package est le hook **`useDuticotac`**. Il fournit un dialog responsive integre (modal sur desktop, bottom sheet sur mobile) et une API imperative simple :
|
|
54
|
+
|
|
51
55
|
```tsx
|
|
52
|
-
import { useState } from "react";
|
|
53
56
|
import { DuticotacSDK } from "@applite/duticotac";
|
|
54
|
-
import {
|
|
57
|
+
import { useDuticotac } from "@applite/duticotac-react";
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
const sdk = new DuticotacSDK({
|
|
58
|
-
apiKey: "your_api_key",
|
|
59
|
-
appId: "your_app_id", // optionnel
|
|
60
|
-
});
|
|
59
|
+
const sdk = new DuticotacSDK({ apiKey: "your_api_key" });
|
|
61
60
|
|
|
62
61
|
function CheckoutPage() {
|
|
63
|
-
const
|
|
62
|
+
const { pay, Dialog } = useDuticotac({
|
|
63
|
+
sdk,
|
|
64
|
+
getTransactionId: async () => {
|
|
65
|
+
const res = await fetch("/api/create-transaction", { method: "POST" });
|
|
66
|
+
const data = await res.json();
|
|
67
|
+
return data.transactionId;
|
|
68
|
+
},
|
|
69
|
+
});
|
|
64
70
|
|
|
65
71
|
return (
|
|
66
72
|
<>
|
|
67
|
-
<button onClick={() =>
|
|
73
|
+
<button onClick={() => pay({
|
|
74
|
+
amount: 5000,
|
|
75
|
+
onSuccess: (tx) => console.log("Paiement confirme :", tx),
|
|
76
|
+
})}>
|
|
68
77
|
Acheter — 5 000 FCFA
|
|
69
78
|
</button>
|
|
79
|
+
{Dialog}
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
70
84
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
> Pour un controle plus fin (modal custom, Shadcn Dialog, etc.), utilisez directement le composant [`DuticotacPaymentModal`](#duticotacpaymentmodal) avec sa prop `renderModal`.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Hook `useDuticotac`
|
|
90
|
+
|
|
91
|
+
La maniere la plus simple d'integrer Duticotac. Le hook retourne une fonction `pay()` et un element `Dialog` avec un dialog responsive integre (modal centre sur desktop, bottom sheet sur mobile).
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
import { DuticotacSDK } from "@applite/duticotac";
|
|
95
|
+
import { useDuticotac } from "@applite/duticotac-react";
|
|
96
|
+
|
|
97
|
+
const sdk = new DuticotacSDK({ apiKey: "your_api_key" });
|
|
98
|
+
|
|
99
|
+
function CheckoutPage() {
|
|
100
|
+
const { pay, Dialog } = useDuticotac({
|
|
101
|
+
sdk,
|
|
102
|
+
providers: ["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"],
|
|
103
|
+
getTransactionId: async () => {
|
|
104
|
+
const res = await fetch("/api/create-transaction", { method: "POST" });
|
|
105
|
+
const data = await res.json();
|
|
106
|
+
return data.transactionId;
|
|
107
|
+
},
|
|
108
|
+
name: "Jean Dupont",
|
|
109
|
+
email: "jean@example.com",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
<button onClick={() => pay({
|
|
115
|
+
amount: 5000,
|
|
116
|
+
productReference: "premium-monthly",
|
|
117
|
+
onSuccess: (tx) => {
|
|
118
|
+
console.log("Paiement confirme :", tx);
|
|
119
|
+
},
|
|
120
|
+
})}>
|
|
121
|
+
Acheter — 5 000 FCFA
|
|
122
|
+
</button>
|
|
123
|
+
{Dialog}
|
|
91
124
|
</>
|
|
92
125
|
);
|
|
93
126
|
}
|
|
94
127
|
```
|
|
95
128
|
|
|
129
|
+
### API du hook
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const { pay, Dialog, isOpen, close } = useDuticotac(config);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**`config` (UseDuticotacConfig)**
|
|
136
|
+
|
|
137
|
+
| Champ | Type | Requis | Description |
|
|
138
|
+
|-------|------|--------|-------------|
|
|
139
|
+
| `sdk` | `DuticotacSDK` | Oui | Instance du SDK |
|
|
140
|
+
| `getTransactionId` | `() => Promise<string>` | Oui | Generateur d'ID transaction |
|
|
141
|
+
| `providers` | `PaymentProvider[]` | Non | Providers disponibles (defaut: OM, MTN, MOOV, WAVE) |
|
|
142
|
+
| `productReference` | `string` | Non | Reference produit par defaut |
|
|
143
|
+
| `name` | `string` | Non | Nom du client par defaut |
|
|
144
|
+
| `email` | `string` | Non | Email du client par defaut |
|
|
145
|
+
| `customerId` | `string` | Non | ID du client |
|
|
146
|
+
| `kolaboReference` | `string` | Non | Reference Kolabo |
|
|
147
|
+
| `app` | `CoreApp` | Non | Application source |
|
|
148
|
+
| `platform` | `PlatformType` | Non | Plateforme |
|
|
149
|
+
| `initialPhone` | `string` | Non | Pre-remplir le telephone |
|
|
150
|
+
| `pollTimeout` | `number` | Non | Timeout polling en ms (defaut: 90000) |
|
|
151
|
+
| `title` | `string` | Non | Titre du dialog (defaut: "Paiement") |
|
|
152
|
+
|
|
153
|
+
**Retour**
|
|
154
|
+
|
|
155
|
+
| Champ | Type | Description |
|
|
156
|
+
|-------|------|-------------|
|
|
157
|
+
| `pay` | `(options: PayOptions) => void` | Ouvre le dialog de paiement |
|
|
158
|
+
| `Dialog` | `ReactNode` | Element JSX a rendre une fois dans l'arbre |
|
|
159
|
+
| `isOpen` | `boolean` | Etat du dialog |
|
|
160
|
+
| `close` | `() => void` | Fermer le dialog programmatiquement |
|
|
161
|
+
|
|
162
|
+
**`PayOptions`** (argument de `pay()`)
|
|
163
|
+
|
|
164
|
+
| Champ | Type | Requis | Description |
|
|
165
|
+
|-------|------|--------|-------------|
|
|
166
|
+
| `amount` | `number` | Oui | Montant en FCFA |
|
|
167
|
+
| `providers` | `PaymentProvider[]` | Non | Override les providers |
|
|
168
|
+
| `productReference` | `string` | Non | Override la reference produit |
|
|
169
|
+
| `name` | `string` | Non | Override le nom |
|
|
170
|
+
| `email` | `string` | Non | Override l'email |
|
|
171
|
+
| `customerId` | `string` | Non | Override l'ID client |
|
|
172
|
+
| `successMessage` | `string` | Non | Message de succes personnalise |
|
|
173
|
+
| `onSuccess` | `(tx: TransactionModel) => void` | Non | Callback de succes |
|
|
174
|
+
| `onClose` | `() => void` | Non | Callback de fermeture |
|
|
175
|
+
|
|
176
|
+
### Comportement responsive
|
|
177
|
+
|
|
178
|
+
Le dialog s'adapte automatiquement :
|
|
179
|
+
|
|
180
|
+
| Ecran | Comportement |
|
|
181
|
+
|-------|-------------|
|
|
182
|
+
| Desktop (> 640px) | Modal centre, max-width 440px, animation scale |
|
|
183
|
+
| Mobile (<= 640px) | Bottom sheet, glisse depuis le bas, coins arrondis en haut, poignee de drag |
|
|
184
|
+
|
|
185
|
+
Les deux modes incluent : fermeture par backdrop click, touche Escape, verrouillage du scroll, et transitions CSS fluides.
|
|
186
|
+
|
|
96
187
|
---
|
|
97
188
|
|
|
98
189
|
## Flux de paiement
|
|
@@ -162,6 +253,43 @@ import { DuticotacPaymentModal } from "@applite/duticotac-react";
|
|
|
162
253
|
|
|
163
254
|
---
|
|
164
255
|
|
|
256
|
+
### `ResponsiveDialog`
|
|
257
|
+
|
|
258
|
+
Dialog responsive qui s'adapte automatiquement : modal centre sur desktop, bottom sheet sur mobile. Utilise en interne par `useDuticotac`, mais peut etre utilise independamment.
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
import { ResponsiveDialog } from "@applite/duticotac-react";
|
|
262
|
+
|
|
263
|
+
<ResponsiveDialog
|
|
264
|
+
open={isOpen}
|
|
265
|
+
onClose={() => setOpen(false)}
|
|
266
|
+
title="Mon dialog"
|
|
267
|
+
>
|
|
268
|
+
<p>Contenu du dialog</p>
|
|
269
|
+
</ResponsiveDialog>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Props
|
|
273
|
+
|
|
274
|
+
| Prop | Type | Requis | Description |
|
|
275
|
+
|------|------|--------|-------------|
|
|
276
|
+
| `open` | `boolean` | Oui | Visibilite du dialog |
|
|
277
|
+
| `onClose` | `() => void` | Oui | Callback de fermeture |
|
|
278
|
+
| `title` | `string` | Oui | Titre affiche dans le header |
|
|
279
|
+
| `children` | `ReactNode` | Oui | Contenu du dialog |
|
|
280
|
+
| `className` | `string` | Non | Classes CSS additionnelles |
|
|
281
|
+
|
|
282
|
+
#### Fonctionnalites
|
|
283
|
+
|
|
284
|
+
- **Desktop** (> 640px) : Modal centre, max-width 440px, animation d'echelle
|
|
285
|
+
- **Mobile** (<= 640px) : Bottom sheet, slide-up, coins arrondis en haut, poignee de drag
|
|
286
|
+
- Rendu via `createPortal` dans `document.body`
|
|
287
|
+
- Fermeture : backdrop click, touche Escape
|
|
288
|
+
- Verrouillage du scroll body quand ouvert
|
|
289
|
+
- Transitions CSS fluides (250ms)
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
165
293
|
### `ProviderSelector`
|
|
166
294
|
|
|
167
295
|
Grille de selection des providers de paiement avec logos et indicateur de selection.
|
|
@@ -493,8 +621,13 @@ import type {
|
|
|
493
621
|
### Exports de `@applite/duticotac-react`
|
|
494
622
|
|
|
495
623
|
```ts
|
|
624
|
+
// Hook (recommande)
|
|
625
|
+
export { useDuticotac } from "@applite/duticotac-react";
|
|
626
|
+
export type { UseDuticotacConfig, UseDuticotacReturn, PayOptions } from "@applite/duticotac-react";
|
|
627
|
+
|
|
496
628
|
// Composants
|
|
497
629
|
export { DuticotacPaymentModal } from "@applite/duticotac-react";
|
|
630
|
+
export { ResponsiveDialog } from "@applite/duticotac-react";
|
|
498
631
|
export { ProviderSelector } from "@applite/duticotac-react";
|
|
499
632
|
export { PhoneInput } from "@applite/duticotac-react";
|
|
500
633
|
export { OtpInput } from "@applite/duticotac-react";
|
|
@@ -502,6 +635,7 @@ export { TransactionStatus } from "@applite/duticotac-react";
|
|
|
502
635
|
|
|
503
636
|
// Types des props
|
|
504
637
|
export type { DuticotacPaymentModalProps } from "@applite/duticotac-react";
|
|
638
|
+
export type { ResponsiveDialogProps } from "@applite/duticotac-react";
|
|
505
639
|
export type { ProviderSelectorProps } from "@applite/duticotac-react";
|
|
506
640
|
export type { PhoneInputProps } from "@applite/duticotac-react";
|
|
507
641
|
export type { OtpInputProps } from "@applite/duticotac-react";
|
package/dist/index.d.mts
CHANGED
|
@@ -90,6 +90,69 @@ interface TransactionStatusProps {
|
|
|
90
90
|
}
|
|
91
91
|
declare function TransactionStatus({ status, elapsedSeconds, message, errorMessage, paymentUrl, onRetry, onClose, onOpenPaymentUrl, className, }: TransactionStatusProps): react_jsx_runtime.JSX.Element;
|
|
92
92
|
|
|
93
|
+
interface ResponsiveDialogProps {
|
|
94
|
+
open: boolean;
|
|
95
|
+
onClose: () => void;
|
|
96
|
+
title: string;
|
|
97
|
+
children: React.ReactNode;
|
|
98
|
+
className?: string;
|
|
99
|
+
}
|
|
100
|
+
declare function ResponsiveDialog({ open, onClose, title, children, className, }: ResponsiveDialogProps): React.ReactPortal | null;
|
|
101
|
+
|
|
102
|
+
interface UseDuticotacConfig {
|
|
103
|
+
/** DuticotacSDK instance. */
|
|
104
|
+
sdk: DuticotacSDK;
|
|
105
|
+
/** Available payment providers. */
|
|
106
|
+
providers?: PaymentProvider[];
|
|
107
|
+
/** Called to get a unique transaction ID before cashout. */
|
|
108
|
+
getTransactionId: () => Promise<string>;
|
|
109
|
+
/** Shared defaults — can be overridden per pay() call. */
|
|
110
|
+
productReference?: string;
|
|
111
|
+
name?: string;
|
|
112
|
+
email?: string;
|
|
113
|
+
customerId?: string;
|
|
114
|
+
kolaboReference?: string;
|
|
115
|
+
app?: CoreApp;
|
|
116
|
+
platform?: PlatformType;
|
|
117
|
+
initialPhone?: string;
|
|
118
|
+
/** Polling timeout in ms (default: 90000). */
|
|
119
|
+
pollTimeout?: number;
|
|
120
|
+
/** Dialog title (default: "Paiement"). */
|
|
121
|
+
title?: string;
|
|
122
|
+
}
|
|
123
|
+
interface PayOptions {
|
|
124
|
+
/** Amount in FCFA. */
|
|
125
|
+
amount: number;
|
|
126
|
+
/** Override providers for this payment. */
|
|
127
|
+
providers?: PaymentProvider[];
|
|
128
|
+
/** Override product reference. */
|
|
129
|
+
productReference?: string;
|
|
130
|
+
/** Override customer details. */
|
|
131
|
+
name?: string;
|
|
132
|
+
email?: string;
|
|
133
|
+
customerId?: string;
|
|
134
|
+
/** Custom success message. */
|
|
135
|
+
successMessage?: string;
|
|
136
|
+
/** Called when payment is confirmed. */
|
|
137
|
+
onSuccess?: (transaction: TransactionModel) => void;
|
|
138
|
+
/** Called when the dialog closes (success or cancel). */
|
|
139
|
+
onClose?: () => void;
|
|
140
|
+
}
|
|
141
|
+
interface UseDuticotacReturn {
|
|
142
|
+
/** Open the payment dialog with the given options. */
|
|
143
|
+
pay: (options: PayOptions) => void;
|
|
144
|
+
/** Whether the dialog is currently open. */
|
|
145
|
+
isOpen: boolean;
|
|
146
|
+
/** Close the dialog programmatically. */
|
|
147
|
+
close: () => void;
|
|
148
|
+
/**
|
|
149
|
+
* The dialog JSX element. Render this once somewhere in your component tree.
|
|
150
|
+
* @example return <>{Dialog}</>
|
|
151
|
+
*/
|
|
152
|
+
Dialog: React.ReactNode;
|
|
153
|
+
}
|
|
154
|
+
declare function useDuticotac(config: UseDuticotacConfig): UseDuticotacReturn;
|
|
155
|
+
|
|
93
156
|
declare function cn(...inputs: ClassValue[]): string;
|
|
94
157
|
|
|
95
158
|
declare function formatCFA(n: number): string;
|
|
@@ -99,4 +162,4 @@ declare function validatePhone(phone: string, provider: PaymentProvider): string
|
|
|
99
162
|
declare function normalizePhone(phone: string): string;
|
|
100
163
|
declare function needsOtp(provider: PaymentProvider): boolean;
|
|
101
164
|
|
|
102
|
-
export { DuticotacPaymentModal, type DuticotacPaymentModalProps, OtpInput, type OtpInputProps, PhoneInput, type PhoneInputProps, ProviderSelector, type ProviderSelectorProps, TransactionStatus, type TransactionStatusProps, cn, formatCFA, getPhoneRegex, needsOtp, normalizePhone, validatePhone };
|
|
165
|
+
export { DuticotacPaymentModal, type DuticotacPaymentModalProps, OtpInput, type OtpInputProps, type PayOptions, PhoneInput, type PhoneInputProps, ProviderSelector, type ProviderSelectorProps, ResponsiveDialog, type ResponsiveDialogProps, TransactionStatus, type TransactionStatusProps, type UseDuticotacConfig, type UseDuticotacReturn, cn, formatCFA, getPhoneRegex, needsOtp, normalizePhone, useDuticotac, validatePhone };
|
package/dist/index.d.ts
CHANGED
|
@@ -90,6 +90,69 @@ interface TransactionStatusProps {
|
|
|
90
90
|
}
|
|
91
91
|
declare function TransactionStatus({ status, elapsedSeconds, message, errorMessage, paymentUrl, onRetry, onClose, onOpenPaymentUrl, className, }: TransactionStatusProps): react_jsx_runtime.JSX.Element;
|
|
92
92
|
|
|
93
|
+
interface ResponsiveDialogProps {
|
|
94
|
+
open: boolean;
|
|
95
|
+
onClose: () => void;
|
|
96
|
+
title: string;
|
|
97
|
+
children: React.ReactNode;
|
|
98
|
+
className?: string;
|
|
99
|
+
}
|
|
100
|
+
declare function ResponsiveDialog({ open, onClose, title, children, className, }: ResponsiveDialogProps): React.ReactPortal | null;
|
|
101
|
+
|
|
102
|
+
interface UseDuticotacConfig {
|
|
103
|
+
/** DuticotacSDK instance. */
|
|
104
|
+
sdk: DuticotacSDK;
|
|
105
|
+
/** Available payment providers. */
|
|
106
|
+
providers?: PaymentProvider[];
|
|
107
|
+
/** Called to get a unique transaction ID before cashout. */
|
|
108
|
+
getTransactionId: () => Promise<string>;
|
|
109
|
+
/** Shared defaults — can be overridden per pay() call. */
|
|
110
|
+
productReference?: string;
|
|
111
|
+
name?: string;
|
|
112
|
+
email?: string;
|
|
113
|
+
customerId?: string;
|
|
114
|
+
kolaboReference?: string;
|
|
115
|
+
app?: CoreApp;
|
|
116
|
+
platform?: PlatformType;
|
|
117
|
+
initialPhone?: string;
|
|
118
|
+
/** Polling timeout in ms (default: 90000). */
|
|
119
|
+
pollTimeout?: number;
|
|
120
|
+
/** Dialog title (default: "Paiement"). */
|
|
121
|
+
title?: string;
|
|
122
|
+
}
|
|
123
|
+
interface PayOptions {
|
|
124
|
+
/** Amount in FCFA. */
|
|
125
|
+
amount: number;
|
|
126
|
+
/** Override providers for this payment. */
|
|
127
|
+
providers?: PaymentProvider[];
|
|
128
|
+
/** Override product reference. */
|
|
129
|
+
productReference?: string;
|
|
130
|
+
/** Override customer details. */
|
|
131
|
+
name?: string;
|
|
132
|
+
email?: string;
|
|
133
|
+
customerId?: string;
|
|
134
|
+
/** Custom success message. */
|
|
135
|
+
successMessage?: string;
|
|
136
|
+
/** Called when payment is confirmed. */
|
|
137
|
+
onSuccess?: (transaction: TransactionModel) => void;
|
|
138
|
+
/** Called when the dialog closes (success or cancel). */
|
|
139
|
+
onClose?: () => void;
|
|
140
|
+
}
|
|
141
|
+
interface UseDuticotacReturn {
|
|
142
|
+
/** Open the payment dialog with the given options. */
|
|
143
|
+
pay: (options: PayOptions) => void;
|
|
144
|
+
/** Whether the dialog is currently open. */
|
|
145
|
+
isOpen: boolean;
|
|
146
|
+
/** Close the dialog programmatically. */
|
|
147
|
+
close: () => void;
|
|
148
|
+
/**
|
|
149
|
+
* The dialog JSX element. Render this once somewhere in your component tree.
|
|
150
|
+
* @example return <>{Dialog}</>
|
|
151
|
+
*/
|
|
152
|
+
Dialog: React.ReactNode;
|
|
153
|
+
}
|
|
154
|
+
declare function useDuticotac(config: UseDuticotacConfig): UseDuticotacReturn;
|
|
155
|
+
|
|
93
156
|
declare function cn(...inputs: ClassValue[]): string;
|
|
94
157
|
|
|
95
158
|
declare function formatCFA(n: number): string;
|
|
@@ -99,4 +162,4 @@ declare function validatePhone(phone: string, provider: PaymentProvider): string
|
|
|
99
162
|
declare function normalizePhone(phone: string): string;
|
|
100
163
|
declare function needsOtp(provider: PaymentProvider): boolean;
|
|
101
164
|
|
|
102
|
-
export { DuticotacPaymentModal, type DuticotacPaymentModalProps, OtpInput, type OtpInputProps, PhoneInput, type PhoneInputProps, ProviderSelector, type ProviderSelectorProps, TransactionStatus, type TransactionStatusProps, cn, formatCFA, getPhoneRegex, needsOtp, normalizePhone, validatePhone };
|
|
165
|
+
export { DuticotacPaymentModal, type DuticotacPaymentModalProps, OtpInput, type OtpInputProps, type PayOptions, PhoneInput, type PhoneInputProps, ProviderSelector, type ProviderSelectorProps, ResponsiveDialog, type ResponsiveDialogProps, TransactionStatus, type TransactionStatusProps, type UseDuticotacConfig, type UseDuticotacReturn, cn, formatCFA, getPhoneRegex, needsOtp, normalizePhone, useDuticotac, validatePhone };
|
package/dist/index.js
CHANGED
|
@@ -35,12 +35,14 @@ __export(index_exports, {
|
|
|
35
35
|
OtpInput: () => OtpInput,
|
|
36
36
|
PhoneInput: () => PhoneInput,
|
|
37
37
|
ProviderSelector: () => ProviderSelector,
|
|
38
|
+
ResponsiveDialog: () => ResponsiveDialog,
|
|
38
39
|
TransactionStatus: () => TransactionStatus,
|
|
39
40
|
cn: () => cn,
|
|
40
41
|
formatCFA: () => formatCFA,
|
|
41
42
|
getPhoneRegex: () => getPhoneRegex,
|
|
42
43
|
needsOtp: () => needsOtp,
|
|
43
44
|
normalizePhone: () => normalizePhone,
|
|
45
|
+
useDuticotac: () => useDuticotac,
|
|
44
46
|
validatePhone: () => validatePhone
|
|
45
47
|
});
|
|
46
48
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -703,18 +705,276 @@ function DuticotacPaymentModal({
|
|
|
703
705
|
] })
|
|
704
706
|
] });
|
|
705
707
|
}
|
|
708
|
+
|
|
709
|
+
// src/components/responsive-dialog.tsx
|
|
710
|
+
var React3 = __toESM(require("react"));
|
|
711
|
+
var import_react_dom = require("react-dom");
|
|
712
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
713
|
+
var ANIMATION_MS = 250;
|
|
714
|
+
function ResponsiveDialog({
|
|
715
|
+
open,
|
|
716
|
+
onClose,
|
|
717
|
+
title,
|
|
718
|
+
children,
|
|
719
|
+
className
|
|
720
|
+
}) {
|
|
721
|
+
const [mounted, setMounted] = React3.useState(false);
|
|
722
|
+
const [visible, setVisible] = React3.useState(false);
|
|
723
|
+
const panelRef = React3.useRef(null);
|
|
724
|
+
React3.useEffect(() => {
|
|
725
|
+
if (open) {
|
|
726
|
+
setMounted(true);
|
|
727
|
+
requestAnimationFrame(() => {
|
|
728
|
+
requestAnimationFrame(() => setVisible(true));
|
|
729
|
+
});
|
|
730
|
+
} else if (mounted) {
|
|
731
|
+
setVisible(false);
|
|
732
|
+
const timer = setTimeout(() => setMounted(false), ANIMATION_MS);
|
|
733
|
+
return () => clearTimeout(timer);
|
|
734
|
+
}
|
|
735
|
+
}, [open]);
|
|
736
|
+
React3.useEffect(() => {
|
|
737
|
+
if (!open) return;
|
|
738
|
+
const handler = (e) => {
|
|
739
|
+
if (e.key === "Escape") onClose();
|
|
740
|
+
};
|
|
741
|
+
document.addEventListener("keydown", handler);
|
|
742
|
+
return () => document.removeEventListener("keydown", handler);
|
|
743
|
+
}, [open, onClose]);
|
|
744
|
+
React3.useEffect(() => {
|
|
745
|
+
if (!mounted) return;
|
|
746
|
+
const prev = document.body.style.overflow;
|
|
747
|
+
document.body.style.overflow = "hidden";
|
|
748
|
+
return () => {
|
|
749
|
+
document.body.style.overflow = prev;
|
|
750
|
+
};
|
|
751
|
+
}, [mounted]);
|
|
752
|
+
if (!mounted) return null;
|
|
753
|
+
const dialog = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
754
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `
|
|
755
|
+
.dtc-dialog-backdrop {
|
|
756
|
+
position: fixed;
|
|
757
|
+
inset: 0;
|
|
758
|
+
z-index: 9998;
|
|
759
|
+
background: rgba(0,0,0,0);
|
|
760
|
+
transition: background ${ANIMATION_MS}ms ease;
|
|
761
|
+
}
|
|
762
|
+
.dtc-dialog-backdrop.dtc-visible {
|
|
763
|
+
background: rgba(0,0,0,0.5);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/* \u2500\u2500 Desktop: centered modal \u2500\u2500 */
|
|
767
|
+
.dtc-dialog-panel {
|
|
768
|
+
position: fixed;
|
|
769
|
+
z-index: 9999;
|
|
770
|
+
background: var(--background, #fff);
|
|
771
|
+
border: 1px solid var(--border, #e5e7eb);
|
|
772
|
+
box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);
|
|
773
|
+
|
|
774
|
+
/* Desktop default */
|
|
775
|
+
top: 50%;
|
|
776
|
+
left: 50%;
|
|
777
|
+
transform: translate(-50%, -50%) scale(0.95);
|
|
778
|
+
opacity: 0;
|
|
779
|
+
width: calc(100% - 32px);
|
|
780
|
+
max-width: 440px;
|
|
781
|
+
max-height: calc(100vh - 64px);
|
|
782
|
+
border-radius: 16px;
|
|
783
|
+
overflow-y: auto;
|
|
784
|
+
|
|
785
|
+
transition:
|
|
786
|
+
transform ${ANIMATION_MS}ms cubic-bezier(0.22, 1, 0.36, 1),
|
|
787
|
+
opacity ${ANIMATION_MS}ms ease;
|
|
788
|
+
}
|
|
789
|
+
.dtc-dialog-panel.dtc-visible {
|
|
790
|
+
transform: translate(-50%, -50%) scale(1);
|
|
791
|
+
opacity: 1;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/* \u2500\u2500 Mobile: bottom sheet \u2500\u2500 */
|
|
795
|
+
@media (max-width: 640px) {
|
|
796
|
+
.dtc-dialog-panel {
|
|
797
|
+
top: auto;
|
|
798
|
+
left: 0;
|
|
799
|
+
right: 0;
|
|
800
|
+
bottom: 0;
|
|
801
|
+
transform: translateY(100%);
|
|
802
|
+
opacity: 1;
|
|
803
|
+
width: 100%;
|
|
804
|
+
max-width: 100%;
|
|
805
|
+
max-height: 90vh;
|
|
806
|
+
border-radius: 20px 20px 0 0;
|
|
807
|
+
border-bottom: none;
|
|
808
|
+
}
|
|
809
|
+
.dtc-dialog-panel.dtc-visible {
|
|
810
|
+
transform: translateY(0);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
` }),
|
|
814
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
815
|
+
"div",
|
|
816
|
+
{
|
|
817
|
+
className: cn("dtc-dialog-backdrop", visible && "dtc-visible"),
|
|
818
|
+
onClick: onClose,
|
|
819
|
+
"aria-hidden": true
|
|
820
|
+
}
|
|
821
|
+
),
|
|
822
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
823
|
+
"div",
|
|
824
|
+
{
|
|
825
|
+
ref: panelRef,
|
|
826
|
+
className: cn("dtc-dialog-panel", visible && "dtc-visible", className),
|
|
827
|
+
role: "dialog",
|
|
828
|
+
"aria-modal": "true",
|
|
829
|
+
"aria-label": title,
|
|
830
|
+
children: [
|
|
831
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dtc-mobile-handle", style: {
|
|
832
|
+
display: "none",
|
|
833
|
+
justifyContent: "center",
|
|
834
|
+
paddingTop: 12,
|
|
835
|
+
paddingBottom: 4
|
|
836
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
|
|
837
|
+
width: 36,
|
|
838
|
+
height: 4,
|
|
839
|
+
borderRadius: 2,
|
|
840
|
+
background: "var(--border, #d1d5db)"
|
|
841
|
+
} }) }),
|
|
842
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `
|
|
843
|
+
@media (max-width: 640px) {
|
|
844
|
+
.dtc-mobile-handle { display: flex !important; }
|
|
845
|
+
}
|
|
846
|
+
` }),
|
|
847
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
|
|
848
|
+
display: "flex",
|
|
849
|
+
alignItems: "center",
|
|
850
|
+
justifyContent: "space-between",
|
|
851
|
+
padding: "16px 20px 0"
|
|
852
|
+
}, children: [
|
|
853
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { style: {
|
|
854
|
+
fontSize: 18,
|
|
855
|
+
fontWeight: 700,
|
|
856
|
+
color: "var(--foreground, #111)",
|
|
857
|
+
margin: 0
|
|
858
|
+
}, children: title }),
|
|
859
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
860
|
+
"button",
|
|
861
|
+
{
|
|
862
|
+
type: "button",
|
|
863
|
+
onClick: onClose,
|
|
864
|
+
style: {
|
|
865
|
+
background: "none",
|
|
866
|
+
border: "none",
|
|
867
|
+
padding: 4,
|
|
868
|
+
cursor: "pointer",
|
|
869
|
+
borderRadius: 6,
|
|
870
|
+
color: "var(--muted-foreground, #6b7280)",
|
|
871
|
+
display: "flex",
|
|
872
|
+
alignItems: "center",
|
|
873
|
+
justifyContent: "center"
|
|
874
|
+
},
|
|
875
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
876
|
+
"path",
|
|
877
|
+
{
|
|
878
|
+
d: "M6 18L18 6M6 6l12 12",
|
|
879
|
+
stroke: "currentColor",
|
|
880
|
+
strokeWidth: "2",
|
|
881
|
+
strokeLinecap: "round"
|
|
882
|
+
}
|
|
883
|
+
) })
|
|
884
|
+
}
|
|
885
|
+
)
|
|
886
|
+
] }),
|
|
887
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { padding: "16px 20px 20px" }, children })
|
|
888
|
+
]
|
|
889
|
+
}
|
|
890
|
+
)
|
|
891
|
+
] });
|
|
892
|
+
if (typeof document === "undefined") return null;
|
|
893
|
+
return (0, import_react_dom.createPortal)(dialog, document.body);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// src/hooks/use-duticotac.tsx
|
|
897
|
+
var React4 = __toESM(require("react"));
|
|
898
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
899
|
+
function useDuticotac(config) {
|
|
900
|
+
const [open, setOpen] = React4.useState(false);
|
|
901
|
+
const [payOptions, setPayOptions] = React4.useState(null);
|
|
902
|
+
const configRef = React4.useRef(config);
|
|
903
|
+
configRef.current = config;
|
|
904
|
+
const pay = React4.useCallback((options) => {
|
|
905
|
+
setPayOptions(options);
|
|
906
|
+
setOpen(true);
|
|
907
|
+
}, []);
|
|
908
|
+
const close = React4.useCallback(() => {
|
|
909
|
+
setOpen(false);
|
|
910
|
+
}, []);
|
|
911
|
+
const handleClose = React4.useCallback(() => {
|
|
912
|
+
setOpen(false);
|
|
913
|
+
payOptions?.onClose?.();
|
|
914
|
+
}, [payOptions]);
|
|
915
|
+
const handleSuccess = React4.useCallback(
|
|
916
|
+
(tx) => {
|
|
917
|
+
payOptions?.onSuccess?.(tx);
|
|
918
|
+
},
|
|
919
|
+
[payOptions]
|
|
920
|
+
);
|
|
921
|
+
const DialogElement = React4.useMemo(() => {
|
|
922
|
+
if (!payOptions) return null;
|
|
923
|
+
const cfg = configRef.current;
|
|
924
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
925
|
+
DuticotacPaymentModal,
|
|
926
|
+
{
|
|
927
|
+
open,
|
|
928
|
+
onClose: handleClose,
|
|
929
|
+
onSuccess: handleSuccess,
|
|
930
|
+
sdk: cfg.sdk,
|
|
931
|
+
amount: payOptions.amount,
|
|
932
|
+
providers: payOptions.providers ?? cfg.providers,
|
|
933
|
+
getTransactionId: cfg.getTransactionId,
|
|
934
|
+
productReference: payOptions.productReference ?? cfg.productReference,
|
|
935
|
+
initialPhone: cfg.initialPhone,
|
|
936
|
+
name: payOptions.name ?? cfg.name,
|
|
937
|
+
email: payOptions.email ?? cfg.email,
|
|
938
|
+
customerId: payOptions.customerId ?? cfg.customerId,
|
|
939
|
+
kolaboReference: cfg.kolaboReference,
|
|
940
|
+
app: cfg.app,
|
|
941
|
+
platform: cfg.platform,
|
|
942
|
+
pollTimeout: cfg.pollTimeout,
|
|
943
|
+
title: cfg.title,
|
|
944
|
+
successMessage: payOptions.successMessage,
|
|
945
|
+
renderModal: ({ open: isOpen, onClose: modalClose, title: modalTitle, children }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
946
|
+
ResponsiveDialog,
|
|
947
|
+
{
|
|
948
|
+
open: isOpen,
|
|
949
|
+
onClose: modalClose,
|
|
950
|
+
title: modalTitle,
|
|
951
|
+
children
|
|
952
|
+
}
|
|
953
|
+
)
|
|
954
|
+
}
|
|
955
|
+
);
|
|
956
|
+
}, [open, payOptions, handleClose, handleSuccess]);
|
|
957
|
+
return {
|
|
958
|
+
pay,
|
|
959
|
+
isOpen: open,
|
|
960
|
+
close,
|
|
961
|
+
Dialog: DialogElement
|
|
962
|
+
};
|
|
963
|
+
}
|
|
706
964
|
// Annotate the CommonJS export names for ESM import in node:
|
|
707
965
|
0 && (module.exports = {
|
|
708
966
|
DuticotacPaymentModal,
|
|
709
967
|
OtpInput,
|
|
710
968
|
PhoneInput,
|
|
711
969
|
ProviderSelector,
|
|
970
|
+
ResponsiveDialog,
|
|
712
971
|
TransactionStatus,
|
|
713
972
|
cn,
|
|
714
973
|
formatCFA,
|
|
715
974
|
getPhoneRegex,
|
|
716
975
|
needsOtp,
|
|
717
976
|
normalizePhone,
|
|
977
|
+
useDuticotac,
|
|
718
978
|
validatePhone
|
|
719
979
|
});
|
|
720
980
|
//# sourceMappingURL=index.js.map
|