HTTP 402 Payment Required middleware for Mantle Network
Monetize your APIs with on-chain blockchain payments
Quick Start • Features • How It Works • API Reference • Examples
Mantle-402 is an Express.js middleware that implements the HTTP 402 "Payment Required" status code, enabling API monetization through blockchain payments on Mantle Network.
Why Mantle-402?
- 💰 Direct Payments: Receive payments directly to your wallet, no intermediaries
- 🔒 Blockchain Verified: All payments verified on-chain, no chargebacks
- ⚡ Low Fees: Mantle's L2 makes micropayments practical
- 🛠️ Easy Integration: Drop-in middleware for Express.js
npm install mantle-402import express from 'express';
import { mantle402 } from 'mantle-402';
const app = express();
// Free endpoint - no payment required
app.get('/api/free', (req, res) => {
res.json({ message: 'This is free!' });
});
// Paid endpoint - requires 0.01 MNT
app.use('/api/premium', mantle402({
recipient: 'YOUR_WALLET_ADDRESS',
amount: '0.01',
amountType: 'ether',
network: 'mainnet',
description: 'Premium API Access'
}));
app.get('/api/premium', (req, res) => {
res.json({
message: 'Payment verified!',
data: 'Your premium content here'
});
});
app.listen(3000);- ✅ Express.js Middleware - Drop-in integration
- ✅ TypeScript Native - Full type definitions included
- ✅ Mantle Network - Mainnet and Testnet support
- ✅ Flexible Pricing - Per-endpoint pricing configuration
- ✅ Event Hooks - React to payment events
- ✅ Automatic Verification - On-chain payment validation
| Feature | Description |
|---|---|
| Per-Request | Charge for each API call |
| Tiered Pricing | Different prices for different endpoints |
| Custom Amounts | Set any price in MNT or wei |
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │ │ Your API │ │ Mantle │
│ │ │ (Express) │ │ Network │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ 1. Request API │ │
│──────────────────>│ │
│ │ │
│ 2. 402 Payment │ │
│ Required │ │
│<──────────────────│ │
│ │ │
│ 3. Send MNT Payment │
│──────────────────────────────────────>│
│ │ │
│ 4. Retry with TX Hash │
│──────────────────>│ │
│ │ 5. Verify TX │
│ │──────────────────>│
│ │ │
│ │ 6. Confirmed │
│ │<──────────────────│
│ │ │
│ 7. 200 OK + Data │ │
│<──────────────────│ │
│ │ │
Creates a payment middleware for a specific endpoint.
interface Mantle402Options {
// Required
recipient: string; // Wallet address to receive payments
amount: string; // Payment amount
// Optional
amountType?: 'wei' | 'ether'; // Amount format (default: 'wei')
network?: 'mainnet' | 'testnet'; // Network (default: 'mainnet')
description?: string; // Endpoint description
expiresIn?: number; // Challenge expiration in ms (default: 300000)
// Hooks
hooks?: {
onChallenge?: (challenge, context) => Promise<Challenge>;
onProofReceived?: (proof, context) => Promise<void>;
onPaymentVerified?: (receipt, context) => Promise<void>;
onPaymentFailed?: (error, context) => Promise<void>;
};
}Creates a reusable paywall instance.
const paywall = createMantle402({
recipient: 'YOUR_WALLET_ADDRESS',
network: 'mainnet',
});
// Use with different prices
app.use('/api/basic', paywall.middleware({ amount: '0.01' }));
app.use('/api/premium', paywall.middleware({ amount: '0.05' }));
app.use('/api/enterprise', paywall.middleware({ amount: '1.0' }));import { etherToWei, weiToEther, isValidAddress, isValidTxHash } from 'mantle-402';
// Convert between units
etherToWei('0.01'); // '10000000000000000'
weiToEther('10000000000000000'); // '0.01'
// Validation
isValidAddress('0x...'); // true/false
isValidTxHash('0x...'); // true/falseimport { mantle402, createMantle402 } from 'mantle-402';
const paywall = createMantle402({
recipient: process.env.WALLET_ADDRESS,
network: 'mainnet',
});
// Basic tier - $0.01
app.use('/api/basic', paywall.middleware({
amount: '0.01',
description: 'Basic Access'
}));
// Premium tier - $0.05
app.use('/api/premium', paywall.middleware({
amount: '0.05',
description: 'Premium Access'
}));
// Enterprise tier - $1.00
app.use('/api/enterprise', paywall.middleware({
amount: '1.0',
description: 'Enterprise Access'
}));app.use('/api/premium', mantle402({
recipient: 'YOUR_WALLET',
amount: '0.01',
network: 'mainnet',
hooks: {
onPaymentVerified: async (receipt, context) => {
// Log to analytics
await analytics.track('payment', {
amount: receipt.payment.amount,
sender: receipt.payment.sender,
endpoint: context.path,
});
// Send notification
await notify(`Payment received: ${receipt.payment.amount} MNT`);
},
onPaymentFailed: async (error, context) => {
console.error('Payment failed:', error.message);
}
}
}));mantle402({
recipient: 'YOUR_WALLET',
amount: '0.01',
network: 'mainnet', // Chain ID: 5000
});mantle402({
recipient: 'YOUR_WALLET',
amount: '0.01',
network: 'testnet', // Chain ID: 5003
});| Network | Chain ID | RPC URL |
|---|---|---|
| Mainnet | 5000 | https://rpc.mantle.xyz |
| Testnet | 5003 | https://rpc.sepolia.mantle.xyz |
For prepaid balance systems, deploy the included smart contract:
# Compile
npm run contract:compile
# Deploy to testnet
npm run contract:deploy:testnet
# Deploy to mainnet
npm run contract:deploy:mainnetContract address will be displayed after deployment.
# Start the example server
npm run example
# In another terminal, test with curl
curl http://localhost:3000/api/free # Free endpoint
curl http://localhost:3000/api/basic # Returns 402 Payment Requirednpm run testCreate a .env file:
# Required for contract-based payments
CONTRACT_ADDRESS=0x...
API_OWNER_PRIVATE_KEY=0x...
# Optional
RECIPIENT_ADDRESS=0x...
NETWORK=testnet
RPC_URL=https://rpc.sepolia.mantle.xyz
PORT=3000
⚠️ Security: Never commit private keys to git!
Contributions are welcome! Please read our contributing guidelines before submitting a PR.
MIT License - see LICENSE for details.
Built on Mantle Network