UNPKG

@ronnakamoto/inp-middleware

Version:

INP Protocol middleware for Express.js and Next.js applications - API-driven implementation with automatic metrics integration

441 lines (333 loc) 11.4 kB
# INP Middleware A TypeScript middleware library for Express.js and Next.js that enables service providers to accept and validate payments using the INP (Internet Native Payments) protocol. ## Features - **Unified Middleware**: Single middleware function handles both service discovery and payment validation - **Payment Validation**: Automatic validation of blockchain payments with support for multiple networks - **Service Discovery**: Automatic discovery of service configuration from INP platform - **Type Safety**: Full TypeScript support with comprehensive type definitions - **Easy Integration**: Simple setup for Express.js and Next.js applications - **Flexible Configuration**: Support for custom payment networks, currencies, and validation rules ## Installation ```bash npm install @ronnakamoto/inp-middleware ``` ## Quick Start ### Express.js ```typescript import express from 'express'; import { inpMiddleware } from '@ronnakamoto/inp-middleware'; const app = express(); // Apply INP middleware to your API routes app.use('/api', inpMiddleware({ projectId: 'your-project-id', logErrors: true })); // Your API endpoints - payment validation happens automatically app.post('/api/payment', (req, res) => { // Payment is already validated by middleware // Access payment info: req.inpPayment // Access service config: req.inpDiscovery res.json({ success: true, message: 'Payment processed successfully' }); }); app.listen(3000); ``` ### Next.js API Routes ```typescript // pages/api/payment.ts import { NextApiRequest, NextApiResponse } from 'next'; import { inpMiddleware } from '@ronnakamoto/inp-middleware'; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Your payment processing logic here res.json({ success: true }); }; export default inpMiddleware({ projectId: 'your-project-id' })(handler); ``` ## How It Works The unified middleware automatically: 1. **Discovers Service Configuration**: Fetches your service's payment requirements from the INP platform 2. **Validates Payments**: Checks payment amount, currency, network, and transaction proof against your service configuration 3. **Verifies Transactions**: Always validates payments on-chain for security 4. **Enriches Requests**: Adds payment and discovery data to the request object 5. **Handles Errors**: Returns appropriate HTTP status codes for payment issues ### Payment Information Sources The middleware can extract payment information from: - **Headers**: `X-INP-Payment` or `X-Payment` - **Request Body**: `{ payment: { ... } }` - **Query Parameters**: `?payment={...}` ### Example Payment Object ```typescript { amount: 0.001, currency: "USDC", network: "algorand", walletAddress: "user-wallet-address", transactionId: "txn_123456789", proof: "optional-payment-proof" } ``` ## Configuration Options ```typescript interface INPMiddlewareOptions { // Required projectId: string; // Your project ID // Optional inpPlatformUrl?: string; // INP platform base URL (defaults to https://internetnativepayment.org) apiKey?: string; // API key for authentication timeout?: number; // Request timeout (default: 30000ms) retries?: number; // Retry attempts (default: 3) // Payment Validation validatePayment?: (payment: INPPayment, endpointConfig: any) => Promise<INPValidationResult> | INPValidationResult; // Error Handling logErrors?: boolean; // Enable error logging (default: true) // Metrics Integration metricsIngestUrl?: string; // Metrics ingest API URL (defaults to inpPlatformUrl/api/metrics/events) metricsIngestKey?: string; // Bearer token for metrics ingest API authentication } ``` ## Metrics Integration The middleware automatically publishes metrics events for all payment-related activities. This enables real-time monitoring and analytics of your INP-powered services. ### Metrics Events The middleware publishes the following events: - **Payment Required**: When a request is made to a paid endpoint without payment - **Payment Validation Failed**: When payment validation fails - **Payment Success**: When payment is successfully validated - **Free Endpoint**: When a request is made to a free endpoint ### Event Structure ```typescript { projectId: string; endpointPath: string; paid: boolean; status: 'success' | 'failed'; latencyMs: number; timestamp: string; amount?: number; currency?: string; walletAddr?: string; userId?: string; } ``` ### Automatic Metrics Publishing **No configuration required!** The middleware automatically publishes metrics events using the same authentication and configuration as your INP platform connection. ```typescript app.use('/api', inpMiddleware({ projectId: 'your-project-id', apiKey: 'your-api-key' // Same key used for metrics })); ``` ### Metrics Pipeline The metrics flow works as follows: 1. **Middleware** Automatically publishes events to INP platform 2. **INP Platform** Processes events and stores in database 3. **Dashboard** Displays real-time metrics and analytics Events are published asynchronously (fire-and-forget) to ensure they don't impact request performance. ### Benefits of This Design - **Zero Configuration**: No additional environment variables needed - **Secure**: Uses existing INP platform authentication - **Simple**: Services don't need to know about metrics infrastructure - **Reliable**: Leverages existing INP client with retry logic ## Service Configuration Your service configuration is defined in the INP platform and includes: ```typescript { "endpoints": { "payment": { "pricing_model": "fixed", "price": { "amount": "0.001", "currency": "USDC" }, "networks": ["algorand", "ethereum"], "guaranteed_response_time": "5s" } } } ``` ## Error Handling The middleware returns appropriate HTTP status codes: - `402 Payment Required`: Payment is required but not provided - `402 Payment Validation Failed`: Payment validation failed - `400 Bad Request`: Invalid request or configuration - `500 Internal Server Error`: Unexpected errors ### Example Error Response ```json { "error": "Payment Required", "code": "PAYMENT_REQUIRED", "required": { "amount": "0.001", "currency": "USDC", "networks": ["algorand", "ethereum"] } } ``` ## Advanced Usage ### Custom Payment Validation You can implement custom payment validation logic by providing a `validatePayment` function: ```typescript app.use('/api', inpMiddleware({ projectId: 'your-project-id', validatePayment: async (payment, endpointConfig) => { // Your custom validation logic here if (payment.amount < 0.001) { return { isValid: false, error: 'Payment amount too small' }; } // Call your own payment verification service const verification = await verifyPaymentWithYourService(payment); if (!verification.success) { return { isValid: false, error: 'Payment verification failed' }; } return { isValid: true, details: { transactionVerified: true, paymentDetails: { amount: payment.amount, currency: payment.currency, network: payment.network, timestamp: new Date().toISOString() } } }; } })); ``` The `validatePayment` function receives: - `payment`: The payment object from the request - `endpointConfig`: The endpoint configuration from the INP platform It should return an `INPValidationResult` object with `isValid` boolean and optional `error` and `details` properties. ### Accessing Payment Data ```typescript app.post('/api/service', (req, res) => { // Payment information const payment = req.inpPayment; console.log('Payment amount:', payment?.amount); console.log('Payment network:', payment?.network); // Service configuration const discovery = req.inpDiscovery; console.log('Service endpoints:', discovery?.endpoints); // Validation result const validation = req.inpValidation; console.log('Payment valid:', validation?.isValid); res.json({ success: true }); }); ``` ### Direct API Client Usage ```typescript import { INPClient } from '@ronnakamoto/inp-middleware'; const client = new INPClient({ // Uses https://internetnativepayment.org by default apiKey: 'your-api-key' }); // Discover service configuration const discovery = await client.getDiscoveryEndpoint({ projectId: 'your-project-id' }); // Validate payment const validation = await client.validatePayment({ endpointId: 'endpoint-id', payment: paymentData }); ``` ## TypeScript Support Full TypeScript support with comprehensive type definitions: ```typescript import type { INPRequest, INPPayment, INPDiscoveryEndpoint, INPValidationResult } from '@ronnakamoto/inp-middleware'; // Your request handler function handler(req: INPRequest, res: Response) { const payment: INPPayment | undefined = req.inpPayment; const discovery: INPDiscoveryEndpoint | undefined = req.inpDiscovery; const validation: INPValidationResult | undefined = req.inpValidation; } ``` ### Error Handling ```typescript import { INPError, INPPaymentError } from '@ronnakamoto/inp-middleware'; app.use((error: Error, req: Request, res: Response, next: NextFunction) => { if (error instanceof INPPaymentError) { return res.status(402).json({ error: error.message, code: error.code, details: error.details }); } if (error instanceof INPError) { return res.status(error.statusCode).json({ error: error.message, code: error.code, details: error.details }); } next(error); }); ``` ## API Reference ### Middleware Functions #### `inpMiddleware(options)` Main middleware function that handles both service discovery and payment validation for Express.js routes. #### `clearDiscoveryCache(projectId?)` Clears the discovery cache for testing or manual invalidation. #### `getCachedDiscoveryData(projectId?)` Gets cached discovery data for debugging. ### Client Class #### `INPClient` HTTP client for communicating with the INP platform. - `getDiscoveryEndpoint(request)`: Fetch discovery endpoint - `validatePayment(request)`: Validate payment - `invokeService(request)`: Invoke service through platform - `checkProjectPaymentStatus(projectId)`: Check if project has payment endpoints ### Error Classes - `INPError`: Base error class - `INPValidationError`: Validation errors - `INPPaymentError`: Payment-related errors - `INPDiscoveryError`: Discovery errors - `INPClientError`: Client errors - `INPConfigError`: Configuration errors ## Testing ```bash # Run tests npm test # Run tests with coverage npm run test:coverage # Run tests in watch mode npm run test:watch ``` ## Development ```bash # Install dependencies npm install # Build the library npm run build # Development build with watch npm run dev # Lint code npm run lint ``` ## Contributing 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Add tests 5. Submit a pull request ## License MIT License - see LICENSE file for details. ## Support - [Documentation](https://github.com/inp-protocol/inp) - [Issues](https://github.com/inp-protocol/inp/issues) - [Discussions](https://github.com/inp-protocol/inp/discussions)