Skip to main content
x402 payment middleware for Next.js App Router.

Installation

# npm
npm install @armory-sh/middleware-next

# yarn
yarn add @armory-sh/middleware-next

# pnpm
pnpm add @armory-sh/middleware-next

# bun
bun add @armory-sh/middleware-next

Features

  • Per-route payment configuration
  • Route pattern matching (exact, wildcard, parameterized)
  • Resource server for payment scheme management
  • Built-in verification integration
  • Full TypeScript support

Quick Start

// middleware.ts
import { paymentProxy, x402ResourceServer } from "@armory-sh/middleware-next";

const verificationClient = {
  async verify(headers: Headers) {
    return { success: true, payerAddress: "0x..." };
  },
};

const resourceServer = new x402ResourceServer(verificationClient);

export const proxy = paymentProxy(
  {
    "/api/protected": {
      accepts: {
        scheme: "exact",
        price: "1000000",
        network: "eip155:8453",
        payTo: "0xYourAddress...",
      },
    },
  },
  resourceServer
);

export const config = { matcher: ["/api/protected/:path*"] };

API

paymentProxy(routes, resourceServer)

Creates a payment proxy handler for Next.js middleware. Parameters:
  • routes: Record mapping route patterns to payment config
  • resourceServer: x402ResourceServer instance
Returns: Next.js middleware function

x402ResourceServer

Registers and manages payment schemes. Methods:
  • register(chainId, scheme): Register a payment scheme
  • getRequirements(chainId): Get requirements for a chain
  • getAllRequirements(): Get all registered requirements

Types

interface VerificationClient {
  verify(headers: Headers): Promise<{
    success: boolean;
    payerAddress?: string;
    error?: string
  }>;
  settle?(headers: Headers): Promise<{
    success: boolean;
    txHash?: string;
    error?: string
  }>;
}

interface PaymentScheme {
  name: string;
  getRequirements(): PaymentRequirementsV2;
}

interface RoutePaymentConfig {
  accepts: {
    scheme: string;
    price: string;
    network: string;
    payTo: string;
  };
  description?: string;
}

Route Patterns

Supports three types of route patterns:
  • Exact: /api/users - matches only /api/users
  • Wildcard: /api/* - matches /api/users, /api/posts/123
  • Parameterized: /api/users/:id - matches /api/users/123

Examples

See the Next.js Middleware guide for complete examples.

Advanced Mix-and-Match Config (Current API)

If you are using paymentMiddleware directly, you can route by chain/token/facilitator and attach extensions:
import { paymentMiddleware } from "@armory-sh/middleware-next";

const middleware = paymentMiddleware({
  payTo: "0x1234567890123456789012345678901234567890",
  chains: ["base", "skale-base"],
  tokens: ["usdc", "usdt", "weth", "wbtc"],
  amount: "1.0",
  facilitatorUrl: "https://fallback-facilitator.example",
  facilitatorUrlByChain: {
    base: "https://payai.example",
  },
  facilitatorUrlByToken: {
    base: { usdc: "https://payai.example" },
    "skale-base": {
      usdc: "https://payai.example",
      usdt: "https://kobaru.example",
      weth: "https://kobaru.example",
      wbtc: "https://kobaru.example",
    },
  },
  extensions: {
    bazaar: { info: { input: { sku: "pro-plan" } }, schema: { type: "object" } },
    "sign-in-with-x": { info: { domain: "api.example.com" }, schema: { type: "object" } },
  },
});
For mixed amounts per route/combination, use explicit requirements arrays:
paymentMiddleware({
  requirements: [
    {
      scheme: "exact",
      network: "eip155:8453",
      amount: "1000000",
      asset: "0x833589fCD6eDb6E08f4c7C32D4f71B54bdA02913",
      payTo: "0x1234567890123456789012345678901234567890",
      maxTimeoutSeconds: 300,
    },
    {
      scheme: "exact",
      network: "eip155:1187947933",
      amount: "2500000",
      asset: "0x5f9beea3f6f22be1f4f8efc120f5095f58dbb8a2",
      payTo: "0x1234567890123456789012345678901234567890",
      maxTimeoutSeconds: 300,
    },
  ],
  facilitatorUrlByToken: {
    base: { usdc: "https://payai.example" },
    "skale-base": { usdt: "https://kobaru.example" },
  },
});
Unsupported extension keys are fail-open filtered out per facilitator capability response.