Express middleware to enforce x402 payments for APIs on Cronos
npm install cronos-merchant-payment-middlewaretxHash + nonce
req.payment attached
bash
npm install cronos-merchant-payment-middleware
`
---
🚀 Quick Start
`typescript
import express from "express";
import { paymentMiddleware } from "cronos-merchant-payment-middleware";
const app = express();
app.use(
paymentMiddleware({
merchantId: "merchant_123",
gatewayUrl: "https://cronos-x-402.onrender.com",
facilitatorUrl: "https://cronos-x-402.onrender.com",
network: "cronos-testnet"
})
);
app.get("/premium-data", (req, res) => {
res.json({
message: "Paid access granted",
payment: req.payment
});
});
app.listen(3000);
`
---
🔐 Payment Receipt
After successful verification, the middleware injects a trusted payment receipt into the request.
`typescript
interface PaymentReceipt {
txHash: string;
payer: string; // Derived from chain (never trusted from headers)
amount: number;
currency: string;
}
`
Usage:
`typescript
const receipt = req.payment;
console.log(receipt.txHash);
`
---
⚠️ 402 Payment Required Response
When payment is missing or invalid, the middleware responds with:
HTTP Status
402 Payment Required
Headers (discoverable by agents & browsers)
`http
X-Payment-Required: true
X-Payment-Amount: 1.5
X-Payment-Currency: USDC
X-Payment-Network: cronos-testnet
X-Payment-PayTo: 0xMerchantWallet
X-Merchant-ID: merchant_123
X-Facilitator-URL: https://cronos-x-402.onrender.com
X-Nonce: abc123...
X-Chain-ID: 338
X-Route: GET /premium-data
`
JSON Body
`json
{
"error": "PAYMENT_REQUIRED",
"message": "Payment required. Sign and broadcast transaction with provided nonce.",
"paymentRequest": {
"chainId": 338,
"merchantId": "merchant_123",
"amount": 1.5,
"currency": "USDC",
"payTo": "0xMerchantWallet",
"nonce": "abc123",
"route": "GET /premium-data"
}
}
`
---
🔁 Replay Protection (Critical)
This middleware enforces strict replay protection:
1. Every payment request includes a nonce
2. The Facilitator binds: merchantId + method + path + nonce
3. Reusing a transaction or nonce results in: 402 REPLAY_DETECTED
> ⚠️ Nonce must match the transaction — the middleware never mutates it after payment starts.
---
🧠 Fail Mode (Merchant Safety)
You can choose how your API behaves if the Gateway or Facilitator is unreachable.
`typescript
failMode?: "open" | "closed";
`
| Mode | Behavior | Use Case |
|------|----------|----------|
| Default (closed) | API is blocked if payment cannot be verified | Safest option (recommended) |
| Optional (open) | API continues if the payment infrastructure is temporarily unavailable | Protects merchant uptime during outages |
Configuration:
`typescript
interface PaymentMiddlewareConfig {
merchantId: string;
gatewayUrl: string;
facilitatorUrl: string;
network: "cronos-mainnet" | "cronos-testnet";
// Optional
cacheTTLms?: number; // Default: 30s
failMode?: "open" | "closed"; // Default: closed
}
`
---
🌐 Browser & CORS Support
All required payment headers are exposed automatically:
`http
Access-Control-Expose-Headers:
x-nonce,
x-payment-required,
x-payment-amount,
x-payment-currency,
x-payment-payto,
x-merchant-id,
x-facilitator-url,
x-chain-id,
x-route
``