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 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.2/dist/cinetpay-seamless.umd.cjs"></script>
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.2/dist/cinetpay-seamless.umd.cjs"></script>
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({ paymentToken: payment.paymentToken })
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.2/dist/cinetpay-seamless.umd.cjs"></script>
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.net/checkout/abc...' }
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 (`api.cinetpay.net`) | Développement et tests |
624
- | `sk_live_...` | Production (`api.cinetpay.co`) | Transactions réelles |
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
@@ -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
@@ -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;AAKD;;;;;;;;;;;GAWG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,UAAU,CAA6C;IAC/D,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,oBAAoB,CAAK;IAEjC,OAAO,CAAC,eAAe,CAAC,CAAY;IACpC,OAAO,CAAC,wBAAwB,CAAC,CAAiC;IAClE,OAAO,CAAC,uBAAuB,CAAC,CAAiC;IACjE,OAAO,CAAC,wBAAwB,CAAC,CAAiC;IAClE,OAAO,CAAC,eAAe,CAAC,CAAoC;IAC5D,OAAO,CAAC,eAAe,CAAC,CAA+B;IACvD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAc;gBAEjB,OAAO,EAAE,eAAe;IAWpC;;;;OAIG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAkB9B;;OAEG;IACH,KAAK,IAAI,IAAI;IAkCb,OAAO,CAAC,MAAM,CAAC,QAAQ;IAIvB,OAAO,CAAC,MAAM,CAAC,YAAY;IAU3B,OAAO,CAAC,MAAM,CAAC,SAAS;IASxB,OAAO,CAAC,MAAM,CAAC,QAAQ;IAKvB,OAAO,CAAC,MAAM,CAAC,QAAQ;IASvB,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAU/B,OAAO,CAAC,MAAM,CAAC,eAAe;IA+C9B,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAyBnC,iEAAiE;IACjE,OAAO,CAAC,gBAAgB;IAgCxB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,aAAa;IAqCrB;;OAEG;IACH,OAAO,CAAC,SAAS;IA6BjB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,WAAW;IAOnB,wDAAwD;IACxD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAOtC;IAED,4DAA4D;IAC5D,OAAO,CAAC,iBAAiB;CAsC1B"}
1
+ {"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"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"}