### Start Application Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/readme.md Run the Node.js application after installation and configuration. ```shell npm start ``` -------------------------------- ### Usage Example for Authentication Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/authenticate.md Demonstrates how to import and use the authenticate function to get an access token. It includes basic error handling and logging. ```javascript import { authenticate } from './src/utils/authenticate.js'; async function setupPayment() { try { const accessToken = await authenticate(); console.log('Authentication successful'); console.log('Access token:', accessToken); return accessToken; } catch (error) { console.error('Authentication failed:', error); } } await setupPayment(); ``` -------------------------------- ### Install Dependencies Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/readme.md Install the necessary Node.js dependencies for the project. ```shell npm install ``` -------------------------------- ### Error Handling for Database Connection Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/database-connection.md Wrap the connect function call in a try-catch block to handle potential connection errors gracefully. This example demonstrates exiting the application if the database connection fails during setup. ```javascript import connect from './database/db.js'; async function setupDatabase() { try { await connect(); } catch (error) { console.error('Database setup failed:', error); process.exit(1); // Exit application on connection failure } } ``` -------------------------------- ### Environment Variables Setup Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/readme.md Configure essential environment variables for PayMob integration in a .env file. ```shell PAY_API HMAC_KEY PASSWORD USERNAME MONGO_URI PORT ``` -------------------------------- ### Full Refund Usage Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/refund-transaction.md Example demonstrating how to perform a full refund of a captured transaction. It first retrieves transaction details to ensure it's eligible for a refund. ```javascript import { refundTransaction } from './src/utils/refundTrx.js'; import { getTransactionById } from './src/utils/getTrx.js'; async function refundOrder(transactionId) { try { // First, get transaction details to confirm it can be refunded const transaction = await getTransactionById(transactionId); if (!transaction || !transaction.is_capture) { console.error('Transaction cannot be refunded: not captured'); return; } if (transaction.is_refunded) { console.error('Transaction already refunded'); return; } // Refund the full transaction amount const refundResponse = await refundTransaction( transactionId, transaction.amount_cents ); console.log('Full refund processed:', refundResponse); return refundResponse; } catch (error) { console.error('Refund failed:', error.message); } } await refundOrder(12345678); ``` -------------------------------- ### Partial Refund Usage Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/refund-transaction.md Example demonstrating how to perform a partial refund of a transaction by specifying a specific amount in cents. ```javascript import { refundTransaction } from './src/utils/refundTrx.js'; async function processPartialRefund(transactionId, partialAmountCents) { try { const refundResponse = await refundTransaction( transactionId, partialAmountCents ); console.log('Partial refund of', partialAmountCents, 'cents processed'); console.log('Response:', refundResponse); return refundResponse; } catch (error) { console.error('Partial refund failed:', error.message); } } // Refund 50000 cents (500 in major units) of a larger transaction await processPartialRefund(12345678, 50000); ``` -------------------------------- ### BillingData Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/types.md Provides an example of a BillingData object, which includes detailed customer and address information for the `pay()` checkout function. Ensure all fields are populated correctly. ```javascript const billingData = { apartment: "803", email: "customer@example.com", floor: "42", first_name: "John", street: "123 Main Street", building: "8028", phone_number: "+1 (555) 123-4567", shipping_method: "PKG", postal_code: "01898", city: "New York", country: "US", last_name: "Doe", state: "NY" }; ``` -------------------------------- ### Example Database Connection Error Log Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/errors.md This is an example log message for a database connection error, specifically indicating a connection refused error. ```text Database connection error: Error: connect ECONNREFUSED 127.0.0.1:27017 ``` -------------------------------- ### Example Paymob Payment Key Generation Error Response Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/errors.md Shows an example error response from Paymob when attempting to generate a payment key, such as when an integration is not found. ```json { "error_message": "Integration not found" } ``` -------------------------------- ### Example Paymob Authentication Error Response Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/errors.md This is an example of an error response received from the Paymob API when authentication fails due to incorrect credentials. ```json { "detail": "Incorrect API Key" } ``` -------------------------------- ### Paymob Callback Request Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/callback-handler.md Example of a POST request Paymob sends to your webhook URL containing transaction details. Your server must extract these fields to verify the HMAC signature. ```javascript // Paymob sends callback to your webhook POST https://your-domain.com/api/processed?hmac=a1b2c3d4... Content-Type: application/json { "obj": { "id": 123456789, "order": { "id": 987654321 }, "amount_cents": 2400000, "currency": "EGP", "success": true, "is_capture": true, "is_refunded": false, "is_voided": false, "is_auth": true, "is_3d_secure": true, "is_standalone_payment": false, "has_parent_transaction": false, "pending": false, "error_occured": false, "created_at": "2024-06-23T10:30:45Z", "owner": "merchant@example.com", "integration_id": 2329228, "source_data": { "type": "card", "sub_type": "VISA", "pan": "4111" } } } // Your server: // 1. Extracts all fields from req.body.obj // 2. Builds lexicographical string // 3. Computes HMAC-SHA512 // 4. Compares with req.query.hmac // 5. If valid: saves to MongoDB paymentDetails collection // 6. Responds with 200 OK ``` -------------------------------- ### Get Transaction Details Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/readme.md Retrieves the details of a specific transaction. This function requires prior authentication. ```javascript // Assuming 'token' is obtained from authenticate() // const getTransaction = async (transaction_id, token) => { // // Implementation details for getting transaction details // // ... // }; ``` -------------------------------- ### Clone Repository Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/CONTRIBUTING.md Clone the forked repository to your local machine to start making changes. ```bash git clone https://github.com/your-username/paymob-integration.git ``` -------------------------------- ### Complete Payment Flow Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Illustrates a typical e-commerce payment flow, from frontend checkout to Paymob callback and status polling. ```javascript // 1. Frontend calls checkout endpoint POST /api/checkout { order_cart: [ { name: "Product", amount_cents: 100000, description: "...", quantity: 1 } ], billing_data: { /* customer info */ }, amount_cents: 100000 } // 2. Response contains payment link Response: "https://accept.paymob.com/api/acceptance/iframes/416800?payment_token=..." // 3. Frontend redirects user to payment link // User completes payment on Paymob // 4. Paymob sends webhook callback POST /api/processed?hmac=xxxxx { obj: { transaction data } } // 5. Application verifies HMAC and stores in MongoDB // 6. Frontend polls /api/orders?orderId=xxx or /api/trx?transactionId=xxx to check status ``` -------------------------------- ### Example Paymob Transaction Fetch Error Response Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/errors.md An example of an error response from Paymob when a requested transaction cannot be found. ```json { "detail": "Not found." } ``` -------------------------------- ### Transaction Object Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/types.md Illustrates a sample TransactionObject with populated fields, including details about amount, currency, status, and associated order and source data. ```javascript const transaction = { id: 123456789, amount_cents: 2400000, currency: "EGP", success: true, is_auth: true, is_capture: true, is_refunded: false, is_voided: false, is_3d_secure: true, is_standalone_payment: false, has_parent_transaction: false, pending: false, error_occured: false, created_at: "2024-06-23T10:30:45Z", order: { id: 987654321 }, source_data: { type: "card", sub_type: "VISA", pan: "4111" }, owner: "merchant@example.com", integration_id: 2329228 }; ``` -------------------------------- ### Integrate Database Connection in Server Startup Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/database-connection.md Call the connect function during the server startup process before setting up routes or starting the server. This ensures the database is available when needed. ```javascript // In src/server.js import connect from './database/db.js'; import express from 'express'; import router from './routes/index.js'; const server = express(); const PORT = process.env.PORT || 3000; // Middleware setup server.use(express.json()); // Database connection connect(); // Routes server.use('/api', router); // Start server server.listen(PORT, () => { console.log(`server running on port ${PORT}`); }); ``` -------------------------------- ### Usage Example for Payment Checkout Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/checkout.md Demonstrates how to use the `pay` function to initiate a payment. Includes setting up order cart and billing data, calling the `pay` function, constructing the payment link, and handling potential errors. Redirect the user to the generated payment link to complete the transaction. ```javascript import { pay } from './src/utils/checkout.js'; async function initiatePayment() { const orderCart = [ { name: 'Premium Widget', amount_cents: 500000, description: 'High-quality widget', quantity: 1 } ]; const billingData = { apartment: '101', email: 'customer@example.com', floor: '1', first_name: 'John', street: 'Main Street', building: '123', phone_number: '+1234567890', shipping_method: 'PKG', postal_code: '12345', city: 'New York', country: 'US', last_name: 'Doe', state: 'NY' }; const amountCents = 500000; try { const paymentToken = await pay(orderCart, billingData, amountCents); const paymentLink = `https://accept.paymob.com/api/acceptance/iframes/416800?payment_token=${paymentToken}`; console.log('Payment link:', paymentLink); // Redirect user to paymentLink } catch (error) { console.error('Payment checkout failed:', error.message); } } await initiatePayment(); ``` -------------------------------- ### Refund a Transaction Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Demonstrates the process of refunding a transaction, including verification and the refund API call. ```javascript // 1. Verify transaction can be refunded GET /api/trx?transactionId=12345 // Response: { is_capture: true, is_refunded: false, amount_cents: 100000 } // 2. Call refund endpoint POST /api/refund { transactionId: 12345 } // 3. Paymob processes refund asynchronously Response: { id, is_refunded: true, amount_cents: 100000 } ``` -------------------------------- ### Void a Transaction Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Illustrates how to void a transaction, including the verification step and the void API call. ```javascript // 1. Verify transaction can be voided GET /api/trx?transactionId=12345 // Response: { is_capture: false, is_voided: false } // 2. Call void endpoint POST /api/void { transactionId: 12345 } // 3. Paymob immediately voids the authorization Response: { id, is_voided: true } ``` -------------------------------- ### OrderItem Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/types.md Illustrates how to construct an OrderItem object for the `pay()` checkout function. The `amount_cents` and `quantity` fields can be either strings or numbers. ```javascript const item = { name: "Premium Widget", amount_cents: 500000, description: "High-quality widget with warranty", quantity: 2 }; ``` -------------------------------- ### Callback Request Body Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/callback-handler.md Illustrates the expected JSON structure for the request body received by the callback handler. This includes transaction details and source data. ```json { "obj": { "amount_cents": number, "created_at": string, "currency": string, "error_occured": boolean, "has_parent_transaction": boolean, "id": number, "integration_id": number, "is_3d_secure": boolean, "is_auth": boolean, "is_capture": boolean, "is_refunded": boolean, "is_standalone_payment": boolean, "is_voided": boolean, "order": { "id": number }, "owner": string, "pending": boolean, "source_data": { "pan": string, "sub_type": string, "type": string }, "success": boolean } } ``` -------------------------------- ### Transaction Decision Flow Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md This snippet demonstrates how to decide whether to refund or void a transaction based on its capture status. It requires fetching the transaction details first. ```javascript const txn = await getTransactionById(txnId); if (txn.is_capture) { // Transaction is captured → must refund await refundTransaction(txnId, txn.amount_cents); } else { // Transaction is authorized only → can void await voidTransaction(txnId); } ``` -------------------------------- ### Refund Transaction Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/readme.md Performs a refund operation on a captured payment. This function requires prior authentication. ```javascript // Assuming 'token' is obtained from authenticate() // const refund = async (order_id, amount_cents, token) => { // // Implementation details for refunding a transaction // // ... // }; ``` -------------------------------- ### Void Transaction Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/readme.md Performs a void operation on a transaction. This function requires prior authentication. ```javascript // Assuming 'token' is obtained from authenticate() // const voidTransaction = async (order_id, token) => { // // Implementation details for voiding a transaction // // ... // }; ``` -------------------------------- ### Usage Example for getTransactionById Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/get-transaction.md Demonstrates how to use the getTransactionById function to fetch and log transaction details. It includes error handling for cases where the transaction is not found or an error occurs. ```javascript import { getTransactionById } from './src/utils/getTrx.js'; async function checkTransactionStatus() { const transactionId = 12345678; try { const transaction = await getTransactionById(transactionId); if (!transaction) { console.error('Transaction not found or error occurred'); return; } console.log('Transaction Amount:', transaction.amount_cents, 'cents'); console.log('Success:', transaction.success); console.log('Status - Is Captured:', transaction.is_capture); console.log('Status - Is Refunded:', transaction.is_refunded); console.log('Status - Is Voided:', transaction.is_voided); console.log('Payment Method:', transaction.source_data.type); console.log('Created At:', transaction.created_at); return transaction; } catch (error) { console.error('Error fetching transaction:', error.message); } } await checkTransactionStatus(); ``` -------------------------------- ### Example Paymob Order Registration Validation Error Response Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/errors.md Illustrates common validation errors returned by the Paymob API when registering an order with invalid data. ```json { "amount_cents": ["This field may not be blank."], "items": ["This field is required."] } ``` -------------------------------- ### Decision Flow Example: Handle Cancellation Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/void-transaction.md Illustrates a decision flow to either void an uncaptured transaction or refund a captured one using `voidTransaction` and `refundTransaction`. This ensures the correct operation is performed based on the transaction's capture status. ```javascript import { voidTransaction } from './src/utils/voidTrx.js'; import { refundTransaction } from './src/utils/refundTrx.js'; import { getTransactionById } from './src/utils/getTrx.js'; async function handleCancellation(transactionId) { const txn = await getTransactionById(transactionId); if (!txn) { console.error('Transaction not found'); return; } if (txn.is_capture) { // Transaction is captured, must refund console.log('Initiating refund...'); return await refundTransaction(transactionId, txn.amount_cents); } else { // Transaction is not captured, can void console.log('Initiating void...'); return await voidTransaction(transactionId); } } await handleCancellation(12345678); ``` -------------------------------- ### GET /api - Index/Info Endpoint Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/endpoints.md Retrieves general application information. This endpoint renders an EJS template and is used for displaying API documentation or status pages. ```http GET http://localhost:3000/api ``` -------------------------------- ### GET /state?success=... Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Displays a success or failure page based on the provided status. This endpoint is part of the Data Flow Routes. ```APIDOC ## GET /state?success=... ### Description Displays a success or failure page. ### Method GET ### Endpoint /state ### Parameters #### Query Parameters - **success** (boolean) - Required - Indicates whether the operation was successful. ``` -------------------------------- ### Advanced Usage of getTransactionById Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/get-transaction.md An advanced example showing how to use getTransactionById to analyze transaction flow and determine its status (completed, pending, refundable, voidable). ```javascript import { getTransactionById } from './src/utils/getTrx.js'; async function analyzeTransactionFlow(transactionId) { const txn = await getTransactionById(transactionId); if (!txn) return null; const status = { isCompleted: txn.success && txn.is_capture, isPending: txn.pending && !txn.is_capture, canRefund: txn.is_capture && !txn.is_refunded, canVoid: !txn.is_capture && !txn.is_voided, }; return { transaction: txn, status }; } ``` -------------------------------- ### GET /api Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/endpoints.md Returns the info page with general application information. This is a view endpoint that renders an EJS template, often used for displaying API documentation or a status page. ```APIDOC ## GET /api ### Description Returns the info page with general application information. ### Method GET ### Endpoint /api ### Response #### Success Response (200) - **Content-Type**: `text/html` - **Body**: Rendered `info.ejs` view ### Notes - This is a view endpoint that renders EJS template - Used for displaying API documentation or status page ``` -------------------------------- ### Query Payment Records Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/payment-model.md Provides examples for querying payment records from the database using various criteria. These queries can find all payments for an order, a specific transaction, all successful transactions, or all refunded transactions. ```javascript import payStore from "../../models/payment.js"; // Find all payments for an order const orderPayments = await payStore.find({ order_id: 987654321 }); // Find a specific transaction const transaction = await payStore.findOne({ id: 123456789 }); // Find all successful transactions const successful = await payStore.find({ success: true }); // Find refunded transactions const refunded = await payStore.find({ is_refunded: true }); ``` -------------------------------- ### Calculate HMAC Signature Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/callback-handler.md Example of how to calculate the HMAC-SHA512 signature for a Paymob callback using Node.js crypto module. Ensure all transaction fields are included in the lexicographical string in the correct order. ```javascript import { createHmac } from 'crypto'; const hmacKey = process.env.HMAC_KEY; const txnData = { amount_cents: 2400000, created_at: "2024-06-23T10:30:45Z", // ... all other fields }; // Build lexicographical string (same order as handler) const lex = txnData.amount_cents + txnData.created_at + // ... continue with all fields in correct order // Compute hash const hash = createHmac("sha512", hmacKey).update(lex).digest("hex"); console.log('Computed HMAC:', hash); ``` -------------------------------- ### Copy ENV_EXAMPLE to .env Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/configuration.md Use this command to create a local .env file from the provided template. Edit the .env file with your specific API keys and credentials. ```bash # Copy template to .env cp ENV_EXAMPLE .env # Edit with your values nano .env ``` -------------------------------- ### Verify Configuration Output Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/configuration.md The application logs connection status upon successful startup. Check for 'Database connected' and 'server running on port [port]' messages. ```text Database connected server running on port 3000 ``` -------------------------------- ### Create .env File Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/configuration.md Create a .env file in the project root directory to store configuration variables. ```bash cd /path/to/paymob-payment-gateway touch .env ``` -------------------------------- ### Initialize Database Connection Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/database-connection.md Import and call the connect function to establish the MongoDB connection. This should be done once during application startup. Connection status logs will appear in the console. ```javascript import connect from './src/database/db.js'; async function startServer() { try { // Initialize database connection await connect(); // Connection listeners will log to console // Now safe to use mongoose models console.log('Database is ready'); } catch (error) { console.error('Failed to connect to database:', error); } } startServer(); ``` -------------------------------- ### Get Transaction by ID Function Signature Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/get-transaction.md The signature for the getTransactionById function. ```javascript export async function getTransactionById(transactionId) ``` -------------------------------- ### Example Void Error Response Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/errors.md This JSON structure indicates that a transaction cannot be voided due to its current state. ```json { "detail": "Transaction cannot be voided in its current state" } ``` -------------------------------- ### pay(order_cart, billing_data, amount_cents) Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/checkout.md Initiates a payment checkout by registering an order with Paymob and obtaining a payment key token. This function orchestrates a three-step process: authentication, order registration, and payment key generation. The returned payment key token is used to redirect customers to the Paymob payment iframe. ```APIDOC ## pay(order_cart, billing_data, amount_cents) ### Description Initiates a payment checkout by registering an order with Paymob and obtaining a payment key token. This function orchestrates a three-step process: authentication, order registration, and payment key generation. The returned payment key token is used to redirect customers to the Paymob payment iframe. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Method This is a JavaScript function, not an HTTP endpoint. ### Endpoint N/A ### Parameters #### `order_cart` - **Type**: `Array` - **Required**: Yes - **Description**: Array of items being purchased. Each item should contain `name`, `amount_cents`, `description`, and `quantity`. #### `billing_data` - **Type**: `BillingData` - **Required**: Yes - **Description**: Customer billing and shipping address information. Must include fields like `first_name`, `last_name`, `email`, `phone_number`, `address`, `city`, `country`, etc. #### `amount_cents` - **Type**: `number` - **Required**: Yes - **Description**: Total payment amount in cents (multiply by 100 for currency conversion). For example, 2400000 cents equals 24000 in major currency units. ### Return Type `Promise` Returns a promise that resolves to a payment key token as a string. This token is embedded in the Paymob payment iframe URL to initiate the payment flow. ### Usage Example ```javascript import { pay } from './src/utils/checkout.js'; async function initiatePayment() { const orderCart = [ { name: 'Premium Widget', amount_cents: 500000, description: 'High-quality widget', quantity: 1 } ]; const billingData = { apartment: '101', email: 'customer@example.com', floor: '1', first_name: 'John', street: 'Main Street', building: '123', phone_number: '+1234567890', shipping_method: 'PKG', postal_code: '12345', city: 'New York', country: 'US', last_name: 'Doe', state: 'NY' }; const amountCents = 500000; try { const paymentToken = await pay(orderCart, billingData, amountCents); const paymentLink = `https://accept.paymob.com/api/acceptance/iframes/416800?payment_token=${paymentToken}`; console.log('Payment link:', paymentLink); // Redirect user to paymentLink } catch (error) { console.error('Payment checkout failed:', error.message); } } await initiatePayment(); ``` ### Error Handling If any step fails, an error is thrown by axios. The calling code must handle promise rejection with a try-catch block or `.catch()` handler. ``` -------------------------------- ### POST /api/checkout - Initiate Payment Checkout Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/endpoints.md Initiates a payment checkout by creating an order and generating a payment token for Paymob's payment iframe. Requires detailed billing and order information. ```http POST http://localhost:3000/api/checkout Content-Type: application/json { "order_cart": [ { "name": "string", "amount_cents": "number|string", "description": "string", "quantity": "number|string" } ], "billing_data": { "apartment": "string", "email": "string", "floor": "string", "first_name": "string", "street": "string", "building": "string", "phone_number": "string", "shipping_method": "string", "postal_code": "string", "city": "string", "country": "string", "last_name": "string", "state": "string" }, "amount_cents": "number" } ``` -------------------------------- ### Example Refund Error Response Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/errors.md This JSON structure represents an error response when a transaction is not in a valid state for a refund. ```json { "detail": "Transaction not in valid state for refund" } ``` -------------------------------- ### Configure Environment Variables Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/configuration.md Edit the .env file with your Paymob and MongoDB credentials. Ensure all required variables are set correctly. ```env PAY_API=sk_live_XXXXXXXXXXXXXXXXXXXXXXXX HMAC_KEY=your_hmac_secret_key_from_paymob USERNAME=your_paymob_username PASSWORD=your_paymob_password MONGO_URI=mongodb+srv://user:password@cluster.mongodb.net/paymob-payments PORT=3000 ``` -------------------------------- ### Field Type Conversion Example Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/callback-handler.md All fields are concatenated as strings for HMAC calculation. No type conversion or JSON encoding is necessary before concatenation. ```javascript // amount_cents: 2400000 (number) // is_capture: true (boolean) // created_at: "2024-06-23T10:30:45Z" (string) // Concatenated as-is: "2400000" + "true" + "2024-06-23T10:30:45Z" + ... ``` -------------------------------- ### GET /trx?transactionId=... Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Fetches the details of a specific transaction using its ID. This endpoint is part of the Data Flow Routes. ```APIDOC ## GET /trx?transactionId=... ### Description Fetches transaction details by transaction ID. ### Method GET ### Endpoint /trx ### Parameters #### Query Parameters - **transactionId** (string) - Required - The ID of the transaction to fetch. ``` -------------------------------- ### Verify Database Connection with MongoDB Client Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/errors.md Connect to your MongoDB instance and query the 'paymentDetails' collection to verify the database connection. ```bash # From MongoDB client mongo --uri "mongodb+srv://user:pass@cluster.mongodb.net/db" use paymob-payments db.paymentDetails.find().limit(1) ``` -------------------------------- ### GET /orders?orderId=... Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Queries stored payment records using an order ID. This endpoint is part of the Data Flow Routes. ```APIDOC ## GET /orders?orderId=... ### Description Queries stored payment records by order ID. ### Method GET ### Endpoint /orders ### Parameters #### Query Parameters - **orderId** (string) - Required - The ID of the order to query. ``` -------------------------------- ### POST /api/checkout Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/endpoints.md Initiates a payment checkout by creating an order and generating a payment token for Paymob's payment iframe. It requires detailed cart and billing information. ```APIDOC ## POST /api/checkout ### Description Initiates a payment checkout by creating an order and generating a payment token for Paymob's payment iframe. ### Method POST ### Endpoint /api/checkout ### Parameters #### Request Body - **order_cart** (Array) - Required - Array of items being purchased - **order_cart[].name** (String) - Required - Item name - **order_cart[].amount_cents** (Number | String) - Required - Item price in cents - **order_cart[].description** (String) - Required - Item description - **order_cart[].quantity** (Number | String) - Required - Item quantity - **billing_data** (Object) - Required - Customer billing and shipping address - **billing_data.apartment** (String) - Required - Apartment number - **billing_data.email** (String) - Required - Customer email - **billing_data.floor** (String) - Required - Floor number - **billing_data.first_name** (String) - Required - Customer first name - **billing_data.street** (String) - Required - Street name - **billing_data.building** (String) - Required - Building number - **billing_data.phone_number** (String) - Required - Customer phone number - **billing_data.shipping_method** (String) - Required - Shipping method code (e.g., "PKG") - **billing_data.postal_code** (String) - Required - Postal code - **billing_data.city** (String) - Required - City name - **billing_data.country** (String) - Required - Country code (e.g., "US", "CR") - **billing_data.last_name** (String) - Required - Customer last name - **billing_data.state** (String) - Required - State/Province name - **amount_cents** (Number) - Required - Total transaction amount in cents ### Request Example ```json { "order_cart": [ { "name": "string", "amount_cents": "number|string", "description": "string", "quantity": "number|string" } ], "billing_data": { "apartment": "string", "email": "string", "floor": "string", "first_name": "string", "street": "string", "building": "string", "phone_number": "string", "shipping_method": "string", "postal_code": "string", "city": "string", "country": "string", "last_name": "string", "state": "string" }, "amount_cents": "number" } ``` ### Response #### Success Response (200) - **Content-Type**: `application/json` - **Body**: Payment iframe URL as a JSON string (the URL contains the payment token) #### Response Example ```json "https://accept.paymob.com/api/acceptance/iframes/416800?payment_token=YOUR_TOKEN_HERE" ``` #### Error Response (400) - **Content-Type**: `application/json` - **Body**: { "message": "error details from axios or paymob" } ### Implementation - Uses [`pay()` function](./api-reference/checkout.md) from `src/utils/checkout.js` - Route handler: `src/routes/v1/checkout.js` (lines 6-22) ``` -------------------------------- ### GET Transaction Details Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/endpoints.md Retrieves detailed information about a specific transaction using its ID. Requires a valid transaction ID as a query parameter. ```http GET http://localhost:3000/api/trx?transactionId=TRANSACTION_ID ``` -------------------------------- ### Enable Detailed Logging in Server Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/errors.md Add this middleware to your server to log incoming requests and their bodies for debugging. ```javascript // Add to src/server.js server.use((req, res, next) => { console.log(`${req.method} ${req.path}`, req.body); next(); }); ``` -------------------------------- ### Request Flow Diagram Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Illustrates the sequence of events from client request to payment processing and callback handling. ```text Client Browser ↓ Express Server (port 3000) ↓ (POST /api/checkout) Route Handler (checkout.js) ↓ pay() function (checkout.js) ├→ authenticate() → Paymob /auth/tokens ├→ Order Registration → Paymob /ecommerce/orders └→ Payment Key → Paymob /acceptance/payment_keys ↓ Payment Token + iframe URL ↓ Client redirected to Paymob payment page ↓ Paymob processes payment ↓ Paymob POST callback → /api/processed webhook ↓ HMAC verification → MongoDB payment storage ``` -------------------------------- ### Project Directory Structure Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Outlines the organization of the Node.js project, detailing the purpose of each directory and key files. ```text src/ ├── server.js # Express server initialization ├── database/ │ └── db.js # MongoDB connection ├── models/ │ └── payment.js # Payment record schema ├── routes/ │ ├── index.js # Route dispatcher │ └── v1/ │ ├── checkout.js # POST /checkout │ ├── refund.js # POST /refund │ ├── void.js # POST /void │ ├── trx.js # GET /trx │ ├── order.js # GET /orders │ ├── state.js # GET /state (result page) │ └── callback.js # POST /processed (webhook) └── utils/ ├── authenticate.js # Paymob authentication ├── checkout.js # Order + payment key creation ├── getTrx.js # Transaction details retrieval ├── refundTrx.js # Refund processing └── voidTrx.js # Void processing ``` -------------------------------- ### connect() - Establish MongoDB Connection Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/database-connection.md Establishes a MongoDB connection using Mongoose. It configures connection pooling and event listeners for connection status. This function should be called once during application startup. ```APIDOC ## connect() ### Description Establishes a MongoDB connection using Mongoose. This function configures the database connection with connection pooling options and event listeners for connection status monitoring. It is called once during application startup. ### Method Asynchronous function call ### Parameters None. The function uses the `MONGO_URI` environment variable for the connection string. ### Connection Options - `useNewUrlParser`: `true` - Use the new connection string parser (required for newer MongoDB versions) - `useUnifiedTopology`: `true` - Use the new topology engine to manage connections ### Environment Variables Required - `MONGO_URI` (string): MongoDB connection URI (e.g., `mongodb://localhost:27017/database-name` or MongoDB Atlas connection string) ### Connection Events - `connected`: Fired when the connection is successfully established. Logs "Database connected" to the console. - `error`: Fired when a connection error occurs. Logs "Database connection error: " + err to the console. ### Return Value No explicit return value. The function executes asynchronously and modifies the global mongoose connection state. ### Usage Example ```javascript import connect from './src/database/db.js'; async function startServer() { try { // Initialize database connection await connect(); // Connection listeners will log to console // Now safe to use mongoose models console.log('Database is ready'); } catch (error) { console.error('Failed to connect to database:', error); } } startServer(); ``` ### Error Handling The function attaches an error listener but does not throw exceptions. Connection errors are logged to the console. To implement proper error handling, wrap the connection call: ```javascript import connect from './database/db.js'; async function setupDatabase() { try { await connect(); } catch (error) { console.error('Database setup failed:', error); process.exit(1); // Exit application on connection failure } } ``` ``` -------------------------------- ### HMAC Verification Process Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/callback-handler.md This outlines the server-side steps for verifying Paymob callback requests. It involves constructing a string from transaction data, computing an HMAC-SHA512 hash, and comparing it with the hash provided in the callback URL. ```text Paymob Server: 1. Construct lexicographical string from transaction data 2. Compute HMAC-SHA512(string, HMAC_KEY) 3. Append hash to callback URL query parameter 4. POST callback to your webhook URL Your Server: 1. Receive callback with hmac query parameter 2. Construct same lexicographical string from request body 3. Compute HMAC-SHA512(string, HMAC_KEY) 4. Compare: if computed hash === query hmac, valid 5. If valid: save to database 6. If invalid: silently ignore (don't save) ``` -------------------------------- ### Handle Database Connection Errors in JavaScript Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/errors.md Connect to the database asynchronously and implement a timeout mechanism to detect connection failures. This prevents application startup from being blocked by immediate connection errors. ```javascript import connect from './database/db.js'; async function startApp() { // This may not throw immediately due to async connection connect(); // Add timeout to detect connection failures setTimeout(() => { // Verify connection is ready if (!mongoose.connection.readyState) { console.error('Database failed to connect'); process.exit(1); } }, 5000); } ``` -------------------------------- ### Retrieve Order Status Request Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/endpoints.md Use a GET request with an order ID to retrieve associated payment records from the database. Returns an empty array if no records are found. ```http GET http://localhost:3000/api/orders?orderId=ORDER_ID ``` -------------------------------- ### POST /checkout Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Creates an order and retrieves a payment token for the Paymob iframe redirect. This endpoint is part of the Data Flow Routes. ```APIDOC ## POST /checkout ### Description Creates an order and gets a payment token for iframe redirect. ### Method POST ### Endpoint /checkout ### Parameters #### Request Body - **order_cart** (object) - Required - Details of the order. - **billing_data** (object) - Required - Billing information. - **amount_cents** (integer) - Required - The total amount in cents. ``` -------------------------------- ### Usage Example: Cancel Uncaptured Payment Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/void-transaction.md Demonstrates how to check transaction status and void an uncaptured payment using `voidTransaction`. Ensure the transaction is not captured or already voided before proceeding. ```javascript import { voidTransaction } from './src/utils/voidTrx.js'; import { getTransactionById } from './src/utils/getTrx.js'; async function cancelUnCapturedPayment(transactionId) { try { // Verify the transaction exists and is not yet captured const transaction = await getTransactionById(transactionId); if (!transaction) { console.error('Transaction not found'); return; } if (transaction.is_capture) { console.error('Cannot void: transaction already captured. Use refund instead.'); return; } if (transaction.is_voided) { console.error('Transaction already voided'); return; } // Void the transaction const voidResponse = await voidTransaction(transactionId); console.log('Transaction voided successfully:', voidResponse); return voidResponse; } catch (error) { console.error('Void operation failed:', error.message); } } await cancelUnCapturedPayment(12345678); ``` -------------------------------- ### Checkout and Create Payment Key Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/readme.md Registers an order with PayMob and obtains a payment key token for redirecting the user to the payment page. ```javascript /** * Registers an order and obtains a payment key token for the PayMob payment flow. * @param {Array} order_cart - An array of item objects for the order. * @param {number} amount_cents - The total amount to be paid in cents. * @param {object} billing_data - An object containing the customer's billing details. * @returns {Promise} A promise that resolves to an object containing the payment key token. */ const checkout = async (order_cart, amount_cents, billing_data) => { // Implementation details for checkout and payment key generation // ... return { token: "payment_key_token" }; // Placeholder for actual token }; ``` -------------------------------- ### Load Environment Variables with dotenv Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/configuration.md Import and configure the dotenv package to automatically load variables from your .env file. Access these variables using process.env. ```javascript import dotenv from "dotenv"; dotenv.config(); // Loads .env file const apiKey = process.env.PAY_API; // Access variables ``` -------------------------------- ### Build Lexicographical String Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/callback-handler.md Concatenates transaction fields in a specific order required for Paymob's HMAC calculation. ```javascript let lexogragical = amount_cents + created_at + currency + error_occured + has_parent_transaction + id + integration_id + is_3d_secure + is_auth + is_capture + is_refunded + is_standalone_payment + is_voided + order_id + owner + pending + source_data_pan + source_data_sub_type + source_data_type + success; ``` -------------------------------- ### Get Transaction by ID Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/api-reference/get-transaction.md Retrieves detailed information about a specific transaction from Paymob servers by its transaction ID. This function performs authentication and then queries the Paymob API to fetch transaction data including payment status, amount, timestamps, and payment method details. ```APIDOC ## GET /v1/accept/api/acceptance/transactions/{transactionId} ### Description Retrieves detailed information about a specific transaction from Paymob servers by its transaction ID. This function performs authentication and then queries the Paymob API to fetch transaction data including payment status, amount, timestamps, and payment method details. ### Method GET ### Endpoint `https://accept.paymob.com/api/acceptance/transactions/{transactionId}` ### Parameters #### Path Parameters - **transactionId** (string | number) - Required - The unique identifier of the transaction to retrieve. This ID comes from Paymob when a transaction is created. #### Headers - **Content-Type** (string) - Required - `application/json` - **Authorization** (string) - Required - `Bearer {accessToken}` ### Response #### Success Response (200) - **id** (number) - Transaction ID - **amount_cents** (number) - Transaction amount in cents - **currency** (string) - Currency code, typically "EGP" - **success** (boolean) - Whether the transaction was successful - **is_auth** (boolean) - Whether transaction is in auth state - **is_capture** (boolean) - Whether transaction is captured - **is_refunded** (boolean) - Whether transaction has been refunded - **is_voided** (boolean) - Whether transaction has been voided - **order** (object) - Associated order object with order ID - **created_at** (string) - Timestamp of transaction creation - **source_data** (object) - Payment method details (object with `type`, `sub_type`, `pan`) ### Error Handling In case of failure, the function logs the error response to the console via `console.error()` but does not throw an exception. The promise may resolve to `undefined` if an error occurs. Calling code should handle this by checking for null/undefined before using the returned data. ``` -------------------------------- ### Order Registration Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Register a new order with details such as amount, currency, and items. Requires an active authentication token. ```APIDOC ## POST /ecommerce/orders ### Description Registers a new order in the Paymob system. ### Method POST ### Endpoint https://accept.paymob.com/api/ecommerce/orders ### Parameters #### Headers - **Authorization** (string) - Required - Bearer token obtained from the authentication endpoint (e.g., `Bearer {token}`). #### Request Body - **auth_token** (string) - Required - The access token obtained from the authentication endpoint. - **amount_cents** (integer) - Required - The total amount of the order in cents. - **currency** (string) - Required - The currency of the order (e.g., "EGP"). - **items** (array) - Required - A list of items included in the order. - **delivery_needed** (boolean) - Required - Indicates if delivery is needed for the order. ### Response #### Success Response (200) - **id** (string) - The unique identifier for the registered order. ``` -------------------------------- ### Create New Branch Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/CONTRIBUTING.md Create a new branch for your contribution to keep your changes organized. ```bash git checkout -b your-branch-name ``` -------------------------------- ### Authentication Endpoint Source: https://github.com/rafa763/paymob-payment-gateway/blob/master/_autodocs/README.md Obtain an access token by providing your API key, username, and password. ```APIDOC ## POST /auth/tokens ### Description Authenticates with the Paymob API to obtain an access token. ### Method POST ### Endpoint https://accept.paymob.com/api/auth/tokens ### Parameters #### Request Body - **api_key** (string) - Required - Your Paymob API key. - **username** (string) - Required - Your Paymob username. - **password** (string) - Required - Your Paymob password. ### Response #### Success Response (200) - **token** (string) - The access token for subsequent API calls. ```