├── .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 | Nest Logo 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 | --------------------------------------------------------------------------------