@paystack/checkout-js 1.32.0 → 1.33.0-dev.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 CHANGED
@@ -4,54 +4,1349 @@ This is the client-side JS library for billing customers with Paystack. It makes
4
4
 
5
5
  It is used in Checkout, Paystack's proprietary checkout form.
6
6
 
7
- # Documentation
7
+ # Installation
8
8
 
9
- See the [Checkout JS public docs](./PUBLIC_DOCS.md) and [development docs](./DEV_DOCS.md)
9
+ ```bash
10
+ npm install @paystack/checkout-js --save
11
+ ```
10
12
 
11
- # Installation
13
+ or just grab the js file and add it to your html directly
14
+
15
+ ```html
16
+ <script src="https://js.paystack.co/v1/checkout.js"></script>
17
+ ```
18
+
19
+ # Quick Start
20
+
21
+ ## Usage via NPM/Yarn
22
+
23
+ ```js
24
+ import { Transaction } from 'checkout-js';
25
+
26
+ async function chargeCustomerCard() {
27
+ const transactionData = {
28
+ email: 'test@paystack.com', // replace this with customer email
29
+ amount: 100,
30
+ key: 'your_paystack_public_key',
31
+ };
32
+
33
+ try {
34
+ const transaction = await Transaction.request(transactionData);
35
+ await transaction.setCard({
36
+ number: '4084084084084081',
37
+ cvv: '408',
38
+ month: '01',
39
+ year: '20',
40
+ pin: '1234',
41
+ });
42
+ // charge resolves to a ChargeResponse
43
+ const chargeResponse = await transaction.chargeCard();
44
+ if (chargeResponse.status === 'success') {
45
+ console.log('card was charged successfully!');
46
+ }
47
+ } catch (error) {
48
+ console.log(error);
49
+ }
50
+ }
51
+
52
+ chargeCustomerCard();
53
+ ```
54
+
55
+ ## Usage via inline script
56
+
57
+ Add the Checkout JS script to your `HTML` document
58
+
59
+ ```html
60
+ <script src="https://js.paystack.co/v1/checkout.js"></script>
61
+ ```
62
+
63
+ And then access the available classes in your JavaScript file;
64
+
65
+ ```js
66
+ var transactionData = {
67
+ email: 'test@paystack.com', // replace this with customer email
68
+ amount: 100,
69
+ key: 'your_paystack_public_key',
70
+ };
71
+
72
+ var customerCardDetails = {
73
+ number: '4084084084084081',
74
+ cvv: '408',
75
+ month: '01',
76
+ year: '20',
77
+ pin: '1234',
78
+ };
79
+
80
+ function chargeCustomerCard() {
81
+ Transaction.request(transactionData).then(function(createdTransaction) {
82
+ createdTransaction.setCard(customerCardDetails);
83
+ return createdTransaction.chargeCard();
84
+ }).then(function(chargeResponse) {
85
+ if (chargeResponse.status === 'success') {
86
+ console.log('card was charged successfully!');
87
+ }
88
+ }).catch(function(error) {
89
+ console.log(error);
90
+ });
91
+ }
92
+
93
+ chargeCustomerCard();
94
+ ```
95
+
96
+ # Getting Started
97
+
98
+ These are the classes exported by `checkout js`:
99
+
100
+ 1. **Transaction**
101
+ 2. **Toolkit**
102
+ 3. **Card**
103
+ 4. **BankAccount**
104
+
105
+ The `Transaction` class is the most important class in Checkout JS. All the functions required for initializing a transaction, updating it, and charging a customer using the supported payment channels are bound to this class.
106
+
107
+ For example, charging a customer's card could entail the following steps;
108
+
109
+ 1. Request a new transaction
110
+ 2. Collect and validate card details from customer
111
+ 3. Save the card to the transaction
112
+ 4. Charge the card
113
+ 5. Tada!
114
+
115
+ ## Table of contents
116
+
117
+ This is a grouping of the methods exported by the `Transaction` class:
118
+
119
+ - Initializing a transaction
120
+ - `Transaction.request`
121
+ - `Transaction.requestWithAccessCode`
122
+ - `Transaction.validate`
123
+ - Charging a card
124
+ - `Transaction.setCard`
125
+ - `Transaction.chargeCard`
126
+ - `Transaction.authenticateCard`
127
+ - `Transaction.listenFor3DSCharge`
128
+ - Charging a Bank account
129
+ - `Transaction.setBankAccount`
130
+ - `Transaction.chargeBankAccount`
131
+ - `Transaction.registerBankAccount`
132
+ - `Transaction.authenticateBankAccount`
133
+ - `Transaction.getIbankUrl`
134
+ - `Transaction.listenForIbankCharge`
135
+ - `Transaction.chargeDigitalBankMandate`
136
+ - `Transaction.listenForDigitalBankMandateCharge`
137
+ - Charging via USSD
138
+ - `Transaction.getUSSDShortcode`
139
+ - `Transaction.listenForUSSDCharge`
140
+ - Charging via QR
141
+ - `Transaction.getQRCode`
142
+ - `Transaction.listenForQRCharge`
143
+ - Charging via Mobile Money (Only available for GHS transactions)
144
+ - `Transaction.chargeMobileMoney`
145
+ - `Transaction.listenForMobileMoneyCharge`
146
+ - Transaction analytics
147
+ - `Transaction.initializeLog`
148
+ - `Transaction.getTimeSpent`
149
+ - `Transaction.logPageOpen`
150
+ - `Transaction.logChannelSwitch`
151
+ - `Transaction.logPageClose`
152
+ - `Transaction.logInput`
153
+ - `Transaction.logValidationErrors`
154
+ - `Transaction.logAuthWindowOpen`
155
+ - `Transaction.logAuthWindowClose`
156
+ - `Transaction.logAPIResponse`
157
+ - `Transaction.logAttempt`
158
+ - `Transaction.saveReferrer`
159
+ - Check transaction status
160
+ - `Transaction.requery`
161
+
162
+ Utility classes:
163
+
164
+ - Toolkit: This contains utilities that you can use during a transaction
165
+ - Toolkit.contents
166
+ - Toolkit.fetch
167
+ - Card: Use this to validate your customer’s cards
168
+ - Card.validate
169
+ - Card.isNumberValid
170
+ - Card.getType
171
+ - Card.isCvvValid
172
+ - Card.isExpiryValid
173
+ - BankAccount: Use this to validate your customer’s bank accounts
174
+ - BankAccount.validate
175
+ - BankAccount.isValidNubanAccount
176
+
177
+ ## # Initializing a transaction
178
+
179
+ ## Transaction.request(transactionData)
180
+
181
+ This method returns a new transaction instance. The instance returned is what is used to complete the customer’s payment.
182
+
183
+ Base parameters for this method are:
184
+
185
+ | Parameter | Required | Type | Description |
186
+ | ----------------------------- | --------------| --------------------| ---------------------------------------------------- |
187
+ | email | True | `String` | The customer's email address |
188
+ | amount | True | `String` | `Number` | Amount in smallest currency unit (kobo/pesewa/cents). Ignored if creating a subscription |
189
+ | key | True | `String` | Your Paystack public key. You can find this on your dashboard in Settings > API Keys & Webhooks |
190
+ | reference | False | `String` | Unique case sensitive transaction reference. Only `-`,`.`, `=` and alphanumeric characters allowed |
191
+ | channels | False | `Array` | An array of payment channels to use. Defaults to all available channels on an integration |
192
+ | currency | False | `String` | The currency of the transaction. Default is `NGN`
193
+ | amount | True | `String` | Amount in smallest currency unit (kobo/pesewa/cents). Ignored if creating a subscription |
194
+ | firstName | False | `String` | The first name of the customer |
195
+ | lastName | False | `String` | The last name of the customer |
196
+ | phone | False | `String` | The phone number of the customer |
197
+ | metadata | False | `Object` | A valid object of extra information that you want to be saved to the transaction. |
198
+ | cusotmer_code | False | `String` | The customer code for the customer on Paystack |
199
+
200
+
201
+ Other optional parameters for [Split Payments](https://paystack.com/docs/payments/split-payments/) and [Recurring Charges](https://paystack.com/docs/payments/recurring-charges/)
202
+
203
+ | Parameter | Required | Type | Description |
204
+ | ----------------------------- | --------------| --------------------| ---------------------------------------------------- |
205
+ | plan | False | `String` | If transaction is to create a subscription to a predefined plan, provide plan code here |
206
+ | subaccountCode | False | `String` | A valid Paystack subaccount code e.g. `ACCT_8f4s1eq7ml6rlzj` |
207
+ | split_code | False | `String` | A valid Paystack split code e.g. `SPL_qQsdYLXddd` |
208
+ | bearer | False | `Number` | Who bears Paystack charges? `account` or `subaccount` (defaults to `account`). |
209
+ | transactionCharge | False | `String` | A flat fee (in kobo) to charge the subaccount for this transaction. This overrides the split percentage set when the subaccount was created. |
210
+ | planInterval | False | `String` | Interval for the plan. Valid intervals are `hourly`, `daily`, `weekly`, `monthly`, `annually` |
211
+ | subscriptionStartDate | False | `String` | The start date for the subscription (after the first charge) |
212
+ | planCode | False | `String` | A valid Paystack plan code e.g. `PLN_cujsmvoyq2209ws` |
213
+ | subscriptionCount | False | `Number` | The number of subscriptions to create for this plan |
214
+ | subscriptionLimit | False | `Number` | The number of times to charge for this subscription |
215
+
216
+
217
+ **Example**
218
+
219
+ ```ts
220
+ import { Transaction } from 'checkout-js';
221
+
222
+ interface Parameters {
223
+ key: string;
224
+ email: string;
225
+ amount: number;
226
+ ref?: string;
227
+ customer_code?: string;
228
+ plan?: string;
229
+ transaction_charge?: string;
230
+ bearer?: string;
231
+ channels?: string[];
232
+ currency?: string;
233
+ firstname?: string;
234
+ lastname?: string;
235
+ phone?: string;
236
+ remark?: string;
237
+ payment_page?: string;
238
+ payment_request?: string;
239
+ quantity?: string;
240
+ coupon?: string;
241
+ start_date?: string;
242
+ interval?: string;
243
+ invoice_limit?: number;
244
+ subaccount?: string;
245
+ hash?: string;
246
+ metadata?: string;
247
+ }
248
+
249
+ const transactionData: Parameters = {
250
+ email: 'test@paystack.com', // replace this with the customer's email
251
+ amount: 100,
252
+ key: 'your_paystack_public_key',
253
+ };
254
+
255
+ // returns an instance of the Transaction class
256
+ const transaction = await Transaction.request(transactionData);
257
+ ```
258
+
259
+ ## Transaction.requestWithAccessCode(accessCode)
260
+
261
+ While the `Transaction.request` method lets you create transactions from scratch in the browser, `Transaction.requestWithAccessCode` lets you complete a transaction [initialized from your server](https://paystack.com/docs/api/#transaction-initialize) on the frontend. This method can be more secure and it, ensures no new transactions are created on the frontend.
262
+
263
+ **Example**
264
+
265
+ ```js
266
+ import { Transaction } from 'checkout-js';
267
+
268
+ const accessCode = 'ltxo947mrhrbz6z'; // Access code for transaction created on your server using the Paystack API
269
+
270
+ // returns an instance of the transaction class
271
+ const transaction = await Transaction.requestWithAccessCode(accessCode);
272
+ ```
273
+
274
+ ## Transaction.validate(transaction)
275
+
276
+ This is a utility method used to validate the parameters for requesting a new transaction.
277
+
278
+ **Example**
279
+
280
+ ```js
281
+ import { Transaction } from 'checkout-js';
282
+
283
+ const transactionData = {
284
+ email: 'test@paystack.com', // replace this with the customer's email
285
+ amount: 100,
286
+ key: 'your_paystack_public_key',
287
+ };
288
+
289
+ try {
290
+ Transaction.validate(transactionData); // throws validation errors
291
+ } catch (error) {
292
+ // handle validation errors thrown by Transaction.validate
293
+ }
294
+ ```
295
+
296
+ NB: `Transaction.request` calls this validation function implicitly, so you do not need to call it yourself except if you need to handle parameter validation separately.
297
+
298
+ ## Attempting a Charge
299
+
300
+ For each attempt to charge a payment instrument, `checkout-js` responds with a `ChargeResponse`. This `ChargeResponse` has the following interface depending on the kind of response received:
301
+
302
+ ```ts
303
+ interface ChargeResponse {
304
+ status: string;
305
+ message: string;
306
+ data?: Object
307
+ errors?: Array;
308
+ }
309
+ ```
310
+ reference will be made to this `ChargeResponse` throughout the rest of the documentation. More details [here](#charge-responses)
311
+
312
+ ## # Charging a card
313
+
314
+ Charging a card includes the following steps:
315
+
316
+ 1. Set the card on the transaction
317
+ 2. Charge the card
318
+ 3. If required, authenticate the charge
319
+
320
+ ## transaction.setCard({ number, cvv, month, year, pin })
321
+
322
+ This is an instance method on transactions to set a card to be charged for the transaction. With this, you can
323
+
324
+ **Example**
325
+
326
+ ```js
327
+ import { Transaction } from 'checkout-js';
328
+
329
+ const transactionData = { ... };
330
+ const transaction = await Transaction.request(transactionData)
331
+
332
+ interface Card {
333
+ number: string;
334
+ cvv: string;
335
+ month: string;
336
+ year: string;
337
+ }
338
+
339
+ /**
340
+ * Card number can have varying lengths, typically from 16 to 19 characters
341
+ * CVV can be 3 or 4 characters in length
342
+ */
343
+
344
+ const newCard = {
345
+ number: '4084084084084081',
346
+ cvv: '408',
347
+ month: '01',
348
+ year: '20'
349
+ }
350
+
351
+ await transaction.setCard(newCard);
352
+ ```
353
+
354
+ ## transaction.chargeCard()
355
+
356
+ After setting a card on a transaction, you can now attempt to charge it by calling `chargeCard()`
357
+
358
+ **Example**
359
+
360
+ ```jsx
361
+ import { Transaction } from 'checkout-js';
362
+ ...
363
+ const chargeResponse = await transaction.chargeCard();
364
+ ```
365
+ ## transaction.authenticateCard(phoneOrOtp)
366
+
367
+ If `chargeResponse` returns a response with `status ===` `'auth'` you can request the customer's phone number or the OTP sent to their phone number. `data.auth` on the `chargeResponse` states what type of authentication is required.
368
+
369
+ **Example**
370
+
371
+ ```js
372
+ import { Transaction } from 'checkout-js';
373
+
374
+ const newCard = {
375
+ number: '4084084084084081',
376
+ cvv: '408',
377
+ month: '01',
378
+ year: '20'
379
+ }
380
+
381
+ await transaction.setCard(newCard);
382
+
383
+ ...
384
+
385
+ let chargeResponse = await transaction.chargeCard();
386
+
387
+ if (chargeResponse.status === 'auth') {
388
+ let authResponse;
389
+ if (chargeResponse.data.auth === 'pin') {
390
+ // request customers phone number
391
+ newCard.pin = '1234'
392
+ await transaction.setCard(newCard)
393
+ chargeResponse = await transaction.chargeChard(); // attempt to charge the card again with the pin included
394
+ }
395
+ if (chargeResponse.data.auth === 'otp') {
396
+ // request otp sent to customer
397
+ const otp = '123456';
398
+ authResponse = await transaction.authenticateCard(otp); // returns a ChargeResponse
399
+ }
400
+ }
401
+ ```
402
+
403
+ ## transaction.listenFor3DSCharge(callback)
404
+
405
+ Use this function to wait for the response from the 3DS authentication from the customer. You’d need this if the charge response returns an auth type of `3ds`:
406
+
407
+ ```js
408
+ const chargeResponse = {
409
+ status: 'auth',
410
+ message: 'url-for-3ds-authentication',
411
+ data: {
412
+ auth: '3ds',
413
+ },
414
+ }
415
+ ```
416
+
417
+ The callback function will be called with the response and a function to unsubscribe from pusher.
418
+
419
+ **Example**
420
+
421
+ ```js
422
+ const chargeResponse = await transaction.chargeCard();
423
+ ...
424
+ function handleResponse(response, unsubscribe) {}
425
+
426
+ if (chargeResponse.status === 'auth') {
427
+ if (chargeResponse.data.auth === '3DS') {
428
+ // redirect customer to 3ds url in `chargeResponse.message`
429
+ // listen for response from 3ds authentication
430
+ listenFor3DSCharge(handleResponse);
431
+ }
432
+ }
433
+ ```
434
+
435
+ ## # Charging a bank account
436
+
437
+ To charge a bank account, you need to make sure it’s from one of the banks that Paystack supports bank payments from. To get a list of supported banks, call `Toolkit.fetch('banks')`.
438
+
439
+ ```js
440
+ import { Toolkit } from 'checkout-js';
441
+
442
+ const banks = await Toolkit.fetch('banks');
443
+
444
+ // This will return an array of available banks like;
445
+ [
446
+ {
447
+ "name": "Access Bank",
448
+ "slug": "access-bank",
449
+ "code": "044",
450
+ "longcode": "044150149",
451
+ "gateway": "emandate",
452
+ "pay_with_bank": true,
453
+ "active": true,
454
+ "is_deleted": null,
455
+ "country": "Nigeria",
456
+ "currency": "NGN",
457
+ "type": "nuban",
458
+ "id": 1,
459
+ "createdAt": "2016-07-14T10:04:29.000Z",
460
+ "updatedAt": "2016-07-14T10:04:29.000Z"
461
+ },
462
+ {
463
+ "name": "Guaranty Trust Bank",
464
+ "slug": "guaranty-trust-bank",
465
+ "code": "058",
466
+ "longcode": "058152036",
467
+ "gateway": "ibank",
468
+ "pay_with_bank": true,
469
+ "active": true,
470
+ "is_deleted": null,
471
+ "country": "Nigeria",
472
+ "currency": "NGN",
473
+ "type": "nuban",
474
+ "id": 9,
475
+ "createdAt": "2016-07-14T10:04:29.000Z",
476
+ "updatedAt": "2016-07-14T10:04:29.000Z"
477
+ },
478
+ ...
479
+ ]
480
+ ```
481
+ `checkout-js` currently handles three kinds of bank payments determined by the `gateway` on the bank object. The gateways are `emandate`, `ibank` and `digitalbankmandate`.
482
+
483
+ - `emandate` payments follow a “registration → verification → authentication” payment flow.
484
+ - `ibank` payments require that your customers be redirected to their internet banking portal for authentication, after which they will be redirected back to your payment flow.
485
+ - `digitalbankmandate` payments need to be authenticated with a phone number and a `payId`
486
+
487
+ Most banks use the `emandate` flow while Guaranty Trust Bank and First Bank use the `ibank` flow and Kuda Bank uses the `digitalbankmandate` flow.
488
+
489
+ ## # Ibank Flow
490
+
491
+ ## transaction.getIbankUrl(bank)
492
+
493
+ This method is used to fetch the internet banking url through which a customer can complete a transaction.
494
+
495
+ **Example**
496
+
497
+ ```js
498
+ ...
499
+
500
+ const bank = {
501
+ ...
502
+ bankId: 9,
503
+ name: 'Guaranty Trust Bank',
504
+ gateway: 'ibank',
505
+ ...
506
+ };
507
+
508
+ await transaction.getIbankUrl(bank);
509
+ ```
510
+
511
+ ## transaction.listenForIbankCharge(callback)
512
+
513
+ When the customer has been redirected to their internet banking portal to complete the transaction, you can wait for the final result using this function, passing a callback that will be called with the response and a function to unsubscribe from `pusher`.
514
+
515
+ **Example**
516
+
517
+ ```js
518
+ ...
519
+ // fetch internet banking URL
520
+ const url = await transaction.getIbankUrl(bank);
521
+
522
+ // redirect customer to internet banking portal
523
+ ...
524
+ // listen for response from internet banking portal
525
+ function handleResponse(response, unsubscribe) {}
526
+
527
+ transaction.listenForIbankCharge(handleResponse);
528
+ // resolves to ChargeResponse
529
+ ```
530
+
531
+ ## # DigitalBankMandate Flow (Kuda Bank)
532
+
533
+ ## transaction.chargeDigitalBankMandate({ phoneNumber, payId, bankId })
534
+
535
+ This function makes an attempt to charge a kuda bank account using the customer's phone number and a 6-digit `payID` which the customer will generate from their kuda bank app
536
+
537
+ **Example**
538
+
539
+ ```js
540
+ ...
541
+
542
+ const bank = {
543
+ ...
544
+ id: 67,
545
+ name: 'Kuda Bank',
546
+ gateway: 'digitalbankmandate',
547
+ ...
548
+ };
549
+ const phoneNumber = '07033773883'
550
+ const payId = 288299;
551
+ const params = {
552
+ phoneNumber,
553
+ bankId: bank.id,
554
+ payId
555
+ }
556
+
557
+ // resolves to ChargeResponse
558
+ await transaction.chargeDigitalBankMandate(params);
559
+ ```
560
+
561
+ ## transaction.listenForDigitalBankMandateCharge(callback, pusherChannel)
562
+
563
+ After calling the `chargeDigitalBankMandate` function, you need to call the `listenForDigitalBankMandateCharge` function to be notified of the status of the payment. This function accepts a `callback` which will be called with the response as well as a function to unsubscribe from pusher — and a `pusher_channel`, a property on `data` gotten from the response to `chargeDigitalBankMandate`.
564
+
565
+ ```js
566
+ const {
567
+ status,
568
+ message,
569
+ data
570
+ } = await transaction.chargeDigitalBankMandate(params);
571
+
572
+ function handleResponse(response, unsubscribe) {}
573
+
574
+ transaction.listenForDigitalBankMandateCharge(handleResponse, data.pusher_channel)
575
+ ```
576
+
577
+ ## # Emandate flow
578
+
579
+ ## transaction.setBankAccount({ bankId, accountType, accountNumber })
580
+
581
+ Sets the bank account the customer is going to use to complete the transaction.
582
+
583
+ **Example**
584
+
585
+ ```js
586
+ import { Transaction, Toolkit } from 'checkout-js';
587
+ ...
588
+ const bank = {
589
+ ...
590
+ id: 1,
591
+ name: 'Access Bank',
592
+ gateway: 'emandate',
593
+ type: 'nuban',
594
+ ...
595
+ };
596
+
597
+ const bankAccount = {
598
+ bankId: bank.id,
599
+ accountType: bank.type,
600
+ accountNumber: '1234567890', // customer account number
601
+ }
602
+
603
+ await transaction.setBankAccount(bankAccount);
604
+ ```
605
+
606
+ ## transaction.chargeBankAccount()
607
+
608
+ This method is responsible for charging the bank account that has been set on the transaction using `transaction.setBankAccount()`.
609
+
610
+ **Example**
611
+
612
+ ```js
613
+ const chargeResponse = await transaction.chargeBankAccount();
614
+ // resolves to a ChargeResponse
615
+ ```
616
+
617
+ ## transaction.registerBankAccount(birthday)
618
+
619
+ Use this to register new customer bank accounts for e-mandate so they can be charged. You should do this if you get a response requiring birthday registration after attempting to charge a bank account with `transaction.chargeBankAccount`.
620
+
621
+ ```js
622
+ {
623
+ status: 'auth',
624
+ message: 'This customer needs to register their account',
625
+ data: {
626
+ auth: 'birthday'
627
+ }
628
+ }
629
+ ```
630
+
631
+ **Example**
632
+
633
+ ```jsx
634
+ ...
635
+ await transaction.setbankAccount({...});
636
+
637
+ const chargeResponse = await transaction.chargeBankAccount();
638
+
639
+ if (chargeResponse.status === 'auth' && chargeResponse.data.auth === 'birthday') {
640
+ // pass the customer's birthday in the format 'dd-MM-yyyy'
641
+ const authResponse = await transaction.registerBankAccount('12-05-1980');
642
+ // resolves to a ChargeResponse
643
+ }
644
+ ```
645
+
646
+ ## transaction.authenticateBankAccount(otp)
647
+
648
+ This method is used to authenticate bank transactions using an `otp`.
649
+
650
+ **Example**
651
+
652
+ ```jsx
653
+ ...
654
+ await transaction.setbankAccount({...});
655
+ const chargeResponse = await transaction.chargeBankAccount();
656
+
657
+ if (chargeResponse.status === 'auth' && chargeResponse.data.auth === 'otp') {
658
+ const authResponse = await transaction.authenticateBankAccount(123456);
659
+ // resolves to a ChargeResponse
660
+ }
661
+ ```
662
+
663
+ ## # Charging via USSD
664
+
665
+ The only available USSD channels for now are GTB’s 737, Sterling's 822, Zenith's 966 and UBA's 919
666
+
667
+ ## transaction.getUSSDShortcode({ channel })
668
+
669
+ Use this to generate a USSD shortcode
670
+
671
+ **Example**
672
+
673
+ ```jsx
674
+ // initialize transaction
675
+ const response = await transaction.getUSSDShortcode({ channel: '919' });
676
+
677
+ // returns a response in the format
678
+ {
679
+ "status": true,
680
+ "message": "Offline Reference Generated Successfully",
681
+ "data": {
682
+ "reference": "887462",
683
+ "channel": "api_919_shortcode_00000",
684
+ "code": "*919*00*1*0000#"
685
+ }
686
+ }
687
+ ```
688
+
689
+ Present the code in `data.code` to your customers for payment.
690
+
691
+ ## transaction.listenForUSSDCharge({ channel }, callback)
692
+
693
+ Listen for when the customer completes the transaction via USSD. This function accepts a callback function to be called with the response and a function to unsubscribe from pusher.
694
+
695
+ **Example**
696
+
697
+ ```js
698
+ // initialize transaction
699
+ ...
700
+ function handleResponse(response, unsubscribe) {}
701
+
702
+ transaction.listenForUSSDCharge({ channel: '919' }, handleResponse);
703
+
704
+ // return a ChargeResponse
705
+ ```
706
+
707
+ ## # Charging via QR
708
+
709
+ The available QR channels are `visa` in Nigeria and `MPASS_OLTI` in South Africa .
710
+
711
+ ## transaction.getQRCode({ channel })
712
+
713
+ Use this to generate a QR code
714
+
715
+ **Example**
716
+
717
+ ```js
718
+ // initialize transaction
719
+ const response = await transaction.getQRCode({ channel: 'visa' });
720
+
721
+ // returns a response in the format
722
+ {
723
+ "status": true,
724
+ "message": "QR successfully generated",
725
+ "data": {
726
+ "errors": false,
727
+ "url": "qr code image",
728
+ "qr_code": "qr code",
729
+ "status": "success",
730
+ "channel": "qr channel"
731
+ }
732
+ }
733
+ ```
734
+ Display the QR code image in `data.url` for your customers to scan.
735
+
736
+
737
+ ## transaction.listenForQRCharge({ channel }, callback)
738
+
739
+ Listen for when the customer has completed the transaction by scanning the QR code. This function accepts an object containing the QR `channel` and a callback function to be called with the response and a function to unsubscribe from pusher.
740
+
741
+ **Example**
742
+
743
+ ```js
744
+ // initialize transaction
745
+ ...
746
+ function handleResponse(response, unsubscribe) {}
747
+
748
+ transaction.listenForQRCharge({ channel: 'visa' }, handleResponse);
749
+
750
+ // return a ChargeResponse
751
+ ```
752
+
753
+ ## # Charging a mobile money wallet
754
+
755
+ ## transaction.chargeMobileMoney({ phoneNumber, provider, voucher?, registrationToken? })
756
+
757
+ This function is used to attempt a mobile money charge with a `phoneNumber`, a `provider` and optionally, a `voucher` and a `registrationToken`
758
+
759
+ **Available providers**
760
+
761
+ ```js
762
+ [
763
+ { name: 'MTN', value: 'MTN', prefix: ['+23354', '+23355', '+23324'] },
764
+ { name: 'TGO',
765
+ value: 'Airtel/Tigo',
766
+ prefix: ['+23327', '+23357', '+23326', '+23356']
767
+ },
768
+ { name: 'VOD', value: 'Vodafone', prefix: ['+23320', '+23350'] },
769
+ ]
770
+ ```
771
+
772
+ **Example**
773
+
774
+ ```js
775
+ // initialize transaction
776
+ ...
777
+ const response = await transaction.chargeMobileMoney(
778
+ { phoneNumber: '05012345678', provider: 'VOD' }
779
+ );
780
+
781
+ // returns a response in the format
782
+ {
783
+ "status": true,
784
+ "message": "Charge attempted",
785
+ "data": {
786
+ "transaction": 2,
787
+ "phone": "05012345678",
788
+ "device": "96640369ca64b60d3dcf",
789
+ "provider": "VOD",
790
+ "channel_name": "MOBILE_MONEY_2",
791
+ "display": {
792
+ "type": "voucher",
793
+ "message": "Please enter your voucher code to complete this payment"
794
+ }
795
+ }
796
+ }
797
+
798
+ /**
799
+ * Depending on what the `display.type` is from the previous response, you might
800
+ * need to collect additional information to complete the payment
801
+ */
802
+
803
+ /**
804
+ * If you get a `display.type === voucher`, collect a voucher from the customer
805
+ * and call chargeMobileMoney again, this time including the voucher as one of the
806
+ * arguments
807
+ */
808
+ const response = await transaction.chargeMobileMoney(
809
+ {
810
+ phoneNumber: '05012345678',
811
+ provider: 'VOD',
812
+ voucher: 'voucher-code'
813
+ }
814
+ );
815
+
816
+ /**
817
+ * If you get a `display.type === registration_token`, collect a registrationToken from the customer
818
+ * and call chargeMobileMoney again, this time including the registrationToken
819
+ * as one of the arguments
820
+ */
821
+ const response = await transaction.chargeMobileMoney(
822
+ {
823
+ phoneNumber: '05012345678',
824
+ provider: 'VOD',
825
+ registrationToken: 'token'
826
+ }
827
+ );
828
+
829
+ /**
830
+ * The `registrationToken` phase should be the last stage in the process after
831
+ * which you should start listening for a response as stated in the next section
832
+ */
833
+
834
+ ```
835
+
836
+ ## transaction.listenForMobileMoneyCharge(callback)
837
+
838
+ Listen for when the customer has completed the payment, passing a callback function to be called with the response and a function to unsubscribe from pusher.
839
+
840
+ **Example**
841
+
842
+ ```js
843
+ // initialize transaction
844
+ ...
845
+ function handleResponse(response, unsubscribe) {}
846
+
847
+ transaction.listenForQRCharge(handleResponse);
848
+
849
+ // return a ChargeResponse
850
+ ```
851
+
852
+ ## # Transaction Analytics
853
+
854
+ These are a group of functions that help to populate the transaction timeline which is attached to every transaction and can be viewed on the Paystack dashboard.
855
+
856
+ Checkout JS implicitly handles some analytics like logging inputs, bank selection, errors, authentications and transaction success.
857
+
858
+ ## transaction.initializeLog()
859
+
860
+ This function initializes transaction logging.
861
+
862
+ **Example**
863
+
864
+ ```js
865
+ // initialize transaction
866
+ ...
867
+ transaction.initializeLog();
868
+ ```
869
+ ## transaction.getTimeSpent()
870
+
871
+ This function returns the amount of time since the transaction was requested.
872
+
873
+ **Example**
874
+
875
+ ```js
876
+ // initialize transaction
877
+ ...
878
+ transaction.getTimeSpent();
879
+ ```
880
+
881
+ ## transaction.logPageOpen()
882
+
883
+ Logs the time when the transaction page was opened
884
+
885
+ **Example**
886
+
887
+ ```js
888
+ // initialize transaction
889
+ ...
890
+ transaction.logPageOpen();
891
+ ```
892
+
893
+ ## transaction.logAuthWindowOpen()
894
+
895
+ Logs the time when the transaction authentication window was opened
896
+
897
+ **Example**
898
+
899
+ ```js
900
+ // initialize transaction
901
+ ...
902
+ transaction.logAuthWindowOpen();
903
+ ```
904
+
905
+ ## transaction.logAuthWindowClose()
906
+
907
+ Logs the time when the transaction authentication window was closed
908
+
909
+ **Example**
910
+
911
+ ```js
912
+ // initialize transaction
913
+ ...
914
+ transaction.logAuthWindowClose();
915
+ ```
916
+
917
+ ## transaction.logChannelSwitch(channelName)
918
+
919
+ Log switch to a channel with name `channelName`
920
+
921
+ **Example**
922
+
923
+ ```js
924
+ // initialize transaction
925
+ ...
926
+ transaction.logChannelSwitch('card');
927
+ ```
928
+ ## transaction.logValidationErrors(inputs)
929
+
930
+ Log validation errors on an array of `inputs`
931
+
932
+ **Example**
933
+
934
+ ```js
935
+ // initialize transaction
936
+ ...
937
+ transaction.logValidationErrors(['card number']);
938
+ ```
939
+
940
+ ## transaction.logInput(fieldName)
941
+
942
+ Log when a customer filled a certain input field
943
+
944
+ **Example**
945
+
946
+ ```js
947
+ // initialize transaction
948
+ ...
949
+ transaction.logInput('card number');
950
+ ```
951
+
952
+ ## transaction.logAttempt(channel)
953
+
954
+ Log when a customer makes an attempt on a channel
955
+
956
+ **Example**
957
+
958
+ ```js
959
+ // initialize transaction
960
+ ...
961
+ transaction.logAttempt('card');
962
+ ```
963
+
964
+ ## transaction.logBankSelect(bankName)
965
+
966
+ Log what bank the user selected while trying to pay with a bank account
967
+
968
+ **Example**
969
+
970
+ ```js
971
+ // initialize transaction
972
+ ...
973
+ transaction.logBankSelect('Access Bank');
974
+ ```
975
+
976
+ ## transaction.logError(message)
977
+
978
+ Log when a customer encounters an error while attempting to pay
979
+
980
+ **Example**
981
+
982
+ ```js
983
+ // initialize transaction
984
+ try {
985
+ // initialize transaction
986
+ // attempt to charge transaction
987
+ } catch(error) {
988
+ transaction.logError(error.message);
989
+ }
990
+ ```
991
+
992
+ ## transaction.logAuth(authType)
993
+
994
+ Log when a customer needs to be authenticated.
995
+
996
+ **Example**
997
+
998
+ ```js
999
+ // attempt a charge and get a response
1000
+
1001
+ if (response.status === 'auth') {
1002
+ // log the authentication required
1003
+ transaction.logAuth(response.data.auth);
1004
+ }
1005
+ ```
1006
+
1007
+ ## transaction.logSuccess()
1008
+
1009
+ Log when a customer has successfully completed a transaction
1010
+
1011
+ **Example**
1012
+
1013
+ ```js
1014
+ // attempt a charge and get a response
1015
+
1016
+ if (response.status === 'success') {
1017
+ transaction.logSuccess();
1018
+ }
1019
+ ```
1020
+ ## transaction.logPending()
1021
+
1022
+ Log when a transaction status is `pending`
1023
+
1024
+ **Example**
1025
+
1026
+ ```js
1027
+ // attempt a charge and get a response
1028
+
1029
+ if (response.status === 'pending') {
1030
+ transaction.logPending();
1031
+ }
1032
+ ```
1033
+
1034
+ ## transaction.logPageClose()
1035
+
1036
+ Log when a customer closes a payment page
1037
+
1038
+ **Example**
1039
+
1040
+ ```js
1041
+ // listen for a page close event
1042
+ transaction.logPageClose();
1043
+ ```
1044
+
1045
+ ## transaction.saveReferrer(referrerUrl)
1046
+
1047
+ Log the page a payment page was opened from
1048
+
1049
+ **Example**
1050
+
1051
+ ```js
1052
+ // initialize transaction
1053
+ transaction.saveReferrer(document.referrer)
1054
+ ```
1055
+
1056
+ ## # Checking transaction status
1057
+
1058
+ ## transaction.requery()
1059
+
1060
+ Used to check the status of a transaction
1061
+
1062
+ **Example**
1063
+
1064
+ ```js
1065
+ await transaction.requery();
1066
+ // responds with a ChargeResponse
1067
+ ```
1068
+
1069
+ ## # Toolkit
1070
+
1071
+ Houses various utility functions like error resolutions, supported banks, and celebrations.
1072
+
1073
+ ## Toolkit.contents
1074
+
1075
+ Returns an array of possible parameters to be used with the `Toolkit.fetch()` and their corresponding usage
1076
+
1077
+ **Example**
1078
+
1079
+ ```js
1080
+ import { Toolkit } from 'checkout-js';
1081
+
1082
+ Toolkit.contents();
1083
+
1084
+ // output
1085
+ [{
1086
+ key: 'banks',
1087
+ usage: 'Returns a list of banks that can be used to complete checkout'
1088
+ }, {
1089
+ key: 'celebrations',
1090
+ usage: 'Returns a list of upcoming celebrations'
1091
+ }, {
1092
+ key: 'resolutions',
1093
+ usage: 'Returns a list of known errors, mapped to their suggested resolutions'
1094
+ }]
1095
+ ```
1096
+
1097
+ ## Toolkit.fetch(option)
1098
+
1099
+ A static method on the `Toolkit` class that operates on valid `keys` from `Toolkit.contents()` and returns data consistent with the `usage` of each `key`.
1100
+
1101
+ **Example**
1102
+
1103
+ ```js
1104
+ import { Toolkit } from 'checkout-js'
1105
+ ...
1106
+ const celebrations = await Toolkit.fetch('celebrations')
1107
+
1108
+ // celebrations.data returns a list of National celebrations and their dates:
1109
+ {
1110
+ "2018-06-07": "Happy Valentine's Day ❤️",
1111
+ "2018-03-06": "Happy Independence Day 🇬🇭",
1112
+ "2018-04-01": "Happy Easter 🙏",
1113
+ "2018-05-01": "Happy Worker's Day 💪",
1114
+ "2018-05-27": "Happy Children's Day 👶",
1115
+ "2018-05-29": "Happy Democracy Day 🇳🇬",
1116
+ "2018-06-15": "Barka da Sallah 🎉",
1117
+ "2018-10-01": "Happy Independence Day 🇳🇬",
1118
+ "2018-12-25": "Merry Christmas 🎉",
1119
+ "2018-12-26": "Happy Boxing Day 🎁"
1120
+ }
1121
+ ...
1122
+
1123
+ // params is an optional object which defaults to {pay_with_bank: '1'} if no value is passed.
1124
+ const banks = await Toolkit.fetch('banks', params)
1125
+
1126
+ // banks.data returns;
1127
+ [{
1128
+ name: "Access Bank"
1129
+ slug: "access-bank"
1130
+ code: "044"
1131
+ longcode: "044150149"
1132
+ gateway: "emandate"
1133
+ pay_with_bank: true
1134
+ active: true
1135
+ is_deleted: null
1136
+ country: "Nigeria"
1137
+ currency: "NGN"
1138
+ type: "nuban"
1139
+ id: 1
1140
+ createdAt: "2016-07-14T10:04:29.000Z"
1141
+ updatedAt: "2016-07-14T10:04:29.000Z"
1142
+ }, { ... } ...]
1143
+ ```
1144
+
1145
+ ## # Validating a Card
1146
+
1147
+ ## Card.validate({ number, cvv, month, year })
1148
+
1149
+ A static method on the `Card` class that takes an object containing `cardNumber` , `cvv`, `month` and `year` returning an object specifying if the card is valid.
1150
+
1151
+ **Example**
1152
+
1153
+ ```js
1154
+ import { Card } from 'checkout-js';
1155
+
1156
+ const cardDetails = {
1157
+ number: '4084084084084081',
1158
+ cvv: '408',
1159
+ month: '03',
1160
+ year: '21',
1161
+ }
1162
+
1163
+ Card.validate(cardDetails);
1164
+
1165
+ // output
1166
+ // { isValid: true, errors: [] }
1167
+ ```
1168
+
1169
+ ## Card.isNumberValid(cardNumber)
1170
+
1171
+ A static method on the `Card` class that takes a card number and returns true if the card number is valid.
1172
+
1173
+ **Example**
1174
+
1175
+ ```js
1176
+ import { Card } from 'checkout-js';
1177
+
1178
+ const cardNumber = '4084084084084081';
1179
+
1180
+ Card.isValidNumber(cardNumber);
1181
+
1182
+ // output
1183
+ // true
1184
+ ```
1185
+
1186
+ ## Card.getType(cardNumber)
1187
+
1188
+ A static method on the `Card` class that takes a card number and returns the type of card for a valid card number
1189
+
1190
+ **Example**
1191
+
1192
+ ```js
1193
+ import { Card } from 'checkout-js';
1194
+
1195
+ const cardNumber = '4084084084084081';
1196
+
1197
+ Card.getType(cardNumber);
1198
+
1199
+ // output
1200
+ // {type: 'visa', maxLength: 19}
1201
+
1202
+ // output for invalid card number
1203
+ // {type: null, maxLength: 0}
1204
+ ```
1205
+
1206
+ ## Card.isCvvValid(cvv)
1207
+
1208
+ A static method on the `Card` class that takes a cvv returns true if cvv supplied is valid
1209
+
1210
+ **Example**
1211
+
1212
+ ```js
1213
+ import { Card } from 'checkout-js';
1214
+
1215
+ const cvv = '408';
1216
+
1217
+ Card.isCvvValid(cvv);
1218
+
1219
+ // output
1220
+ // true
1221
+ ```
1222
+
1223
+ ## Card.isExpiryValid(month, year)
1224
+
1225
+ A static method on the `Card` class that takes an expiry month and year returning `true` if valid
1226
+
1227
+ **Example**
1228
+
1229
+ ```js
1230
+ import { Card } from 'checkout-js';
1231
+
1232
+ const expiryMonth = '10';
1233
+ const expiryYear = '2020'
1234
+
1235
+ Card.isExpiryValid(expiryMonth, expiryYear);
1236
+
1237
+ // output
1238
+ // true
1239
+ ```
1240
+
1241
+ ## # Validating a bank account
1242
+
1243
+ ## BankAccount.validate({ bankId, accountNumber, accountType })
1244
+
1245
+ A static method that takes an object containing `bankId`, `accountType` and `accountNumber`, returning an object specifying the validity of the account number supplied.
1246
+
1247
+ **Example**
1248
+
1249
+ ```js
1250
+ import { BankAccount } from 'checkout-js';
1251
+
1252
+ // valid accountTypes: BankAccount.types
1253
+ // valid bankIds: Toolkit.fetch('banks')
1254
+ const bankDetails = {
1255
+ bankId: 1,
1256
+ accountType: 'nuban',
1257
+ accountNumber: '0011163526'
1258
+ }
1259
+
1260
+ BankAccount.validate(bankDetails);
1261
+
1262
+ // output
1263
+ // {isValid: true, errors: []}
1264
+ ```
1265
+
1266
+ ## BankAccount.isValidNubanAccount(accountNumber)
1267
+
1268
+ A static method on the `BankAccount` class that takes a bank account number and returns `true` if it is a valid ‘nuban’ account number.
1269
+
1270
+ **Example**
1271
+
1272
+ ```js
1273
+ import { BankAccount } from 'checkout-js';
1274
+
1275
+ const accountNumber = '08033991672'
1276
+
1277
+ BankAccount.isValidNubanAccount(accountNumber);
12
1278
 
13
- Clone this repository
1279
+ // output
1280
+ // true / false
1281
+ ```
14
1282
 
15
- git clone git@github.com:PaystackHQ/checkout-js.git
1283
+ ## # Charge Responses
16
1284
 
17
- Install dependencies with:
1285
+ This is the format in which all charge responses in `checkout-js` are returned. A charge response will always have a `status` and a `message`.
18
1286
 
19
- npm install
1287
+ ## Success
20
1288
 
21
- # Development
1289
+ This means that the charge attempt was successful
22
1290
 
23
- The source code can be found in the `src/` folder.
1291
+ **Example**
24
1292
 
25
- # Testing
1293
+ ```js
1294
+ {
1295
+ status: 'success',
1296
+ message: 'Transaction successful',
1297
+ data: { ... }
1298
+ }
1299
+ ```
26
1300
 
27
- Testing is done with Jest. To test your changes, run
1301
+ ## Auth
28
1302
 
29
- npm test
1303
+ This means that additional authentication has to be carried out to complete the transaction
30
1304
 
31
- To run tests in watch mode, run
1305
+ **Example**
32
1306
 
33
- npm run test:watch
1307
+ ```js
1308
+ {
1309
+ status: 'auth',
1310
+ message: 'Authentication Required', // for 3DS, this would be the 3DS redirect URL
1311
+ data: {
1312
+ auth: 'pin' // can either be [pin, otp or 3DS]
1313
+ }
1314
+ }
1315
+ ```
34
1316
 
35
- # Build
1317
+ ## Failed
36
1318
 
37
- Checkout JS is built with Rollup. To build, run the command
1319
+ This means that the charge attempt failed
38
1320
 
39
- npm run build
1321
+ **Example**
40
1322
 
41
- You can also specify an environment to build for: `dev`, `staging`, or `production`. Configuration variables are set in `src/config.js`.
1323
+ ```js
1324
+ {
1325
+ status: 'failed',
1326
+ message: 'Transaction failed',
1327
+ errors: [],
1328
+ /* the 'error' object is only available if a card transaction fails. It tells us the bank the failed card was issued by. You can use this to suggest that your customer pays with their bank account, if it's one of the supported banks in Toolkit.fetch('banks') */
1329
+ error: {
1330
+ bank: 'Guaranty Trust Bank'
1331
+ }
1332
+ }
1333
+ ```
42
1334
 
43
- # Deployment
1335
+ # Additional Notes
44
1336
 
45
- Checkout JS is deployed to three environments:
1337
+ ### Pusher
46
1338
 
47
- - Staging: [https://js-studio.paystack.co/v1/checkout.js](https://js-studio.paystack.co/v1/checkout.js)
48
- - Production: [https://js.paystack.co/v1/checkout.js](https://js.paystack.co/v1/checkout.js)
49
- - npm: [@paystack/checkout-js](https://npmjs.com/package/@paystack/checkout-js)
1339
+ This library uses a third party service, Pusher, to listen for API responses in the following methods
50
1340
 
51
- # Releases
1341
+ - `Transaction.listenFor3DSCharge`
1342
+ - `Transaction.listenForIbankCharge`
1343
+ - `Transaction.listenForUSSDCharge`
1344
+ - `Transaction.listenForQRCharge`
1345
+ - `Transaction.listenForMobileMoneyCharge`
1346
+ - `Transaction.listenForDigitalBankMandateCharge`
52
1347
 
53
- Checkout JS is automatically packaged and published to the NPM registry on every push to the `master` and `dev` branches.
1348
+ However, this does not work on non-Paystack domains so you have to use the `transaction.requery()` method to check the status of the transaction when using any of those payment methods.
54
1349
 
55
- The `master` branch is publised under the `latest` tag and can be installed by running `npm i @paystack/checkout-js@latest` while the `dev` branch is published under the `dev` tag and can be installed using `npm i @paystack/checkout-js@dev`
1350
+ ### Error Handling
56
1351
 
57
- All published versions are listed here on NPM https://www.npmjs.com/package/@paystack/checkout-js
1352
+ To ensure higher success rates, errors and failure messages should not be taken as an indication of finality, for example when a payment fails on the Paystack Checkout, the customer is prompted to retry via the same payment method or an alternative payment method.