NERONERO 402
Getting started

Quickstart for merchants

Gate an HTTP endpoint behind x402 payment in any of Express, Hono, Fastify, or Next.js.

This guide walks through gating an HTTP endpoint behind a paid call. By the end your endpoint will return 402 Payment Required to unpaid agents, accept a signed payment payload on retry, and deliver content only after the on-chain settlement succeeds.

A complete runnable version with both an Express server and an integration test is in examples/express-paid-api/.

What you need

  • A facilitator URL the middleware can call. For local development, run the reference facilitator on :3000. For production, point at facilitator.x402.nerochain.io.
  • A merchant address that receives the settlement transfers. This is just an EOA or contract address you control on NERO Chain.
  • The package: pnpm add @nerochain/x402-server.

Express

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:689",
      amount: "1000",                                          // 0.001 USDT (atomic units, 6 decimals)
      asset:   "0x9a2eabdecda3eae051bca75fbe71a3dbdb35f9d4",   // DEMO-USDT on NERO testnet
      payTo:   "0xYourMerchantAddress",
      maxTimeoutSeconds: 60,
    },
    facilitator: { url: "http://localhost:3000" },
    resource: { url: "/api/llm", description: "demo paid endpoint" },
  }),
  (req, res) => {
    res.json({ ok: true, payer: req.x402Payer, content: "demo response" });
  },
);

app.listen(4040);

The middleware does three things automatically:

  1. On a request without a PAYMENT-SIGNATURE header, it returns 402 with the paymentRequirements payload.
  2. On a request with a payload, it forwards to the facilitator's /verify. If verification fails, it returns 402 with the error code.
  3. After your handler runs successfully, it forwards to /settle and attaches the PAYMENT-RESPONSE header with the on-chain receipt.

The handler runs only after verification has passed. Inside the handler, req.x402Payer is the SCW that paid.

Hono

import { Hono } from "hono";
import { x402Hono } from "@nerochain/x402-server/hono";

const app = new Hono();

app.use(
  "/api/llm",
  x402Hono({ paymentRequirements, facilitator: { url } }),
);
app.post("/api/llm", (c) => c.json({ ok: true, payer: c.get("x402Payer") }));

Fastify

import Fastify from "fastify";
import { x402Fastify } from "@nerochain/x402-server/fastify";

const app = Fastify();
app.register(x402Fastify, {
  prefix: "/api/llm",
  paymentRequirements,
  facilitator: { url },
});
app.post("/api/llm", async (req) => ({ ok: true, payer: req.x402Payer }));

Next.js App Router

// app/api/llm/route.ts
import { x402Next } from "@nerochain/x402-server/next";

export const POST = x402Next(
  { paymentRequirements, facilitator: { url } },
  async (req) => Response.json({ ok: true }),
);

Pricing and asset selection

paymentRequirements.amount is in atomic units (no decimal point). For a token with 6 decimals like USDT/USDC, 1000 is 0.001 of a token. The asset address must be on the allowlist of the configured SettlementContract. On NERO mainnet that's USDT (0xff13a7a12fd485bc9687ff88d8ae1a6b655ab469), USDC.e (0x97eec1c29f745dc7c267f90292aa663d997a601d), and USDC.arb (0x8712796136Ac8e0EEeC123251ef93702f265aa80); on testnet it's DEMO-USDT (0x9a2eabdecda3eae051bca75fbe71a3dbdb35f9d4).

To accept multiple schemes or assets, pass an array:

paymentRequirements: [
  { scheme: "aa-native", network: "eip155:689", amount: "1000", asset: USDT, payTo, maxTimeoutSeconds: 60 },
  { scheme: "exact",     network: "eip155:689", amount: "1000", asset: USDT, payTo, maxTimeoutSeconds: 60 },
],

The client picks whichever it can satisfy.

Next

On this page