Skip to main content

Next.js Integration

Start accepting x402 payments in your Next.js application in 2 minutes

Share
Last updated on October 5, 2026
On this page

Start accepting x402 payments in your Next.js application in 2 minutes.

Example Code

You can find the full code for this example on GitHub.

Step 1: Install Dependencies​

Install the required packages for your Next.js application:

bash
npm install x402 next react react-dom

Or with other package managers:

bash
# pnpm
pnpm add x402 next react react-dom
 
# yarn
yarn add x402 next react react-dom
 
# bun
bun add x402 next react react-dom

Step 2: Set Your Environment Variables​

Create a .env.local file in your project root:

bash
echo "ADDRESS=0x...\nFACILITATOR_URL=https://x402.bex.co\nNETWORK=sui\nBEX_API_KEY=your_api_key_here" > .env.local

Your .env.local file should look like this:

env
ADDRESS=0x... # wallet public address you want to receive payments to
FACILITATOR_URL=https://x402.bex.co
NETWORK=sui # recommended for fastest settlement
BEX_API_KEY=your_api_key_here # get from https://bex.co/dash/
Network Options

bex.co supports multiple networks:

  • sui (recommended for fastest settlement)
  • ethereum
  • base
  • polygon
  • avalanche

Step 3: Create Payment-Protected API Routes​

Next.js uses a file-based routing system. Create API routes in the app/api directory (App Router) or pages/api directory (Pages Router).

Using App Router (Next.js 13+)​

Create app/api/weather/route.ts:

typescript
import { NextRequest } from "next/server";
import { requirePayment } from "x402-nextjs";
 
const facilitatorUrl = process.env.FACILITATOR_URL!;
const payTo = process.env.ADDRESS! as `0x${string}`;
const network = process.env.NETWORK || "sui";
const apiKey = process.env.BEX_API_KEY!;
 
export const GET = requirePayment(
  {
    price: "$0.001",
    network,
  },
  payTo,
  {
    url: facilitatorUrl,
    apiKey,
  },
  async (request: NextRequest) => {
    return Response.json({
      report: {
        weather: "sunny",
        temperature: 70,
        location: "San Francisco",
      },
    });
  },
);

Create app/api/premium/content/route.ts:

typescript
import { NextRequest } from "next/server";
import { requirePayment } from "x402-nextjs";
 
const facilitatorUrl = process.env.FACILITATOR_URL!;
const payTo = process.env.ADDRESS! as `0x${string}`;
const network = process.env.NETWORK || "sui";
const apiKey = process.env.BEX_API_KEY!;
 
export const GET = requirePayment(
  {
    price: "$0.05",
    network,
  },
  payTo,
  {
    url: facilitatorUrl,
    apiKey,
  },
  async (request: NextRequest) => {
    return Response.json({
      content: "This is premium content",
      type: "article",
      timestamp: new Date().toISOString(),
    });
  },
);

Using Pages Router (Next.js 12 and below)​

Create pages/api/weather.ts:

typescript
import type { NextApiRequest, NextApiResponse } from "next";
import { requirePayment } from "x402-nextjs/pages";
 
const facilitatorUrl = process.env.FACILITATOR_URL!;
const payTo = process.env.ADDRESS! as `0x${string}`;
const network = process.env.NETWORK || "sui";
const apiKey = process.env.BEX_API_KEY!;
 
async function handler(req: NextApiRequest, res: NextApiResponse) {
  return res.status(200).json({
    report: {
      weather: "sunny",
      temperature: 70,
      location: "San Francisco",
    },
  });
}
 
export default requirePayment(
  {
    price: "$0.001",
    network,
  },
  payTo,
  {
    url: facilitatorUrl,
    apiKey,
  },
  handler,
);

Step 4: Run the Development Server​

Start your Next.js development server:

bash
npm run dev
Your Next.js application is now accepting x402 payments!

Your API routes will be available at:

  • http://localhost:3000/api/weather
  • http://localhost:3000/api/premium/content

Step 5: Test the Server​

You can test payments against your API routes using HTTP clients like curl, Postman, or by building a client application.

Coming Soon

Client implementation guides for fetch API and axios will be available soon.

Manual Testing with curl​

bash
# This will return a 402 Payment Required response
curl http://localhost:3000/api/weather
 
# The response will include payment details in headers

Payment Configuration Options​

The requirePayment wrapper accepts flexible payment configurations:

Simple Dollar Amount​

typescript
export const GET = requirePayment(
  {
    price: "$0.001",
    network: "sui",
  },
  payTo,
  {
    url: facilitatorUrl,
    apiKey,
  },
  async (request: NextRequest) => {
    return Response.json({ data: "Your data" });
  },
);

Token Amount with Specific Asset​

typescript
export const GET = requirePayment(
  {
    price: {
      amount: "10000",
      asset: {
        address: "0x...", // Token contract address
        decimals: 6,
        name: "USDC",
      },
    },
    network: "sui",
  },
  payTo,
  {
    url: facilitatorUrl,
    apiKey,
  },
  async (request: NextRequest) => {
    return Response.json({ data: "Premium data" });
  },
);

Accessing Payment Information​

typescript
export const GET = requirePayment(
  {
    price: "$0.001",
    network: "sui",
  },
  payTo,
  {
    url: facilitatorUrl,
    apiKey,
  },
  async (request: NextRequest) => {
    // Payment information is attached to the request
    const paymentData = (request as any).paymentData;
 
    return Response.json({
      data: "Your data",
      payment: {
        txHash: paymentData?.txHash,
        network: paymentData?.network,
      },
    });
  },
);

Advanced Features​

Custom Payment Verification Callback​

Add custom logic when payments are verified:

typescript
import { requirePayment } from "x402-nextjs";
 
export const GET = requirePayment(
  {
    price: "$0.001",
    network: "sui",
  },
  payTo,
  {
    url: facilitatorUrl,
    apiKey,
    onPaymentVerified: (paymentData) => {
      console.log("Payment verified:", paymentData.txHash);
      // Custom logging, analytics, or business logic
    },
  },
  async (request: NextRequest) => {
    return Response.json({ data: "Your data" });
  },
);

Error Handling​

Create a custom error handler in app/api/error/route.ts:

typescript
import { NextRequest } from "next/server";
 
export async function GET(request: NextRequest) {
  return Response.json(
    {
      error: "Internal server error",
      timestamp: new Date().toISOString(),
    },
    { status: 500 },
  );
}

CORS Configuration​

Enable CORS for your API routes using Next.js middleware.

Create middleware.ts in your project root:

typescript
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
 
export function middleware(request: NextRequest) {
  const response = NextResponse.next();
 
  // Set CORS headers
  response.headers.set("Access-Control-Allow-Origin", "*");
  response.headers.set(
    "Access-Control-Allow-Methods",
    "GET, POST, PUT, DELETE, OPTIONS",
  );
  response.headers.set(
    "Access-Control-Allow-Headers",
    "Content-Type, Authorization",
  );
 
  // Handle preflight requests
  if (request.method === "OPTIONS") {
    return new NextResponse(null, { status: 200, headers: response.headers });
  }
 
  return response;
}
 
export const config = {
  matcher: "/api/:path*",
};

This wildcard-origin example is for cross-origin requests without credentials: "include". For credentialed requests, check the requesting origin against your application's allowlist, return that exact origin, and add Access-Control-Allow-Credentials: true to both OPTIONS and final responses. Include every non-safelisted header your installed payment client sends in Access-Control-Allow-Headers; name Authorization explicitly. To read custom payment-response headers in JavaScript, also list those names in Access-Control-Expose-Headers.

To investigate a failure, open the CORS debugger. Enter the page origin, endpoint, method, credentials mode, and only the headers your client code sets. Copy the OPTIONS response status and headers from the browser's Network panel into the preflight field, then copy the final response headers separately. Leave unavailable evidence blank and inspect one response at a time when redirects occur. The tool explains preflight permission and response-header visibility separately. It never contacts the endpoint; generated curl commands observe HTTP responses but do not enforce browser CORS. The decisions follow the Fetch Standard.

Environment-Specific Configuration​

Create different configurations for development and production:

typescript
// lib/x402-config.ts
export const x402Config = {
  facilitator: {
    url: process.env.FACILITATOR_URL || "https://x402.bex.co",
    apiKey: process.env.BEX_API_KEY!,
  },
  network: process.env.NETWORK || "sui",
  payTo: process.env.ADDRESS! as `0x${string}`,
};
 
// Validate configuration
if (!x402Config.facilitator.apiKey || !x402Config.payTo) {
  throw new Error("Missing required x402 environment variables");
}

Then use it in your routes:

typescript
import { requirePayment } from "x402-nextjs";
import { x402Config } from "@/lib/x402-config";
 
export const GET = requirePayment(
  {
    price: "$0.001",
    network: x402Config.network,
  },
  x402Config.payTo,
  x402Config.facilitator,
  async (request: NextRequest) => {
    return Response.json({ data: "Your data" });
  },
);

Production Deployment​

Before deploying to production:

  1. Switch to production network (e.g., sui instead of sui-testnet)
  2. Use environment variables for all sensitive data
  3. Enable HTTPS (handled by most hosting platforms)
  4. Set up proper error logging
  5. Configure CORS for your production domains
  6. Add rate limiting to prevent abuse

Deployment on Vercel​

Next.js works seamlessly with Vercel. Set your environment variables in the Vercel dashboard:

  1. Go to your project settings
  2. Navigate to "Environment Variables"
  3. Add your variables:
    • ADDRESS
    • FACILITATOR_URL
    • NETWORK
    • BEX_API_KEY

Deployment on Other Platforms​

For other platforms (Netlify, AWS, etc.), ensure your environment variables are set and your build command is:

bash
npm run build

Production Example​

typescript
// app/api/data/route.ts
import { NextRequest } from "next/server";
import { requirePayment } from "x402-nextjs";
 
// Production configuration
const config = {
  facilitator: {
    url: "https://x402.bex.co",
    apiKey: process.env.BEX_API_KEY!,
    onPaymentVerified: (paymentData) => {
      // Log to your analytics service
      console.log("Payment verified:", paymentData.txHash);
    },
  },
  network: "sui", // Production Sui network
  payTo: process.env.ADDRESS! as `0x${string}`,
};
 
export const GET = requirePayment(
  {
    price: "$0.01",
    network: config.network,
  },
  config.payTo,
  config.facilitator,
  async (request: NextRequest) => {
    try {
      // Your business logic here
      const data = { content: "Protected data" };
 
      return Response.json({
        data,
        payment: {
          verified: true,
        },
      });
    } catch (error) {
      console.error("Error processing request:", error);
      return Response.json({ error: "Internal server error" }, { status: 500 });
    }
  },
);

Troubleshooting​

Common Issues​

Payment verification fails

  • Verify your BEX_API_KEY is correct in .env.local
  • Check that the wallet address format matches the network
  • Ensure the facilitator URL is accessible
  • Restart the Next.js dev server after changing environment variables

CORS errors

  • Add the middleware configuration as shown above
  • Check that the client origin is allowed
  • Verify CORS headers are being set correctly

Network mismatch

  • Ensure client and server use the same network
  • Check that the token address is valid for the network

Environment variables not loading

  • Use .env.local for local development
  • Restart the dev server after changes
  • Use NEXT_PUBLIC_ prefix only for client-side variables
  • Never expose sensitive keys (API keys) to the client

TypeScript errors

  • Ensure you have @types/node and @types/react installed
  • Check that your tsconfig.json is properly configured
  • Use type assertions for environment variables: process.env.VAR!

Why Choose Next.js?​

Next.js is an excellent choice for x402 payment applications:

  • Full-stack framework: API routes + React frontend in one project
  • Server-side rendering: Optimal performance and SEO
  • File-based routing: Intuitive API route organization
  • Edge functions: Deploy globally with low latency
  • Vercel integration: Zero-config deployments
  • TypeScript support: First-class TypeScript experience

App Router vs Pages Router​

Pros:

  • Modern React features (Server Components, Suspense)
  • Better performance and smaller bundle sizes
  • Improved routing and layouts
  • Built-in loading and error states

Use when:

  • Starting a new project
  • Want the latest Next.js features
  • Need advanced routing capabilities

Pages Router (Legacy)​

Pros:

  • More mature and stable
  • Extensive community resources
  • Simpler mental model

Use when:

  • Maintaining an existing project
  • Need maximum stability
  • Team is familiar with older Next.js versions

Next Steps​

Need Help?​

Join Our Community

Have questions or want to connect with other developers?

Join Discord

Was this page helpful?

One API key for 30+ chains

bex router is a single multi-chain gateway — one key, one bill, metered in compute units instead of an account per network.

Explore bex router