### Complete NowPayments Middleware Example Source: https://github.com/hanslove/nowpayments-middleware/blob/main/README.md A full Express.js application demonstrating the setup and usage of NowPaymentsMiddleware for creating payments, handling webhooks, and implementing error handling. ```typescript import express from 'express'; import { NowPaymentsMiddleware, PaymentStatus, NowPaymentsError } from '@taloon/nowpayments-middleware'; const app = express(); app.use(express.json()); // Configuration NowPaymentsMiddleware.configure({ apiKey: process.env.NOWPAYMENTS_API_KEY!, email: process.env.NOWPAYMENTS_EMAIL, password: process.env.NOWPAYMENTS_PASSWORD, errorHandling: 'next', }); // Create payment app.post('/orders', NowPaymentsMiddleware.createPayment({ mapRequest: (req, res) => ({ price_amount: req.body.amount, price_currency: req.body.currency, pay_currency: req.body.cryptoCurrency, order_id: `order_${Date.now()}`, order_description: `Order for ${req.body.amount} ${req.body.currency}`, customer_email: req.body.email, ipn_callback_url: `${req.protocol}://${req.get('host')}/webhook/payment`, }), transformResponse: (response) => ({ orderId: response.order_id, paymentId: response.payment_id, status: response.payment_status, payAddress: response.pay_address, payAmount: response.pay_amount, payCurrency: response.pay_currency, expiresAt: response.expiration_estimate_date, }), }), (req, res) => { const payment = res.locals.nowPaymentsResponse; res.status(201).json({ success: true, data: payment }); } ); // Payment webhook app.post('/webhook/payment', NowPaymentsMiddleware.paymentWebhook({ onFinished: async (payload) => { console.log(`Payment completed: ${payload.payment_id}`); // Update database, send confirmation email, etc. }, onFailed: async (payload) => { console.log(`Payment failed: ${payload.payment_id}`); // Handle failure, cancel order, notify user, etc. }, onExpired: async (payload) => { console.log(`Payment expired: ${payload.payment_id}`); // Cancel order, notify user about expiration }, }) ); // Error handling app.use((error: any, req: express.Request, res: express.Response, next: express.NextFunction) => { console.error('Application error:', error); if (error instanceof NowPaymentsError) { return res.status(error.statusCode || 400).json({ success: false, error: { code: error.code, message: error.message, }, }); } res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred', }, }); }); app.listen(3000, () => { console.log('Server running on port 3000'); }); ``` -------------------------------- ### Pack and Install Middleware Source: https://github.com/hanslove/nowpayments-middleware/blob/main/local-installation.md This method creates a tarball of the library, mimicking a standard npm installation. It's useful for testing the installation process without global links. ```bash # 1. In the middleware directory, create tarball cd /path/to/NowpaymentsMiddleware pnpm pack # 2. Install the generated tarball cd /path/to/your-project pnpm add /path/to/NowpaymentsMiddleware/taloon-nowpayments-middleware-1.0.0.tgz ``` -------------------------------- ### Testing Configuration for NowPayments Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/configuration-examples.mdx Provides a TypeScript example for setting up a test configuration for the NowPayments middleware, using mock API keys and a sandbox base URL. It includes a `beforeAll` hook for test setup. ```typescript // test-config.ts import { NowPaymentsMiddleware } from '@taloon/nowpayments-middleware'; export const setupTestConfig = () => { NowPaymentsMiddleware.configure({ apiKey: 'test-api-key', email: 'test@example.com', password: 'test-password', baseURL: 'https://api-sandbox.nowpayments.io/v1', errorHandling: 'direct', }); }; // In your tests beforeAll(() => { setupTestConfig(); }); ``` -------------------------------- ### Install NowPayments Middleware Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/quick-start.mdx Install the middleware package using npm. This is the first step to integrating NowPayments into your project. ```bash npm install @taloon/nowpayments-middleware ``` -------------------------------- ### Build the Middleware Library Source: https://github.com/hanslove/nowpayments-middleware/blob/main/local-installation.md Before installing locally, ensure the library is built. Navigate to the middleware directory and run the build command. ```bash cd /path/to/NowpaymentsMiddleware pnpm run build ``` -------------------------------- ### Install from Local Directory Source: https://github.com/hanslove/nowpayments-middleware/blob/main/local-installation.md This method is recommended for simple local testing. It directly adds the library from its local path. Changes to the library will require reinstallation. ```bash # From your project directory pnpm add file:/path/to/NowpaymentsMiddleware # Or with npm npm install file:/path/to/NowpaymentsMiddleware ``` -------------------------------- ### Install Peer Dependencies for EVM and Tron Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/dispersion-guide.mdx Install the necessary libraries for Polygon (EVM) and Tron networks. For EVM networks, install ethers. For Tron, install tronweb. ```bash # For Polygon (and any EVM network) pnpm add ethers@^6 # For Tron pnpm add tronweb@^6 ``` -------------------------------- ### Configure and Use NowPayments Middleware Source: https://github.com/hanslove/nowpayments-middleware/blob/main/local-installation.md After installation, configure the middleware with your API key and sandbox settings. Then, use it to create payment endpoints in your application. ```typescript import { NowPaymentsMiddleware } from '@taloon/nowpayments-middleware'; // Configure NowPaymentsMiddleware.configure({ apiKey: 'your-api-key', sandbox: true }); // Use middleware app.post('/payment', NowPaymentsMiddleware.createPayment({ mapRequest: (req) => ({ price_amount: req.body.amount, price_currency: req.body.currency, pay_currency: req.body.cryptoCurrency, }) })); ``` -------------------------------- ### Install Peer Dependency (Express) Source: https://github.com/hanslove/nowpayments-middleware/blob/main/local-installation.md Ensure the required peer dependency, Express.js, is installed in your project. This is crucial for the middleware to function correctly, especially when using global linking. ```bash pnpm add express # Or npm install express ``` -------------------------------- ### Example Test Structure Source: https://github.com/hanslove/nowpayments-middleware/blob/main/AGENTS.md Illustrates the directory structure for unit tests, mirroring the source code organization. Unit tests are located in the `tests/unit/` directory. ```tree tests/unit/ ├── client/ │ └── NowPaymentsClient.test.ts ├── config/ │ └── NowPaymentsConfig.test.ts ├── middlewares/ │ ├── webhooks/ │ │ └── paymentWebhook.test.ts │ └── createPayment.test.ts └── utils/ └── errors.test.ts ``` -------------------------------- ### Create Global Link for Development Source: https://github.com/hanslove/nowpayments-middleware/blob/main/local-installation.md Use this method for active development to get live updates. First, create a global link in the middleware directory, then link your project to this global package. ```bash # 1. In the middleware directory, create global link cd /path/to/NowpaymentsMiddleware pnpm link --global # 2. In your project, link to the global package cd /path/to/your-project pnpm link --global @taloon/nowpayments-middleware ``` -------------------------------- ### Advanced Payment with Order Management Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/payment-examples.mdx This example demonstrates creating an order with NOWPayments, including mapping request data, transforming the response, and storing order details. It also includes an endpoint to retrieve order information. ```typescript interface Order { id: string; amount: number; currency: string; cryptoCurrency: string; status: 'pending' | 'paid' | 'failed' | 'expired'; paymentId?: string; customerEmail: string; } const orders = new Map(); app.post('/orders', NowPaymentsMiddleware.createPayment({ mapRequest: (req, res) => { const orderId = `order_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const order: Order = { id: orderId, amount: req.body.amount, currency: req.body.currency, cryptoCurrency: req.body.cryptoCurrency, status: 'pending', customerEmail: req.body.email, }; orders.set(orderId, order); return { price_amount: req.body.amount, price_currency: req.body.currency, pay_currency: req.body.cryptoCurrency, order_id: orderId, order_description: `Order ${orderId}`, customer_email: req.body.email, ipn_callback_url: `${req.protocol}://${req.get('host')}/webhook/payment`, }; }, transformResponse: (response) => { const orderId = response.order_id!; const order = orders.get(orderId); if (order) { order.paymentId = response.payment_id; orders.set(orderId, order); } return { orderId, paymentId: response.payment_id, status: response.payment_status, payAddress: response.pay_address, payAmount: response.pay_amount, payCurrency: response.pay_currency, expiresAt: response.expiration_estimate_date, network: response.network, }; }, }), (req, res) => { const payment = res.locals.nowPaymentsResponse as any; res.status(201).json({ success: true, message: 'Order created successfully', data: payment, }); } ); app.get('/orders/:orderId', (req, res) => { const order = orders.get(req.params.orderId); if (!order) { return res.status(404).json({ error: 'Order not found', }); } res.json({ success: true, data: order, }); }); ``` -------------------------------- ### Dispersion Module Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/api-reference.mdx Documentation for the Dispersion Module, including the `DispersionProvider` interface and an example implementation with `PolygonDispersionProvider`. ```APIDOC ## Dispersion Module ### `DispersionProvider` Interface Contract that all dispersion providers must implement. ```typescript interface DispersionProvider { readonly network: string; connect(): Promise; disconnect(): Promise; isConnected(): boolean; validateAddress(address: string): boolean; getTokenBalance(address: string): Promise; getNativeBalance(address: string): Promise; estimateGasFee(toAddress: string, amount: string): Promise; sendToken(toAddress: string, amount: string): Promise; getControlledAddress(): string; } ``` ### `PolygonDispersionProvider` EVM-compatible provider using ethers.js v6 (optional peer dependency). ```typescript import { PolygonDispersionProvider } from '@taloon/nowpayments-middleware'; const provider = new PolygonDispersionProvider({ network: 'polygon', rpcUrl: 'https://polygon-rpc.com', controlledAddress: '0xYourControlledWallet', privateKey: process.env.POLYGON_PRIVATE_KEY, tokenContractAddress: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', // USDT Polygon tokenDecimals: 6, }); ``` Requires: `pnpm add ethers@^6` ``` -------------------------------- ### Create Configurable Express Middleware Source: https://github.com/hanslove/nowpayments-middleware/blob/main/AGENTS.md Example of a higher-order function returning an Express middleware, supporting dependency injection via options. ```typescript // Pattern: Function that returns middleware export const createPayment = ( options: CreatePaymentMiddlewareOptions ): ExpressMiddleware => getCreatePaymentMiddleware().create(options); ``` ```typescript // Usage allows dependency injection app.post( '/payment', NowPaymentsMiddleware.createPayment({ mapRequest: req => ({ /* transform request */ }), transformResponse: response => ({ /* transform response */ }), }), (req, res) => { const result = res.locals.nowPaymentsResponse; res.json(result); } ); ``` -------------------------------- ### POST /create-payment Endpoint Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/api-reference.mdx Example of using the createPayment endpoint with custom request mapping and response transformation, including middleware for authentication data. ```APIDOC ## POST /create-payment ### Description This endpoint creates a payment using the NowPayments API. It utilizes middleware to add user and account information to `res.locals` and allows for custom mapping of the request body and transformation of the API response. ### Method POST ### Endpoint /create-payment ### Parameters #### Request Body - **amount** (number) - Required - The amount for the payment. - **currency** (string) - Required - The fiat currency for the payment. - **cryptoCurrency** (string) - Required - The cryptocurrency to be used for payment. - **email** (string) - Required - The customer's email address. ### Request Example ```json { "amount": 100, "currency": "USD", "cryptoCurrency": "BTC", "email": "customer@example.com" } ``` ### Response #### Success Response (200) - **payment** (object) - Contains details of the created payment. - **paymentId** (string) - The ID of the payment. - **status** (string) - The status of the payment. - **payAddress** (string) - The payment address. - **amount** (string) - The amount to be paid. - **currency** (string) - The currency of the payment. #### Response Example ```json { "success": true, "payment": { "paymentId": "PN202310271234567890", "status": "waiting", "payAddress": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", "amount": "0.0001", "currency": "BTC" } } ``` ``` -------------------------------- ### Create and Automatically Verify Payouts Source: https://github.com/hanslove/nowpayments-middleware/blob/main/README.md Example of creating a payout using the NOWPayments middleware. When 2FA is configured, payouts are automatically verified upon creation. The response will contain the verified payout details. ```typescript // Payouts will now be automatically verified app.post('/create-payout', NowPaymentsMiddleware.createPayout({ mapRequest: (req, res) => ({ withdrawals: req.body.withdrawals, payout_description: req.body.payoutType, // e.g., "affiliate_commission" ipn_callback_url: 'https://your-domain.com/webhook/payout', }), }), (req, res) => { const payout = res.locals.nowPaymentsResponse; res.json({ success: true, payout }); // Payout is already verified } ); ``` -------------------------------- ### PolygonDispersionProvider Implementation Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/api-reference.mdx An example of implementing the `DispersionProvider` interface using `ethers.js` v6 for Polygon network interactions. It requires network details, a controlled wallet address, and optionally a private key and token contract address. ```typescript import { PolygonDispersionProvider } from '@taloon/nowpayments-middleware'; const provider = new PolygonDispersionProvider({ network: 'polygon', rpcUrl: 'https://polygon-rpc.com', controlledAddress: '0xYourControlledWallet', privateKey: process.env.POLYGON_PRIVATE_KEY, tokenContractAddress: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', // USDT Polygon tokenDecimals: 6, }); ``` -------------------------------- ### Error Handler Priority Example Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/error-handling.mdx Demonstrates the priority system where per-middleware onError handlers override the global onError configuration. ```typescript // Global handler (applies to all operations) NowPaymentsMiddleware.configure({ apiKey: 'your-api-key', onError: async (error, req, res, next) => { console.log('Global handler'); res.status(500).json({ error: 'Global error' }); }, }); // This uses the per-middleware handler (overrides global) app.post('/payments', NowPaymentsMiddleware.createPayment({ mapRequest: (req) => ({ /* ... */ }), onError: async (error, req, res, next) => { console.log('Per-middleware handler (takes priority)'); res.status(500).json({ error: 'Payment-specific error' }); }, }) ); // This uses the global handler (no per-middleware override) app.post('/payouts', NowPaymentsMiddleware.createPayout({ mapRequest: (req) => ({ /* ... */ }), // No onError - falls back to global onError }) ); ``` -------------------------------- ### Direct Environment Variable Access Source: https://github.com/hanslove/nowpayments-middleware/blob/main/AGENTS.md Example of directly accessing environment variables for configuration. This approach is harder to test and mock; extracting variable reading to a separate module is recommended for future refactoring. ```typescript this.config = { apiKey: process.env.NOWPAYMENTS_API_KEY || '', email: process.env.NOWPAYMENTS_EMAIL, // ... }; ``` -------------------------------- ### Module-Level Mutable State Example Source: https://github.com/hanslove/nowpayments-middleware/blob/main/AGENTS.md Illustrates the current pattern of using module-level mutable state for middleware instances. This pattern can lead to issues in testing and concurrent scenarios. ```typescript let createPaymentMiddleware: CreatePaymentMiddleware | null = null; ``` -------------------------------- ### Handle Payout Webhooks with Express Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/webhook-examples.mdx Set up an Express.js endpoint to receive and process payout status updates from NowPayments. This example covers payout states such as waiting, processing, sending, finished, failed, and rejected, including logging and basic error handling. ```typescript app.post('/webhook/payout', NowPaymentsMiddleware.payoutWebhook({ onWaiting: async (payload) => { console.log('Payout waiting:', payload.id); // Payout is waiting to be processed }, onProcessing: async (payload) => { console.log('Payout processing:', payload.id); // Payout is being processed }, onSending: async (payload) => { console.log('Payout sending:', payload.id); // Payout is being sent to destination address }, onFinished: async (payload) => { console.log('Payout finished:', payload.id); console.log('Hash:', payload.hash); try { // Update payout status in database // Notify user about successful payout console.log('✅ Payout completed successfully'); } catch (error) { console.error('Error updating payout status:', error); } }, onFailed: async (payload) => { console.log('Payout failed:', payload.id); console.log('Error:', payload.error); try { // Mark payout as failed // Investigate issue // Potentially retry or refund console.log('❌ Payout failed - manual review required'); } catch (error) { console.error('Error handling failed payout:', error); } }, onRejected: async (payload) => { console.log('Payout rejected:', payload.id); try { // Handle rejected payout // Return funds to user balance // Notify user about rejection console.log('🚫 Payout rejected - returning funds'); } catch (error) { console.error('Error handling rejected payout:', error); } }, }) ); ``` -------------------------------- ### Handle NOWPayments Payment Webhooks Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/webhook-examples.mdx Implement a POST endpoint to receive and process NOWPayments payment webhook events. This example demonstrates handling 'waiting', 'finished', 'failed', and 'expired' states, updating order statuses accordingly. Ensure your application has an Express app instance and the NowPaymentsMiddleware is correctly imported. ```typescript const orders = new Map(); const payouts = new Map(); app.post('/webhook/payment', NowPaymentsMiddleware.paymentWebhook({ onWaiting: async (payload) => { console.log(`💰 Payment ${payload.payment_id} is waiting for funds`); const order = Array.from(orders.values()).find(o => o.paymentId === payload.payment_id.toString()); if (order) { order.status = 'pending'; orders.set(order.id, order); } }, onFinished: async (payload) => { console.log(`🎉 Payment ${payload.payment_id} completed successfully!`); console.log(`Order: ${payload.order_id}`); console.log(`Amount: ${payload.actually_paid} ${payload.pay_currency}`); if (payload.order_id) { const order = orders.get(payload.order_id); if (order) { order.status = 'paid'; orders.set(payload.order_id, order); console.log(`📦 Order ${payload.order_id} marked as paid`); } } }, onFailed: async (payload) => { console.log(`❌ Payment ${payload.payment_id} failed`); if (payload.order_id) { const order = orders.get(payload.order_id); if (order) { order.status = 'failed'; orders.set(payload.order_id, order); console.log(`📦 Order ${payload.order_id} marked as failed`); } } }, onExpired: async (payload) => { console.log(`⏰ Payment ${payload.payment_id} expired`); if (payload.order_id) { const order = orders.get(payload.order_id); if (order) { order.status = 'expired'; orders.set(payload.order_id, order); console.log(`📦 Order ${payload.order_id} marked as expired`); } } }, }) ); ``` -------------------------------- ### Set Up Environment Variables for NowPayments Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/quick-start.mdx Configure the middleware using environment variables. NOWPAYMENTS_API_KEY is required, while email, password, and 2FA secret are optional for payouts and verification. A custom API base URL can also be specified. ```bash # Required NOWPAYMENTS_API_KEY=your-api-key # Optional: Authentication for payouts NOWPAYMENTS_EMAIL=your-email@example.com NOWPAYMENTS_PASSWORD=your-password # Optional: 2FA for automatic payout verification NOWPAYMENTS_2FA_SECRET=your-base32-encoded-2fa-secret # Optional: Custom API base URL NOWPAYMENTS_BASE_URL=https://api.nowpayments.io/v1 ``` -------------------------------- ### Docker Compose Environment Configuration Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/configuration-examples.mdx Illustrates how to set up NowPayments middleware configuration using a docker-compose.yml file, defining environment variables and linking to a production .env file. ```yaml # docker-compose.yml version: '3.8' services: app: build: . ports: - "3000:3000" environment: - NODE_ENV=production - NOWPAYMENTS_API_KEY=${NOWPAYMENTS_API_KEY} - NOWPAYMENTS_EMAIL=${NOWPAYMENTS_EMAIL} - NOWPAYMENTS_PASSWORD=${NOWPAYMENTS_PASSWORD} - NOWPAYMENTS_BASE_URL=https://api.nowpayments.io/v1 env_file: - .env.production ``` -------------------------------- ### Advanced Configuration with Environment Variables Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/configuration-examples.mdx Demonstrates advanced configuration of the NowPayments middleware using environment variables and includes validation for required variables. It sets up the middleware with API key, email, password, and base URL. ```typescript import { NowPaymentsMiddleware } from '@taloon/nowpayments-middleware'; class ConfigManager { static initialize() { const requiredEnvVars = ['NOWPAYMENTS_API_KEY']; const missingVars = requiredEnvVars.filter(varName => !process.env[varName]); if (missingVars.length > 0) { throw new Error(`Missing required environment variables: ${missingVars.join(', ')}`); } NowPaymentsMiddleware.configure({ apiKey: process.env.NOWPAYMENTS_API_KEY!, email: process.env.NOWPAYMENTS_EMAIL, password: process.env.NOWPAYMENTS_PASSWORD, baseURL: process.env.NOWPAYMENTS_BASE_URL || 'https://api.nowpayments.io/v1', errorHandling: (process.env.NODE_ENV === 'production' ? 'next' : 'direct') as 'next' | 'direct', }); console.log('✅ NowPayments middleware configured successfully'); } static getConfig() { return { apiKey: process.env.NOWPAYMENTS_API_KEY, hasBearer: !!process.env.NOWPAYMENTS_BEARER_TOKEN, environment: process.env.NODE_ENV || 'development', baseURL: process.env.NOWPAYMENTS_BASE_URL || 'https://api.nowpayments.io/v1', }; } } // Initialize configuration ConfigManager.initialize(); ``` -------------------------------- ### Configure and Create Basic Payment Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/payment-examples.mdx Configure the middleware with your API key, email, and password. Then, use `createPayment` to process a payment based on request body details. The `mapRequest` function prepares the payload, and `transformResponse` formats the API response. ```typescript import express from 'express'; import { NowPaymentsMiddleware } from '@taloon/nowpayments-middleware'; const app = express(); app.use(express.json()); NowPaymentsMiddleware.configure({ apiKey: 'your-api-key-here', email: 'your-email@example.com', password: 'your-password', errorHandling: 'next', }); app.post('/create-payment', NowPaymentsMiddleware.createPayment({ mapRequest: (req, res) => ({ price_amount: req.body.amount, price_currency: req.body.currency, pay_currency: req.body.cryptoCurrency, order_id: req.body.orderId, order_description: req.body.description, customer_email: req.body.email, ipn_callback_url: 'https://your-domain.com/webhook/payment', }), transformResponse: (response) => ({ paymentId: response.payment_id, status: response.payment_status, payAddress: response.pay_address, amount: response.pay_amount, currency: response.pay_currency, expiresAt: response.expiration_estimate_date, }), }), (req, res) => { const payment = res.locals.nowPaymentsResponse; res.status(201).json({ success: true, payment, }); } ); ``` -------------------------------- ### Implement Singleton Pattern for Configuration Source: https://github.com/hanslove/nowpayments-middleware/blob/main/AGENTS.md Demonstrates the singleton pattern with lazy initialization for managing application configuration. Includes a reset method for testing. ```typescript // Pattern: Private constructor + getInstance() class NowPaymentsConfigSingleton { private static instance: NowPaymentsConfigSingleton; private config: NowPaymentsConfig | null = null; static getInstance(): NowPaymentsConfigSingleton { if (!NowPaymentsConfigSingleton.instance) { NowPaymentsConfigSingleton.instance = new NowPaymentsConfigSingleton(); } return NowPaymentsConfigSingleton.instance; } // Includes reset() method for testing reset(): void { this.config = null; } } ``` -------------------------------- ### Per-Middleware Error Handler Example Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/error-handling.mdx Implement custom error handling for a specific endpoint like '/create-payment'. This handler intercepts errors and sends tailored responses based on the error type. ```typescript app.post('/create-payment', NowPaymentsMiddleware.createPayment({ mapRequest: (req) => ({ price_amount: req.body.amount, price_currency: req.body.currency, pay_currency: req.body.cryptoCurrency, }), onError: async (error, req, res, next) => { console.error('Payment creation error:', error); if (error instanceof NowPaymentsApiError) { return res.status(503).json({ success: false, error: 'Payment service temporarily unavailable. Please try again.', }); } if (error instanceof NowPaymentsValidationError) { return res.status(400).json({ success: false, error: error.message, }); } res.status(500).json({ success: false, error: 'Payment creation failed', }); }, }), (req, res) => { const payment = res.locals.nowPaymentsResponse; res.status(201).json({ success: true, payment }); } ); ``` -------------------------------- ### Usage of Path Aliases in Source Files Source: https://github.com/hanslove/nowpayments-middleware/blob/main/AGENTS.md Demonstrates how to use the configured path aliases for importing modules from different parts of the project. ```typescript import { NowPaymentsClient } from '@/client/NowPaymentsClient'; import { BaseMiddleware } from '@/middlewares/base/BaseMiddleware'; import { NowPaymentsValidationError } from '@/utils/errors'; ``` -------------------------------- ### Docker Environment Configuration (Dockerfile) Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/configuration-examples.mdx Shows how to configure NowPayments middleware within a Dockerfile, setting essential environment variables like NODE_ENV and NOWPAYMENTS_BASE_URL for a production build. ```dockerfile # Dockerfile FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . # Environment variables for runtime ENV NODE_ENV=production ENV NOWPAYMENTS_BASE_URL=https://api.nowpayments.io/v1 EXPOSE 3000 CMD ["npm", "start"] ``` -------------------------------- ### Configure NowPayments Middleware and Set Up Endpoints Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/configuration-examples.mdx Configure the NowPayments middleware with API credentials and set up /health and /config/validate endpoints. Ensure environment variables for API key, email, and password are set. ```typescript import express from 'express'; import { NowPaymentsMiddleware } from '@taloon/nowpayments-middleware'; const app = express(); // Configuration NowPaymentsMiddleware.configure({ apiKey: process.env.NOWPAYMENTS_API_KEY!, email: process.env.NOWPAYMENTS_EMAIL, password: process.env.NOWPAYMENTS_PASSWORD, errorHandling: 'next', }); // Health check endpoint app.get('/health', (req, res) => { const config = { hasApiKey: !!process.env.NOWPAYMENTS_API_KEY, hasAuth: !!(process.env.NOWPAYMENTS_EMAIL && process.env.NOWPAYMENTS_PASSWORD), environment: process.env.NODE_ENV || 'development', timestamp: new Date().toISOString(), }; res.json({ status: 'ok', service: 'nowpayments-middleware', config, }); }); // Configuration validation endpoint app.get('/config/validate', (req, res) => { try { const isValid = !!process.env.NOWPAYMENTS_API_KEY; res.json({ valid: isValid, message: isValid ? 'Configuration is valid' : 'Missing required API key', }); } catch (error) { res.status(500).json({ valid: false, error: 'Configuration validation failed', }); } }); ``` -------------------------------- ### Basic NowPayments Middleware Configuration Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/configuration-examples.mdx Configure the middleware with your API key. The email and password are optional but required for payout operations. ```typescript import { NowPaymentsMiddleware } from '@taloon/nowpayments-middleware'; // Simple configuration NowPaymentsMiddleware.configure({ apiKey: 'your-nowpayments-api-key', email: 'your-email@example.com', // Optional, required for payouts password: 'your-password', // Optional, required for payouts }); ``` -------------------------------- ### Environment Variables for NowPayments Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/configuration-examples.mdx Lists common environment variables used for configuring NowPayments middleware, including API keys, email, password, and base URL for different environments (development, production). ```bash # .env file NOWPAYMENTS_API_KEY=your-api-key-here NOWPAYMENTS_EMAIL=your-email@example.com NOWPAYMENTS_PASSWORD=your-password NOWPAYMENTS_BASE_URL=https://api.nowpayments.io/v1 # Development NOWPAYMENTS_API_KEY_DEV=your-dev-api-key NOWPAYMENTS_EMAIL_DEV=your-dev-email@example.com NOWPAYMENTS_PASSWORD_DEV=your-dev-password # Production NOWPAYMENTS_API_KEY_PROD=your-prod-api-key NOWPAYMENTS_EMAIL_PROD=your-prod-email@example.com NOWPAYMENTS_PASSWORD_PROD=your-prod-password ``` -------------------------------- ### Perform Pre-publish Checks Source: https://github.com/hanslove/nowpayments-middleware/blob/main/AGENTS.md Execute pre-publish checks, including cleaning the build, type checking, and compiling the project. Ensures the package is ready for publishing. ```bash npm run prepublishOnly ``` -------------------------------- ### Create a Simple Payment with NowPayments Middleware Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/quick-start.mdx Use the `createPayment` middleware to initiate a payment. It maps request data to NowPayments parameters and transforms the response. A per-middleware error handler can be defined. ```typescript import express from 'express'; const app = express(); app.use(express.json()); app.post('/create-payment', NowPaymentsMiddleware.createPayment({ mapRequest: (req, res) => ({ price_amount: req.body.amount, price_currency: req.body.currency, pay_currency: req.body.cryptoCurrency, order_id: req.body.orderId, customer_email: req.body.email, ipn_callback_url: 'https://your-domain.com/webhook/payment', }), transformResponse: (response) => ({ paymentId: response.payment_id, status: response.payment_status, payAddress: response.pay_address, amount: response.pay_amount, currency: response.pay_currency, }), onError: async (error, req, res, next) => { // Optional: Per-middleware error handler (overrides global handler) console.error('Payment creation error:', error); if (error instanceof NowPaymentsValidationError) { return res.status(400).json({ success: false, error: { message: error.message }, }); } res.status(500).json({ success: false, error: { message: 'Payment creation failed' }, }); }, }), (req, res) => { const payment = res.locals.nowPaymentsResponse; res.json({ success: true, payment }); } ); ``` -------------------------------- ### Initialize TronDispersionProvider Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/api-reference.mdx Configure and initialize the TronDispersionProvider for handling TRON network transactions. Requires tronweb@^6. ```typescript import { TronDispersionProvider } from '@taloon/nowpayments-middleware'; const provider = new TronDispersionProvider({ network: 'tron', fullNode: 'https://api.trongrid.io', controlledAddress: 'TYourControlledWallet', privateKey: process.env.TRON_PRIVATE_KEY, tokenContractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT Tron tokenDecimals: 6, apiKey: process.env.TRONGRID_API_KEY, // Optional }); ``` -------------------------------- ### Environment-Based Configuration for NowPayments Middleware Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/configuration-examples.mdx Configure the middleware differently for development and production environments, including sandbox URLs and error handling strategies. ```typescript // Development environment if (process.env.NODE_ENV === 'development') { NowPaymentsMiddleware.configure({ apiKey: process.env.NOWPAYMENTS_API_KEY_DEV!, email: process.env.NOWPAYMENTS_EMAIL_DEV, password: process.env.NOWPAYMENTS_PASSWORD_DEV, baseURL: 'https://api-sandbox.nowpayments.io/v1', // Sandbox URL errorHandling: 'direct', // More verbose errors in development }); } // Production environment if (process.env.NODE_ENV === 'production') { NowPaymentsMiddleware.configure({ apiKey: process.env.NOWPAYMENTS_API_KEY!, email: process.env.NOWPAYMENTS_EMAIL, password: process.env.NOWPAYMENTS_PASSWORD, baseURL: 'https://api.nowpayments.io/v1', errorHandling: 'next', // Use Express error handling }); } ``` -------------------------------- ### Configure Dispersion Providers Source: https://context7.com/hanslove/nowpayments-middleware/llms.txt Configure NowPaymentsMiddleware with dispersion providers for Polygon and Tron networks. Ensure all required environment variables for API keys, RPC URLs, and private keys are set. ```typescript import { NowPaymentsMiddleware, PolygonDispersionProvider, TronDispersionProvider, } from '@taloon/nowpayments-middleware'; // Configure dispersion providers NowPaymentsMiddleware.configure({ apiKey: process.env.NOWPAYMENTS_API_KEY!, email: process.env.NOWPAYMENTS_EMAIL, password: process.env.NOWPAYMENTS_PASSWORD, twoFactorSecretKey: process.env.NOWPAYMENTS_2FA_SECRET, dispersion: { providers: [ new PolygonDispersionProvider({ network: 'polygon', rpcUrl: process.env.POLYGON_RPC_URL!, controlledAddress: process.env.CONTROLLED_POLYGON_ADDRESS!, privateKey: process.env.CONTROLLED_POLYGON_PRIVATE_KEY!, tokenContractAddress: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', // USDT tokenDecimals: 6, }), new TronDispersionProvider({ network: 'tron', fullNode: 'https://api.trongrid.io', controlledAddress: process.env.CONTROLLED_TRON_ADDRESS!, privateKey: process.env.CONTROLLED_TRON_PRIVATE_KEY!, tokenContractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT tokenDecimals: 6, }), ], callbacks: { onStatusChange: async (withdrawalId, prevStatus, status, ctx) => { await db.updateWithdrawal(withdrawalId, { dispersion_status: status, ...(ctx.txHash && { tx_hash: ctx.txHash }), ...(ctx.error && { error: ctx.error }), }); }, onDispersionStart: async (withdrawalId, target, network) => { console.log(`Dispersion started: ${withdrawalId} on ${network}`); }, onDispersionSent: async (withdrawalId, txHash, network) => { console.log(`Dispersion sent: ${txHash} on ${network}`); await notifyUser(withdrawalId, 'dispersion_complete', txHash); }, onDispersionFailed: async (withdrawalId, error, network) => { await alerting.notify(`Dispersion failed ${withdrawalId}: ${error}`); }, onGasLow: async (network, balance, threshold) => { await alerting.warn(`Low gas on ${network}: ${balance} ${threshold.currency}`); }, onGasCritical: async (network, balance, threshold) => { await alerting.urgent(`CRITICAL: Top up ${network}: ${balance} ${threshold.currency}`); }, }, gasThresholds: { polygon: { minimum: '0.1', critical: '0.05', currency: 'MATIC' }, tron: { minimum: '10', critical: '5', currency: 'TRX' }, }, }, }); ``` -------------------------------- ### Configure NowPayments Middleware Globally Source: https://github.com/hanslove/nowpayments-middleware/blob/main/README.md Configure the middleware globally with your API key and optional email/password for payouts. The error handling can be set to 'next' or 'direct'. ```typescript import { NowPaymentsMiddleware } from '@taloon/nowpayments-middleware'; // Configure globally NowPaymentsMiddleware.configure({ apiKey: 'your-nowpayments-api-key', email: 'your-email@example.com', // Optional, required for payouts password: 'your-password', // Optional, required for payouts errorHandling: 'next', // or 'direct' }); ``` -------------------------------- ### Configuration Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/api-reference.mdx Details on the configuration options available for the NowPayments Middleware, including API key, optional credentials, and error handling settings. ```APIDOC ## Configuration ```typescript interface NowPaymentsConfig { apiKey: string; // Required: Your NowPayments API key email?: string; // Optional: Email for authentication (required for payouts) password?: string; // Optional: Password for authentication (required for payouts) twoFactorSecretKey?: string; // Optional: TOTP secret for automatic payout verification baseURL?: string; // Optional: API base URL (default: https://api.nowpayments.io/v1) errorHandling?: 'next' | 'direct'; // Optional: Legacy error handling mode (default: 'next') onError?: ErrorHandler; // Optional: Global error handler (takes priority over errorHandling) dispersion?: DispersionConfig; // Optional: Dispersion module configuration } // Configuration method NowPaymentsMiddleware.configure(config: NowPaymentsConfig): void ``` ``` -------------------------------- ### DispersionTargetStore Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/api-reference.mdx In-memory singleton for bridging payout creation with webhook-triggered dispersion. ```APIDOC ### DispersionTargetStore In-memory singleton that bridges payout creation with webhook-triggered dispersion. ### Usage ```typescript import { DispersionTargetStore } from '@taloon/nowpayments-middleware'; // Managed automatically by createPayoutWithDispersion // Manual use for custom flows: DispersionTargetStore.register(target); DispersionTargetStore.registerBatch(targets); const target = DispersionTargetStore.get(withdrawalId); DispersionTargetStore.remove(withdrawalId); ``` **Caveat:** In-memory only. If the process restarts between payout creation and webhook delivery, registered targets are lost. Use `onDispersionFailed` callbacks to handle this case. ``` -------------------------------- ### Create Basic Payout Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/payment-examples.mdx Use `createPayout` to initiate a payout. The `mapRequest` function should format withdrawal details, including address, currency, and amount, for each withdrawal. The `transformResponse` formats the payout details from the API. ```typescript app.post('/create-payout', NowPaymentsMiddleware.createPayout({ mapRequest: (req, res) => ({ withdrawals: req.body.withdrawals.map((withdrawal: any) => ({ address: withdrawal.address, currency: withdrawal.currency, amount: withdrawal.amount, ipn_callback_url: 'https://your-domain.com/webhook/payout', })), }), transformResponse: (response) => ({ payoutId: response.id, withdrawals: response.withdrawals.map(w => ({ id: w.id, status: w.status, address: w.address, amount: w.amount, currency: w.currency, })), }), }), (req, res) => { const payout = res.locals.nowPaymentsResponse; res.status(200).json({ success: true, payout, }); } ); ``` -------------------------------- ### Singleton Configuration Pattern Source: https://github.com/hanslove/nowpayments-middleware/blob/main/AGENTS.md Shows the current implementation of a singleton pattern for `NowPaymentsConfiguration`. A `reset()` method is available for test cleanup, but dependency injection is a future consideration. ```typescript export const NowPaymentsConfiguration = NowPaymentsConfigSingleton.getInstance(); ``` -------------------------------- ### Define Classes and Types in TypeScript Source: https://github.com/hanslove/nowpayments-middleware/blob/main/AGENTS.md Demonstrates PascalCase naming conventions for classes and interfaces/types in TypeScript. ```typescript // Classes class NowPaymentsClient {} class BaseMiddleware {} // Interfaces/Types interface CreatePaymentRequest {} type ExpressMiddleware = (req, res, next) => void | Promise; ``` -------------------------------- ### Configuration Options Interface Source: https://github.com/hanslove/nowpayments-middleware/blob/main/README.md TypeScript interface defining the available configuration options for NowPaymentsMiddleware, including API key, authentication details, 2FA secret, base URL, and error handling. ```typescript interface NowPaymentsConfig { apiKey: string; email?: string; password?: string; twoFactorSecretKey?: string; baseURL?: string; errorHandling?: 'next' | 'direct'; onError?: ErrorHandler; } type ErrorHandler = ( error: unknown, req: Request, res: Response, next: NextFunction ) => void | Promise; ``` -------------------------------- ### Configure NowPayments Middleware Globally Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/quick-start.mdx Configure the middleware globally with your API key and optional payout credentials. The error handling mode can be set to 'next' or 'direct'. A global error handler can also be provided. ```typescript import { NowPaymentsMiddleware } from '@taloon/nowpayments-middleware'; // Configure globally NowPaymentsMiddleware.configure({ apiKey: 'your-nowpayments-api-key', email: 'your-email@example.com', // Optional, required for payouts password: 'your-password', // Optional, required for payouts errorHandling: 'next', // or 'direct' (legacy) onError: async (error, req, res, next) => { // Optional: Global error handler (takes priority over errorHandling mode) if (error instanceof NowPaymentsError) { return res.status(error.statusCode || 500).json({ success: false, error: { code: error.code, message: 'Payment service error', }, }); } next(error); }, }); ``` -------------------------------- ### Configure NowPayments Middleware with Dispersion Providers Source: https://github.com/hanslove/nowpayments-middleware/blob/main/docs/dispersion-guide.mdx Configure the NowPayments middleware with API keys, email, password, and 2FA secret. Set up dispersion providers for Polygon and Tron, including network details, RPC URLs, controlled wallet addresses and private keys, token contract addresses, and decimals. Define callbacks for status changes, dispersion failures, and gas alerts, along with gas thresholds for each network. ```typescript import { NowPaymentsMiddleware, PolygonDispersionProvider, TronDispersionProvider, } from '@taloon/nowpayments-middleware'; NowPaymentsMiddleware.configure({ apiKey: process.env.NOWPAYMENTS_API_KEY!, email: process.env.NOWPAYMENTS_EMAIL, password: process.env.NOWPAYMENTS_PASSWORD, twoFactorSecretKey: process.env.NOWPAYMENTS_2FA_SECRET, dispersion: { providers: [ new PolygonDispersionProvider({ network: 'polygon', rpcUrl: process.env.POLYGON_RPC_URL!, controlledAddress: process.env.CONTROLLED_POLYGON_ADDRESS!, privateKey: process.env.CONTROLLED_POLYGON_PRIVATE_KEY!, tokenContractAddress: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', tokenDecimals: 6, }), new TronDispersionProvider({ network: 'tron', fullNode: 'https://api.trongrid.io', controlledAddress: process.env.CONTROLLED_TRON_ADDRESS!, privateKey: process.env.CONTROLLED_TRON_PRIVATE_KEY!, tokenContractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', tokenDecimals: 6, }), ], callbacks: { // ctx always includes amount and tokenCurrency - no DB lookup needed to log or notify onStatusChange: async (withdrawalId, _prev, status, ctx) => { await db.updateWithdrawal(withdrawalId, { dispersion_status: status, ...(ctx.txHash && { tx_hash: ctx.txHash }), ...(ctx.error && { error: ctx.error }), }); }, onDispersionFailed: async (withdrawalId, error, network) => { await alerting.notify(`Dispersion failed ${withdrawalId} on ${network}: ${error}`); }, onGasCritical: async (network, balance, threshold) => { await alerting.notifyUrgent(`Top up ${network} gas: ${balance} ${threshold.currency}`); }, }, gasThresholds: { polygon: { minimum: '0.1', critical: '0.05', currency: 'MATIC' }, tron: { minimum: '10', critical: '5', currency: 'TRX' }, }, }, }); ``` -------------------------------- ### Format Code with Prettier Source: https://github.com/hanslove/nowpayments-middleware/blob/main/AGENTS.md Apply consistent code formatting across the project using Prettier. This ensures a uniform style for all source files. ```bash npm run format ```