From f18ec606e5af4284f0b1f757a5cbf199e87ce324 Mon Sep 17 00:00:00 2001 From: Abderraouf Zine Date: Mon, 4 Mar 2024 06:27:03 -0500 Subject: [PATCH] feat: deploy v0.0.1-alpha candidate --- .gitignore | 28 ++ README.md | 429 +++++++++++++++++++++++++++++- package-lock.json | 590 ++++++++++++++++++++++++++++++++++++++++++ package.json | 46 ++++ src/classes/client.ts | 435 +++++++++++++++++++++++++++++++ src/consts/index.ts | 2 + src/index.ts | 14 + src/types/data.ts | 336 ++++++++++++++++++++++++ src/types/param.ts | 203 +++++++++++++++ src/types/response.ts | 55 ++++ tsconfig.json | 12 + 11 files changed, 2147 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/classes/client.ts create mode 100644 src/consts/index.ts create mode 100644 src/index.ts create mode 100644 src/types/data.ts create mode 100644 src/types/param.ts create mode 100644 src/types/response.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..61ca3c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Dependency directory +node_modules/ + +# Build outputs +lib/ +dist/ + +# TypeScript cache +*.tsbuildinfo + +# Environment configuration files +.env + +# IDE-specific files and directories +.vscode/ +.idea/ +*.swp +*.swo + +# Logs +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# IDE +.vscode diff --git a/README.md b/README.md index a8cda15..59f7358 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,428 @@ -# Chargily Pay V2 +# Chargily Pay V2 JavaScript Library -JavaScript Library for Chargily Pay™ Gateway - V2. +## Introduction -The easiest and free way to integrate e-payment API through EDAHABIA of Algerie Poste and CIB of SATIM into your JS project. +The Chargily Pay JavaScript Library offers a convenient way to integrate the Chargily e-payment gateway with JavaScript applications. Developed by Abderraouf Zine, a passionate software engineer, this library supports various operations such as managing customers, products, prices, and checkouts, as well as generating payment links and retrieving account balances. It is designed for use in both Node.js and browser environments. + +## Key Features + +- Easy integration with Chargily Pay e-payment gateway +- Support for both EDAHABIA of Algerie Poste and CIB of SATIM +- Comprehensive management of customers, products, and prices +- Efficient handling of checkouts and payment links +- Compatible with Node.js and browser environments + +## Installation + +To include this library in your project, you can use npm or yarn: + +`npm install chargily-pay-javascript` + +or + +`yarn add chargily-pay-javascript` + +## Getting Started + +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: + +```ts +import { ChargilyClient } from 'chargily-pay-javascript'; + +const client = new ChargilyClient({ + api_key: 'YOUR_API_KEY_HERE', + mode: 'test', // Change to 'live' when deploying your application +}); +``` + +This initializes the Chargily client, ready for communication with the Chargily Pay API. + +## Creating a Customer + +To create a customer, you can use the `createCustomer` method: + +```ts +const customerData = { + name: 'John Doe', + email: 'john.doe@example.com', + phone: '+213xxxxxxxx', + address: { + country: 'DZ', + state: 'Algiers', + address: '123 Main St', + }, + metadata: { + notes: 'Important customer', + }, +}; + +client + .createCustomer(customerData) + .then((customer) => console.log(customer)) + .catch((error) => console.error(error)); +``` + +This method returns a promise with the created customer object. + +## Updating a Customer + +To update an existing customer, use the `updateCustomer` method with the customer's ID and the data you want to update: + +```ts +const updateData = { + email: 'new.email@example.com', + metadata: { notes: 'Updated customer info' }, +}; + +client + .updateCustomer('customer_id_here', updateData) + .then((customer) => console.log(customer)) + .catch((error) => console.error(error)); +``` + +This will update the specified fields of the customer and return the updated customer object. + +## Creating a Product + +To create a new product, you can use the `createProduct` method. Here's how to create a product named "Super Product": + +```ts +const productData = { + name: 'Super Product', + description: 'An amazing product that does everything!', + images: ['http://example.com/image1.jpg', 'http://example.com/image2.jpg'], + metadata: { category: 'electronics' }, +}; + +client + .createProduct(productData) + .then((product) => console.log(product)) + .catch((error) => console.error(error)); +``` + +This method requires the `name` of the product and optionally accepts `description`, an array of `images`, and `metadata`. + +## Deleting a Customer + +To delete a customer from the Chargily Pay system, you can use the `deleteCustomer` method with the customer's ID: + +```ts +client + .deleteCustomer('customer_id_here') + .then((response) => console.log(response)) + .catch((error) => console.error(error)); +``` + +This method will return a response indicating whether the deletion was successful. + +## Listing Customers + +You can list all customers with optional pagination using the `listCustomers` method. Specify the number of customers per page using the `per_page` parameter: + +```ts +client + .listCustomers(20) // List 20 customers per page + .then((customersList) => console.log(customersList)) + .catch((error) => console.error(error)); +``` + +The response will include a paginated list of customers along with pagination details. + +## Updating a Customer + +To update an existing customer, you'll need the customer's ID: + +```ts +const updatedCustomer = await client.updateCustomer('CUSTOMER_ID', { + name: 'Jane Doe', + email: 'jane.doe@example.com', + phone: '987654321', + address: { + country: 'DZ', + state: 'Oran', + address: '4321 Main St', + }, + metadata: { + custom_field_updated: 'new value', + }, +}); +``` + +This call updates the specified customer and returns the updated customer object. + +## Deleting a Customer + +To delete a customer, use their ID: + +```ts +const deleteResponse = await client.deleteCustomer('CUSTOMER_ID'); +``` + +This method returns a response indicating whether the deletion was successful. + +## Creating a Product + +To add a new product to your catalog: + +```ts +const newProduct = await client.createProduct({ + name: 'Awesome Product', + description: 'A description of your awesome product', + images: ['https://example.com/image.png'], + metadata: { + category: 'Electronics', + }, +}); +``` + +This creates a new product and returns the product object. + +## Updating a Product + +Similar to customers, you can update products using their ID: + +```ts +const updatedProduct = await client.updateProduct('PRODUCT_ID', { + name: 'Even More Awesome Product', + description: 'An updated description', + images: ['https://example.com/newimage.png'], + metadata: { + category: 'Updated Category', + }, +}); +``` + +This updates the product details and returns the updated product object. + +## Creating a Price + +To create a price for a product, you need the product's ID: + +```ts +const newPrice = await client.createPrice({ + amount: 5000, + currency: 'dzd', + product_id: 'PRODUCT_ID', + metadata: { + size: 'M', + }, +}); +``` + +This creates a new price for the specified product and returns the price object. + +## Updating a Price + +You can update the metadata of a price by its ID: + +```ts +const updatedPrice = await client.updatePrice('PRICE_ID', { + metadata: { + size: 'L', + }, +}); +``` + +This updates the price's metadata and returns the updated price object. + +## Creating a Checkout + +To create a checkout session for a customer to make a payment: + +```ts +const checkout = await client.createCheckout({ + items: [ + { + price: 'PRICE_ID', + quantity: 1, + }, + ], + success_url: 'https://your-website.com/success', + failure_url: 'https://your-website.com/failure', + payment_method: 'edahabia', // Optional, defaults to 'edahabia' + locale: 'en', // Optional, defaults to 'ar' + pass_fees_to_customer: true, // Optional, defaults to false + shipping_address: '123 Test St, Test City, DZ', // Optional + collect_shipping_address: true, // Optional, defaults to false + metadata: { + order_id: '123456', + }, +}); +``` + +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. + +## Creating a Payment Link + +Payment links are URLs that you can share with your customers for payment: + +```ts +const paymentLink = await client.createPaymentLink({ + name: 'Product Payment', + items: [ + { + price: 'PRICE_ID', + quantity: 1, + adjustable_quantity: false, + }, + ], + after_completion_message: 'Thank you for your purchase!', + locale: 'en', + pass_fees_to_customer: true, + collect_shipping_address: true, + metadata: { + campaign: 'Summer Sale', + }, +}); +``` + +This creates a new payment link and returns the payment link object, including the URL that you can share with your customers. + +## Handling Prices + +### Creating a Price + +To set up a price for a product, you can use the product's ID: + +```ts +const newPrice = await client.createPrice({ + amount: 5000, + currency: 'dzd', + product_id: 'PRODUCT_ID', + metadata: { + discount: '10%', + }, +}); +``` + +This call creates a new price for the specified product and returns the price object. + +### Updating a Price + +Update a price by its ID: + +```ts +const updatedPrice = await client.updatePrice('PRICE_ID', { + metadata: { + discount: '15%', + }, +}); +``` + +This updates the metadata for the price and returns the updated price object. + +### Fetching Prices + +To retrieve all prices for a product: + +```ts +const prices = await client.listPrices(); +``` + +This returns a paginated list of all prices. + +## Working with Checkouts + +### Creating a Checkout + +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. + +Here's how you can create a checkout: + +```ts +const newCheckout = await client.createCheckout({ + items: [ + { price: 'PRICE_ID', quantity: 2 }, + { price: 'ANOTHER_PRICE_ID', quantity: 1 }, + ], + success_url: 'https://yourdomain.com/success', + failure_url: 'https://yourdomain.com/failure', + payment_method: 'edahabia', + customer_id: 'CUSTOMER_ID', + metadata: { orderId: '123456' }, + locale: 'en', + pass_fees_to_customer: false, +}); +``` + +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. + +### Retrieving a Checkout + +To fetch details of a specific checkout session: + +```ts +const checkoutDetails = await client.getCheckout('CHECKOUT_ID'); +``` + +This retrieves the details of the specified checkout session. + +## Managing Payment Links + +### Creating a Payment Link + +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: + +```ts +const paymentLink = await client.createPaymentLink({ + name: 'Subscription Service', + items: [{ price: 'PRICE_ID', quantity: 1, adjustable_quantity: false }], + after_completion_message: 'Thank you for your subscription!', + locale: 'en', + pass_fees_to_customer: true, + collect_shipping_address: true, + metadata: { subscriptionId: 'sub_12345' }, +}); +``` + +This creates a new payment link with specified details and returns the payment link object including the URL to be shared with your customers. + +### Updating a Payment Link + +To update an existing payment link: + +```ts +const updatedLink = await client.updatePaymentLink('PAYMENT_LINK_ID', { + name: 'Updated Subscription Service', + after_completion_message: 'Thank you for updating your subscription!', + metadata: { subscriptionId: 'sub_67890' }, +}); +``` + +This updates the specified payment link and returns the updated object. + +### Fetching a Payment Link + +Retrieve the details of a specific payment link: + +```ts +const linkDetails = await client.getPaymentLink('PAYMENT_LINK_ID'); +``` + +This call retrieves the specified payment link's details. + +### Listing Payment Links + +To list all your payment links: + +```ts +const allLinks = await client.listPaymentLinks(); +``` + +This returns a paginated list of all payment links you've created. + +## Final Notes + +This documentation covers the basic functionality of the `chargily-pay-javascript` library. For more advanced features and comprehensive details, refer to the official [Chargily Pay API documentation](https://dev.chargily.com/pay-v2/introduction). + +By integrating the `chargily-pay-javascript` library into your project, you're equipped to seamlessly incorporate Chargily's payment gateway, enhancing your application's payment processing capabilities. + +If you encounter any issues or have further questions, please visit our [GitHub repository](https://github.com/chargily/chargily-pay-javascript) to report issues or seek guidance. + +Happy coding! + +--- + +**Abderraouf Zine** +_Software Engineer_ +[GitHub Profile](https://github.com/rofazayn) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0bc846c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,590 @@ +{ + "name": "chargily-pay-javascript", + "version": "0.0.1-alpha", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "chargily-pay-javascript", + "version": "0.0.1-alpha", + "license": "MIT", + "devDependencies": { + "@types/node": "^14.0.0", + "nodemon": "^3.1.0", + "ts-node": "^10.9.2", + "typescript": "^4.9.5" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nodemon": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", + "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f266bb0 --- /dev/null +++ b/package.json @@ -0,0 +1,46 @@ +{ + "name": "chargily-pay-javascript", + "version": "0.0.1-alpha", + "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.", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "files": [ + "lib" + ], + "scripts": { + "dev": "nodemon --exec ts-node ./src/index.ts", + "build": "tsc", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/chargily/chargily-pay-javascript.git" + }, + "keywords": [ + "chargily", + "payment", + "gateway", + "e-payment", + "algeria", + "edahabia", + "cib", + "javascript", + "typescript" + ], + "author": { + "name": "Abderraouf Zine", + "email": "rofazayn@gmail.com", + "url": "https://github.com/rofazayn" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/chargily/chargily-pay-javascript/issues" + }, + "homepage": "https://github.com/chargily/chargily-pay-javascript#readme", + "devDependencies": { + "@types/node": "^14.0.0", + "nodemon": "^3.1.0", + "ts-node": "^10.9.2", + "typescript": "^4.9.5" + } +} diff --git a/src/classes/client.ts b/src/classes/client.ts new file mode 100644 index 0000000..8bab95b --- /dev/null +++ b/src/classes/client.ts @@ -0,0 +1,435 @@ +import { CHARGILY_LIVE_URL, CHARGILY_TEST_URL } from '../consts'; +import { + Balance, + Checkout, + Customer, + PaymentLink, + Price, + Product, + ProductPrice, +} from '../types/data'; +import { + CheckoutItemParams, + CreateCheckoutParams, + CreateCustomerParams, + CreatePaymentLinkParams, + CreatePriceParams, + CreateProductParams, + PaymentLinkItemParams, + UpdateCustomerParams, + UpdatePaymentLinkParams, + UpdatePriceParams, + UpdateProductParams, +} from '../types/param'; +import { DeleteItemResponse, ListResponse } from '../types/response'; + +/** + * Configuration options for ChargilyClient. + */ +export interface ChargilyClientOptions { + /** + * The API key for authentication with Chargily API. + * @type {string} + */ + api_key: string; + + /** + * Operating mode of the client, indicating whether to use the test or live API endpoints. + * @type {'test' | 'live'} + */ + mode: 'test' | 'live'; +} + +/** + * A client for interacting with Chargily's API, supporting operations for customers, products, prices, checkouts, and payment links. + */ +export class ChargilyClient { + private api_key: string; + private base_url: string; + + /** + * Constructs a ChargilyClient instance. + * @param {ChargilyClientOptions} options - Configuration options including API key and mode. + */ + constructor(options: ChargilyClientOptions) { + this.api_key = options.api_key; + this.base_url = + options.mode === 'test' ? CHARGILY_TEST_URL : CHARGILY_LIVE_URL; + } + + /** + * Internal method to make requests to the Chargily API. + * @param {string} endpoint - The endpoint path to make the request to. + * @param {string} [method='GET'] - The HTTP method for the request. + * @param {Object} [body] - The request payload, necessary for POST or PATCH requests. + * @returns {Promise} - The JSON response from the API. + * @private + */ + private async request( + endpoint: string, + method: string = 'GET', + body?: any + ): Promise { + const url = `${this.base_url}/${endpoint}`; + const headers = { + Authorization: `Bearer ${this.api_key}`, + 'Content-Type': 'application/json', + }; + + const fetchOptions: RequestInit = { + method, + headers, + }; + + if (body !== undefined) { + fetchOptions.body = JSON.stringify(body); + } + + try { + const response = await fetch(url, fetchOptions); + + if (!response.ok) { + throw new Error( + `API request failed with status ${response.status}: ${response.statusText}` + ); + } + + return response.json(); + } catch (error) { + throw new Error(`Failed to make API request: ${error}`); + } + } + + /** + * Retrieves the current balance information from the Chargily API. + * @returns {Promise} - A promise that resolves to the balance information. + */ + public async getBalance(): Promise { + return this.request('balance', 'GET'); + } + + /** + * Creates a new customer with specified details. + * @param {CreateCustomerParams} customer_data - The data for creating a new customer. + * @returns {Promise} - A promise that resolves to the newly created customer. + */ + public async createCustomer( + customer_data: CreateCustomerParams + ): Promise { + return this.request('customers', 'POST', customer_data); + } + + /** + * Fetches a customer by their unique identifier. + * @param {string} customer_id - The ID of the customer to retrieve. + * @returns {Promise} - A promise that resolves to the customer details. + */ + public async getCustomer(customer_id: string): Promise { + return this.request(`customers/${customer_id}`, 'GET'); + } + + /** + * Updates an existing customer's details. + * @param {string} customer_id - The ID of the customer to update. + * @param {UpdateCustomerParams} update_data - New data for updating the customer. + * @returns {Promise} - A promise that resolves to the updated customer details. + */ + public async updateCustomer( + customer_id: string, + update_data: UpdateCustomerParams + ): Promise { + return this.request(`customers/${customer_id}`, 'PATCH', update_data); + } + + /** + * Deletes a customer by their unique identifier. + * @param {string} customer_id - The ID of the customer to delete. + * @returns {Promise} - A promise that resolves to the deletion response. + */ + public async deleteCustomer( + customer_id: string + ): Promise { + return this.request(`customers/${customer_id}`, 'DELETE'); + } + + /** + * Lists customers, optionally paginated. + * @param {number} [per_page=10] - The number of customers to return per page. + * @returns {Promise>} - A promise that resolves to a paginated list of customers. + */ + public async listCustomers( + per_page: number = 10 + ): Promise> { + const endpoint = `customers?per_page=${per_page}`; + const response: ListResponse = await this.request( + endpoint, + 'GET' + ); + return response; + } + + /** + * Creates a new product with the given details. + * @param {CreateProductParams} product_data - The data for creating the product. + * @returns {Promise} The created product. + */ + public async createProduct( + product_data: CreateProductParams + ): Promise { + return this.request('products', 'POST', product_data); + } + + /** + * Updates an existing product identified by its ID. + * @param {string} product_id - The ID of the product to update. + * @param {UpdateProductParams} update_data - The data to update the product with. + * @returns {Promise} The updated product. + */ + public async updateProduct( + product_id: string, + update_data: UpdateProductParams + ): Promise { + return this.request(`products/${product_id}`, 'POST', update_data); + } + + /** + * Retrieves a single product by its ID. + * @param {string} product_id - The ID of the product to retrieve. + * @returns {Promise} The requested product. + */ + public async getProduct(product_id: string): Promise { + return this.request(`products/${product_id}`, 'GET'); + } + + /** + * Lists all products with optional pagination. + * @param {number} [per_page=10] - The number of products to return per page. + * @returns {Promise>} A paginated list of products. + */ + public async listProducts( + per_page: number = 10 + ): Promise> { + const endpoint = `products?per_page=${per_page}`; + const response: ListResponse = await this.request(endpoint, 'GET'); + return response; + } + + /** + * Deletes a product by its ID. + * @param {string} product_id - The ID of the product to delete. + * @returns {Promise} Confirmation of the product deletion. + */ + public async deleteProduct(product_id: string): Promise { + return this.request(`products/${product_id}`, 'DELETE'); + } + + /** + * Retrieves all prices associated with a product, with optional pagination. + * @param {string} product_id - The ID of the product whose prices are to be retrieved. + * @param {number} [per_page=10] - The number of prices to return per page. + * @returns {Promise>} A paginated list of prices for the specified product. + */ + public async getProductPrices( + product_id: string, + per_page: number = 10 + ): Promise> { + const endpoint = `products/${product_id}/prices?per_page=${per_page}`; + const response: ListResponse = await this.request( + endpoint, + 'GET' + ); + return response; + } + + /** + * Creates a new price for a product. + * @param {CreatePriceParams} price_data - The details of the new price to be created. + * @returns {Promise} The created price object. + */ + public async createPrice(price_data: CreatePriceParams): Promise { + return this.request('prices', 'POST', price_data); + } + + /** + * Updates the details of an existing price. + * @param {string} price_id - The ID of the price to be updated. + * @param {UpdatePriceParams} update_data - The new details for the price. + * @returns {Promise} The updated price object. + */ + public async updatePrice( + price_id: string, + update_data: UpdatePriceParams + ): Promise { + return this.request(`prices/${price_id}`, 'POST', update_data); + } + + /** + * Retrieves a single price by its ID. + * @param {string} price_id - The ID of the price to retrieve. + * @returns {Promise} The requested price object. + */ + public async getPrice(price_id: string): Promise { + return this.request(`prices/${price_id}`, 'GET'); + } + + /** + * Lists all prices for products with optional pagination. + * @param {number} [per_page=10] - The number of price objects to return per page. + * @returns {Promise>} A paginated list of prices. + */ + public async listPrices(per_page: number = 10): Promise> { + const endpoint = `prices?per_page=${per_page}`; + const response: ListResponse = await this.request(endpoint, 'GET'); + return response; + } + + /** + * Creates a new checkout session with the specified details. + * @param {CreateCheckoutParams} checkout_data - The details for the new checkout session. + * @returns {Promise} The created checkout object. + */ + public async createCheckout( + checkout_data: CreateCheckoutParams + ): Promise { + if ( + !checkout_data.success_url.startsWith('http') && + !checkout_data.success_url.startsWith('https') + ) { + throw new Error('Invalid success_url, it must begin with http or https.'); + } + + if ( + !checkout_data.items && + (!checkout_data.amount || !checkout_data.currency) + ) { + throw new Error( + 'The items field is required when amount and currency are not present.' + ); + } + + return this.request('checkouts', 'POST', checkout_data); + } + + /** + * Retrieves details of a specific checkout session by its ID. + * @param {string} checkout_id - The ID of the checkout session to retrieve. + * @returns {Promise} The requested checkout object. + */ + public async getCheckout(checkout_id: string): Promise { + return this.request(`checkouts/${checkout_id}`, 'GET'); + } + + /** + * Lists all checkout sessions with optional pagination. + * @param {number} [per_page=10] - The number of checkout objects to return per page. + * @returns {Promise>} A paginated list of checkout sessions. + */ + public async listCheckouts( + per_page: number = 10 + ): Promise> { + const endpoint = `checkouts?per_page=${per_page}`; + const response: ListResponse = await this.request( + endpoint, + 'GET' + ); + return response; + } + + /** + * Retrieves all items included in a specific checkout session, with optional pagination. + * @param {string} checkout_id - The ID of the checkout session. + * @param {number} [per_page=10] - The number of items to return per page. + * @returns {Promise>} A paginated list of items in the checkout session. + */ + public async getCheckoutItems( + checkout_id: string, + per_page: number = 10 + ): Promise> { + const endpoint = `checkouts/${checkout_id}/items?per_page=${per_page}`; + const response: ListResponse = await this.request( + endpoint, + 'GET' + ); + return response; + } + + /** + * Expires a specific checkout session before its automatic expiration. + * @param {string} checkout_id - The ID of the checkout session to expire. + * @returns {Promise} The expired checkout object, indicating the session is no longer valid for payment. + */ + public async expireCheckout(checkout_id: string): Promise { + return this.request(`checkouts/${checkout_id}/expire`, 'POST'); + } + + /** + * Creates a new payment link. + * @param {CreatePaymentLinkParams} payment_link_data - The details for the new payment link. + * @returns {Promise} The created payment link object. + */ + public async createPaymentLink( + payment_link_data: CreatePaymentLinkParams + ): Promise { + return this.request('payment-links', 'POST', payment_link_data); + } + + /** + * Updates an existing payment link identified by its ID. + * @param {string} payment_link_id - The ID of the payment link to update. + * @param {UpdatePaymentLinkParams} update_data - The new details for the payment link. + * @returns {Promise} The updated payment link object. + */ + public async updatePaymentLink( + payment_link_id: string, + update_data: UpdatePaymentLinkParams + ): Promise { + return this.request( + `payment-links/${payment_link_id}`, + 'POST', + update_data + ); + } + + /** + * Retrieves details of a specific payment link by its ID. + * @param {string} payment_link_id - The ID of the payment link to retrieve. + * @returns {Promise} The requested payment link object. + */ + public async getPaymentLink(payment_link_id: string): Promise { + return this.request(`payment-links/${payment_link_id}`, 'GET'); + } + + /** + * Lists all payment links with optional pagination. + * @param {number} [per_page=10] - The number of payment link objects to return per page. + * @returns {Promise>} A paginated list of payment links. + */ + public async listPaymentLinks( + per_page: number = 10 + ): Promise> { + const endpoint = `payment-links?per_page=${per_page}`; + const response: ListResponse = await this.request( + endpoint, + 'GET' + ); + return response; + } + + /** + * Retrieves all items associated with a specific payment link, with optional pagination. + * @param {string} payment_link_id - The ID of the payment link whose items are to be retrieved. + * @param {number} [per_page=10] - The number of items to return per page. + * @returns {Promise>} A paginated list of items associated with the payment link. + */ + public async getPaymentLinkItems( + payment_link_id: string, + per_page: number = 10 + ): Promise> { + const endpoint = `payment-links/${payment_link_id}/items?per_page=${per_page}`; + const response: ListResponse = await this.request( + endpoint, + 'GET' + ); + return response; + } +} diff --git a/src/consts/index.ts b/src/consts/index.ts new file mode 100644 index 0000000..ba995c8 --- /dev/null +++ b/src/consts/index.ts @@ -0,0 +1,2 @@ +export const CHARGILY_TEST_URL = 'https://pay.chargily.net/test/api/v2'; +export const CHARGILY_LIVE_URL = 'https://pay.chargily.net/api/v2'; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..f9ef89f --- /dev/null +++ b/src/index.ts @@ -0,0 +1,14 @@ +// Exporting all constants from consts/index.ts +export * from './consts'; + +// Exporting all types from types/data.ts +export * from './types/data'; + +// Exporting all types from types/param.ts +export * from './types/param'; + +// Exporting all types from types/response.ts +export * from './types/response'; + +// Exporting the ChargilyClient class from classes/client.ts +export * from './classes/client'; diff --git a/src/types/data.ts b/src/types/data.ts new file mode 100644 index 0000000..bff492f --- /dev/null +++ b/src/types/data.ts @@ -0,0 +1,336 @@ +/** Represents a wallet with its currency and balances. */ +export interface Wallet { + /** A lowercase ISO currency code. */ + currency: string; + + /** The total balance available in the wallet. */ + balance: number; + + /** The amount available for payout. */ + ready_for_payout: number; + + /** The amount currently on hold. */ + on_hold: number; +} + +/** Represents the balance object containing wallet information. */ +export interface Balance { + /** A string representing the type of the object. */ + entity: string; + + /** True for Live Mode, False for Test Mode. */ + livemode: boolean; + + /** An array of wallet objects for each currency. */ + wallets: Wallet[]; +} + +/** Represents a physical address with country, state, and detailed address. */ +export interface Address { + /** Two-letter country code (ISO 3166-1 alpha-2). */ + country: string; + + /** The state or region of the address. */ + state: string; + + /** Detailed address line including street name, number, etc. */ + address: string; +} + +/** Represents a customer with their personal and contact information. */ +export interface Customer { + /** Unique identifier of the customer. */ + id: string; + + /** A string representing the type of the object. */ + entity: string; + + /** True for Live Mode, False for Test Mode. */ + livemode: boolean; + + /** Full name of the customer. */ + name: string; + + /** Email address of the customer. Can be null if not provided. */ + email: string | null; + + /** Phone number of the customer. Can be null if not provided. */ + phone: string | null; + + /** Physical address of the customer. Can be null if not provided. */ + address: Address | null; + + /** A set of key-value pairs that can be used to store additional information about the customer. */ + metadata: Record; + + /** Timestamp indicating when the customer was created. */ + created_at: number; + + /** Timestamp indicating when the customer was last updated. */ + updated_at: number; +} + +/** Represents a product with its details and associated metadata. */ +export interface Product { + /** Unique identifier of the product. */ + id: string; + + /** A string representing the type of the object. */ + entity: string; + + /** True for Live Mode, False for Test Mode. */ + livemode: boolean; + + /** Name of the product. */ + name: string; + + /** Description of the product. Can be null if not provided. */ + description: string | null; + + /** Array of image URLs associated with the product. */ + images: string[]; + + /** A set of key-value pairs that can be used to store additional information about the product. */ + metadata: Record; + + /** Timestamp indicating when the product was created. */ + created_at: number; + + /** Timestamp indicating when the product was last updated. */ + updated_at: number; +} + +/** Represents a price object associated with a product. */ +export interface ProductPrice { + /** Unique identifier of the price. */ + id: string; + + /** A string representing the type of the object, "price" in this context. */ + entity: string; + + /** The amount specified for this price. */ + amount: number; + + /** The currency code for the price, e.g., "dzd". */ + currency: string; + + /** Metadata associated with the price. Can be null. */ + metadata: Record | null; + + /** Timestamp indicating when the price was created. */ + created_at: number; + + /** Timestamp indicating when the price was last updated. */ + updated_at: number; + + /** The product ID associated with this price. */ + product_id: string; +} + +/** Represents a price object with additional product association. */ +export interface Price { + /** Unique identifier of the price. */ + id: string; + + /** A string representing the type of the object. */ + entity: string; + + /** True for Live Mode, False for Test Mode. */ + livemode: boolean; + + /** The amount specified for this price. */ + amount: number; + + /** The currency code for the price, e.g., "dzd". */ + currency: string; + + /** The product ID associated with this price. */ + product_id: string; + /** A set of key-value pairs that can be used to store additional information about the price. */ + metadata: Record; + + /** Timestamp indicating when the price was created. */ + created_at: number; + + /** Timestamp indicating when the price was last updated. */ + updated_at: number; +} + +/** Represents a checkout object with details of the transaction. */ +export interface Checkout { + /* Unique identifier of the checkout. */ + id: string; + + /** A string representing the type of the object. */ + entity: string; + + /** True for Live Mode, False for Test Mode. */ + livemode: boolean; + + /** The total amount of the transaction. */ + amount: number; + + /** The currency code for the transaction. */ + currency: string; + + /** The fees associated with the transaction. */ + fees: number; + + /** Indicates whether the fees are passed to the customer. */ + pass_fees_to_customer: boolean; + + /** The current status of the checkout. */ + status: 'pending' | 'processing' | 'paid' | 'failed' | 'canceled'; + + /** The language of the checkout page. */ + locale: 'ar' | 'en' | 'fr'; + + /** A description of the transaction. */ + description: string; + + /** The URL to which the customer will be redirected after a successful payment. */ + success_url: string; + + /** The URL to which the customer will be redirected after a failed or canceled payment. */ + failure_url: string; + + /** The webhook endpoint for receiving payment events. */ + webhook_endpoint: string; + + /** The payment method used, can be null if not specified. */ + payment_method: string | null; + + /** The invoice ID associated with the payment, can be null if not specified. */ + invoice_id: string | null; + + /** The customer ID associated with the payment, can be null if not specified. */ + customer_id: string | null; + + /** The payment link ID associated with the payment, can be null if not specified. */ + payment_link_id: string | null; + + /** A set of key-value pairs that can be used to store additional information about the checkout. */ + metadata: Record; + + /** Timestamp indicating when the checkout was created. */ + created_at: number; + + /** Timestamp indicating when the checkout was last updated. */ + updated_at: number; + + /** The shipping address for the checkout. */ + shipping_address: Address; + + /** Indicates whether the shipping address should be collected. */ + collect_shipping_address: boolean; + + /** The URL for the checkout page where the customer completes the payment. */ + checkout_url: string; +} + +/** Represents an individual item within a checkout. */ +export interface CheckoutItem { + /* Unique identifier of the checkout item. */ + id: string; + + /** A string representing the type of the object, "price" in this context. */ + entity: string; + + /** The amount specified for this checkout item. */ + amount: number; + + /** The quantity of the item being purchased. */ + quantity: number; + + /** The currency code for the item, e.g., "dzd". */ + currency: string; + + /** Metadata associated with the item. Can be null. */ + metadata: Record | null; + + /** Timestamp indicating when the checkout item was created. */ + created_at: number; + + /** Timestamp indicating when the checkout item was last updated. */ + updated_at: number; + + /** The product ID associated with this checkout item. */ + product_id: string; +} + +/** Represents a payment link object with details for a transaction. */ +export interface PaymentLink { + /* Unique identifier of the payment link. */ + id: string; + + /** A string representing the type of the object. */ + entity: string; + + /** True for Live Mode, False for Test Mode. */ + livemode: boolean; + + /** The name or title of the payment + +link. */ + name: string; + + /** Indicates whether the payment link is active and can be used by customers. */ + active: boolean; + + /** A message to be displayed to the customer after a successful payment. */ + after_completion_message: string; + + /** The language of the checkout page associated with the payment link. */ + locale: 'ar' | 'en' | 'fr'; + + /** Indicates whether the Chargily Pay fees will be paid by the merchant or passed to the customers. */ + pass_fees_to_customer: boolean; + + /** A set of key-value pairs that can be used to store additional information about the payment link. */ + metadata: Record; + + /** Timestamp indicating when the payment link was created. */ + created_at: number; + + /** Timestamp indicating when the payment link was last updated. */ + updated_at: number; + + /** Indicates whether the customer is prompted to provide a shipping address. */ + collect_shipping_address: boolean; + + /** The URL of the payment link that customers can visit to make a payment. */ + url: string; +} + +/** Represents an individual item within a payment link. */ +export interface PaymentLinkItem { + /* Unique identifier of the payment link item. */ + id: string; + + /** A string representing the type of the object, "price" in this context. */ + entity: string; + + /** The amount specified for this payment link item. */ + amount: number; + + /** The quantity of the item offered in the payment link. */ + quantity: number; + + /** Indicates if the quantity is adjustable by the customer. */ + adjustable_quantity: boolean; + + /** The currency code for the item, e.g., "dzd". */ + currency: string; + + /** Metadata associated with the item. Can be null. */ + metadata: Record | null; + + /** Timestamp indicating when the payment link item was created. */ + created_at: number; + + /** Timestamp indicating when the payment link item was last updated. */ + updated_at: number; + + /** The product ID associated with this payment link item. */ + product_id: string; +} diff --git a/src/types/param.ts b/src/types/param.ts new file mode 100644 index 0000000..5ca49c6 --- /dev/null +++ b/src/types/param.ts @@ -0,0 +1,203 @@ +import { Address } from './data'; + +export interface CreateCustomerParams { + /** The name of the customer. */ + name?: string; + + /** The email address of the customer. */ + email?: string; + + /** The phone number of the customer. */ + phone?: string; + + /** The address of the customer. */ + address?: Address; + + /** A set of key-value pairs for additional information about the customer. */ + metadata?: Record; +} + +export interface UpdateCustomerParams { + /** Optional: The name of the customer. */ + name?: string; + + /** Optional: The email address of the customer. */ + email?: string; + + /** Optional: The phone number of the customer. */ + phone?: string; + + /** Optional: The address of the customer, with all fields inside also being optional. */ + address?: Partial
; + + /** Optional: A set of key-value pairs for additional information about the customer. */ + metadata?: Record; +} + +export interface CreateProductParams { + /** Required: The name of the product. */ + name: string; + + /** Optional: The description of the product. */ + description?: string; + + /** Optional: URLs of images of the product, up to 8. */ + images?: string[]; + + /** Optional: A set of key-value pairs for additional information about the product. */ + metadata?: Record; +} + +export interface UpdateProductParams { + /** Optional: The name of the product. */ + name?: string; + + /** Optional: The description of the product. */ + description?: string; + + /** Optional: URLs of images of the product. */ + images?: string[]; + + /** Optional: A set of key-value pairs for additional information about the product. */ + metadata?: Record; +} + +export interface CreatePriceParams { + /** Required: The amount to be charged. */ + amount: number; + + /** Required: A lowercase ISO currency code, e.g., "dzd". */ + currency: string; + + /** Required: The ID of the product. */ + product_id: string; + + /** Optional: A set of key-value pairs for additional information. */ + metadata?: Record; +} + +export interface UpdatePriceParams { + /** A set of key-value pairs for additional information about the price. */ + metadata: Record; +} + +export interface CheckoutItemParams { + /** Required: The ID of the Price associated with the item. */ + price: string; + + /** Required: The quantity of the item being purchased. */ + quantity: number; +} + +export interface CreateCheckoutParams { + /** Required if amount and currency are not provided: An array of items being purchased. */ + items?: CheckoutItemParams[]; + + /** Required if items are not provided: The total amount of the checkout. */ + amount?: number; + + /** Required if amount is provided: The currency code for the checkout. */ + currency?: string; + + /** Optional: The payment method for the checkout, defaults to "edahabia". */ + payment_method?: string; + + /** Required: The URL to redirect to after a successful payment, must be a valid URL that begins with either http or https. */ + success_url: string; + + /** Optional: The URL to redirect to after a failed or canceled payment. */ + failure_url?: string; + + /** Optional: The URL for receiving webhook events. */ + webhook_endpoint?: string; + + /** Optional: A description of the checkout. */ + description?: string; + + /** Optional: The language of the checkout page. */ + locale?: 'ar' | 'en' | 'fr'; + + /** Optional: Indicates who will pay the Chargily Pay fees. */ + pass_fees_to_customer?: boolean; + + /** Optional: The ID of an existing customer. */ + customer_id?: string; + + /** Optional: The shipping address for the checkout. */ + shipping_address?: string; + + /** Optional: Indicates whether the shipping address should be collected. */ + collect_shipping_address?: boolean; + + /** Optional +: Additional information about the checkout. */ + metadata?: Record; +} + +export interface PaymentLinkItemParams { + /** Required: The ID of the Price associated with the item. */ + price: string; + + /** Required: The quantity of the item being offered. */ + quantity: number; + + /** Optional: Indicates if the quantity is adjustable by the customer. Defaults to false. */ + adjustable_quantity?: boolean; +} + +export interface CreatePaymentLinkParams { + /** Required: A descriptive name for the payment link. */ + name: string; + + /** Required: An array of items included in the payment link. */ + items: PaymentLinkItemParams[]; + + /** Optional: A message displayed to the customer after a successful payment. */ + after_completion_message?: string; + + /** Optional: The language of the checkout page. */ + locale?: 'ar' | 'en' | 'fr'; + + /** Optional: Indicates who will pay the Chargily Pay fees. */ + pass_fees_to_customer?: boolean; + + /** Optional: Indicates whether the shipping address should be collected. */ + collect_shipping_address?: boolean; + + /** Optional: Additional information about the payment link. */ + metadata?: Record; +} + +export interface UpdatePaymentLinkItem { + /** Required: The ID of the Price associated with the item. */ + price: string; + + /** Required: The quantity of the item being offered. */ + quantity: number; + + /** Optional: Indicates if the quantity is adjustable by the customer. Defaults to false. */ + adjustable_quantity?: boolean; +} + +export interface UpdatePaymentLinkParams { + /** Optional: A descriptive name for the payment link. */ + name?: string; + + /** Optional: An array of items included in the payment link. */ + items?: UpdatePaymentLinkItem[]; + + /** Optional: A message displayed to the customer after a successful payment. */ + after_completion_message?: string; + + /** Optional: The language of the checkout page. */ + locale?: 'ar' | 'en' | 'fr'; + + /** Optional: Indicates who will pay the Chargily Pay fees. */ + pass_fees_to_customer?: boolean; + + /** Optional: Indicates whether the shipping address should be collected. */ + collect_shipping_address?: boolean; + + /** Optional: Additional information about the payment link. */ + metadata?: Record; +} diff --git a/src/types/response.ts b/src/types/response.ts new file mode 100644 index 0000000..27ba133 --- /dev/null +++ b/src/types/response.ts @@ -0,0 +1,55 @@ +/** + * Represents the standard response for a paginated list from the Chargily API. + * @template T - The data type of the items in the list. + */ +export interface ListResponse { + /** Indicates if the operation was performed in live mode or test mode. */ + livemode: boolean; + + /** The current page number of the list. */ + current_page: number; + + /** An array of items of type T on the current page. */ + data: T[]; + + /** The URL for the first page of the list. */ + first_page_url: string; + + /** The number indicating the last page of the list. */ + last_page: number; + + /** The URL for the last page of the list. */ + last_page_url: string; + + /** The URL for the next page of the list, or null if on the last page. */ + next_page_url: string | null; + + /** The base path URL for the paginated list endpoints. */ + path: string; + + /** The number of items to be displayed per page. */ + per_page: number; + + /** The URL for the previous page of the list, or null if on the first page. */ + prev_page_url: string | null; + + /** The total number of items available across all pages. */ + total: number; +} + +/** + * Represents the response received upon the deletion of an item via the Chargily API. + */ +export interface DeleteItemResponse { + /** Indicates if the operation was performed in live mode or test mode. */ + livemode: boolean; + + /** The unique identifier of the item that was deleted. */ + id: string; + + /** A string representing the type of the object that was deleted. */ + entity: string; + + /** A boolean indicating whether the deletion was successful. */ + deleted: boolean; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..873289f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "outDir": "./lib", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "declaration": true + }, + "include": ["src/**/*"] +}