---
title: Next.js Integration
description: Start accepting x402 payments in your Next.js application in 2 minutes
keywords:
  [x402, Next.js, React, TypeScript, blockchain payments, API monetization]
last_updated: 2026-08-20
---

# Getting Started with Next.js

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

:::info Example Code
You can find the full code for this example on [GitHub](https://github.com/BlockEdenHQ/x402-examples/tree/main/typescript/servers/nextjs).
:::

## 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/
```

:::tip 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
```

<div style={{padding: '1rem', background: '#d1fae5', borderRadius: '0.5rem', color: '#065f46', marginTop: '1rem', marginBottom: '1rem'}}>
  <ion-icon name="checkmark-circle" style={{fontSize: '1.5rem', verticalAlign: 'middle', marginRight: '0.5rem'}}></ion-icon>
  <strong>Your Next.js application is now accepting x402 payments!</strong>
</div>

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.

:::note 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*",
};
```

### 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:

- <ion-icon name="flash"></ion-icon> **Full-stack framework**: API routes + React frontend in one project
- <ion-icon name="server"></ion-icon> **Server-side rendering**: Optimal performance and SEO
- <ion-icon name="construct"></ion-icon> **File-based routing**: Intuitive API route organization
- <ion-icon name="globe"></ion-icon> **Edge functions**: Deploy globally with low latency
- <ion-icon name="rocket"></ion-icon> **Vercel integration**: Zero-config deployments
- <ion-icon name="code-slash"></ion-icon> **TypeScript support**: First-class TypeScript experience

## App Router vs Pages Router

### App Router (Recommended - Next.js 13+)

**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

- <ion-icon name="arrow-forward-circle"></ion-icon> [Build a client-side React component](./x402-for-merchants.md)
- <ion-icon name="arrow-forward-circle"></ion-icon> [Implement a crypto paywall](./implement-a-crypto-paywall-with-x402.md)
- <ion-icon name="arrow-forward-circle"></ion-icon> [Read the Quick Start Guide](./quickstart.md)

## Need Help?

<div style={{padding: '1.5rem', background: '#f3f4f6', borderRadius: '0.5rem', marginTop: '2rem'}}>
  <div style={{display: 'flex', alignItems: 'flex-start', marginBottom: '1rem'}}>
    <ion-icon name="people" style={{fontSize: '1.5rem', marginRight: '0.5rem', color: '#374151'}}></ion-icon>
    <div>
      <h4 style={{margin: 0, fontSize: '1.125rem', fontWeight: 600, color: '#111827'}}>Join Our Community</h4>
      <p style={{margin: '0.25rem 0 0 0', color: '#6b7280'}}>Have questions or want to connect with other developers?</p>
    </div>
  </div>
  <a
    href="https://discord.gg/4Yfvs2HWey"
    target="_blank"
    rel="noopener noreferrer"
    style={{
      display: 'inline-flex',
      alignItems: 'center',
      gap: '0.5rem',
      padding: '0.625rem 1.25rem',
      background: '#5865F2',
      color: 'white',
      borderRadius: '0.5rem',
      textDecoration: 'none',
      fontWeight: 600,
      fontSize: '0.9375rem',
      transition: 'background 0.2s',
      border: 'none'
    }}
    onMouseOver={(e) => e.currentTarget.style.background = '#4752C4'}
    onMouseOut={(e) => e.currentTarget.style.background = '#5865F2'}
  >
    <ion-icon name="logo-discord" style={{fontSize: '1.25rem'}}></ion-icon>
    <span>Join Discord</span>
  </a>
</div>
