Next.js Adapter
The @x402/next package integrates x402 payments with Next.js applications, supporting both App Router and Pages Router.
Installation
App Router (API Routes)
Route Handler
// app/api/premium/route.ts
import { withPayment } from '@x402/next';
export const GET = withPayment(
async (request) => {
const payment = request.x402;
return Response.json({
data: 'Premium content!',
paidBy: payment?.payer,
});
},
{
facilitatorUrl: process.env.FACILITATOR_URL!,
accepts: {
scheme: 'exact',
network: 'eip155:8453',
payTo: process.env.PAYMENT_ADDRESS!,
price: '$0.01',
},
}
);
Dynamic Pricing
// app/api/generate/route.ts
import { withPayment } from '@x402/next';
export const POST = withPayment(
async (request) => {
const { prompt } = await request.json();
const result = await generateText(prompt);
return Response.json({ result });
},
{
facilitatorUrl: process.env.FACILITATOR_URL!,
accepts: {
scheme: 'exact',
network: 'eip155:8453',
payTo: process.env.PAYMENT_ADDRESS!,
price: async (request) => {
const body = await request.clone().json();
const tokens = body.maxTokens || 100;
return `$${(tokens * 0.0001).toFixed(4)}`;
},
},
}
);
Server Actions
Use the paidAction wrapper for Server Actions:
// app/actions.ts
'use server';
import { paidAction } from '@x402/next';
export const generateImage = paidAction(
async (formData: FormData, payment) => {
const prompt = formData.get('prompt') as string;
const image = await generateImageFromPrompt(prompt);
return {
image,
paidBy: payment.payer,
};
},
{
facilitatorUrl: process.env.FACILITATOR_URL!,
accepts: {
scheme: 'exact',
network: 'eip155:8453',
payTo: process.env.PAYMENT_ADDRESS!,
price: '$0.10',
},
}
);
Use in your component:
// app/page.tsx
'use client';
import { useX402 } from '@x402/next/client';
import { generateImage } from './actions';
export default function Page() {
const { payAndExecute, isLoading } = useX402();
async function handleSubmit(formData: FormData) {
const result = await payAndExecute(() => generateImage(formData));
console.log(result.image);
}
return (
<form action={handleSubmit}>
<input name="prompt" placeholder="Describe an image..." />
<button type="submit" disabled={isLoading}>
Generate ($0.10)
</button>
</form>
);
}
Middleware
Protect entire route segments:
// middleware.ts
import { createX402Middleware } from '@x402/next';
export const middleware = createX402Middleware({
facilitatorUrl: process.env.FACILITATOR_URL!,
routes: {
'/api/premium/:path*': {
accepts: {
scheme: 'exact',
network: 'eip155:8453',
payTo: process.env.PAYMENT_ADDRESS!,
price: '$0.01',
},
},
},
});
export const config = {
matcher: '/api/premium/:path*',
};
Pages Router
// pages/api/premium.ts
import { withPaymentHandler } from '@x402/next/pages';
export default withPaymentHandler(
async (req, res) => {
res.json({
data: 'Premium content!',
paidBy: req.x402?.payer,
});
},
{
facilitatorUrl: process.env.FACILITATOR_URL!,
accepts: {
scheme: 'exact',
network: 'eip155:8453',
payTo: process.env.PAYMENT_ADDRESS!,
price: '$0.01',
},
}
);
Client-Side Provider
Wrap your app with the x402 provider for client-side payment handling:
// app/providers.tsx
'use client';
import { X402Provider } from '@x402/next/client';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<X402Provider
config={{
network: 'eip155:8453',
// Optional: provide wallet connector
}}
>
{children}
</X402Provider>
);
}
Paywall Component
Add paywalls to your content:
import { Paywall } from '@x402/next/client';
export default function PremiumContent() {
return (
<Paywall
price="$0.10"
network="eip155:8453"
payTo="0xYourAddress"
onSuccess={() => console.log('Paid!')}
>
<div>
<h1>Premium Content</h1>
<p>This content requires payment to access.</p>
</div>
</Paywall>
);
}