cinetpay-seamless 0.1.2 → 0.1.4
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 +67 -11
- package/dist/checkout.d.ts +16 -0
- package/dist/checkout.d.ts.map +1 -1
- package/dist/cinetpay-seamless.js +268 -180
- package/dist/cinetpay-seamless.js.map +1 -1
- package/dist/cinetpay-seamless.umd.cjs +2 -2
- package/dist/cinetpay-seamless.umd.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +52 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ npm install cinetpay-seamless
|
|
|
36
36
|
### CDN
|
|
37
37
|
|
|
38
38
|
```html
|
|
39
|
-
<script src="https://unpkg.com/cinetpay-seamless@0.1.
|
|
39
|
+
<script src="https://unpkg.com/cinetpay-seamless@0.1.4/dist/cinetpay-seamless.umd.cjs"></script>
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
## Démarrage rapide
|
|
@@ -45,7 +45,7 @@ npm install cinetpay-seamless
|
|
|
45
45
|
import { CinetPaySeamless } from 'cinetpay-seamless'
|
|
46
46
|
|
|
47
47
|
// 1. Obtenir le paymentToken depuis votre backend
|
|
48
|
-
const { paymentToken } = await fetch('/api/pay', {
|
|
48
|
+
const { paymentToken, paymentUrl } = await fetch('/api/pay', {
|
|
49
49
|
method: 'POST',
|
|
50
50
|
headers: { 'Content-Type': 'application/json' },
|
|
51
51
|
body: JSON.stringify({ amount: 5000, orderId: 'ORDER-001' }),
|
|
@@ -54,6 +54,7 @@ const { paymentToken } = await fetch('/api/pay', {
|
|
|
54
54
|
// 2. Ouvrir la popup
|
|
55
55
|
CinetPaySeamless.open({
|
|
56
56
|
paymentToken,
|
|
57
|
+
paymentUrl, // recommandé si votre backend renvoie l'URL CinetPay exacte
|
|
57
58
|
onPaymentSuccess: (data) => {
|
|
58
59
|
console.log('Paiement réussi !', data.amount, data.currency)
|
|
59
60
|
},
|
|
@@ -68,7 +69,7 @@ CinetPaySeamless.open({
|
|
|
68
69
|
```html
|
|
69
70
|
<button id="pay-btn">Payer 5 000 XOF</button>
|
|
70
71
|
|
|
71
|
-
<script src="https://unpkg.com/cinetpay-seamless@0.1.
|
|
72
|
+
<script src="https://unpkg.com/cinetpay-seamless@0.1.4/dist/cinetpay-seamless.umd.cjs"></script>
|
|
72
73
|
<script>
|
|
73
74
|
document.getElementById('pay-btn').addEventListener('click', function() {
|
|
74
75
|
// Appeler votre backend pour obtenir le paymentToken
|
|
@@ -81,6 +82,7 @@ CinetPaySeamless.open({
|
|
|
81
82
|
.then(function(data) {
|
|
82
83
|
CinetPaySeamless.open({
|
|
83
84
|
paymentToken: data.paymentToken,
|
|
85
|
+
paymentUrl: data.paymentUrl,
|
|
84
86
|
onPaymentSuccess: function(result) {
|
|
85
87
|
alert('Merci ! ' + result.amount + ' ' + result.currency)
|
|
86
88
|
},
|
|
@@ -152,6 +154,12 @@ Ouvre la popup de paiement CinetPay.
|
|
|
152
154
|
| Option | Type | Default | Description |
|
|
153
155
|
|---|---|---|---|
|
|
154
156
|
| `paymentToken` | `string` | **requis** | Token obtenu via votre backend (`POST /v1/payment`) |
|
|
157
|
+
| `environment` | `'sandbox' \| 'production'` | `'sandbox'` | Environnement utilisé si `paymentUrl` n'est pas fourni |
|
|
158
|
+
| `paymentUrl` | `string` | - | URL complète renvoyée par CinetPay, prioritaire sur `environment` |
|
|
159
|
+
| `checkoutBaseUrl` | `string` | selon `environment` | Host checkout personnalisé |
|
|
160
|
+
| `statusUrl` | `string \| (ctx) => string` | - | Endpoint de votre backend pour vérifier le statut canonique |
|
|
161
|
+
| `checkStatus` | `(ctx) => Promise<object>` | - | Fonction personnalisée de vérification statut |
|
|
162
|
+
| `statusPollInterval` | `number` | `3000` | Intervalle de vérification statut en ms |
|
|
155
163
|
| `debug` | `boolean` | `false` | Logs console `[CinetPay Seamless]` |
|
|
156
164
|
| `onReady` | `() => void` | - | Iframe chargée |
|
|
157
165
|
| `onPaymentSuccess` | `(data) => void` | - | Paiement accepté |
|
|
@@ -190,6 +198,8 @@ Ferme la popup et l'overlay.
|
|
|
190
198
|
amount: number
|
|
191
199
|
currency: string
|
|
192
200
|
status: 'ACCEPTED' | 'REFUSED' | 'PENDING' | 'INITIATED' | 'EXPIRED' | 'UNKNOWN'
|
|
201
|
+
rawStatus?: string
|
|
202
|
+
apiCode?: number
|
|
193
203
|
paymentMethod: string
|
|
194
204
|
description: string
|
|
195
205
|
transactionId: string
|
|
@@ -199,6 +209,46 @@ Ferme la popup et l'overlay.
|
|
|
199
209
|
}
|
|
200
210
|
```
|
|
201
211
|
|
|
212
|
+
### Vérification statut recommandée
|
|
213
|
+
|
|
214
|
+
Le checkout CinetPay peut finaliser le paiement sans envoyer de `postMessage`
|
|
215
|
+
au parent navigateur. Pour fiabiliser `onPaymentSuccess` et `onPaymentFailed`,
|
|
216
|
+
fournissez un endpoint backend qui vérifie le statut canonique auprès de CinetPay.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
CinetPaySeamless.open({
|
|
220
|
+
paymentToken,
|
|
221
|
+
statusUrl: `/api/cinetpay/status?transactionId=${merchantTransactionId}`,
|
|
222
|
+
statusPollInterval: 3000,
|
|
223
|
+
onPaymentSuccess: (data) => {
|
|
224
|
+
console.log('Paiement accepté', data.transactionId)
|
|
225
|
+
},
|
|
226
|
+
onPaymentFailed: (data) => {
|
|
227
|
+
console.log('Paiement refusé', data.rawStatus)
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Votre endpoint backend doit appeler `GET /v1/payment/{merchant_transaction_id}`
|
|
233
|
+
avec vos clés CinetPay, puis renvoyer la réponse JSON au frontend :
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"code": 100,
|
|
238
|
+
"status": "SUCCESS",
|
|
239
|
+
"merchant_transaction_id": "ORDER-123",
|
|
240
|
+
"transaction_id": "27ba5590f3ae4c6f9585ae1e4f5265dd"
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Le SDK normalise automatiquement :
|
|
245
|
+
|
|
246
|
+
| Statut CinetPay | Event Seamless |
|
|
247
|
+
|---|---|
|
|
248
|
+
| `SUCCESS` / code `100` | `payment.success` |
|
|
249
|
+
| `FAILED` / `INSUFFICIENT_BALANCE` / code `2010` / `2005` | `payment.failed` |
|
|
250
|
+
| `INITIATED` / `PENDING` / `EXPIRED` | `payment.pending` |
|
|
251
|
+
|
|
202
252
|
## Exemples d'intégration
|
|
203
253
|
|
|
204
254
|
> Le Seamless a besoin d'un `paymentToken` obtenu côté serveur. Les exemples ci-dessous
|
|
@@ -244,7 +294,10 @@ app.post('/api/pay', async (req, res) => {
|
|
|
244
294
|
channel: 'PUSH',
|
|
245
295
|
}, 'CI')
|
|
246
296
|
|
|
247
|
-
res.json({
|
|
297
|
+
res.json({
|
|
298
|
+
paymentToken: payment.paymentToken,
|
|
299
|
+
paymentUrl: payment.paymentUrl,
|
|
300
|
+
})
|
|
248
301
|
})
|
|
249
302
|
```
|
|
250
303
|
|
|
@@ -265,9 +318,9 @@ async function pay(amount: number) {
|
|
|
265
318
|
phone: '+2250707000000',
|
|
266
319
|
}),
|
|
267
320
|
})
|
|
268
|
-
const { paymentToken } = await res.json()
|
|
321
|
+
const { paymentToken, paymentUrl } = await res.json()
|
|
269
322
|
|
|
270
|
-
CinetPaySeamless.open({ paymentToken, debug: true })
|
|
323
|
+
CinetPaySeamless.open({ paymentToken, paymentUrl, debug: true })
|
|
271
324
|
}
|
|
272
325
|
```
|
|
273
326
|
|
|
@@ -491,7 +544,7 @@ async function pay() {
|
|
|
491
544
|
<meta charset="UTF-8">
|
|
492
545
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
493
546
|
<title>Paiement</title>
|
|
494
|
-
<script src="https://unpkg.com/cinetpay-seamless@0.1.
|
|
547
|
+
<script src="https://unpkg.com/cinetpay-seamless@0.1.4/dist/cinetpay-seamless.umd.cjs"></script>
|
|
495
548
|
</head>
|
|
496
549
|
<body>
|
|
497
550
|
<form id="payment-form">
|
|
@@ -592,7 +645,7 @@ CinetPaySeamless.open({ paymentToken: 'abc...', debug: true })
|
|
|
592
645
|
|
|
593
646
|
```
|
|
594
647
|
[CinetPay Seamless] CinetPaySeamless.open() called
|
|
595
|
-
[CinetPay Seamless] Opening popup { paymentUrl: 'https://secure.cinetpay.
|
|
648
|
+
[CinetPay Seamless] Opening popup { paymentUrl: 'https://secure.cinetpay.co/checkout/abc...' }
|
|
596
649
|
[CinetPay Seamless] Iframe loaded — checkout ready
|
|
597
650
|
[CinetPay Seamless] Payment response: ACCEPTED { amount: 5000, currency: 'XOF', ... }
|
|
598
651
|
[CinetPay Seamless] Payment accepted
|
|
@@ -620,8 +673,8 @@ Commiter le .env dans git Ajouter .env dans .gitignore
|
|
|
620
673
|
|
|
621
674
|
| Préfixe de clé | Environnement | Usage |
|
|
622
675
|
|---|---|---|
|
|
623
|
-
| `sk_test_...` | Sandbox
|
|
624
|
-
| `sk_live_...` | Production
|
|
676
|
+
| `sk_test_...` | Sandbox API `api.cinetpay.net`, checkout `secure.cinetpay.net` | Développement et tests |
|
|
677
|
+
| `sk_live_...` | Production API `api.cinetpay.co`, checkout `secure.cinetpay.co` | Transactions réelles |
|
|
625
678
|
|
|
626
679
|
**Règles importantes :**
|
|
627
680
|
- Ne **jamais** utiliser des clés `sk_live_` en développement
|
|
@@ -639,12 +692,15 @@ flowchart LR
|
|
|
639
692
|
B -->|"paymentToken"| F
|
|
640
693
|
F -->|"popup"| D["CinetPay Checkout"]
|
|
641
694
|
D -->|"webhook"| B
|
|
642
|
-
D -->|"postMessage"| F
|
|
695
|
+
D -->|"postMessage si disponible"| F
|
|
696
|
+
F -->|"GET /api/cinetpay/status"| B
|
|
697
|
+
B -->|"GET /v1/payment/{id}"| C
|
|
643
698
|
```
|
|
644
699
|
|
|
645
700
|
### Autres protections
|
|
646
701
|
|
|
647
702
|
- **postMessage** : whitelist stricte des domaines CinetPay (bloque les domaines lookalike)
|
|
703
|
+
- **statusUrl recommandé** : vérification canonique via votre backend si `postMessage` final absent
|
|
648
704
|
- **paymentToken validé** : regex `[a-zA-Z0-9_-]{10,128}` avant injection dans l'URL
|
|
649
705
|
- **Popup bloquée** : détection et callback `onError` avec code `POPUP_BLOCKED`
|
|
650
706
|
- **Zero dépendance** runtime — aucun risque supply chain
|
package/dist/checkout.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PaymentResponse, PaymentError } from './types';
|
|
2
2
|
import { Logger } from './logger';
|
|
3
3
|
import { EventEmitter } from './emitter';
|
|
4
|
+
type StatusChecker = () => Promise<unknown>;
|
|
4
5
|
/** @internal Options du constructeur Checkout */
|
|
5
6
|
export interface CheckoutOptions {
|
|
6
7
|
onReady?: () => void;
|
|
@@ -11,6 +12,8 @@ export interface CheckoutOptions {
|
|
|
11
12
|
status: string;
|
|
12
13
|
}) => void;
|
|
13
14
|
onError?: (error: PaymentError) => void;
|
|
15
|
+
statusChecker?: StatusChecker;
|
|
16
|
+
statusPollInterval?: number;
|
|
14
17
|
logger: Logger;
|
|
15
18
|
emitter: EventEmitter;
|
|
16
19
|
}
|
|
@@ -30,9 +33,14 @@ export declare class Checkout {
|
|
|
30
33
|
private overlay;
|
|
31
34
|
private popup;
|
|
32
35
|
private pollTimer;
|
|
36
|
+
private statusTimer;
|
|
33
37
|
private closeTimer;
|
|
34
38
|
private messageHandler;
|
|
35
39
|
private lastStatus;
|
|
40
|
+
private lastDispatchKey;
|
|
41
|
+
private finalStatusDispatched;
|
|
42
|
+
private statusCheckInFlight;
|
|
43
|
+
private isClosing;
|
|
36
44
|
private previousBodyOverflow;
|
|
37
45
|
private onReadyCallback?;
|
|
38
46
|
private onPaymentSuccessCallback?;
|
|
@@ -40,6 +48,8 @@ export declare class Checkout {
|
|
|
40
48
|
private onPaymentPendingCallback?;
|
|
41
49
|
private onCloseCallback?;
|
|
42
50
|
private onErrorCallback?;
|
|
51
|
+
private statusChecker?;
|
|
52
|
+
private statusPollInterval;
|
|
43
53
|
private logger;
|
|
44
54
|
private emitter;
|
|
45
55
|
constructor(options: CheckoutOptions);
|
|
@@ -53,6 +63,7 @@ export declare class Checkout {
|
|
|
53
63
|
* Ferme la popup et l'overlay.
|
|
54
64
|
*/
|
|
55
65
|
close(): void;
|
|
66
|
+
private finishClose;
|
|
56
67
|
private static isRecord;
|
|
57
68
|
private static childRecords;
|
|
58
69
|
private static findValue;
|
|
@@ -60,6 +71,7 @@ export declare class Checkout {
|
|
|
60
71
|
private static asNumber;
|
|
61
72
|
private static asOptionalNumber;
|
|
62
73
|
private static normalizeStatus;
|
|
74
|
+
private static isFinalStatus;
|
|
63
75
|
private static buildPaymentResponse;
|
|
64
76
|
/** Dispatche la réponse vers le bon callback selon le statut. */
|
|
65
77
|
private dispatchResponse;
|
|
@@ -75,9 +87,13 @@ export declare class Checkout {
|
|
|
75
87
|
*/
|
|
76
88
|
private startPolling;
|
|
77
89
|
private stopPolling;
|
|
90
|
+
private startStatusPolling;
|
|
91
|
+
private stopStatusPolling;
|
|
92
|
+
private checkStatus;
|
|
78
93
|
/** Origines autorisées pour les messages postMessage */
|
|
79
94
|
private static readonly ALLOWED_ORIGINS;
|
|
80
95
|
/** Écoute les messages postMessage de la popup CinetPay. */
|
|
81
96
|
private listenForMessages;
|
|
82
97
|
}
|
|
98
|
+
export {};
|
|
83
99
|
//# sourceMappingURL=checkout.d.ts.map
|
package/dist/checkout.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAiB,MAAM,SAAS,CAAA;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAExC,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IAClD,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACjD,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IAClD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IAC5C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,YAAY,CAAA;CACtB;
|
|
1
|
+
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"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"}
|