├── .gitignore
├── LICENSE
├── README.md
├── docs
└── logo.png
├── package.json
├── pnpm-lock.yaml
├── src
├── chapa.ts
├── enums
│ ├── chapa-urls.enum.ts
│ ├── index.ts
│ └── split-type.enum.ts
├── http-exception.ts
├── index.ts
├── interfaces
│ ├── chapa-options.interface.ts
│ ├── create-subaccount.interface.ts
│ ├── direct-charge.interface.ts
│ ├── gen-tx-ref.interface.ts
│ ├── get-banks.interface.ts
│ ├── index.ts
│ ├── payment.interface.ts
│ ├── transaction.interface.ts
│ └── transfer.interface.ts
└── validations
│ ├── create-subaccount.validation.ts
│ ├── direct-charge.validation.ts
│ ├── gen-tx-ref.validation.ts
│ ├── index.ts
│ ├── payment.validation.ts
│ ├── transaction.validation.ts
│ └── transfer.validation.ts
├── test
└── sample.test.ts
├── tsconfig.json
└── types
└── global.d.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | dist
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Fireayehu Zekarias
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
NodeJS SDK for chapa
7 |
8 |
9 |
10 | ## Features
11 |
12 | - Initialize Transaction
13 | - Split Payment
14 | - Verify Payment
15 | - List Banks
16 | - Create Subaccount
17 | - All Transaction
18 | - Transaction Logs
19 | - Transfer
20 | - Bulk Transfer
21 | - Verify Transfer
22 | - All Transfer
23 | - Direct Charge
24 | - Authorize Direct Charge
25 | - Generate Transaction Reference (Utiltiy Function)
26 | - Full TypeScript Support
27 |
28 | ## Installation
29 |
30 | **NPM**
31 |
32 | ```bash
33 | $ npm install chapa-nodejs
34 | ```
35 |
36 | **Yarn**
37 |
38 | ```bash
39 | $ yarn add chapa-nodejs
40 | ```
41 |
42 | **Pnpm**
43 |
44 | ```bash
45 | $ pnpm add chapa-nodejs
46 | ```
47 |
48 | ## Getting started
49 |
50 | Once the installation process is complete, we can import the sdk in any file.
51 |
52 |
53 |
54 | ### Configuration
55 |
56 | Keep in mind to load your secret key from environment variable
57 |
58 | ```typescript
59 | import { Chapa } from 'chapa-nodejs';
60 |
61 | const chapa = new Chapa({
62 | secretKey: 'your-chapa-secret-key',
63 | });
64 | ```
65 |
66 |
67 |
68 | ### Generate Transaction Reference
69 |
70 | This utility method of `Chapa` instance allows you to generating a customizable random alpha numberic transaction reference.
71 |
72 | ```typescript
73 | const tx_ref = await chapa.genTxRef(); // result: TX-JHBUVLM7HYMSWDA
74 |
75 | // Or with options
76 |
77 | const tx_ref = await chapa.genTxRef({
78 | removePrefix: false, // defaults to `false`
79 | prefix: 'TX', // defaults to `TX`
80 | size: 20, // defaults to `15`
81 | });
82 | ```
83 |
84 | ### Initialize Transaction
85 |
86 | To initialize a transaction, we have two possilbe ways. The first one is for web payment, simply call the `initialize` method from `Chapa` instance, and pass to it `InitializeOptions` options. For mobile payment use `mobileInitialize`, it accepts and returns the same format as the `initialize` method.
87 |
88 | ```typescript
89 | // Generate transaction reference using our utility method or provide your own
90 | const tx_ref = await chapa.genTxRef();
91 |
92 | const response = await chapa.initialize({
93 | first_name: 'John',
94 | last_name: 'Doe',
95 | email: 'john@gmail.com',
96 | phone_number: '0911121314',
97 | currency: 'ETB',
98 | amount: '200',
99 | tx_ref: tx_ref,
100 | callback_url: 'https://example.com/',
101 | return_url: 'https://example.com/',
102 | customization: {
103 | title: 'Test Title',
104 | description: 'Test Description',
105 | },
106 | });
107 | ```
108 |
109 | ```typescript
110 | // Generate transaction reference using our utility method or provide your own
111 | const tx_ref = await chapa.genTxRef();
112 |
113 | const response = await chapa.mobileInitialize({
114 | first_name: 'John',
115 | last_name: 'Doe',
116 | email: 'john@gmail.com',
117 | phone_number: '0911121314',
118 | currency: 'ETB',
119 | amount: '200',
120 | tx_ref: tx_ref,
121 | callback_url: 'https://example.com/',
122 | return_url: 'https://example.com/',
123 | customization: {
124 | title: 'Test Title',
125 | description: 'Test Description',
126 | },
127 | });
128 | ```
129 |
130 | #### InitializeOptions
131 |
132 | ```typescript
133 | enum SplitType {
134 | PERCENTAGE = 'percentage',
135 | FLAT = 'flat',
136 | }
137 |
138 | interface Subaccount {
139 | id: string;
140 | split_type?: SplitType;
141 | split_value?: number;
142 | }
143 |
144 | interface InitializeOptions {
145 | first_name?: string;
146 | last_name?: string;
147 | email?: string;
148 | phone_number?: string;
149 | currency: string;
150 | amount: string;
151 | tx_ref: string;
152 | callback_url?: string;
153 | return_url?: string;
154 | customization?: {
155 | title?: string;
156 | description?: string;
157 | logo?: string;
158 | };
159 | subaccounts?: Subaccount[];
160 | }
161 | ```
162 |
163 | #### InitializeResponse
164 |
165 | ```typescript
166 | interface InitializeResponse {
167 | message: string;
168 | status: string;
169 | data: {
170 | checkout_url: string;
171 | };
172 | }
173 | ```
174 |
175 | ### Verify Payment
176 |
177 | To verify payment, simply call the `verify` method from `Chapa` instance, and pass to it `VerifyOptions` options.
178 |
179 | ```typescript
180 | const response = await chapa.verify({
181 | tx_ref: 'TX-JHBUVLM7HYMSWDA',
182 | });
183 | ```
184 |
185 | #### VerifyOptions
186 |
187 | ```typescript
188 | interface VerifyOptions {
189 | tx_ref: string;
190 | }
191 | ```
192 |
193 | #### VerifyResponse
194 |
195 | ```typescript
196 | interface VerifyResponse {
197 | message: string;
198 | status: string;
199 | data: {
200 | first_name: string;
201 | last_name: string;
202 | email: string;
203 | phone_number: string;
204 | currency: string;
205 | amount: string;
206 | charge: string;
207 | mode: string;
208 | method: string;
209 | type: string;
210 | status: string;
211 | reference: string;
212 | tx_ref: string;
213 | customization: {
214 | title: string;
215 | description: string;
216 | logo: string;
217 | };
218 | meta: any;
219 | created_at: Date;
220 | updated_at: Date;
221 | };
222 | }
223 | ```
224 |
225 | ### List Banks
226 |
227 | This section describes how to get bank details for all supported banks `Chapa` is working with. `getBanks` method of `Chapa` instance returns all the Banks information for all currencies. The method does not accept any options.
228 |
229 | ```typescript
230 | const response = await chapa.getBanks();
231 | ```
232 |
233 | #### GetBanksResponse
234 |
235 | ```typescript
236 | type Currency = 'ETB' | 'USD';
237 |
238 | interface Data {
239 | id: number;
240 | swift: string;
241 | name: string;
242 | acct_length: number;
243 | country_id: number;
244 | created_at: Date;
245 | updated_at: Date;
246 | is_rtgs: boolean | null;
247 | is_mobilemoney: boolean | null;
248 | currency: Currency;
249 | }
250 |
251 | interface GetBanksResponse {
252 | message: string;
253 | data: Data[];
254 | }
255 | ```
256 |
257 | ### Create Subaccount
258 |
259 | To create subaccounts, simply call the `createSubaccount` method from `Chapa` instance, and pass to it `CreateSubaccountOptions` options.
260 |
261 | ```typescript
262 | const response = await chapa.createSubaccount({
263 | business_name: 'Test Business',
264 | account_name: 'John Doe',
265 | bank_code: '80a510ea-7497-4499-8b49-ac13a3ab7d07', // Get this from the `getBanks()` method
266 | account_number: '0123456789',
267 | split_type: SplitType.PERCENTAGE,
268 | split_value: 0.02,
269 | });
270 | ```
271 |
272 | #### CreateSubaccountOptions
273 |
274 | ```typescript
275 | interface CreateSubaccountOptions {
276 | business_name: string;
277 | account_name: string;
278 | bank_code: number;
279 | account_number: string;
280 | split_type: SplitType;
281 | split_value: number;
282 | }
283 | ```
284 |
285 | #### CreateSubaccountResponse
286 |
287 | ```typescript
288 | interface CreateSubaccountResponse {
289 | message: string;
290 | status: string;
291 | data: string;
292 | }
293 | ```
294 |
295 | ### Split Payment
296 |
297 | Split payments are carried out by first creating a subaccount, then initializing the split payment. The process of implementing split payment is the same as initialize a transaction, with additional options( i.e `subaccounts`) to the `initialize` method of `Chapa`.
298 |
299 | ```typescript
300 | // Generate transaction reference using our utility method or provide your own
301 | const tx_ref = await chapa.genTxRef();
302 |
303 | const response = chapa.initialize({
304 | first_name: 'John',
305 | last_name: 'Doe',
306 | email: 'john@gmail.com',
307 | phone_number: '0911121314',
308 | currency: 'ETB',
309 | amount: '200',
310 | tx_ref: tx_ref,
311 | callback_url: 'https://example.com/',
312 | return_url: 'https://example.com/',
313 | customization: {
314 | title: 'Test Title',
315 | description: 'Test Description',
316 | },
317 | // Add this for split payment
318 | subaccounts: [
319 | {
320 | id: '80a510ea-7497-4499-8b49-ac13a3ab7d07',
321 | },
322 | ],
323 | });
324 | ```
325 |
326 | #### Overriding The Defaults
327 |
328 | When collecting a payment, you can override the default `split_type` and `split_value` you set when creating the subaccount, by specifying these fields in the subaccounts item.
329 |
330 | ```typescript
331 | subaccounts: [
332 | {
333 | id: '80a510ea-7497-4499-8b49-ac13a3ab7d07',
334 | split_type: SplitType.FLAT,
335 | split_value: 25
336 | },
337 | ],
338 | ```
339 |
340 | ### All Transaction
341 |
342 | This section describes how to get all transactions. `getTransactions` method of `Chapa` instance returns all the Transaction information. The method does not accept any options.
343 |
344 | ```typescript
345 | const response = await chapa.getTransactions();
346 | ```
347 |
348 | #### GetTransactionsResponse
349 |
350 | ```typescript
351 | interface Customer {
352 | id: number;
353 | email: string;
354 | first_name: string;
355 | last_name: string;
356 | mobile: string;
357 | }
358 |
359 | interface Transaction {
360 | status: string;
361 | ref_id: string;
362 | type: string;
363 | created_at: Date;
364 | currency: string;
365 | amount: string;
366 | charge: string;
367 | trans_id: string;
368 | payment_method: string;
369 | customer: Customer;
370 | }
371 |
372 | interface Pagination {
373 | per_page: number;
374 | current_page: number;
375 | first_page_url: string;
376 | next_page_url: string;
377 | prev_page_url: string;
378 | }
379 |
380 | interface GetTransactionsResponse {
381 | message: string;
382 | status: string;
383 | data: {
384 | transactions: Transaction[];
385 | pagination: Pagination;
386 | };
387 | }
388 | ```
389 |
390 | ### Transaction Logs
391 |
392 | This section describes how to get timeline for a transaction. A transaction timeline is a list of events that happened to a selected transaction. To get list of timeline, simply call the `getTransactionLogs` method from `Chapa` instance, and pass to it `GetTransactionLogsOptions` options.
393 |
394 | ```typescript
395 | const response = await chapa.getTransactionLogs({
396 | ref_id: 'chewatatest-6669',
397 | });
398 | ```
399 |
400 | #### GetTransactionLogsOptions
401 |
402 | ```typescript
403 | interface GetTransactionLogsOptions {
404 | ref_id: string;
405 | }
406 | ```
407 |
408 | #### GetTransactionLogsResponse
409 |
410 | ```typescript
411 | interface Log {
412 | item: number;
413 | message: string;
414 | type: string;
415 | created_at: string;
416 | updated_at: string;
417 | }
418 |
419 | interface GetTransactionLogsResponse {
420 | message: string;
421 | status: string;
422 | data: Log[];
423 | }
424 | ```
425 |
426 | ### Transfer
427 |
428 | This section describes how to send funds to Bank accounts. To initiate a transfer, simply call the `transfer` method from `Chapa` instance, and pass to it `TransferOptions` options.
429 |
430 | ```typescript
431 | const response = await chapa.transfer({
432 | account_name: 'John Doe',
433 | account_number: '32423423',
434 | amount: '1',
435 | currency: 'ETB',
436 | reference: '3241342142sfdd',
437 | bank_code: 656,
438 | });
439 | ```
440 |
441 | #### TransferOptions
442 |
443 | ```typescript
444 | interface TransferOptions {
445 | account_name: string;
446 | account_number: string;
447 | amount: string;
448 | currency: string;
449 | reference: string;
450 | bank_code: number;
451 | }
452 | ```
453 |
454 | #### TransferResponse
455 |
456 | ```typescript
457 | interface TransferResponse {
458 | message: string;
459 | status: string;
460 | data: string;
461 | }
462 | ```
463 |
464 | ### Bulk Transfer
465 |
466 | This section describes how to send funds to Bank accounts in bulk. To do this, you'll provide an array of objects called e bulk_data. Each item in this array contains details for one transfer—the same details you specify when making a single transfer. To initiate a transfer, simply call the `bulkTransfer` method from `Chapa` instance, and pass to it `BulkTransferOptions` options.
467 |
468 | ```typescript
469 | const response = await chapa.bulkTransfer({
470 | title: 'This Month Salary!',
471 | currency: 'ETB',
472 | bulk_data: [
473 | {
474 | account_name: 'John Doe',
475 | account_number: '09xxxxxxxx',
476 | amount: 1,
477 | reference: 'b1111124',
478 | bank_code: 128,
479 | },
480 | {
481 | account_name: 'John Doe',
482 | account_number: '09xxxxxxxx',
483 | amount: 1,
484 | reference: 'b2222e5r',
485 | bank_code: 128,
486 | },
487 | ],
488 | });
489 | ```
490 |
491 | #### BulkTransferOptions
492 |
493 | ```typescript
494 | interface BulkData {
495 | account_name: string;
496 | account_number: string;
497 | amount: string;
498 | reference: string;
499 | bank_code: number;
500 | }
501 |
502 | interface BulkTransferOptions {
503 | title: string;
504 | currency: string;
505 | bulk_data: BulkData[];
506 | }
507 | ```
508 |
509 | #### BulkTransferResponse
510 |
511 | ```typescript
512 | interface BulkTransferResponse {
513 | message: string;
514 | status: string;
515 | data: {
516 | id: number;
517 | created_at: string;
518 | };
519 | }
520 | ```
521 |
522 | ### Verify Transfer
523 |
524 | To verify transfer, simply call the `verifyTransfer` method from `Chapa` instance, and pass to it `VerifyTransferOptions` options.
525 |
526 | ```typescript
527 | const response = await chapa.verifyTransfer({
528 | tx_ref: 'TX-JHBUVLM7HYMSWDA',
529 | });
530 | ```
531 |
532 | #### VerifyTransferOptions
533 |
534 | ```typescript
535 | interface VerifyTransferOptions {
536 | tx_ref: string;
537 | }
538 | ```
539 |
540 | #### VerifyTransferResponse
541 |
542 | ```typescript
543 | interface Data {
544 | account_name: string;
545 | account_number: string;
546 | mobile: string;
547 | currency: string;
548 | amount: number;
549 | charge: number;
550 | mode: string;
551 | transfer_method: string;
552 | narration: string;
553 | chapa_transfer_id: string;
554 | bank_code: number;
555 | bank_name: string;
556 | cross_party_reference: string;
557 | ip_address: string;
558 | status: string;
559 | tx_ref: string;
560 | created_at: string;
561 | updated_at: string;
562 | }
563 |
564 | export interface VerifyTransferResponse {
565 | message: string;
566 | status: string;
567 | data: Data;
568 | }
569 | ```
570 |
571 | ### All Transfer
572 |
573 | This section describes how to get all transfers. `getTransfers` method of `Chapa` instance returns all the transfer information. The method does not accept any options.
574 |
575 | ```typescript
576 | const response = await chapa.getTransfers();
577 | ```
578 |
579 | #### GetTransfersResponse
580 |
581 | ```typescript
582 | interface Meta {
583 | current_page: number;
584 | first_page_url: string;
585 | last_page: number;
586 | last_page_url: string;
587 | next_page_url: string;
588 | path: string;
589 | per_page: number;
590 | prev_page_url: null;
591 | to: number;
592 | total: number;
593 | error: any[];
594 | }
595 |
596 | interface Transfer {
597 | account_name: string;
598 | account_number: string;
599 | currency: string;
600 | amount: number;
601 | charge: number;
602 | transfer_type: string;
603 | chapa_reference: string;
604 | bank_code: number;
605 | bank_name: string;
606 | bank_reference: string;
607 | status: string;
608 | reference: string;
609 | created_at: string;
610 | updated_at: string;
611 | }
612 |
613 | export interface GetTransfersResponse {
614 | message: string;
615 | status: string;
616 | data: Transfer[];
617 | meta: Meta;
618 | }
619 | ```
620 |
621 | ### Direct Charge
622 |
623 | This section describes how to integrate direct charges. To initiate a direct charge, simply call the `directCharge` method from `Chapa` instance, and pass to it `DirectChargeOptions` options.
624 |
625 | ```typescript
626 | const response = await chapa.directCharge({
627 | first_name: 'Fireayehu',
628 | last_name: 'Zekarias'
629 | email:"test@gmail.com",
630 | mobile: '09xxxxxxxx',
631 | currency: 'ETB',
632 | amount: '1',
633 | tx_ref: '3241342142sfdd',
634 | type: 'telebirr',
635 | });
636 | ```
637 |
638 | #### DirectChargeOptions
639 |
640 | ```typescript
641 | type DirectChargeType =
642 | | 'telebirr'
643 | | 'mpesa'
644 | | 'Amole'
645 | | 'CBEBirr'
646 | | 'Coopay-Ebirr'
647 | | 'AwashBirr'
648 | | string;
649 |
650 | interface DirectChargeOptions {
651 | first_name?: string;
652 | last_name?: string;
653 | email?: string;
654 | mobile: string;
655 | currency: string;
656 | amount: string;
657 | tx_ref: string;
658 | type: DirectChargeType;
659 | }
660 | ```
661 |
662 | #### DirectChargeResponse
663 |
664 | ```typescript
665 | interface Meta {
666 | message: string;
667 | ref_id: string;
668 | verification_type: string;
669 | status: string;
670 | data: string;
671 | payment_status: string;
672 | }
673 |
674 | interface DirectChargeResponse {
675 | message: string;
676 | status: string;
677 | data: {
678 | auth_type: string;
679 | meta: Meta;
680 | };
681 | }
682 | ```
683 |
684 | ### Authorize Direct Charge
685 |
686 | This section describes the necessary actions taken to authorize transactions after payment using direct charge. To authorize direct charge, simply call the `authorizeDirectCharge` method from `Chapa` instance, and pass to it `AuthorizeDirectChargeOptions` options.
687 |
688 | ```typescript
689 | const response = await chapa.authorizeDirectCharge({
690 | reference: 'CHcuKjgnN0Dk0',
691 | client: '',
692 | type: 'telebirr',
693 | });
694 | ```
695 |
696 | #### AuthorizeDirectChargeOptions
697 |
698 | ```typescript
699 | type DirectChargeType =
700 | | 'telebirr'
701 | | 'mpesa'
702 | | 'Amole'
703 | | 'CBEBirr'
704 | | 'Coopay-Ebirr'
705 | | 'AwashBirr'
706 | | string;
707 |
708 | interface AuthorizeDirectChargeOptions {
709 | reference: string;
710 | client: string;
711 | type: DirectChargeType;
712 | }
713 | ```
714 |
715 | #### AuthorizeDirectChargeResponse
716 |
717 | ```typescript
718 | export interface AuthorizeDirectChargeResponse {
719 | message: string;
720 | trx_ref: string;
721 | processor_id: string;
722 | }
723 | ```
724 |
725 | ## Stay in touch
726 |
727 | - Author - Fireayehu Zekarias
728 | - Github - [https://github.com/fireayehu](https://github.com/fireayehu)
729 | - Twitter - [https://twitter.com/Fireayehu](https://twitter.com/Fireayehu)
730 | - LinkedIn - [https://www.linkedin.com/in/fireayehu/](https://www.linkedin.com/in/fireayehu/)
731 |
732 | ## License
733 |
734 | chapa-nodejs is [MIT licensed](LICENSE).
735 |
--------------------------------------------------------------------------------
/docs/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fireayehu/chapa-nodejs/a42e94316ae442f0733725aea1cd92cd4f6551aa/docs/logo.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.1",
3 | "license": "MIT",
4 | "main": "dist/index.js",
5 | "url": "https://github.com/fireayehu/chapa-nodejs#readme",
6 | "files": [
7 | "dist",
8 | "src"
9 | ],
10 | "engines": {
11 | "node": ">=10"
12 | },
13 | "scripts": {
14 | "start": "tsdx watch",
15 | "build": "tsdx build",
16 | "test": "tsdx test --passWithNoTests",
17 | "lint": "tsdx lint",
18 | "prepare": "tsdx build",
19 | "size": "size-limit",
20 | "analyze": "size-limit --why"
21 | },
22 | "husky": {
23 | "hooks": {
24 | "pre-commit": "tsdx lint"
25 | }
26 | },
27 | "prettier": {
28 | "printWidth": 80,
29 | "semi": true,
30 | "singleQuote": true,
31 | "trailingComma": "es5"
32 | },
33 | "name": "chapa-nodejs",
34 | "author": "Fireayehu Zekarias",
35 | "module": "dist/chapa.esm.js",
36 | "repository": {
37 | "type": "git",
38 | "url": "https://github.com/fireayehu/chapa-nodejs"
39 | },
40 | "keywords": [
41 | "nodejs",
42 | "node",
43 | "javascript",
44 | "typescript",
45 | "chapa",
46 | "chapa-nodejs",
47 | "payment"
48 | ],
49 | "size-limit": [
50 | {
51 | "path": "dist/chapa.cjs.production.min.js",
52 | "limit": "10 KB"
53 | },
54 | {
55 | "path": "dist/chapa.esm.js",
56 | "limit": "10 KB"
57 | }
58 | ],
59 | "dependencies": {
60 | "axios": "^1.7.7",
61 | "nanoid": "^3.3.7",
62 | "nanoid-dictionary": "^4.3.0",
63 | "yup": "^1.4.0"
64 | },
65 | "devDependencies": {
66 | "@size-limit/preset-small-lib": "^11.1.5",
67 | "@types/nanoid-dictionary": "^4.2.3",
68 | "husky": "^9.1.5",
69 | "size-limit": "^11.1.5",
70 | "tsdx": "^0.14.1",
71 | "tslib": "^2.7.0",
72 | "typescript": "^5.6.2"
73 | },
74 | "packageManager": "pnpm@9.10.0"
75 | }
76 |
--------------------------------------------------------------------------------
/src/chapa.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { customAlphabet } from 'nanoid';
3 | import { alphanumeric } from 'nanoid-dictionary';
4 | import { ChapaUrls } from './enums';
5 | import { HttpException } from './http-exception';
6 | import {
7 | AuthorizeDirectChargeOptions,
8 | AuthorizeDirectChargeResponse,
9 | BulkTransferOptions,
10 | BulkTransferResponse,
11 | ChapaOptions,
12 | CreateSubaccountOptions,
13 | CreateSubaccountResponse,
14 | DirectChargeOptions,
15 | DirectChargeResponse,
16 | GenTxRefOptions,
17 | GetBanksResponse,
18 | GetTransactionLogsOptions,
19 | GetTransactionLogsResponse,
20 | GetTransactionsResponse,
21 | GetTransfersResponse,
22 | InitializeOptions,
23 | InitializeResponse,
24 | TransferOptions,
25 | TransferResponse,
26 | VerifyOptions,
27 | VerifyResponse,
28 | VerifyTransferOptions,
29 | VerifyTransferResponse,
30 | } from './interfaces';
31 | import {
32 | validateAuthorizeDirectChargeOptions,
33 | validateBulkTransferOptions,
34 | validateCreateSubaccountOptions,
35 | validateDirectChargeOptions,
36 | validateGetTransactionLogsOptions,
37 | validateInitializeOptions,
38 | validateTransferOptions,
39 | validateVerifyOptions,
40 | } from './validations';
41 |
42 | interface IChapa {
43 | initialize(options: InitializeOptions): Promise;
44 | mobileInitialize(options: InitializeOptions): Promise;
45 | verify(options: VerifyOptions): Promise;
46 | genTxRef(genTxRefOptions?: GenTxRefOptions): Promise;
47 | getBanks(): Promise;
48 | createSubaccount(
49 | options: CreateSubaccountOptions
50 | ): Promise;
51 | getTransactions(): Promise;
52 | getTransactionLogs(
53 | options: GetTransactionLogsOptions
54 | ): Promise;
55 | transfer(options: TransferOptions): Promise;
56 | bulkTransfer(options: BulkTransferOptions): Promise;
57 | verifyTransfer(
58 | options: VerifyTransferOptions
59 | ): Promise;
60 | getTransfers(): Promise;
61 | directCharge(options: DirectChargeOptions): Promise;
62 | authorizeDirectCharge(
63 | options: AuthorizeDirectChargeOptions
64 | ): Promise;
65 | }
66 |
67 | export class Chapa implements IChapa {
68 | constructor(private chapaOptions: ChapaOptions) {}
69 |
70 | async initialize(options: InitializeOptions): Promise {
71 | try {
72 | await validateInitializeOptions(options);
73 |
74 | const response = await axios.post(
75 | ChapaUrls.INITIALIZE,
76 | options,
77 | {
78 | headers: {
79 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
80 | },
81 | }
82 | );
83 | return response.data;
84 | } catch (error) {
85 | if (error.response) {
86 | throw new HttpException(
87 | error.response.data.message,
88 | error.response.status
89 | );
90 | } else if (error.name === 'ValidationError') {
91 | throw new HttpException(error.errors[0], 400);
92 | } else {
93 | throw error;
94 | }
95 | }
96 | }
97 |
98 | async mobileInitialize(
99 | options: InitializeOptions
100 | ): Promise {
101 | try {
102 | await validateInitializeOptions(options);
103 |
104 | const response = await axios.post(
105 | ChapaUrls.MOBILE_INITIALIZE,
106 | options,
107 | {
108 | headers: {
109 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
110 | },
111 | }
112 | );
113 | return response.data;
114 | } catch (error) {
115 | if (error.response) {
116 | throw new HttpException(
117 | error.response.data.message,
118 | error.response.status
119 | );
120 | } else if (error.name === 'ValidationError') {
121 | throw new HttpException(error.errors[0], 400);
122 | } else {
123 | throw error;
124 | }
125 | }
126 | }
127 |
128 | async verify(options: VerifyOptions): Promise {
129 | try {
130 | await validateVerifyOptions(options);
131 | const response = await axios.get(
132 | `${ChapaUrls.VERIFY}/${options.tx_ref}`,
133 | {
134 | headers: {
135 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
136 | },
137 | }
138 | );
139 | return response.data;
140 | } catch (error) {
141 | if (error.response) {
142 | throw new HttpException(
143 | error.response.data.message,
144 | error.response.status
145 | );
146 | } else if (error.name === 'ValidationError') {
147 | throw new HttpException(error.errors[0], 400);
148 | } else {
149 | throw error;
150 | }
151 | }
152 | }
153 |
154 | async genTxRef(options?: GenTxRefOptions): Promise {
155 | const { removePrefix = false, prefix = 'TX', size = 15 } = options || {};
156 |
157 | const nanoid = customAlphabet(alphanumeric, size);
158 | const reference = nanoid();
159 |
160 | if (removePrefix) {
161 | return reference.toUpperCase();
162 | }
163 | return `${prefix}-${reference.toUpperCase()}`;
164 | }
165 |
166 | async getBanks(): Promise {
167 | try {
168 | const banks = await axios.get(ChapaUrls.BANK, {
169 | headers: {
170 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
171 | },
172 | });
173 | return banks.data;
174 | } catch (error) {
175 | if (error.response) {
176 | throw new HttpException(
177 | error.response.data.message,
178 | error.response.status
179 | );
180 | } else {
181 | throw error;
182 | }
183 | }
184 | }
185 |
186 | async createSubaccount(
187 | options: CreateSubaccountOptions
188 | ): Promise {
189 | try {
190 | await validateCreateSubaccountOptions(options);
191 | const response = await axios.post(
192 | ChapaUrls.SUBACCOUNT,
193 | options,
194 | {
195 | headers: {
196 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
197 | },
198 | }
199 | );
200 | return response.data;
201 | } catch (error) {
202 | if (error.response) {
203 | throw new HttpException(
204 | error.response.data.message,
205 | error.response.status
206 | );
207 | } else if (error.name === 'ValidationError') {
208 | throw new HttpException(error.errors[0], 400);
209 | } else {
210 | throw error;
211 | }
212 | }
213 | }
214 |
215 | async getTransactions(): Promise {
216 | try {
217 | const response = await axios.get(
218 | ChapaUrls.TRANSACTION,
219 | {
220 | headers: {
221 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
222 | },
223 | }
224 | );
225 | return response.data;
226 | } catch (error) {
227 | if (error.response) {
228 | throw new HttpException(
229 | error.response.data.message,
230 | error.response.status
231 | );
232 | } else {
233 | throw error;
234 | }
235 | }
236 | }
237 |
238 | async getTransactionLogs(
239 | options: GetTransactionLogsOptions
240 | ): Promise {
241 | try {
242 | await validateGetTransactionLogsOptions(options);
243 | const response = await axios.get(
244 | `${ChapaUrls.TRANSACTION_LOG}/${options.ref_id}`,
245 | {
246 | headers: {
247 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
248 | },
249 | }
250 | );
251 | return response.data;
252 | } catch (error) {
253 | if (error.response) {
254 | throw new HttpException(
255 | error.response.data.message,
256 | error.response.status
257 | );
258 | } else if (error.name === 'ValidationError') {
259 | throw new HttpException(error.errors[0], 400);
260 | } else {
261 | throw error;
262 | }
263 | }
264 | }
265 |
266 | async transfer(options: TransferOptions): Promise {
267 | try {
268 | await validateTransferOptions(options);
269 |
270 | const response = await axios.post(
271 | ChapaUrls.TRANSFER,
272 | options,
273 | {
274 | headers: {
275 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
276 | },
277 | }
278 | );
279 | return response.data;
280 | } catch (error) {
281 | if (error.response) {
282 | throw new HttpException(
283 | error.response.data.message,
284 | error.response.status
285 | );
286 | } else if (error.name === 'ValidationError') {
287 | throw new HttpException(error.errors[0], 400);
288 | } else {
289 | throw error;
290 | }
291 | }
292 | }
293 |
294 | async bulkTransfer(
295 | options: BulkTransferOptions
296 | ): Promise {
297 | try {
298 | await validateBulkTransferOptions(options);
299 |
300 | const response = await axios.post(
301 | ChapaUrls.BULK_TRANSFER,
302 | options,
303 | {
304 | headers: {
305 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
306 | },
307 | }
308 | );
309 | return response.data;
310 | } catch (error) {
311 | if (error.response) {
312 | throw new HttpException(
313 | error.response.data.message,
314 | error.response.status
315 | );
316 | } else if (error.name === 'ValidationError') {
317 | throw new HttpException(error.errors[0], 400);
318 | } else {
319 | throw error;
320 | }
321 | }
322 | }
323 |
324 | async verifyTransfer(
325 | options: VerifyTransferOptions
326 | ): Promise {
327 | try {
328 | await validateVerifyOptions(options);
329 | const response = await axios.get(
330 | `${ChapaUrls.VERIFY_TRANSFER}/${options.tx_ref}`,
331 | {
332 | headers: {
333 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
334 | },
335 | }
336 | );
337 | return response.data;
338 | } catch (error) {
339 | if (error.response) {
340 | throw new HttpException(
341 | error.response.data.message,
342 | error.response.status
343 | );
344 | } else if (error.name === 'ValidationError') {
345 | throw new HttpException(error.errors[0], 400);
346 | } else {
347 | throw error;
348 | }
349 | }
350 | }
351 |
352 | async getTransfers(): Promise {
353 | try {
354 | const response = await axios.get(
355 | ChapaUrls.TRANSFER,
356 | {
357 | headers: {
358 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
359 | },
360 | }
361 | );
362 | return response.data;
363 | } catch (error) {
364 | if (error.response) {
365 | throw new HttpException(
366 | error.response.data.message,
367 | error.response.status
368 | );
369 | } else {
370 | throw error;
371 | }
372 | }
373 | }
374 |
375 | async directCharge(
376 | options: DirectChargeOptions
377 | ): Promise {
378 | try {
379 | await validateDirectChargeOptions(options);
380 |
381 | const response = await axios.post(
382 | ChapaUrls.DIRECT_CHARGE,
383 | options,
384 | {
385 | params: {
386 | type: options.type,
387 | },
388 | headers: {
389 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
390 | },
391 | }
392 | );
393 | return response.data;
394 | } catch (error) {
395 | if (error.response) {
396 | throw new HttpException(
397 | error.response.data.message,
398 | error.response.status
399 | );
400 | } else if (error.name === 'ValidationError') {
401 | throw new HttpException(error.errors[0], 400);
402 | } else {
403 | throw error;
404 | }
405 | }
406 | }
407 |
408 | async authorizeDirectCharge(
409 | options: AuthorizeDirectChargeOptions
410 | ): Promise {
411 | try {
412 | await validateAuthorizeDirectChargeOptions(options);
413 |
414 | const response = await axios.post(
415 | ChapaUrls.AUTHORIZE_DIRECT_CHARGE,
416 | options,
417 | {
418 | params: {
419 | type: options.type,
420 | },
421 | headers: {
422 | Authorization: `Bearer ${this.chapaOptions.secretKey}`,
423 | },
424 | }
425 | );
426 | return response.data;
427 | } catch (error) {
428 | if (error.response) {
429 | throw new HttpException(
430 | error.response.data.message,
431 | error.response.status
432 | );
433 | } else if (error.name === 'ValidationError') {
434 | throw new HttpException(error.errors[0], 400);
435 | } else {
436 | throw error;
437 | }
438 | }
439 | }
440 | }
441 |
--------------------------------------------------------------------------------
/src/enums/chapa-urls.enum.ts:
--------------------------------------------------------------------------------
1 | export enum ChapaUrls {
2 | INITIALIZE = 'https://api.chapa.co/v1/transaction/initialize',
3 | MOBILE_INITIALIZE = 'https://api.chapa.co/v1/transaction/mobile-initialize',
4 | VERIFY = 'https://api.chapa.co/v1/transaction/verify',
5 | BANK = 'https://api.chapa.co/v1/banks',
6 | SUBACCOUNT = 'https://api.chapa.co/v1/subaccount',
7 | TRANSACTION = 'https://api.chapa.co/v1/transactions',
8 | TRANSACTION_LOG = 'https://api.chapa.co/v1/transaction/events',
9 | TRANSFER = 'https://api.chapa.co/v1/transfers',
10 | BULK_TRANSFER = 'https://api.chapa.co/v1/bulk-transfers',
11 | VERIFY_TRANSFER = 'https://api.chapa.co/v1/transfers/verify',
12 | DIRECT_CHARGE = 'https://api.chapa.co/v1/charges',
13 | AUTHORIZE_DIRECT_CHARGE = 'https://api.chapa.co/v1/validate',
14 | }
15 |
--------------------------------------------------------------------------------
/src/enums/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chapa-urls.enum';
2 | export * from './split-type.enum';
3 |
--------------------------------------------------------------------------------
/src/enums/split-type.enum.ts:
--------------------------------------------------------------------------------
1 | export enum SplitType {
2 | PERCENTAGE = 'percentage',
3 | FLAT = 'flat',
4 | }
5 |
--------------------------------------------------------------------------------
/src/http-exception.ts:
--------------------------------------------------------------------------------
1 | export class HttpException extends Error {
2 | public status: number;
3 | constructor(message: string, status: number) {
4 | super(message);
5 | this.status = status;
6 | Error.captureStackTrace(this, this.constructor);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chapa';
2 | export * from './interfaces';
3 | export * from './enums';
4 |
--------------------------------------------------------------------------------
/src/interfaces/chapa-options.interface.ts:
--------------------------------------------------------------------------------
1 | export interface ChapaOptions {
2 | secretKey: string;
3 | }
4 |
--------------------------------------------------------------------------------
/src/interfaces/create-subaccount.interface.ts:
--------------------------------------------------------------------------------
1 | import { SplitType } from '../enums';
2 |
3 | export interface CreateSubaccountOptions {
4 | business_name: string;
5 | account_name: string;
6 | bank_code: number;
7 | account_number: string;
8 | split_type: SplitType;
9 | split_value: number;
10 | }
11 |
12 | export interface CreateSubaccountResponse {
13 | message: string;
14 | status: string;
15 | data: string;
16 | }
17 |
--------------------------------------------------------------------------------
/src/interfaces/direct-charge.interface.ts:
--------------------------------------------------------------------------------
1 | export type DirectChargeType =
2 | | 'telebirr'
3 | | 'mpesa'
4 | | 'Amole'
5 | | 'CBEBirr'
6 | | 'Coopay-Ebirr'
7 | | 'AwashBirr'
8 | | string;
9 |
10 | /**
11 | * Direct Charge
12 | */
13 |
14 | export interface DirectChargeOptions {
15 | first_name?: string;
16 | last_name?: string;
17 | email?: string;
18 | mobile: string;
19 | currency: string;
20 | amount: string;
21 | tx_ref: string;
22 | type: DirectChargeType;
23 | }
24 |
25 | interface Meta {
26 | message: string;
27 | ref_id: string;
28 | verification_type: string;
29 | status: string;
30 | data: string;
31 | payment_status: string;
32 | }
33 |
34 | export interface DirectChargeResponse {
35 | message: string;
36 | status: string;
37 | data: {
38 | auth_type: string;
39 | meta: Meta;
40 | };
41 | }
42 |
43 | /**
44 | * Auhorize Direct Charge
45 | */
46 |
47 | export interface AuthorizeDirectChargeOptions {
48 | reference: string;
49 | client: string;
50 | type: DirectChargeType;
51 | }
52 |
53 | export interface AuthorizeDirectChargeResponse {
54 | message: string;
55 | trx_ref: string;
56 | processor_id: string;
57 | }
58 |
--------------------------------------------------------------------------------
/src/interfaces/gen-tx-ref.interface.ts:
--------------------------------------------------------------------------------
1 | export interface GenTxRefOptions {
2 | removePrefix?: boolean;
3 | prefix?: string;
4 | size?: number;
5 | }
6 |
--------------------------------------------------------------------------------
/src/interfaces/get-banks.interface.ts:
--------------------------------------------------------------------------------
1 | type Currency = 'ETB' | 'USD';
2 |
3 | interface Data {
4 | id: number;
5 | swift: string;
6 | name: string;
7 | acct_length: number;
8 | country_id: number;
9 | created_at: Date;
10 | updated_at: Date;
11 | is_rtgs: boolean | null;
12 | is_mobilemoney: boolean | null;
13 | currency: Currency;
14 | }
15 |
16 | export interface GetBanksResponse {
17 | message: string;
18 | data: Data[];
19 | }
20 |
--------------------------------------------------------------------------------
/src/interfaces/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chapa-options.interface';
2 | export * from './payment.interface';
3 | export * from './gen-tx-ref.interface';
4 | export * from './get-banks.interface';
5 | export * from './create-subaccount.interface';
6 | export * from './transaction.interface';
7 | export * from './transfer.interface';
8 | export * from './direct-charge.interface';
9 |
--------------------------------------------------------------------------------
/src/interfaces/payment.interface.ts:
--------------------------------------------------------------------------------
1 | import { SplitType } from '../enums';
2 |
3 | /**
4 | * Initialize Payment
5 | */
6 |
7 | interface Subaccount {
8 | id: string;
9 | split_type?: SplitType;
10 | split_value?: number;
11 | }
12 |
13 | export interface InitializeOptions {
14 | first_name?: string;
15 | last_name?: string;
16 | email?: string;
17 | phone_number?: string;
18 | currency: string;
19 | amount: string;
20 | tx_ref: string;
21 | callback_url?: string;
22 | return_url?: string;
23 | customization?: {
24 | title?: string;
25 | description?: string;
26 | logo?: string;
27 | };
28 | subaccounts?: Subaccount[];
29 | }
30 |
31 | interface IData {
32 | checkout_url: string;
33 | }
34 |
35 | export interface InitializeResponse {
36 | message: string;
37 | status: string;
38 | data?: IData;
39 | }
40 |
41 | /**
42 | * Verify Payment
43 | */
44 |
45 | export interface VerifyOptions {
46 | tx_ref: string;
47 | }
48 |
49 | export interface VerifyResponse {
50 | message: string;
51 | status: string;
52 | data: {
53 | first_name: string;
54 | last_name: string;
55 | email: string;
56 | currency: string;
57 | amount: string;
58 | charge: string;
59 | mode: string;
60 | method: string;
61 | type: string;
62 | status: string;
63 | reference: string;
64 | tx_ref: string;
65 | customization: {
66 | title: string;
67 | description: string;
68 | logo: string;
69 | };
70 | meta: any;
71 | created_at: Date;
72 | updated_at: Date;
73 | };
74 | }
75 |
--------------------------------------------------------------------------------
/src/interfaces/transaction.interface.ts:
--------------------------------------------------------------------------------
1 | export interface GetTransactionLogsOptions {
2 | ref_id: string;
3 | }
4 |
5 | interface Customer {
6 | id: number;
7 | email: string;
8 | first_name: string;
9 | last_name: string;
10 | mobile: string;
11 | }
12 |
13 | interface Log {
14 | item: number;
15 | message: string;
16 | type: string;
17 | created_at: string;
18 | updated_at: string;
19 | }
20 |
21 | interface Transaction {
22 | status: string;
23 | ref_id: string;
24 | type: string;
25 | created_at: Date;
26 | currency: string;
27 | amount: string;
28 | charge: string;
29 | trans_id: string;
30 | payment_method: string;
31 | customer: Customer;
32 | }
33 |
34 | interface Pagination {
35 | per_page: number;
36 | current_page: number;
37 | first_page_url: string;
38 | next_page_url: string;
39 | prev_page_url: string;
40 | }
41 |
42 | export interface GetTransactionsResponse {
43 | message: string;
44 | status: string;
45 | data: {
46 | transactions: Transaction[];
47 | pagination: Pagination;
48 | };
49 | }
50 |
51 | export interface GetTransactionLogsResponse {
52 | message: string;
53 | status: string;
54 | data: Log[];
55 | }
56 |
--------------------------------------------------------------------------------
/src/interfaces/transfer.interface.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Transfer
3 | */
4 |
5 | export interface TransferOptions {
6 | account_name: string;
7 | account_number: string;
8 | amount: string;
9 | currency: string;
10 | reference: string;
11 | bank_code: number;
12 | }
13 |
14 | export interface TransferResponse {
15 | message: string;
16 | status: string;
17 | data: string;
18 | }
19 |
20 | /**
21 | * Bulk Transfer
22 | */
23 |
24 | export interface BulkData {
25 | account_name: string;
26 | account_number: string;
27 | amount: string;
28 | reference: string;
29 | bank_code: number;
30 | }
31 |
32 | export interface BulkTransferOptions {
33 | title: string;
34 | currency: string;
35 | bulk_data: BulkData[];
36 | }
37 |
38 | export interface BulkTransferResponse {
39 | message: string;
40 | status: string;
41 | data: {
42 | id: number;
43 | created_at: string;
44 | };
45 | }
46 |
47 | /**
48 | * Get Transfers
49 | */
50 |
51 | interface Meta {
52 | current_page: number;
53 | first_page_url: string;
54 | last_page: number;
55 | last_page_url: string;
56 | next_page_url: string;
57 | path: string;
58 | per_page: number;
59 | prev_page_url: null;
60 | to: number;
61 | total: number;
62 | error: any[];
63 | }
64 |
65 | interface Transfer {
66 | account_name: string;
67 | account_number: string;
68 | currency: string;
69 | amount: number;
70 | charge: number;
71 | transfer_type: string;
72 | chapa_reference: string;
73 | bank_code: number;
74 | bank_name: string;
75 | bank_reference: string;
76 | status: string;
77 | reference: string;
78 | created_at: string;
79 | updated_at: string;
80 | }
81 |
82 | export interface GetTransfersResponse {
83 | message: string;
84 | status: string;
85 | data: Transfer[];
86 | meta: Meta;
87 | }
88 |
89 | /**
90 | * Verify Transfer
91 | */
92 |
93 | export interface VerifyTransferOptions {
94 | tx_ref: string;
95 | }
96 |
97 | interface Data {
98 | account_name: string;
99 | account_number: string;
100 | mobile: string;
101 | currency: string;
102 | amount: number;
103 | charge: number;
104 | mode: string;
105 | transfer_method: string;
106 | narration: string;
107 | chapa_transfer_id: string;
108 | bank_code: number;
109 | bank_name: string;
110 | cross_party_reference: string;
111 | ip_address: string;
112 | status: string;
113 | tx_ref: string;
114 | created_at: string;
115 | updated_at: string;
116 | }
117 |
118 | export interface VerifyTransferResponse {
119 | message: string;
120 | status: string;
121 | data: Data;
122 | }
123 |
--------------------------------------------------------------------------------
/src/validations/create-subaccount.validation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 | import { SplitType } from '../enums';
3 | import { CreateSubaccountOptions } from '../interfaces';
4 |
5 | export const validateCreateSubaccountOptions = async (
6 | createSubaccountOptions: CreateSubaccountOptions
7 | ) => {
8 | const schema = yup.object().shape({
9 | business_name: yup.string().required(),
10 | account_name: yup.string().required(),
11 | bank_code: yup.number().required(),
12 | account_number: yup.string().required(),
13 | split_type: yup
14 | .mixed()
15 | .oneOf(Object.values(SplitType))
16 | .required(),
17 | split_value: yup.number().required(),
18 | });
19 |
20 | return await schema.validate(createSubaccountOptions);
21 | };
22 |
--------------------------------------------------------------------------------
/src/validations/direct-charge.validation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 | import {
3 | AuthorizeDirectChargeOptions,
4 | DirectChargeOptions,
5 | } from '../interfaces';
6 |
7 | export const validateDirectChargeOptions = async (
8 | options: DirectChargeOptions
9 | ) => {
10 | const schema = yup.object().shape({
11 | first_name: yup
12 | .string()
13 | .nullable()
14 | .optional(),
15 | last_name: yup
16 | .string()
17 | .nullable()
18 | .optional(),
19 | email: yup
20 | .string()
21 | .email()
22 | .nullable()
23 | .optional(),
24 | mobile: yup
25 | .string()
26 | .matches(
27 | /^0[79]\d{8}$/,
28 | 'Phone number must be 10 digits and start with 09 or 07'
29 | )
30 | .required(),
31 | currency: yup.string().required(),
32 | amount: yup.string().required(),
33 | tx_ref: yup.string().required(),
34 | type: yup.string().required(),
35 | });
36 |
37 | return await schema.validate(options);
38 | };
39 |
40 | export const validateAuthorizeDirectChargeOptions = async (
41 | options: AuthorizeDirectChargeOptions
42 | ) => {
43 | const schema = yup.object().shape({
44 | reference: yup.string().required(),
45 | client: yup.string().required(),
46 | type: yup.string().required(),
47 | });
48 |
49 | return await schema.validate(options);
50 | };
51 |
--------------------------------------------------------------------------------
/src/validations/gen-tx-ref.validation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 | import { GenTxRefOptions } from '../interfaces';
3 |
4 | export const validateGenTxRefOptions = async (
5 | genTxRefOptions: GenTxRefOptions
6 | ) => {
7 | const schema = yup.object().shape({
8 | removePrefix: yup.boolean().optional(),
9 | prefix: yup.string().optional(),
10 | size: yup.number().optional(),
11 | });
12 |
13 | return await schema.validate(genTxRefOptions);
14 | };
15 |
--------------------------------------------------------------------------------
/src/validations/index.ts:
--------------------------------------------------------------------------------
1 | export * from './payment.validation';
2 | export * from './create-subaccount.validation';
3 | export * from './transaction.validation';
4 | export * from './transfer.validation';
5 | export * from './direct-charge.validation';
6 |
--------------------------------------------------------------------------------
/src/validations/payment.validation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 | import { InitializeOptions, VerifyOptions } from '../interfaces';
3 |
4 | export const validateInitializeOptions = async (options: InitializeOptions) => {
5 | const schema = yup.object().shape({
6 | first_name: yup
7 | .string()
8 | .nullable()
9 | .optional(),
10 | last_name: yup
11 | .string()
12 | .nullable()
13 | .optional(),
14 | email: yup
15 | .string()
16 | .email()
17 | .nullable()
18 | .optional(),
19 | phone_number: yup
20 | .string()
21 | .matches(
22 | /^0[79]\d{8}$/,
23 | 'Phone number must be 10 digits and start with 09 or 07'
24 | )
25 | .nullable()
26 | .optional(),
27 | currency: yup.string().required(),
28 | amount: yup.string().required(),
29 | tx_ref: yup.string().required(),
30 | callback_url: yup
31 | .string()
32 | .nullable()
33 | .optional(),
34 | return_url: yup
35 | .string()
36 | .nullable()
37 | .optional(),
38 | customization: yup
39 | .object()
40 | .shape({
41 | title: yup
42 | .string()
43 | .nullable()
44 | .optional(),
45 | description: yup
46 | .string()
47 | .nullable()
48 | .optional(),
49 | logo: yup
50 | .string()
51 | .nullable()
52 | .optional(),
53 | })
54 | .optional(),
55 | subaccounts: yup
56 | .array()
57 | .of(
58 | yup.object().shape({
59 | id: yup.string().required(),
60 | split_type: yup
61 | .string()
62 | .nullable()
63 | .optional(),
64 | split_value: yup
65 | .string()
66 | .nullable()
67 | .optional(),
68 | })
69 | )
70 | .nullable()
71 | .optional(),
72 | });
73 |
74 | return await schema.validate(options);
75 | };
76 |
77 | export const validateVerifyOptions = async (options: VerifyOptions) => {
78 | const schema = yup.object().shape({
79 | tx_ref: yup.string().required(),
80 | });
81 |
82 | return await schema.validate(options);
83 | };
84 |
--------------------------------------------------------------------------------
/src/validations/transaction.validation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 | import { GetTransactionLogsOptions } from '../interfaces';
3 |
4 | export const validateGetTransactionLogsOptions = async (
5 | options: GetTransactionLogsOptions
6 | ) => {
7 | const schema = yup.object().shape({
8 | ref_id: yup.string().required(),
9 | });
10 |
11 | return await schema.validate(options);
12 | };
13 |
--------------------------------------------------------------------------------
/src/validations/transfer.validation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 | import {
3 | BulkTransferOptions,
4 | TransferOptions,
5 | VerifyTransferOptions,
6 | } from '../interfaces';
7 |
8 | export const validateTransferOptions = async (options: TransferOptions) => {
9 | const schema = yup.object().shape({
10 | account_name: yup.string().required(),
11 | account_number: yup.string().required(),
12 | amount: yup.string().required(),
13 | currency: yup.string().required(),
14 | reference: yup.string().required(),
15 | bank_code: yup.number().required(),
16 | });
17 |
18 | return await schema.validate(options);
19 | };
20 |
21 | export const validateBulkTransferOptions = async (
22 | options: BulkTransferOptions
23 | ) => {
24 | const schema = yup.object().shape({
25 | title: yup.string().required(),
26 | currency: yup.string().required(),
27 | bulk_data: yup
28 | .array()
29 | .of(
30 | yup.object().shape({
31 | account_name: yup.string().required(),
32 | account_number: yup.string().required(),
33 | amount: yup.string().required(),
34 | reference: yup.string().required(),
35 | bank_code: yup.number().required(),
36 | })
37 | )
38 | .required(),
39 | });
40 |
41 | return await schema.validate(options);
42 | };
43 |
44 | export const validateVerifyTransferOptions = async (
45 | options: VerifyTransferOptions
46 | ) => {
47 | const schema = yup.object().shape({
48 | tx_ref: yup.string().required(),
49 | });
50 |
51 | return await schema.validate(options);
52 | };
53 |
--------------------------------------------------------------------------------
/test/sample.test.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fireayehu/chapa-nodejs/a42e94316ae442f0733725aea1cd92cd4f6551aa/test/sample.test.ts
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs
3 | "include": ["src", "types"],
4 | "compilerOptions": {
5 | "module": "esnext",
6 | "lib": ["dom", "esnext"],
7 | "importHelpers": true,
8 | // output .d.ts declaration files for consumers
9 | "declaration": true,
10 | // output .js.map sourcemap files for consumers
11 | "sourceMap": true,
12 | // match output dir to input dir. e.g. dist/index instead of dist/src/index
13 | "rootDir": "./src",
14 | // stricter type-checking for stronger correctness. Recommended by TS
15 | // linter checks for common issues
16 | "noImplicitReturns": true,
17 | "noFallthroughCasesInSwitch": true,
18 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | // use Node's module resolution algorithm, instead of the legacy TS one
22 | "moduleResolution": "node",
23 | // transpile JSX to React.createElement
24 | "jsx": "react",
25 | // interop between ESM and CJS modules. Recommended by TS
26 | "esModuleInterop": true,
27 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS
28 | "skipLibCheck": true,
29 | // error out if import and file system have a casing mismatch. Recommended by TS
30 | "forceConsistentCasingInFileNames": true,
31 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc`
32 | "noEmit": true
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/types/global.d.ts:
--------------------------------------------------------------------------------
1 | interface ErrorConstructor {
2 | captureStackTrace(targetObject: Object, constructorOpt?: Function): void;
3 | }
4 |
--------------------------------------------------------------------------------