@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
Markdown
# 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)