@nerochain/x402-server
Merchant middleware adapters for Express, Hono, Fastify, and Next.js.
@nerochain/x402-server is the merchant-side package. It wraps a normal HTTP handler with the x402 payment gate: 402 on missing payment, forwards the payload to the facilitator on retry, and attaches PAYMENT-RESPONSE on the eventual 200.
Install
pnpm add @nerochain/x402-serverAdapters
The package exports four adapters via subpath imports — choose the one that matches your framework:
import { x402Express } from "@nerochain/x402-server/express";
import { x402Hono } from "@nerochain/x402-server/hono";
import { x402Fastify } from "@nerochain/x402-server/fastify";
import { x402Next } from "@nerochain/x402-server/next";All four take the same options object.
Options
type X402ServerOptions = {
paymentRequirements: PaymentRequirements | PaymentRequirements[];
facilitator: { url: string; auth?: string };
resource?: { url: string; description?: string; mimeType?: string };
resourceUrl?: string;
settleBeforeHandler?: boolean;
};paymentRequirements— what the merchant accepts. A single entry or an array of entries (multi-scheme or multi-asset).facilitator.url— the facilitator's base URL. The middleware will POST to{url}/verifyand{url}/settle.facilitator.auth— optional bearer token, sent asAuthorization: Bearer <auth>on facilitator requests.resource— descriptor included in the 402 body. Useful for documentation; not required.settleBeforeHandler— whentrue, the middleware settles before invoking the protected handler. Defaultfalse(handler runs first, then settle). The default lets the handler skip settlement on cache hits.
Express example
import express from "express";
import { x402Express } from "@nerochain/x402-server/express";
const app = express();
app.use(express.json());
app.use(
"/api/llm",
x402Express({
paymentRequirements: {
scheme: "aa-native",
network: "eip155:1689",
amount: "1000",
asset: USDT,
payTo: MERCHANT,
maxTimeoutSeconds: 60,
},
facilitator: { url: "https://facilitator.x402.nerochain.io" },
}),
(req, res) => {
res.json({ ok: true, payer: req.x402Payer });
},
);After successful verification, req.x402Payer is populated with the SCW (or EOA, for exact) that paid for this request.
Multi-scheme acceptance
paymentRequirements: [
{ scheme: "aa-native", network: "eip155:1689", amount: "1000", asset: USDT, payTo, maxTimeoutSeconds: 60 },
{ scheme: "exact", network: "eip155:1689", amount: "1000", asset: USDT, payTo, maxTimeoutSeconds: 60 },
],The client picks one entry to satisfy. The middleware verifies whichever scheme the client used.
Paywall (browser-friendly 402 page)
For resources that are paid but also accessible from a browser (a paid article, a gated download), the package exposes a paywall variant that returns a styled HTML page on a 402 challenge when the request's Accept header includes text/html. Programmatic clients still receive the JSON body; the PAYMENT-REQUIRED header is preserved on both branches.
import express from "express";
import { x402ExpressPaywall } from "@nerochain/x402-server/paywall";
const app = express();
app.use(express.json());
app.use(
"/api/article/:id",
x402ExpressPaywall(
{
paymentRequirements: { ... },
facilitator: { url: "https://facilitator.x402.nerochain.io" },
},
{
title: "Pay to read this article",
brandColor: "#000",
playgroundUrl: "https://x402.nerochain.io/playground",
},
),
(req, res) => res.json({ content: "..." }),
);The pure helpers are also exported for users assembling their own adapters:
import {
isBrowserRequest,
renderPaywallHtml,
paywallizeRejection,
} from "@nerochain/x402-server/paywall";renderPaywallHtml(paymentRequired, options) returns the full HTML document; isBrowserRequest(headers) is the Accept-header detector; paywallizeRejection(rejection, headers, options) turns a GateRejection into either the JSON or HTML response.
The page is self-contained — inline styles, no external CSS or JS, light + dark mode via prefers-color-scheme, and graceful degradation to the JSON 402 for any client without Accept: text/html.
Programmatic core
If you're writing your own framework adapter, @nerochain/x402-server exposes the core directly:
import { gateRequest, settleAfterHandler } from "@nerochain/x402-server";
const gate = await gateRequest({ method, url, headers }, options);
if (gate.kind !== "proceed") return gate; // 402 or 502 to send back
// run merchant handler...
const settled = await settleAfterHandler(gate, options);
if (settled.kind !== "ok") return settled; // 502 to send back
// attach PAYMENT-RESPONSE header from settled.paymentResponseHeader, return 200The four adapters are 30-50 line wrappers around this core. You can write a fifth for any framework that exposes a request/response interface.