Hono Server Example
Protect Hono routes with x402 middleware
@x402/hono Example Server
Hono server demonstrating how to protect API endpoints with a paywall using the @x402/hono middleware.
import { Hono } from "hono";
import { paymentMiddleware, x402ResourceServer } from "@x402/hono";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
const app = new Hono();
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: { scheme: "exact", price: "$0.001", network: "eip155:84532", payTo: evmAddress },
description: "Weather data",
mimeType: "application/json",
},
},
new x402ResourceServer(new HTTPFacilitatorClient({ url: facilitatorUrl }))
.register("eip155:84532", new ExactEvmScheme()),
),
);
app.get("/weather", c => c.json({ weather: "sunny", temperature: 70 }));
Prerequisites
- Node.js v20+ (install via nvm)
- pnpm v10 (install via pnpm.io/installation)
- Valid EVM and SVM addresses for receiving payments
- URL of a facilitator supporting the desired payment network, see facilitator list
Setup
- Copy
.env-localto.env:
cp .env-local .env
and fill required environment variables:
FACILITATOR_URL- Facilitator endpoint URLEVM_ADDRESS- Ethereum address to receive paymentsSVM_ADDRESS- Solana address to receive payments
- Install and build all packages from the typescript examples root:
cd ../../
pnpm install && pnpm build
cd servers/hono
- Run the server
pnpm dev
Testing the Server
You can test the server using one of the example clients:
Using the Fetch Client
cd ../clients/fetch
# Ensure .env is setup
pnpm dev
Using the Axios Client
cd ../clients/axios
# Ensure .env is setup
pnpm dev
These clients will demonstrate how to:
- Make an initial request to get payment requirements
- Process the payment requirements
- Make a second request with the payment token
Example Endpoint
The server includes a single example endpoint at /weather that requires a payment of 0.001 USDC on Base Sepolia or Solana Devnet to access. The endpoint returns a simple weather report.
Response Format
Payment Required (402)
HTTP/1.1 402 Payment Required
Content-Type: application/json; charset=utf-8
PAYMENT-REQUIRED: <base64-encoded JSON>
{}
The PAYMENT-REQUIRED header contains base64-encoded JSON with the payment requirements.
Note: amount is in atomic units (e.g., 1000 = 0.001 USDC, since USDC has 6 decimals):
{
"x402Version": 2,
"error": "Payment required",
"resource": {
"url": "http://localhost:4021/weather",
"description": "Weather data",
"mimeType": "application/json"
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:84532",
"amount": "1000",
"asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"payTo": "0x1c47E9C085c2B7458F5b6C16cCBD65A65255a9f6",
"maxTimeoutSeconds": 300,
"extra": {
"name": "USDC",
"version": "2",
"resourceUrl": "http://localhost:4021/weather"
}
},
{
"scheme": "exact",
"network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
"amount": "1000",
"asset": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
"payTo": "FV6JPj6Fy12HG8SYStyHdcecXYmV1oeWERAokrh4GQ1n",
"maxTimeoutSeconds": 300,
"extra": {
"feePayer": "...",
"resourceUrl": "http://localhost:4021/weather"
}
}
]
}
Successful Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
PAYMENT-RESPONSE: <base64-encoded JSON>
{"report":{"weather":"sunny","temperature":70}}
The PAYMENT-RESPONSE header contains base64-encoded JSON with the settlement details:
{
"success": true,
"transaction": "0x...",
"network": "eip155:84532",
"payer": "0x...",
"requirements": {
"scheme": "exact",
"network": "eip155:84532",
"amount": "1000",
"asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"payTo": "0x...",
"maxTimeoutSeconds": 300,
"extra": {
"name": "USDC",
"version": "2",
"resourceUrl": "http://localhost:4021/weather"
}
}
}
Extending the Example
To add more paid endpoints, follow this pattern:
// First, configure the payment middleware with your routes
app.use(
paymentMiddleware(
{
"GET /your-endpoint": {
accepts: {
scheme: "exact",
price: "$0.10",
network: "eip155:84532",
payTo: evmAddress,
},
description: "Your endpoint description",
mimeType: "application/json",
},
},
resourceServer,
),
);
// Then define your routes as normal
app.get("/your-endpoint", (c) => {
return c.json({
// Your response data
});
});
Network identifiers use CAIP-2 format, for example:
eip155:84532— Base Sepoliaeip155:8453— Base Mainnetsolana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1— Solana Devnetsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp— Solana Mainnet
x402ResourceServer Config
The x402ResourceServer uses a builder pattern to register payment schemes that declare how payments for each network should be processed:
const resourceServer = new x402ResourceServer(facilitatorClient)
.register("eip155:*", new ExactEvmScheme()) // All EVM chains
.register("solana:*", new ExactSvmScheme()); // All SVM chains
Facilitator Config
The HTTPFacilitatorClient connects to a facilitator service that verifies and settles payments on-chain:
const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
// Or use multiple facilitators for redundancy
const facilitatorClient = [
new HTTPFacilitatorClient({ url: primaryFacilitatorUrl }),
new HTTPFacilitatorClient({ url: backupFacilitatorUrl }),
];
Next Steps
See Advanced Examples for:
- Bazaar discovery — make your API discoverable
- Dynamic pricing — price based on request context
- Dynamic payTo — route payments to different recipients
- Lifecycle hooks — custom logic on verify/settle
- Custom tokens — accept payments in custom tokens
Related Content
Looking for more? Check out our other typescript examples or browse by server content.