NERONERO 402
SDK

@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-server

Adapters

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}/verify and {url}/settle.
  • facilitator.auth — optional bearer token, sent as Authorization: Bearer <auth> on facilitator requests.
  • resource — descriptor included in the 402 body. Useful for documentation; not required.
  • settleBeforeHandler — when true, the middleware settles before invoking the protected handler. Default false (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 200

The four adapters are 30-50 line wrappers around this core. You can write a fifth for any framework that exposes a request/response interface.

On this page