├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── classes │ └── client.ts ├── consts │ └── index.ts ├── index.ts ├── types │ ├── data.ts │ ├── param.ts │ └── response.ts └── utils │ └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | node_modules/ 3 | 4 | # Build outputs 5 | lib/ 6 | dist/ 7 | 8 | # TypeScript cache 9 | *.tsbuildinfo 10 | 11 | # Environment configuration files 12 | .env 13 | 14 | # IDE-specific files and directories 15 | .vscode/ 16 | .idea/ 17 | *.swp 18 | *.swo 19 | 20 | # Logs 21 | logs/ 22 | *.log 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # IDE 28 | .vscode 29 | 30 | # Other 31 | .DS_Store 32 | src/test.ts 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Chargily 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to JavaScript Package Repository 2 | # for [Chargily Pay](https://chargily.com/business/pay "Chargily Pay")™ Gateway - V2. 3 | 4 | Thank you for your interest in JS Package of Chargily Pay™, an open source project by Chargily, a leading fintech company in Algeria specializing in payment solutions and e-commerce facilitating, this Package is providing the easiest and free way to integrate e-payment API through widespread payment methods in Algeria such as EDAHABIA (Algerie Post) and CIB (SATIM) into your JavaScript/Node.js back-end projects. 5 | 6 | **IMPORTANT: This package is meant to be ONLY used in the server-side** 7 | 8 | This package is developed by **Abderraouf Zine ([ruzolut](https://github.com/ruzolut))** and is open to contributions from developers like you. 9 | 10 | 11 | ## Key Features 12 | 13 | - Easy integration with Chargily Pay e-payment gateway 14 | - Support for both EDAHABIA of Algerie Poste and CIB of SATIM 15 | - Comprehensive management of customers, products, and prices 16 | - Efficient handling of checkouts and payment links 17 | - Compatible with Node.js and browser environments 18 | - Support for webhooks. 19 | 20 | ## Installation 21 | 22 | To include this library in your project, you can use npm or yarn: 23 | 24 | ```shell 25 | npm install @chargily/chargily-pay 26 | ``` 27 | 28 | or 29 | 30 | ```shell 31 | yarn add @chargily/chargily-pay 32 | ``` 33 | 34 | ## Getting Started 35 | 36 | Before utilizing the library, you must configure it with your [Chargily API key](https://dev.chargily.com/pay-v2/api-keys) and specify the mode (test or live). Here's an example to get started: 37 | 38 | ```ts 39 | import { ChargilyClient } from '@chargily/chargily-pay'; 40 | 41 | const client = new ChargilyClient({ 42 | api_key: 'YOUR_API_SECRET_KEY_HERE', 43 | mode: 'test', // Change to 'live' when deploying your application 44 | }); 45 | ``` 46 | 47 | This initializes the Chargily client, ready for communication with the Chargily Pay API. 48 | 49 | ## Webhooks Notice 50 | 51 | **Important Notice:** 52 | 53 | Using webhooks is only suitable for back-end environments. 54 | 55 | Webhooks allow your application to react to events from Chargily Pay by receiving HTTP requests with JSON payloads. To set up and handle webhooks securely, you must implement them on a server-side environment. This ensures the proper verification and processing of webhook events without exposing sensitive information or risking security issues. 56 | 57 | When implementing webhooks: 58 | 59 | 1. **Verify the Signature:** Ensure the request is legitimate and untampered. 60 | 2. **Identify the Event:** Use the event type to determine the action. 61 | 3. **Handle the Event:** Execute the necessary actions based on the event type. 62 | 4. **Respond with 200:** Confirm receipt of the webhook. 63 | 64 | ### Example Webhook Endpoint 65 | 66 | We will be using express for this example, so first let's install some dependencies. 67 | 68 | ```shell 69 | npm install express body-parser 70 | ``` 71 | 72 | Then install the needed types for express 73 | 74 | ```shell 75 | npm i @types/express --save-dev 76 | ``` 77 | 78 | Now, here's how you can set up a secure webhook endpoint using Express: 79 | 80 | ```ts 81 | import bodyParser from 'body-parser'; 82 | import express, { Request, Response } from 'express'; 83 | import { verifySignature } from '@chargily/chargily-pay'; 84 | 85 | const API_SECRET_KEY = 'YOUR_API_SECRET_KEY_HERE'; 86 | 87 | const app = express(); 88 | const port = 4000; 89 | 90 | // Middleware to capture raw body as Buffer 91 | app.use( 92 | bodyParser.json({ 93 | verify: (req: Request, res: Response, buf: Buffer) => { 94 | (req as any).rawBody = buf; 95 | }, 96 | }) 97 | ); 98 | 99 | app.post('/webhook', (req: Request, res: Response) => { 100 | const signature = req.get('signature') || ''; 101 | const payload = (req as any).rawBody; 102 | 103 | if (!signature) { 104 | console.log('Signature header is missing'); 105 | res.sendStatus(400); 106 | return; 107 | } 108 | 109 | try { 110 | if (!verifySignature(payload, signature, API_SECRET_KEY)) { 111 | console.log('Signature is invalid'); 112 | res.sendStatus(403); 113 | return; 114 | } 115 | } catch (error) { 116 | console.log( 117 | 'Something happened while trying to process the request to the webhook' 118 | ); 119 | res.sendStatus(403); 120 | return; 121 | } 122 | 123 | const event = req.body; 124 | // You can use the event.type here to implement your own logic 125 | console.log(event); 126 | 127 | res.sendStatus(200); 128 | }); 129 | 130 | app.listen(port, () => { 131 | console.log(`Server is running at http://localhost:${port}`); 132 | }); 133 | ``` 134 | 135 | One more thing, if you wish to test your local webhook, we recommend using a tool like [NGROK](https://ngrok.com). 136 | 137 | You can run ngrok to open a tunnel to your local machine using this command 138 | 139 | ```shell 140 | ngrok http 4000 # or the port you are using 141 | ``` 142 | 143 | Ngrok will then return a public endpoint that you can add to Chargily's dashboard, by going [here](https://pay.chargily.com/test/dashboard/developers-corner) and pasting your endpoint in the webhook endpoint field. Also, make sure you don't forget to add `/webhook` or whatever url extension you used to the URL you paste there. 144 | 145 | ## Creating a Customer 146 | 147 | To create a customer, you can use the `createCustomer` method: 148 | 149 | ```ts 150 | const customerData = { 151 | name: 'John Doe', 152 | email: 'john.doe@example.com', 153 | phone: '+213xxxxxxxx', 154 | address: { 155 | country: 'DZ', 156 | state: 'Algiers', 157 | address: '123 Main St', 158 | }, 159 | metadata: { 160 | notes: 'Important customer', 161 | }, 162 | }; 163 | 164 | client 165 | .createCustomer(customerData) 166 | .then((customer) => console.log(customer)) 167 | .catch((error) => console.error(error)); 168 | ``` 169 | 170 | This method returns a promise with the created customer object. 171 | 172 | ## Updating a Customer 173 | 174 | To update an existing customer, use the `updateCustomer` method with the customer's ID and the data you want to update: 175 | 176 | ```ts 177 | const updateData = { 178 | email: 'new.email@example.com', 179 | metadata: { notes: 'Updated customer info' }, 180 | }; 181 | 182 | client 183 | .updateCustomer('customer_id_here', updateData) 184 | .then((customer) => console.log(customer)) 185 | .catch((error) => console.error(error)); 186 | ``` 187 | 188 | This will update the specified fields of the customer and return the updated customer object. 189 | 190 | ## Creating a Product 191 | 192 | To create a new product, you can use the `createProduct` method. Here's how to create a product named "Super Product": 193 | 194 | ```ts 195 | const productData = { 196 | name: 'Super Product', 197 | description: 'An amazing product that does everything!', 198 | images: ['http://example.com/image1.jpg', 'http://example.com/image2.jpg'], 199 | metadata: { category: 'electronics' }, 200 | }; 201 | 202 | client 203 | .createProduct(productData) 204 | .then((product) => console.log(product)) 205 | .catch((error) => console.error(error)); 206 | ``` 207 | 208 | This method requires the `name` of the product and optionally accepts `description`, an array of `images`, and `metadata`. 209 | 210 | ## Deleting a Customer 211 | 212 | To delete a customer from the Chargily Pay system, you can use the `deleteCustomer` method with the customer's ID: 213 | 214 | ```ts 215 | client 216 | .deleteCustomer('customer_id_here') 217 | .then((response) => console.log(response)) 218 | .catch((error) => console.error(error)); 219 | ``` 220 | 221 | This method will return a response indicating whether the deletion was successful. 222 | 223 | ## Listing Customers 224 | 225 | You can list all customers with optional pagination using the `listCustomers` method. Specify the number of customers per page using the `per_page` parameter: 226 | 227 | ```ts 228 | client 229 | .listCustomers(20) // List 20 customers per page 230 | .then((customersList) => console.log(customersList)) 231 | .catch((error) => console.error(error)); 232 | ``` 233 | 234 | The response will include a paginated list of customers along with pagination details. 235 | 236 | ## Updating a Customer 237 | 238 | To update an existing customer, you'll need the customer's ID: 239 | 240 | ```ts 241 | const updatedCustomer = await client.updateCustomer('CUSTOMER_ID', { 242 | name: 'Jane Doe', 243 | email: 'jane.doe@example.com', 244 | phone: '987654321', 245 | address: { 246 | country: 'DZ', 247 | state: 'Oran', 248 | address: '4321 Main St', 249 | }, 250 | metadata: { 251 | custom_field_updated: 'new value', 252 | }, 253 | }); 254 | ``` 255 | 256 | This call updates the specified customer and returns the updated customer object. 257 | 258 | ## Deleting a Customer 259 | 260 | To delete a customer, use their ID: 261 | 262 | ```ts 263 | const deleteResponse = await client.deleteCustomer('CUSTOMER_ID'); 264 | ``` 265 | 266 | This method returns a response indicating whether the deletion was successful. 267 | 268 | ## Creating a Product 269 | 270 | To add a new product to your catalog: 271 | 272 | ```ts 273 | const newProduct = await client.createProduct({ 274 | name: 'Awesome Product', 275 | description: 'A description of your awesome product', 276 | images: ['https://example.com/image.png'], 277 | metadata: { 278 | category: 'Electronics', 279 | }, 280 | }); 281 | ``` 282 | 283 | This creates a new product and returns the product object. 284 | 285 | ## Updating a Product 286 | 287 | Similar to customers, you can update products using their ID: 288 | 289 | ```ts 290 | const updatedProduct = await client.updateProduct('PRODUCT_ID', { 291 | name: 'Even More Awesome Product', 292 | description: 'An updated description', 293 | images: ['https://example.com/newimage.png'], 294 | metadata: { 295 | category: 'Updated Category', 296 | }, 297 | }); 298 | ``` 299 | 300 | This updates the product details and returns the updated product object. 301 | 302 | ## Creating a Price 303 | 304 | To create a price for a product, you need the product's ID: 305 | 306 | ```ts 307 | const newPrice = await client.createPrice({ 308 | amount: 5000, 309 | currency: 'dzd', 310 | product_id: 'PRODUCT_ID', 311 | metadata: { 312 | size: 'M', 313 | }, 314 | }); 315 | ``` 316 | 317 | This creates a new price for the specified product and returns the price object. 318 | 319 | ## Updating a Price 320 | 321 | You can update the metadata of a price by its ID: 322 | 323 | ```ts 324 | const updatedPrice = await client.updatePrice('PRICE_ID', { 325 | metadata: { 326 | size: 'L', 327 | }, 328 | }); 329 | ``` 330 | 331 | This updates the price's metadata and returns the updated price object. 332 | 333 | ## Creating a Checkout 334 | 335 | To create a checkout session for a customer to make a payment: 336 | 337 | ```ts 338 | const checkout = await client.createCheckout({ 339 | items: [ 340 | { 341 | price: 'PRICE_ID', 342 | quantity: 1, 343 | }, 344 | ], 345 | success_url: 'https://your-website.com/success', 346 | failure_url: 'https://your-website.com/failure', 347 | payment_method: 'edahabia', // Optional, defaults to 'edahabia' 348 | locale: 'en', // Optional, defaults to 'ar' 349 | pass_fees_to_customer: true, // Optional, defaults to false 350 | shipping_address: '123 Test St, Test City, DZ', // Optional 351 | collect_shipping_address: true, // Optional, defaults to false 352 | metadata: { 353 | order_id: '123456', 354 | }, 355 | }); 356 | ``` 357 | 358 | This creates a new checkout session and returns the checkout object, including a `checkout_url` where you can redirect your customer to complete their payment. 359 | 360 | ## Creating a Payment Link 361 | 362 | Payment links are URLs that you can share with your customers for payment: 363 | 364 | ```ts 365 | const paymentLink = await client.createPaymentLink({ 366 | name: 'Product Payment', 367 | items: [ 368 | { 369 | price: 'PRICE_ID', 370 | quantity: 1, 371 | adjustable_quantity: false, 372 | }, 373 | ], 374 | after_completion_message: 'Thank you for your purchase!', 375 | locale: 'en', 376 | pass_fees_to_customer: true, 377 | collect_shipping_address: true, 378 | metadata: { 379 | campaign: 'Summer Sale', 380 | }, 381 | }); 382 | ``` 383 | 384 | This creates a new payment link and returns the payment link object, including the URL that you can share with your customers. 385 | 386 | ## Handling Prices 387 | 388 | ### Creating a Price 389 | 390 | To set up a price for a product, you can use the product's ID: 391 | 392 | ```ts 393 | const newPrice = await client.createPrice({ 394 | amount: 5000, 395 | currency: 'dzd', 396 | product_id: 'PRODUCT_ID', 397 | metadata: { 398 | discount: '10%', 399 | }, 400 | }); 401 | ``` 402 | 403 | This call creates a new price for the specified product and returns the price object. 404 | 405 | ### Updating a Price 406 | 407 | Update a price by its ID: 408 | 409 | ```ts 410 | const updatedPrice = await client.updatePrice('PRICE_ID', { 411 | metadata: { 412 | discount: '15%', 413 | }, 414 | }); 415 | ``` 416 | 417 | This updates the metadata for the price and returns the updated price object. 418 | 419 | ### Fetching Prices 420 | 421 | To retrieve all prices for a product: 422 | 423 | ```ts 424 | const prices = await client.listPrices(); 425 | ``` 426 | 427 | This returns a paginated list of all prices. 428 | 429 | ## Working with Checkouts 430 | 431 | ### Creating a Checkout 432 | 433 | Creating a checkout is a crucial step for initiating a payment process. A checkout can be created by specifying either a list of items (products and quantities) or a total amount directly. You also need to provide a success URL and optionally a failure URL where your customer will be redirected after the payment process. 434 | 435 | Here's how you can create a checkout: 436 | 437 | ```ts 438 | const newCheckout = await client.createCheckout({ 439 | items: [ 440 | { price: 'PRICE_ID', quantity: 2 }, 441 | { price: 'ANOTHER_PRICE_ID', quantity: 1 }, 442 | ], 443 | success_url: 'https://yourdomain.com/success', 444 | failure_url: 'https://yourdomain.com/failure', 445 | payment_method: 'edahabia', 446 | customer_id: 'CUSTOMER_ID', 447 | metadata: { orderId: '123456' }, 448 | locale: 'en', 449 | pass_fees_to_customer: false, 450 | }); 451 | ``` 452 | 453 | This request creates a new checkout session and returns the checkout object, including a `checkout_url` where you should redirect your customer to complete the payment. 454 | 455 | ### Retrieving a Checkout 456 | 457 | To fetch details of a specific checkout session: 458 | 459 | ```ts 460 | const checkoutDetails = await client.getCheckout('CHECKOUT_ID'); 461 | ``` 462 | 463 | This retrieves the details of the specified checkout session. 464 | 465 | ## Managing Payment Links 466 | 467 | ### Creating a Payment Link 468 | 469 | Payment Links provide a versatile way to request payments by generating a unique URL that you can share with your customers. Here's how to create one: 470 | 471 | ```ts 472 | const paymentLink = await client.createPaymentLink({ 473 | name: 'Subscription Service', 474 | items: [{ price: 'PRICE_ID', quantity: 1, adjustable_quantity: false }], 475 | after_completion_message: 'Thank you for your subscription!', 476 | locale: 'en', 477 | pass_fees_to_customer: true, 478 | collect_shipping_address: true, 479 | metadata: { subscriptionId: 'sub_12345' }, 480 | }); 481 | ``` 482 | 483 | This creates a new payment link with specified details and returns the payment link object including the URL to be shared with your customers. 484 | 485 | ### Updating a Payment Link 486 | 487 | To update an existing payment link: 488 | 489 | ```ts 490 | const updatedLink = await client.updatePaymentLink('PAYMENT_LINK_ID', { 491 | name: 'Updated Subscription Service', 492 | after_completion_message: 'Thank you for updating your subscription!', 493 | metadata: { subscriptionId: 'sub_67890' }, 494 | }); 495 | ``` 496 | 497 | This updates the specified payment link and returns the updated object. 498 | 499 | ### Fetching a Payment Link 500 | 501 | Retrieve the details of a specific payment link: 502 | 503 | ```ts 504 | const linkDetails = await client.getPaymentLink('PAYMENT_LINK_ID'); 505 | ``` 506 | 507 | This call retrieves the specified payment link's details. 508 | 509 | ### Listing Payment Links 510 | 511 | To list all your payment links: 512 | 513 | ```ts 514 | const allLinks = await client.listPaymentLinks(); 515 | ``` 516 | 517 | This returns a paginated list of all payment links you've created. 518 | 519 | 520 | ## About Chargily Pay™ packages 521 | 522 | Chargily Pay™ packages/plugins are a collection of open source projects published by Chargily to facilitate the integration of our payment gateway into different programming languages and frameworks. Our goal is to empower developers and businesses by providing easy-to-use tools to seamlessly accept payments. 523 | 524 | ## API Documentation 525 | 526 | For detailed instructions on how to integrate with our API and utilize Chargily Pay™ in your projects, please refer to our [API Documentation](https://dev.chargily.com/pay-v2/introduction). 527 | 528 | ## Developers Community 529 | 530 | Join our developer community on Telegram to connect with fellow developers, ask questions, and stay updated on the latest news and developments related to Chargily Pay™ : [Telegram Community](https://chargi.link/PayTelegramCommunity) 531 | 532 | ## How to Contribute 533 | 534 | We welcome contributions of all kinds, whether it's bug fixes, feature enhancements, documentation improvements, or new plugin/package developments. Here's how you can get started: 535 | 536 | 1. **Fork the Repository:** Click the "Fork" button in the top-right corner of this page to create your own copy of the repository. 537 | 538 | 2. **Clone the Repository:** Clone your forked repository to your local machine using the following command: 539 | 540 | ```bash 541 | git clone https://github.com/Chargily/chargily-pay-javascript.git 542 | ``` 543 | 544 | 3. **Make Changes:** Make your desired changes or additions to the codebase. Be sure to follow our coding standards and guidelines. 545 | 546 | 4. **Test Your Changes:** Test your changes thoroughly to ensure they work as expected. 547 | 548 | 5. **Submit a Pull Request:** Once you're satisfied with your changes, submit a pull request back to the main repository. Our team will review your contributions and provide feedback if needed. 549 | 550 | ## Get in Touch 551 | 552 | Have questions or need assistance? Join our developer community on [Telegram](https://chargi.link/PayTelegramCommunity) and connect with fellow developers and our team. 553 | 554 | We appreciate your interest in contributing to Chargily Pay™! Together, we can build something amazing. 555 | 556 | Happy coding! 557 | 558 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chargily-pay-javascript", 3 | "version": "0.0.1-alpha", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "chargily-pay-javascript", 9 | "version": "0.0.1-alpha", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/node": "^14.0.0", 13 | "nodemon": "^3.1.0", 14 | "ts-node": "^10.9.2", 15 | "typescript": "^4.9.5" 16 | } 17 | }, 18 | "node_modules/@cspotcode/source-map-support": { 19 | "version": "0.8.1", 20 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 21 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 22 | "dev": true, 23 | "dependencies": { 24 | "@jridgewell/trace-mapping": "0.3.9" 25 | }, 26 | "engines": { 27 | "node": ">=12" 28 | } 29 | }, 30 | "node_modules/@jridgewell/resolve-uri": { 31 | "version": "3.1.2", 32 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 33 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 34 | "dev": true, 35 | "engines": { 36 | "node": ">=6.0.0" 37 | } 38 | }, 39 | "node_modules/@jridgewell/sourcemap-codec": { 40 | "version": "1.4.15", 41 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 42 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 43 | "dev": true 44 | }, 45 | "node_modules/@jridgewell/trace-mapping": { 46 | "version": "0.3.9", 47 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 48 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 49 | "dev": true, 50 | "dependencies": { 51 | "@jridgewell/resolve-uri": "^3.0.3", 52 | "@jridgewell/sourcemap-codec": "^1.4.10" 53 | } 54 | }, 55 | "node_modules/@tsconfig/node10": { 56 | "version": "1.0.9", 57 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", 58 | "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", 59 | "dev": true 60 | }, 61 | "node_modules/@tsconfig/node12": { 62 | "version": "1.0.11", 63 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 64 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 65 | "dev": true 66 | }, 67 | "node_modules/@tsconfig/node14": { 68 | "version": "1.0.3", 69 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 70 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 71 | "dev": true 72 | }, 73 | "node_modules/@tsconfig/node16": { 74 | "version": "1.0.4", 75 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", 76 | "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", 77 | "dev": true 78 | }, 79 | "node_modules/@types/node": { 80 | "version": "14.18.63", 81 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", 82 | "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", 83 | "dev": true 84 | }, 85 | "node_modules/abbrev": { 86 | "version": "1.1.1", 87 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 88 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", 89 | "dev": true 90 | }, 91 | "node_modules/acorn": { 92 | "version": "8.11.3", 93 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", 94 | "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", 95 | "dev": true, 96 | "bin": { 97 | "acorn": "bin/acorn" 98 | }, 99 | "engines": { 100 | "node": ">=0.4.0" 101 | } 102 | }, 103 | "node_modules/acorn-walk": { 104 | "version": "8.3.2", 105 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", 106 | "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", 107 | "dev": true, 108 | "engines": { 109 | "node": ">=0.4.0" 110 | } 111 | }, 112 | "node_modules/anymatch": { 113 | "version": "3.1.3", 114 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 115 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 116 | "dev": true, 117 | "dependencies": { 118 | "normalize-path": "^3.0.0", 119 | "picomatch": "^2.0.4" 120 | }, 121 | "engines": { 122 | "node": ">= 8" 123 | } 124 | }, 125 | "node_modules/arg": { 126 | "version": "4.1.3", 127 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 128 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 129 | "dev": true 130 | }, 131 | "node_modules/balanced-match": { 132 | "version": "1.0.2", 133 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 134 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 135 | "dev": true 136 | }, 137 | "node_modules/binary-extensions": { 138 | "version": "2.2.0", 139 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 140 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 141 | "dev": true, 142 | "engines": { 143 | "node": ">=8" 144 | } 145 | }, 146 | "node_modules/brace-expansion": { 147 | "version": "1.1.11", 148 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 149 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 150 | "dev": true, 151 | "dependencies": { 152 | "balanced-match": "^1.0.0", 153 | "concat-map": "0.0.1" 154 | } 155 | }, 156 | "node_modules/braces": { 157 | "version": "3.0.2", 158 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 159 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 160 | "dev": true, 161 | "dependencies": { 162 | "fill-range": "^7.0.1" 163 | }, 164 | "engines": { 165 | "node": ">=8" 166 | } 167 | }, 168 | "node_modules/chokidar": { 169 | "version": "3.6.0", 170 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 171 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 172 | "dev": true, 173 | "dependencies": { 174 | "anymatch": "~3.1.2", 175 | "braces": "~3.0.2", 176 | "glob-parent": "~5.1.2", 177 | "is-binary-path": "~2.1.0", 178 | "is-glob": "~4.0.1", 179 | "normalize-path": "~3.0.0", 180 | "readdirp": "~3.6.0" 181 | }, 182 | "engines": { 183 | "node": ">= 8.10.0" 184 | }, 185 | "funding": { 186 | "url": "https://paulmillr.com/funding/" 187 | }, 188 | "optionalDependencies": { 189 | "fsevents": "~2.3.2" 190 | } 191 | }, 192 | "node_modules/concat-map": { 193 | "version": "0.0.1", 194 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 195 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 196 | "dev": true 197 | }, 198 | "node_modules/create-require": { 199 | "version": "1.1.1", 200 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 201 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 202 | "dev": true 203 | }, 204 | "node_modules/debug": { 205 | "version": "4.3.4", 206 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 207 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 208 | "dev": true, 209 | "dependencies": { 210 | "ms": "2.1.2" 211 | }, 212 | "engines": { 213 | "node": ">=6.0" 214 | }, 215 | "peerDependenciesMeta": { 216 | "supports-color": { 217 | "optional": true 218 | } 219 | } 220 | }, 221 | "node_modules/diff": { 222 | "version": "4.0.2", 223 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 224 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 225 | "dev": true, 226 | "engines": { 227 | "node": ">=0.3.1" 228 | } 229 | }, 230 | "node_modules/fill-range": { 231 | "version": "7.0.1", 232 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 233 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 234 | "dev": true, 235 | "dependencies": { 236 | "to-regex-range": "^5.0.1" 237 | }, 238 | "engines": { 239 | "node": ">=8" 240 | } 241 | }, 242 | "node_modules/fsevents": { 243 | "version": "2.3.3", 244 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 245 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 246 | "dev": true, 247 | "hasInstallScript": true, 248 | "optional": true, 249 | "os": [ 250 | "darwin" 251 | ], 252 | "engines": { 253 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 254 | } 255 | }, 256 | "node_modules/glob-parent": { 257 | "version": "5.1.2", 258 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 259 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 260 | "dev": true, 261 | "dependencies": { 262 | "is-glob": "^4.0.1" 263 | }, 264 | "engines": { 265 | "node": ">= 6" 266 | } 267 | }, 268 | "node_modules/has-flag": { 269 | "version": "3.0.0", 270 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 271 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 272 | "dev": true, 273 | "engines": { 274 | "node": ">=4" 275 | } 276 | }, 277 | "node_modules/ignore-by-default": { 278 | "version": "1.0.1", 279 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 280 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", 281 | "dev": true 282 | }, 283 | "node_modules/is-binary-path": { 284 | "version": "2.1.0", 285 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 286 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 287 | "dev": true, 288 | "dependencies": { 289 | "binary-extensions": "^2.0.0" 290 | }, 291 | "engines": { 292 | "node": ">=8" 293 | } 294 | }, 295 | "node_modules/is-extglob": { 296 | "version": "2.1.1", 297 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 298 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 299 | "dev": true, 300 | "engines": { 301 | "node": ">=0.10.0" 302 | } 303 | }, 304 | "node_modules/is-glob": { 305 | "version": "4.0.3", 306 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 307 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 308 | "dev": true, 309 | "dependencies": { 310 | "is-extglob": "^2.1.1" 311 | }, 312 | "engines": { 313 | "node": ">=0.10.0" 314 | } 315 | }, 316 | "node_modules/is-number": { 317 | "version": "7.0.0", 318 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 319 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 320 | "dev": true, 321 | "engines": { 322 | "node": ">=0.12.0" 323 | } 324 | }, 325 | "node_modules/lru-cache": { 326 | "version": "6.0.0", 327 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 328 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 329 | "dev": true, 330 | "dependencies": { 331 | "yallist": "^4.0.0" 332 | }, 333 | "engines": { 334 | "node": ">=10" 335 | } 336 | }, 337 | "node_modules/make-error": { 338 | "version": "1.3.6", 339 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 340 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 341 | "dev": true 342 | }, 343 | "node_modules/minimatch": { 344 | "version": "3.1.2", 345 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 346 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 347 | "dev": true, 348 | "dependencies": { 349 | "brace-expansion": "^1.1.7" 350 | }, 351 | "engines": { 352 | "node": "*" 353 | } 354 | }, 355 | "node_modules/ms": { 356 | "version": "2.1.2", 357 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 358 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 359 | "dev": true 360 | }, 361 | "node_modules/nodemon": { 362 | "version": "3.1.0", 363 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", 364 | "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==", 365 | "dev": true, 366 | "dependencies": { 367 | "chokidar": "^3.5.2", 368 | "debug": "^4", 369 | "ignore-by-default": "^1.0.1", 370 | "minimatch": "^3.1.2", 371 | "pstree.remy": "^1.1.8", 372 | "semver": "^7.5.3", 373 | "simple-update-notifier": "^2.0.0", 374 | "supports-color": "^5.5.0", 375 | "touch": "^3.1.0", 376 | "undefsafe": "^2.0.5" 377 | }, 378 | "bin": { 379 | "nodemon": "bin/nodemon.js" 380 | }, 381 | "engines": { 382 | "node": ">=10" 383 | }, 384 | "funding": { 385 | "type": "opencollective", 386 | "url": "https://opencollective.com/nodemon" 387 | } 388 | }, 389 | "node_modules/nopt": { 390 | "version": "1.0.10", 391 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 392 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", 393 | "dev": true, 394 | "dependencies": { 395 | "abbrev": "1" 396 | }, 397 | "bin": { 398 | "nopt": "bin/nopt.js" 399 | }, 400 | "engines": { 401 | "node": "*" 402 | } 403 | }, 404 | "node_modules/normalize-path": { 405 | "version": "3.0.0", 406 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 407 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 408 | "dev": true, 409 | "engines": { 410 | "node": ">=0.10.0" 411 | } 412 | }, 413 | "node_modules/picomatch": { 414 | "version": "2.3.1", 415 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 416 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 417 | "dev": true, 418 | "engines": { 419 | "node": ">=8.6" 420 | }, 421 | "funding": { 422 | "url": "https://github.com/sponsors/jonschlinkert" 423 | } 424 | }, 425 | "node_modules/pstree.remy": { 426 | "version": "1.1.8", 427 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 428 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", 429 | "dev": true 430 | }, 431 | "node_modules/readdirp": { 432 | "version": "3.6.0", 433 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 434 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 435 | "dev": true, 436 | "dependencies": { 437 | "picomatch": "^2.2.1" 438 | }, 439 | "engines": { 440 | "node": ">=8.10.0" 441 | } 442 | }, 443 | "node_modules/semver": { 444 | "version": "7.6.0", 445 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", 446 | "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", 447 | "dev": true, 448 | "dependencies": { 449 | "lru-cache": "^6.0.0" 450 | }, 451 | "bin": { 452 | "semver": "bin/semver.js" 453 | }, 454 | "engines": { 455 | "node": ">=10" 456 | } 457 | }, 458 | "node_modules/simple-update-notifier": { 459 | "version": "2.0.0", 460 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", 461 | "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", 462 | "dev": true, 463 | "dependencies": { 464 | "semver": "^7.5.3" 465 | }, 466 | "engines": { 467 | "node": ">=10" 468 | } 469 | }, 470 | "node_modules/supports-color": { 471 | "version": "5.5.0", 472 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 473 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 474 | "dev": true, 475 | "dependencies": { 476 | "has-flag": "^3.0.0" 477 | }, 478 | "engines": { 479 | "node": ">=4" 480 | } 481 | }, 482 | "node_modules/to-regex-range": { 483 | "version": "5.0.1", 484 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 485 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 486 | "dev": true, 487 | "dependencies": { 488 | "is-number": "^7.0.0" 489 | }, 490 | "engines": { 491 | "node": ">=8.0" 492 | } 493 | }, 494 | "node_modules/touch": { 495 | "version": "3.1.0", 496 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 497 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 498 | "dev": true, 499 | "dependencies": { 500 | "nopt": "~1.0.10" 501 | }, 502 | "bin": { 503 | "nodetouch": "bin/nodetouch.js" 504 | } 505 | }, 506 | "node_modules/ts-node": { 507 | "version": "10.9.2", 508 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", 509 | "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", 510 | "dev": true, 511 | "dependencies": { 512 | "@cspotcode/source-map-support": "^0.8.0", 513 | "@tsconfig/node10": "^1.0.7", 514 | "@tsconfig/node12": "^1.0.7", 515 | "@tsconfig/node14": "^1.0.0", 516 | "@tsconfig/node16": "^1.0.2", 517 | "acorn": "^8.4.1", 518 | "acorn-walk": "^8.1.1", 519 | "arg": "^4.1.0", 520 | "create-require": "^1.1.0", 521 | "diff": "^4.0.1", 522 | "make-error": "^1.1.1", 523 | "v8-compile-cache-lib": "^3.0.1", 524 | "yn": "3.1.1" 525 | }, 526 | "bin": { 527 | "ts-node": "dist/bin.js", 528 | "ts-node-cwd": "dist/bin-cwd.js", 529 | "ts-node-esm": "dist/bin-esm.js", 530 | "ts-node-script": "dist/bin-script.js", 531 | "ts-node-transpile-only": "dist/bin-transpile.js", 532 | "ts-script": "dist/bin-script-deprecated.js" 533 | }, 534 | "peerDependencies": { 535 | "@swc/core": ">=1.2.50", 536 | "@swc/wasm": ">=1.2.50", 537 | "@types/node": "*", 538 | "typescript": ">=2.7" 539 | }, 540 | "peerDependenciesMeta": { 541 | "@swc/core": { 542 | "optional": true 543 | }, 544 | "@swc/wasm": { 545 | "optional": true 546 | } 547 | } 548 | }, 549 | "node_modules/typescript": { 550 | "version": "4.9.5", 551 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", 552 | "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", 553 | "dev": true, 554 | "bin": { 555 | "tsc": "bin/tsc", 556 | "tsserver": "bin/tsserver" 557 | }, 558 | "engines": { 559 | "node": ">=4.2.0" 560 | } 561 | }, 562 | "node_modules/undefsafe": { 563 | "version": "2.0.5", 564 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", 565 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", 566 | "dev": true 567 | }, 568 | "node_modules/v8-compile-cache-lib": { 569 | "version": "3.0.1", 570 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 571 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 572 | "dev": true 573 | }, 574 | "node_modules/yallist": { 575 | "version": "4.0.0", 576 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 577 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 578 | "dev": true 579 | }, 580 | "node_modules/yn": { 581 | "version": "3.1.1", 582 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 583 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 584 | "dev": true, 585 | "engines": { 586 | "node": ">=6" 587 | } 588 | } 589 | } 590 | } 591 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@chargily/chargily-pay", 3 | "version": "2.1.0", 4 | "description": "JavaScript Library for Chargily Pay™ Gateway - V2. The easiest and free way to integrate e-payment API through EDAHABIA of Algerie Poste and CIB of SATIM into your JS project.", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "files": [ 8 | "lib" 9 | ], 10 | "scripts": { 11 | "dev": "nodemon --exec ts-node ./src/index.ts", 12 | "build": "tsc" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/chargily/chargily-pay-javascript.git" 17 | }, 18 | "keywords": [ 19 | "chargily", 20 | "payment", 21 | "gateway", 22 | "e-payment", 23 | "algeria", 24 | "edahabia", 25 | "cib", 26 | "javascript", 27 | "typescript" 28 | ], 29 | "author": { 30 | "name": "Abderraouf Zine", 31 | "email": "rofazayn@gmail.com", 32 | "url": "https://github.com/rofazayn" 33 | }, 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/chargily/chargily-pay-javascript/issues" 37 | }, 38 | "homepage": "https://github.com/chargily/chargily-pay-javascript#readme", 39 | "devDependencies": { 40 | "@types/node": "^14.0.0", 41 | "nodemon": "^3.1.0", 42 | "ts-node": "^10.9.2", 43 | "typescript": "^4.9.5" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/classes/client.ts: -------------------------------------------------------------------------------- 1 | import { CHARGILY_LIVE_URL, CHARGILY_TEST_URL } from '../consts'; 2 | import { 3 | Balance, 4 | Checkout, 5 | Customer, 6 | PaymentLink, 7 | Price, 8 | Product, 9 | ProductPrice, 10 | } from '../types/data'; 11 | import { 12 | CheckoutItemParams, 13 | CreateCheckoutParams, 14 | CreateCustomerParams, 15 | CreatePaymentLinkParams, 16 | CreatePriceParams, 17 | CreateProductParams, 18 | PaymentLinkItemParams, 19 | UpdateCustomerParams, 20 | UpdatePaymentLinkParams, 21 | UpdatePriceParams, 22 | UpdateProductParams, 23 | } from '../types/param'; 24 | import { DeleteItemResponse, ListResponse } from '../types/response'; 25 | 26 | /** 27 | * Configuration options for ChargilyClient. 28 | */ 29 | export interface ChargilyClientOptions { 30 | /** 31 | * The API key for authentication with Chargily API. 32 | * @type {string} 33 | */ 34 | api_key: string; 35 | 36 | /** 37 | * Operating mode of the client, indicating whether to use the test or live API endpoints. 38 | * @type {'test' | 'live'} 39 | */ 40 | mode: 'test' | 'live'; 41 | } 42 | 43 | /** 44 | * A client for interacting with Chargily's API, supporting operations for customers, products, prices, checkouts, and payment links. 45 | */ 46 | export class ChargilyClient { 47 | private api_key: string; 48 | private base_url: string; 49 | 50 | /** 51 | * Constructs a ChargilyClient instance. 52 | * @param {ChargilyClientOptions} options - Configuration options including API key and mode. 53 | */ 54 | constructor(options: ChargilyClientOptions) { 55 | this.api_key = options.api_key; 56 | this.base_url = 57 | options.mode === 'test' ? CHARGILY_TEST_URL : CHARGILY_LIVE_URL; 58 | } 59 | 60 | /** 61 | * Internal method to make requests to the Chargily API. 62 | * @param {string} endpoint - The endpoint path to make the request to. 63 | * @param {string} [method='GET'] - The HTTP method for the request. 64 | * @param {Object} [body] - The request payload, necessary for POST or PATCH requests. 65 | * @returns {Promise} - The JSON response from the API. 66 | * @private 67 | */ 68 | private async request( 69 | endpoint: string, 70 | method: string = 'GET', 71 | body?: any 72 | ): Promise { 73 | const url = `${this.base_url}/${endpoint}`; 74 | const headers = { 75 | Authorization: `Bearer ${this.api_key}`, 76 | 'Content-Type': 'application/json', 77 | }; 78 | 79 | const fetchOptions: RequestInit = { 80 | method, 81 | headers, 82 | }; 83 | 84 | if (body !== undefined) { 85 | fetchOptions.body = JSON.stringify(body); 86 | } 87 | 88 | try { 89 | const response = await fetch(url, fetchOptions); 90 | 91 | if (!response.ok) { 92 | throw new Error( 93 | `API request failed with status ${response.status}: ${response.statusText}` 94 | ); 95 | } 96 | 97 | return response.json(); 98 | } catch (error) { 99 | throw new Error(`Failed to make API request: ${error}`); 100 | } 101 | } 102 | 103 | /** 104 | * Retrieves the current balance information from the Chargily API. 105 | * @returns {Promise} - A promise that resolves to the balance information. 106 | */ 107 | public async getBalance(): Promise { 108 | return this.request('balance', 'GET'); 109 | } 110 | 111 | /** 112 | * Creates a new customer with specified details. 113 | * @param {CreateCustomerParams} customer_data - The data for creating a new customer. 114 | * @returns {Promise} - A promise that resolves to the newly created customer. 115 | */ 116 | public async createCustomer( 117 | customer_data: CreateCustomerParams 118 | ): Promise { 119 | return this.request('customers', 'POST', customer_data); 120 | } 121 | 122 | /** 123 | * Fetches a customer by their unique identifier. 124 | * @param {string} customer_id - The ID of the customer to retrieve. 125 | * @returns {Promise} - A promise that resolves to the customer details. 126 | */ 127 | public async getCustomer(customer_id: string): Promise { 128 | return this.request(`customers/${customer_id}`, 'GET'); 129 | } 130 | 131 | /** 132 | * Updates an existing customer's details. 133 | * @param {string} customer_id - The ID of the customer to update. 134 | * @param {UpdateCustomerParams} update_data - New data for updating the customer. 135 | * @returns {Promise} - A promise that resolves to the updated customer details. 136 | */ 137 | public async updateCustomer( 138 | customer_id: string, 139 | update_data: UpdateCustomerParams 140 | ): Promise { 141 | return this.request(`customers/${customer_id}`, 'PATCH', update_data); 142 | } 143 | 144 | /** 145 | * Deletes a customer by their unique identifier. 146 | * @param {string} customer_id - The ID of the customer to delete. 147 | * @returns {Promise} - A promise that resolves to the deletion response. 148 | */ 149 | public async deleteCustomer( 150 | customer_id: string 151 | ): Promise { 152 | return this.request(`customers/${customer_id}`, 'DELETE'); 153 | } 154 | 155 | /** 156 | * Lists customers, optionally paginated. 157 | * @param {number} [per_page=10] - The number of customers to return per page. 158 | * @returns {Promise>} - A promise that resolves to a paginated list of customers. 159 | */ 160 | public async listCustomers( 161 | per_page: number = 10 162 | ): Promise> { 163 | const endpoint = `customers?per_page=${per_page}`; 164 | const response: ListResponse = await this.request( 165 | endpoint, 166 | 'GET' 167 | ); 168 | return response; 169 | } 170 | 171 | /** 172 | * Creates a new product with the given details. 173 | * @param {CreateProductParams} product_data - The data for creating the product. 174 | * @returns {Promise} The created product. 175 | */ 176 | public async createProduct( 177 | product_data: CreateProductParams 178 | ): Promise { 179 | return this.request('products', 'POST', product_data); 180 | } 181 | 182 | /** 183 | * Updates an existing product identified by its ID. 184 | * @param {string} product_id - The ID of the product to update. 185 | * @param {UpdateProductParams} update_data - The data to update the product with. 186 | * @returns {Promise} The updated product. 187 | */ 188 | public async updateProduct( 189 | product_id: string, 190 | update_data: UpdateProductParams 191 | ): Promise { 192 | return this.request(`products/${product_id}`, 'POST', update_data); 193 | } 194 | 195 | /** 196 | * Retrieves a single product by its ID. 197 | * @param {string} product_id - The ID of the product to retrieve. 198 | * @returns {Promise} The requested product. 199 | */ 200 | public async getProduct(product_id: string): Promise { 201 | return this.request(`products/${product_id}`, 'GET'); 202 | } 203 | 204 | /** 205 | * Lists all products with optional pagination. 206 | * @param {number} [per_page=10] - The number of products to return per page. 207 | * @returns {Promise>} A paginated list of products. 208 | */ 209 | public async listProducts( 210 | per_page: number = 10 211 | ): Promise> { 212 | const endpoint = `products?per_page=${per_page}`; 213 | const response: ListResponse = await this.request(endpoint, 'GET'); 214 | return response; 215 | } 216 | 217 | /** 218 | * Deletes a product by its ID. 219 | * @param {string} product_id - The ID of the product to delete. 220 | * @returns {Promise} Confirmation of the product deletion. 221 | */ 222 | public async deleteProduct(product_id: string): Promise { 223 | return this.request(`products/${product_id}`, 'DELETE'); 224 | } 225 | 226 | /** 227 | * Retrieves all prices associated with a product, with optional pagination. 228 | * @param {string} product_id - The ID of the product whose prices are to be retrieved. 229 | * @param {number} [per_page=10] - The number of prices to return per page. 230 | * @returns {Promise>} A paginated list of prices for the specified product. 231 | */ 232 | public async getProductPrices( 233 | product_id: string, 234 | per_page: number = 10 235 | ): Promise> { 236 | const endpoint = `products/${product_id}/prices?per_page=${per_page}`; 237 | const response: ListResponse = await this.request( 238 | endpoint, 239 | 'GET' 240 | ); 241 | return response; 242 | } 243 | 244 | /** 245 | * Creates a new price for a product. 246 | * @param {CreatePriceParams} price_data - The details of the new price to be created. 247 | * @returns {Promise} The created price object. 248 | */ 249 | public async createPrice(price_data: CreatePriceParams): Promise { 250 | return this.request('prices', 'POST', price_data); 251 | } 252 | 253 | /** 254 | * Updates the details of an existing price. 255 | * @param {string} price_id - The ID of the price to be updated. 256 | * @param {UpdatePriceParams} update_data - The new details for the price. 257 | * @returns {Promise} The updated price object. 258 | */ 259 | public async updatePrice( 260 | price_id: string, 261 | update_data: UpdatePriceParams 262 | ): Promise { 263 | return this.request(`prices/${price_id}`, 'POST', update_data); 264 | } 265 | 266 | /** 267 | * Retrieves a single price by its ID. 268 | * @param {string} price_id - The ID of the price to retrieve. 269 | * @returns {Promise} The requested price object. 270 | */ 271 | public async getPrice(price_id: string): Promise { 272 | return this.request(`prices/${price_id}`, 'GET'); 273 | } 274 | 275 | /** 276 | * Lists all prices for products with optional pagination. 277 | * @param {number} [per_page=10] - The number of price objects to return per page. 278 | * @returns {Promise>} A paginated list of prices. 279 | */ 280 | public async listPrices(per_page: number = 10): Promise> { 281 | const endpoint = `prices?per_page=${per_page}`; 282 | const response: ListResponse = await this.request(endpoint, 'GET'); 283 | return response; 284 | } 285 | 286 | /** 287 | * Creates a new checkout session with the specified details. 288 | * @param {CreateCheckoutParams} checkout_data - The details for the new checkout session. 289 | * @returns {Promise} The created checkout object. 290 | */ 291 | public async createCheckout( 292 | checkout_data: CreateCheckoutParams 293 | ): Promise { 294 | if ( 295 | !checkout_data.success_url.startsWith('http') && 296 | !checkout_data.success_url.startsWith('https') 297 | ) { 298 | throw new Error('Invalid success_url, it must begin with http or https.'); 299 | } 300 | 301 | if ( 302 | !checkout_data.items && 303 | (!checkout_data.amount || !checkout_data.currency) 304 | ) { 305 | throw new Error( 306 | 'The items field is required when amount and currency are not present.' 307 | ); 308 | } 309 | 310 | return this.request('checkouts', 'POST', checkout_data); 311 | } 312 | 313 | /** 314 | * Retrieves details of a specific checkout session by its ID. 315 | * @param {string} checkout_id - The ID of the checkout session to retrieve. 316 | * @returns {Promise} The requested checkout object. 317 | */ 318 | public async getCheckout(checkout_id: string): Promise { 319 | return this.request(`checkouts/${checkout_id}`, 'GET'); 320 | } 321 | 322 | /** 323 | * Lists all checkout sessions with optional pagination. 324 | * @param {number} [per_page=10] - The number of checkout objects to return per page. 325 | * @returns {Promise>} A paginated list of checkout sessions. 326 | */ 327 | public async listCheckouts( 328 | per_page: number = 10 329 | ): Promise> { 330 | const endpoint = `checkouts?per_page=${per_page}`; 331 | const response: ListResponse = await this.request( 332 | endpoint, 333 | 'GET' 334 | ); 335 | return response; 336 | } 337 | 338 | /** 339 | * Retrieves all items included in a specific checkout session, with optional pagination. 340 | * @param {string} checkout_id - The ID of the checkout session. 341 | * @param {number} [per_page=10] - The number of items to return per page. 342 | * @returns {Promise>} A paginated list of items in the checkout session. 343 | */ 344 | public async getCheckoutItems( 345 | checkout_id: string, 346 | per_page: number = 10 347 | ): Promise> { 348 | const endpoint = `checkouts/${checkout_id}/items?per_page=${per_page}`; 349 | const response: ListResponse = await this.request( 350 | endpoint, 351 | 'GET' 352 | ); 353 | return response; 354 | } 355 | 356 | /** 357 | * Expires a specific checkout session before its automatic expiration. 358 | * @param {string} checkout_id - The ID of the checkout session to expire. 359 | * @returns {Promise} The expired checkout object, indicating the session is no longer valid for payment. 360 | */ 361 | public async expireCheckout(checkout_id: string): Promise { 362 | return this.request(`checkouts/${checkout_id}/expire`, 'POST'); 363 | } 364 | 365 | /** 366 | * Creates a new payment link. 367 | * @param {CreatePaymentLinkParams} payment_link_data - The details for the new payment link. 368 | * @returns {Promise} The created payment link object. 369 | */ 370 | public async createPaymentLink( 371 | payment_link_data: CreatePaymentLinkParams 372 | ): Promise { 373 | return this.request('payment-links', 'POST', payment_link_data); 374 | } 375 | 376 | /** 377 | * Updates an existing payment link identified by its ID. 378 | * @param {string} payment_link_id - The ID of the payment link to update. 379 | * @param {UpdatePaymentLinkParams} update_data - The new details for the payment link. 380 | * @returns {Promise} The updated payment link object. 381 | */ 382 | public async updatePaymentLink( 383 | payment_link_id: string, 384 | update_data: UpdatePaymentLinkParams 385 | ): Promise { 386 | return this.request( 387 | `payment-links/${payment_link_id}`, 388 | 'POST', 389 | update_data 390 | ); 391 | } 392 | 393 | /** 394 | * Retrieves details of a specific payment link by its ID. 395 | * @param {string} payment_link_id - The ID of the payment link to retrieve. 396 | * @returns {Promise} The requested payment link object. 397 | */ 398 | public async getPaymentLink(payment_link_id: string): Promise { 399 | return this.request(`payment-links/${payment_link_id}`, 'GET'); 400 | } 401 | 402 | /** 403 | * Lists all payment links with optional pagination. 404 | * @param {number} [per_page=10] - The number of payment link objects to return per page. 405 | * @returns {Promise>} A paginated list of payment links. 406 | */ 407 | public async listPaymentLinks( 408 | per_page: number = 10 409 | ): Promise> { 410 | const endpoint = `payment-links?per_page=${per_page}`; 411 | const response: ListResponse = await this.request( 412 | endpoint, 413 | 'GET' 414 | ); 415 | return response; 416 | } 417 | 418 | /** 419 | * Retrieves all items associated with a specific payment link, with optional pagination. 420 | * @param {string} payment_link_id - The ID of the payment link whose items are to be retrieved. 421 | * @param {number} [per_page=10] - The number of items to return per page. 422 | * @returns {Promise>} A paginated list of items associated with the payment link. 423 | */ 424 | public async getPaymentLinkItems( 425 | payment_link_id: string, 426 | per_page: number = 10 427 | ): Promise> { 428 | const endpoint = `payment-links/${payment_link_id}/items?per_page=${per_page}`; 429 | const response: ListResponse = await this.request( 430 | endpoint, 431 | 'GET' 432 | ); 433 | return response; 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /src/consts/index.ts: -------------------------------------------------------------------------------- 1 | export const CHARGILY_TEST_URL = 'https://pay.chargily.net/test/api/v2'; 2 | export const CHARGILY_LIVE_URL = 'https://pay.chargily.net/api/v2'; 3 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Exporting all constants from consts/index.ts 2 | export * from './consts'; 3 | 4 | // Exporting all types from types/data.ts 5 | export * from './types/data'; 6 | 7 | // Exporting all types from types/param.ts 8 | export * from './types/param'; 9 | 10 | // Exporting all types from types/response.ts 11 | export * from './types/response'; 12 | 13 | // Exporting the ChargilyClient class from classes/client.ts 14 | export * from './classes/client'; 15 | 16 | // Exporting all the utility functions from utils folder 17 | export * from './utils'; 18 | -------------------------------------------------------------------------------- /src/types/data.ts: -------------------------------------------------------------------------------- 1 | /** Represents a wallet with its currency and balances. */ 2 | export interface Wallet { 3 | /** A lowercase ISO currency code. */ 4 | currency: string; 5 | 6 | /** The total balance available in the wallet. */ 7 | balance: number; 8 | 9 | /** The amount available for payout. */ 10 | ready_for_payout: number; 11 | 12 | /** The amount currently on hold. */ 13 | on_hold: number; 14 | } 15 | 16 | /** Represents the balance object containing wallet information. */ 17 | export interface Balance { 18 | /** A string representing the type of the object. */ 19 | entity: string; 20 | 21 | /** True for Live Mode, False for Test Mode. */ 22 | livemode: boolean; 23 | 24 | /** An array of wallet objects for each currency. */ 25 | wallets: Wallet[]; 26 | } 27 | 28 | /** Represents a physical address with country, state, and detailed address. */ 29 | export interface Address { 30 | /** Two-letter country code (ISO 3166-1 alpha-2). */ 31 | country: string; 32 | 33 | /** The state or region of the address. */ 34 | state: string; 35 | 36 | /** Detailed address line including street name, number, etc. */ 37 | address: string; 38 | } 39 | 40 | /** Represents a customer with their personal and contact information. */ 41 | export interface Customer { 42 | /** Unique identifier of the customer. */ 43 | id: string; 44 | 45 | /** A string representing the type of the object. */ 46 | entity: string; 47 | 48 | /** True for Live Mode, False for Test Mode. */ 49 | livemode: boolean; 50 | 51 | /** Full name of the customer. */ 52 | name: string; 53 | 54 | /** Email address of the customer. Can be null if not provided. */ 55 | email: string | null; 56 | 57 | /** Phone number of the customer. Can be null if not provided. */ 58 | phone: string | null; 59 | 60 | /** Physical address of the customer. Can be null if not provided. */ 61 | address: Address | null; 62 | 63 | /** A set of key-value pairs that can be used to store additional information about the customer. */ 64 | metadata: Record; 65 | 66 | /** Timestamp indicating when the customer was created. */ 67 | created_at: number; 68 | 69 | /** Timestamp indicating when the customer was last updated. */ 70 | updated_at: number; 71 | } 72 | 73 | /** Represents a product with its details and associated metadata. */ 74 | export interface Product { 75 | /** Unique identifier of the product. */ 76 | id: string; 77 | 78 | /** A string representing the type of the object. */ 79 | entity: string; 80 | 81 | /** True for Live Mode, False for Test Mode. */ 82 | livemode: boolean; 83 | 84 | /** Name of the product. */ 85 | name: string; 86 | 87 | /** Description of the product. Can be null if not provided. */ 88 | description: string | null; 89 | 90 | /** Array of image URLs associated with the product. */ 91 | images: string[]; 92 | 93 | /** A set of key-value pairs that can be used to store additional information about the product. */ 94 | metadata: Record; 95 | 96 | /** Timestamp indicating when the product was created. */ 97 | created_at: number; 98 | 99 | /** Timestamp indicating when the product was last updated. */ 100 | updated_at: number; 101 | } 102 | 103 | /** Represents a price object associated with a product. */ 104 | export interface ProductPrice { 105 | /** Unique identifier of the price. */ 106 | id: string; 107 | 108 | /** A string representing the type of the object, "price" in this context. */ 109 | entity: string; 110 | 111 | /** The amount specified for this price. */ 112 | amount: number; 113 | 114 | /** The currency code for the price, e.g., "dzd". */ 115 | currency: string; 116 | 117 | /** Metadata associated with the price. Can be null. */ 118 | metadata: Record | null; 119 | 120 | /** Timestamp indicating when the price was created. */ 121 | created_at: number; 122 | 123 | /** Timestamp indicating when the price was last updated. */ 124 | updated_at: number; 125 | 126 | /** The product ID associated with this price. */ 127 | product_id: string; 128 | } 129 | 130 | /** Represents a price object with additional product association. */ 131 | export interface Price { 132 | /** Unique identifier of the price. */ 133 | id: string; 134 | 135 | /** A string representing the type of the object. */ 136 | entity: string; 137 | 138 | /** True for Live Mode, False for Test Mode. */ 139 | livemode: boolean; 140 | 141 | /** The amount specified for this price. */ 142 | amount: number; 143 | 144 | /** The currency code for the price, e.g., "dzd". */ 145 | currency: string; 146 | 147 | /** The product ID associated with this price. */ 148 | product_id: string; 149 | /** A set of key-value pairs that can be used to store additional information about the price. */ 150 | metadata: Record; 151 | 152 | /** Timestamp indicating when the price was created. */ 153 | created_at: number; 154 | 155 | /** Timestamp indicating when the price was last updated. */ 156 | updated_at: number; 157 | } 158 | 159 | /** Represents a checkout object with details of the transaction. */ 160 | export interface Checkout { 161 | /* Unique identifier of the checkout. */ 162 | id: string; 163 | 164 | /** A string representing the type of the object. */ 165 | entity: string; 166 | 167 | /** True for Live Mode, False for Test Mode. */ 168 | livemode: boolean; 169 | 170 | /** The total amount of the transaction. */ 171 | amount: number; 172 | 173 | /** The currency code for the transaction. */ 174 | currency: string; 175 | 176 | /** The fees associated with the transaction. */ 177 | fees: number; 178 | 179 | /** Indicates whether the fees are passed to the customer. */ 180 | pass_fees_to_customer: boolean; 181 | 182 | /** The current status of the checkout. */ 183 | status: 'pending' | 'processing' | 'paid' | 'failed' | 'canceled'; 184 | 185 | /** The language of the checkout page. */ 186 | locale: 'ar' | 'en' | 'fr'; 187 | 188 | /** A description of the transaction. */ 189 | description: string; 190 | 191 | /** The URL to which the customer will be redirected after a successful payment. */ 192 | success_url: string; 193 | 194 | /** The URL to which the customer will be redirected after a failed or canceled payment. */ 195 | failure_url: string; 196 | 197 | /** The webhook endpoint for receiving payment events. */ 198 | webhook_endpoint: string; 199 | 200 | /** The payment method used, can be null if not specified. */ 201 | payment_method: string | null; 202 | 203 | /** The invoice ID associated with the payment, can be null if not specified. */ 204 | invoice_id: string | null; 205 | 206 | /** The customer ID associated with the payment, can be null if not specified. */ 207 | customer_id: string | null; 208 | 209 | /** The payment link ID associated with the payment, can be null if not specified. */ 210 | payment_link_id: string | null; 211 | 212 | /** A set of key-value pairs that can be used to store additional information about the checkout. */ 213 | metadata: Record; 214 | 215 | /** Timestamp indicating when the checkout was created. */ 216 | created_at: number; 217 | 218 | /** Timestamp indicating when the checkout was last updated. */ 219 | updated_at: number; 220 | 221 | /** The shipping address for the checkout. */ 222 | shipping_address: Address; 223 | 224 | /** Indicates whether the shipping address should be collected. */ 225 | collect_shipping_address: boolean; 226 | 227 | /** The URL for the checkout page where the customer completes the payment. */ 228 | checkout_url: string; 229 | } 230 | 231 | /** Represents an individual item within a checkout. */ 232 | export interface CheckoutItem { 233 | /* Unique identifier of the checkout item. */ 234 | id: string; 235 | 236 | /** A string representing the type of the object, "price" in this context. */ 237 | entity: string; 238 | 239 | /** The amount specified for this checkout item. */ 240 | amount: number; 241 | 242 | /** The quantity of the item being purchased. */ 243 | quantity: number; 244 | 245 | /** The currency code for the item, e.g., "dzd". */ 246 | currency: string; 247 | 248 | /** Metadata associated with the item. Can be null. */ 249 | metadata: Record | null; 250 | 251 | /** Timestamp indicating when the checkout item was created. */ 252 | created_at: number; 253 | 254 | /** Timestamp indicating when the checkout item was last updated. */ 255 | updated_at: number; 256 | 257 | /** The product ID associated with this checkout item. */ 258 | product_id: string; 259 | } 260 | 261 | /** Represents a payment link object with details for a transaction. */ 262 | export interface PaymentLink { 263 | /* Unique identifier of the payment link. */ 264 | id: string; 265 | 266 | /** A string representing the type of the object. */ 267 | entity: string; 268 | 269 | /** True for Live Mode, False for Test Mode. */ 270 | livemode: boolean; 271 | 272 | /** The name or title of the payment 273 | 274 | link. */ 275 | name: string; 276 | 277 | /** Indicates whether the payment link is active and can be used by customers. */ 278 | active: boolean; 279 | 280 | /** A message to be displayed to the customer after a successful payment. */ 281 | after_completion_message: string; 282 | 283 | /** The language of the checkout page associated with the payment link. */ 284 | locale: 'ar' | 'en' | 'fr'; 285 | 286 | /** Indicates whether the Chargily Pay fees will be paid by the merchant or passed to the customers. */ 287 | pass_fees_to_customer: boolean; 288 | 289 | /** A set of key-value pairs that can be used to store additional information about the payment link. */ 290 | metadata: Record; 291 | 292 | /** Timestamp indicating when the payment link was created. */ 293 | created_at: number; 294 | 295 | /** Timestamp indicating when the payment link was last updated. */ 296 | updated_at: number; 297 | 298 | /** Indicates whether the customer is prompted to provide a shipping address. */ 299 | collect_shipping_address: boolean; 300 | 301 | /** The URL of the payment link that customers can visit to make a payment. */ 302 | url: string; 303 | } 304 | 305 | /** Represents an individual item within a payment link. */ 306 | export interface PaymentLinkItem { 307 | /* Unique identifier of the payment link item. */ 308 | id: string; 309 | 310 | /** A string representing the type of the object, "price" in this context. */ 311 | entity: string; 312 | 313 | /** The amount specified for this payment link item. */ 314 | amount: number; 315 | 316 | /** The quantity of the item offered in the payment link. */ 317 | quantity: number; 318 | 319 | /** Indicates if the quantity is adjustable by the customer. */ 320 | adjustable_quantity: boolean; 321 | 322 | /** The currency code for the item, e.g., "dzd". */ 323 | currency: string; 324 | 325 | /** Metadata associated with the item. Can be null. */ 326 | metadata: Record | null; 327 | 328 | /** Timestamp indicating when the payment link item was created. */ 329 | created_at: number; 330 | 331 | /** Timestamp indicating when the payment link item was last updated. */ 332 | updated_at: number; 333 | 334 | /** The product ID associated with this payment link item. */ 335 | product_id: string; 336 | } 337 | -------------------------------------------------------------------------------- /src/types/param.ts: -------------------------------------------------------------------------------- 1 | import { Address } from './data'; 2 | 3 | export interface CreateCustomerParams { 4 | /** The name of the customer. */ 5 | name?: string; 6 | 7 | /** The email address of the customer. */ 8 | email?: string; 9 | 10 | /** The phone number of the customer. */ 11 | phone?: string; 12 | 13 | /** The address of the customer. */ 14 | address?: Address; 15 | 16 | /** A set of key-value pairs for additional information about the customer. */ 17 | metadata?: Record; 18 | } 19 | 20 | export interface UpdateCustomerParams { 21 | /** Optional: The name of the customer. */ 22 | name?: string; 23 | 24 | /** Optional: The email address of the customer. */ 25 | email?: string; 26 | 27 | /** Optional: The phone number of the customer. */ 28 | phone?: string; 29 | 30 | /** Optional: The address of the customer, with all fields inside also being optional. */ 31 | address?: Partial
; 32 | 33 | /** Optional: A set of key-value pairs for additional information about the customer. */ 34 | metadata?: Record; 35 | } 36 | 37 | export interface CreateProductParams { 38 | /** Required: The name of the product. */ 39 | name: string; 40 | 41 | /** Optional: The description of the product. */ 42 | description?: string; 43 | 44 | /** Optional: URLs of images of the product, up to 8. */ 45 | images?: string[]; 46 | 47 | /** Optional: A set of key-value pairs for additional information about the product. */ 48 | metadata?: Record; 49 | } 50 | 51 | export interface UpdateProductParams { 52 | /** Optional: The name of the product. */ 53 | name?: string; 54 | 55 | /** Optional: The description of the product. */ 56 | description?: string; 57 | 58 | /** Optional: URLs of images of the product. */ 59 | images?: string[]; 60 | 61 | /** Optional: A set of key-value pairs for additional information about the product. */ 62 | metadata?: Record; 63 | } 64 | 65 | export interface CreatePriceParams { 66 | /** Required: The amount to be charged. */ 67 | amount: number; 68 | 69 | /** Required: A lowercase ISO currency code, e.g., "dzd". */ 70 | currency: string; 71 | 72 | /** Required: The ID of the product. */ 73 | product_id: string; 74 | 75 | /** Optional: A set of key-value pairs for additional information. */ 76 | metadata?: Record; 77 | } 78 | 79 | export interface UpdatePriceParams { 80 | /** A set of key-value pairs for additional information about the price. */ 81 | metadata: Record; 82 | } 83 | 84 | export interface CheckoutItemParams { 85 | /** Required: The ID of the Price associated with the item. */ 86 | price: string; 87 | 88 | /** Required: The quantity of the item being purchased. */ 89 | quantity: number; 90 | } 91 | 92 | export interface CreateCheckoutParams { 93 | /** Required if amount and currency are not provided: An array of items being purchased. */ 94 | items?: CheckoutItemParams[]; 95 | 96 | /** Required if items are not provided: The total amount of the checkout. */ 97 | amount?: number; 98 | 99 | /** Required if amount is provided: The currency code for the checkout. */ 100 | currency?: string; 101 | 102 | /** Optional: The payment method for the checkout, defaults to "edahabia". */ 103 | payment_method?: string; 104 | 105 | /** Required: The URL to redirect to after a successful payment, must be a valid URL that begins with either http or https. */ 106 | success_url: string; 107 | 108 | /** Optional: The URL to redirect to after a failed or canceled payment. */ 109 | failure_url?: string; 110 | 111 | /** Optional: The URL for receiving webhook events. */ 112 | webhook_endpoint?: string; 113 | 114 | /** Optional: A description of the checkout. */ 115 | description?: string; 116 | 117 | /** Optional: The language of the checkout page. */ 118 | locale?: 'ar' | 'en' | 'fr'; 119 | 120 | /** Optional: Indicates who will pay the Chargily Pay fees. */ 121 | pass_fees_to_customer?: boolean; 122 | 123 | /** Optional: The ID of an existing customer. */ 124 | customer_id?: string; 125 | 126 | /** Optional: The shipping address for the checkout. */ 127 | shipping_address?: string; 128 | 129 | /** Optional: Indicates whether the shipping address should be collected. */ 130 | collect_shipping_address?: boolean; 131 | 132 | /** Optional 133 | : Additional information about the checkout. */ 134 | metadata?: Record; 135 | } 136 | 137 | export interface PaymentLinkItemParams { 138 | /** Required: The ID of the Price associated with the item. */ 139 | price: string; 140 | 141 | /** Required: The quantity of the item being offered. */ 142 | quantity: number; 143 | 144 | /** Optional: Indicates if the quantity is adjustable by the customer. Defaults to false. */ 145 | adjustable_quantity?: boolean; 146 | } 147 | 148 | export interface CreatePaymentLinkParams { 149 | /** Required: A descriptive name for the payment link. */ 150 | name: string; 151 | 152 | /** Required: An array of items included in the payment link. */ 153 | items: PaymentLinkItemParams[]; 154 | 155 | /** Optional: A message displayed to the customer after a successful payment. */ 156 | after_completion_message?: string; 157 | 158 | /** Optional: The language of the checkout page. */ 159 | locale?: 'ar' | 'en' | 'fr'; 160 | 161 | /** Optional: Indicates who will pay the Chargily Pay fees. */ 162 | pass_fees_to_customer?: boolean; 163 | 164 | /** Optional: Indicates whether the shipping address should be collected. */ 165 | collect_shipping_address?: boolean; 166 | 167 | /** Optional: Additional information about the payment link. */ 168 | metadata?: Record; 169 | } 170 | 171 | export interface UpdatePaymentLinkItem { 172 | /** Required: The ID of the Price associated with the item. */ 173 | price: string; 174 | 175 | /** Required: The quantity of the item being offered. */ 176 | quantity: number; 177 | 178 | /** Optional: Indicates if the quantity is adjustable by the customer. Defaults to false. */ 179 | adjustable_quantity?: boolean; 180 | } 181 | 182 | export interface UpdatePaymentLinkParams { 183 | /** Optional: A descriptive name for the payment link. */ 184 | name?: string; 185 | 186 | /** Optional: An array of items included in the payment link. */ 187 | items?: UpdatePaymentLinkItem[]; 188 | 189 | /** Optional: A message displayed to the customer after a successful payment. */ 190 | after_completion_message?: string; 191 | 192 | /** Optional: The language of the checkout page. */ 193 | locale?: 'ar' | 'en' | 'fr'; 194 | 195 | /** Optional: Indicates who will pay the Chargily Pay fees. */ 196 | pass_fees_to_customer?: boolean; 197 | 198 | /** Optional: Indicates whether the shipping address should be collected. */ 199 | collect_shipping_address?: boolean; 200 | 201 | /** Optional: Additional information about the payment link. */ 202 | metadata?: Record; 203 | } 204 | -------------------------------------------------------------------------------- /src/types/response.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents the standard response for a paginated list from the Chargily API. 3 | * @template T - The data type of the items in the list. 4 | */ 5 | export interface ListResponse { 6 | /** Indicates if the operation was performed in live mode or test mode. */ 7 | livemode: boolean; 8 | 9 | /** The current page number of the list. */ 10 | current_page: number; 11 | 12 | /** An array of items of type T on the current page. */ 13 | data: T[]; 14 | 15 | /** The URL for the first page of the list. */ 16 | first_page_url: string; 17 | 18 | /** The number indicating the last page of the list. */ 19 | last_page: number; 20 | 21 | /** The URL for the last page of the list. */ 22 | last_page_url: string; 23 | 24 | /** The URL for the next page of the list, or null if on the last page. */ 25 | next_page_url: string | null; 26 | 27 | /** The base path URL for the paginated list endpoints. */ 28 | path: string; 29 | 30 | /** The number of items to be displayed per page. */ 31 | per_page: number; 32 | 33 | /** The URL for the previous page of the list, or null if on the first page. */ 34 | prev_page_url: string | null; 35 | 36 | /** The total number of items available across all pages. */ 37 | total: number; 38 | } 39 | 40 | /** 41 | * Represents the response received upon the deletion of an item via the Chargily API. 42 | */ 43 | export interface DeleteItemResponse { 44 | /** Indicates if the operation was performed in live mode or test mode. */ 45 | livemode: boolean; 46 | 47 | /** The unique identifier of the item that was deleted. */ 48 | id: string; 49 | 50 | /** A string representing the type of the object that was deleted. */ 51 | entity: string; 52 | 53 | /** A boolean indicating whether the deletion was successful. */ 54 | deleted: boolean; 55 | } 56 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto'; 2 | 3 | /** 4 | * Verifies the signature of the incoming webhook request. 5 | * @param {Buffer} payload - The raw body buffer of the request. 6 | * @param {string} signature - The signature header from the webhook request. 7 | * @param {string} secretKey - Your Chargily API secret key. 8 | * @returns {boolean} - Returns true if the signature is valid, false otherwise. 9 | */ 10 | export function verifySignature( 11 | payload: Buffer, 12 | signature: string, 13 | secretKey: string 14 | ): boolean { 15 | if (!signature) { 16 | return false; 17 | } 18 | 19 | const sigPrefix = ''; // Define if there's a specific prefix used 20 | const sigHashAlg = 'sha256'; // Define the hashing algorithm 21 | const computedSignature = crypto 22 | .createHmac(sigHashAlg, secretKey) 23 | .update(payload) 24 | .digest('hex'); 25 | 26 | const digest = Buffer.from(sigPrefix + computedSignature, 'utf8'); 27 | const signatureBuffer = Buffer.from(signature, 'utf8'); 28 | 29 | if ( 30 | signatureBuffer.length !== digest.length || 31 | !crypto.timingSafeEqual(digest, signatureBuffer) 32 | ) { 33 | throw new Error('The signature is invalid.'); 34 | } 35 | 36 | console.log('The signature is valid'); 37 | return true; 38 | } 39 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "./lib", 6 | "rootDir": "./src", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "declaration": true 10 | }, 11 | "include": ["src/**/*"] 12 | } 13 | --------------------------------------------------------------------------------