Reference@t402/next

@t402/next

Next.js integration for protecting routes with t402 payment requirements. Supports both App Router middleware and API route wrappers.

Installation

npm install @t402/next @t402/core

Quick Start

// middleware.ts
import { paymentProxy } from '@t402/next';
import { t402ResourceServer, createFacilitatorClient } from '@t402/core/server';
import { registerExactEvmScheme } from '@t402/evm/exact/server';
 
const facilitator = createFacilitatorClient({ url: 'https://facilitator.t402.io' });
const server = new t402ResourceServer(facilitator);
registerExactEvmScheme(server, {});
 
const routes = {
  '/api/premium/*': {
    accepts: {
      scheme: 'exact',
      network: 'eip155:8453',
      payTo: '0xYourAddress',
      price: '$0.01',
    },
    description: 'Premium API',
  },
};
 
export const middleware = paymentProxy(routes, server);
 
export const config = {
  matcher: '/api/premium/:path*',
};

API Reference

paymentProxy

Creates Next.js middleware with a pre-configured server instance.

function paymentProxy(
  routes: RoutesConfig,
  server: t402ResourceServer,
  paywallConfig?: PaywallConfig,
  paywall?: PaywallProvider,
  syncFacilitatorOnStart?: boolean
): (req: NextRequest) => Promise<NextResponse>

Parameters

ParameterTypeDescription
routesRoutesConfigRoute configurations for protected endpoints
servert402ResourceServerPre-configured server instance
paywallConfigPaywallConfigOptional paywall UI configuration
paywallPaywallProviderOptional custom paywall provider
syncFacilitatorOnStartbooleanSync with facilitator on startup (default: true)

paymentProxyFromConfig

Creates Next.js middleware with simple configuration.

function paymentProxyFromConfig(
  routes: RoutesConfig,
  facilitatorClients?: FacilitatorClient | FacilitatorClient[],
  schemes?: SchemeRegistration[],
  paywallConfig?: PaywallConfig,
  paywall?: PaywallProvider,
  syncFacilitatorOnStart?: boolean
): (req: NextRequest) => Promise<NextResponse>

withT402

Wraps an individual API route handler with payment protection.

function withT402<T>(
  routeHandler: (request: NextRequest) => Promise<NextResponse<T>>,
  routeConfig: RouteConfig,
  server: t402ResourceServer,
  paywallConfig?: PaywallConfig,
  paywall?: PaywallProvider,
  syncFacilitatorOnStart?: boolean
): (request: NextRequest) => Promise<NextResponse<T>>

Parameters

ParameterTypeDescription
routeHandlerFunctionThe API route handler to wrap
routeConfigRouteConfigPayment configuration for this route
servert402ResourceServerPre-configured server instance
paywallConfigPaywallConfigOptional paywall UI configuration
paywallPaywallProviderOptional custom paywall provider
syncFacilitatorOnStartbooleanSync with facilitator on startup (default: true)

Middleware vs Route Wrapper

Choose the appropriate approach:

FeaturepaymentProxy (Middleware)withT402 (Route Wrapper)
ScopeMultiple routesSingle route
ConfigurationCentralized in middleware.tsPer-route in route files
Settlement timingAfter middleware passesAfter handler returns
Best forMultiple protected routesIndividual API endpoints

withT402 guarantees that settlement only occurs after the handler returns a successful response (status < 400), providing more precise control.

Middleware Configuration

Route Matching

// middleware.ts
import { paymentProxy } from '@t402/next';
 
const routes = {
  '/api/premium/*': {
    accepts: { scheme: 'exact', network: 'eip155:8453', payTo: '0x...', price: '$0.01' },
    description: 'Premium API',
  },
  '/api/analytics': {
    accepts: { scheme: 'exact', network: 'eip155:8453', payTo: '0x...', price: '$0.05' },
    description: 'Analytics endpoint',
  },
};
 
export const middleware = paymentProxy(routes, server);
 
// Configure which routes the middleware applies to
export const config = {
  matcher: ['/api/premium/:path*', '/api/analytics'],
};

Multi-Network Support

const routes = {
  '/api/premium/*': {
    accepts: [
      { scheme: 'exact', network: 'eip155:8453', payTo: '0x...', price: '$0.01' },
      { scheme: 'exact', network: 'ton:mainnet', payTo: 'EQ...', price: '$0.01' },
      { scheme: 'exact', network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', payTo: '8GG...', price: '$0.01' },
    ],
    description: 'Multi-chain support',
  },
};

API Route Examples

Basic Route Protection

// app/api/premium/data/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { withT402 } from '@t402/next';
 
async function handler(request: NextRequest) {
  const data = await fetchPremiumData();
  return NextResponse.json({ data });
}
 
export const GET = withT402(handler, {
  accepts: {
    scheme: 'exact',
    network: 'eip155:8453',
    payTo: '0xRecipient',
    price: '$0.01',
  },
  description: 'Premium data endpoint',
}, server);

Dynamic Pricing

// app/api/generate/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { withT402 } from '@t402/next';
 
async function handler(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const tokens = parseInt(searchParams.get('tokens') || '100');
 
  const result = await generateContent(tokens);
  return NextResponse.json({ result });
}
 
export const POST = withT402(handler, {
  accepts: {
    scheme: 'exact',
    network: 'eip155:8453',
    payTo: '0xRecipient',
    price: '$0.001', // Base price
  },
  description: 'AI content generation',
  extensions: {
    bazaar: {
      endpoint: '/api/pricing',
    },
  },
}, server);

Multiple HTTP Methods

// app/api/resource/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { withT402 } from '@t402/next';
 
const routeConfig = {
  accepts: {
    scheme: 'exact',
    network: 'eip155:8453',
    payTo: '0xRecipient',
    price: '$0.01',
  },
  description: 'Resource API',
};
 
async function getHandler(request: NextRequest) {
  return NextResponse.json({ data: 'read' });
}
 
async function postHandler(request: NextRequest) {
  const body = await request.json();
  return NextResponse.json({ created: body });
}
 
export const GET = withT402(getHandler, routeConfig, server);
export const POST = withT402(postHandler, routeConfig, server);

Bazaar Extension

Enable dynamic pricing with the bazaar extension:

const routes = {
  '/api/dynamic/*': {
    accepts: {
      scheme: 'exact',
      network: 'eip155:8453',
      payTo: '0xRecipient',
      price: '$0.01',
    },
    description: 'Dynamic pricing endpoint',
    extensions: {
      bazaar: {
        endpoint: 'https://api.example.com/pricing',
      },
    },
  },
};
 
export const middleware = paymentProxy(routes, server);

The bazaar extension is automatically loaded when routes declare it. No additional configuration needed.

Settlement Behavior

With withT402, settlement occurs after the handler returns:

async function handler(request: NextRequest) {
  try {
    const data = await riskyOperation();
    return NextResponse.json({ data }); // 200 -> Settlement occurs
  } catch (error) {
    return NextResponse.json({ error: 'Failed' }, { status: 500 }); // 500 -> No settlement
  }
}
 
export const GET = withT402(handler, routeConfig, server);

Paywall Configuration

const paywallConfig = {
  title: 'Premium API Access',
  description: 'Pay to unlock this endpoint',
  logoUrl: 'https://example.com/logo.png',
  theme: 'dark',
};
 
export const middleware = paymentProxy(routes, server, paywallConfig);

Type Exports

// Functions
export { paymentProxy, paymentProxyFromConfig, withT402 } from '@t402/next';
 
// Types
export type { SchemeRegistration } from '@t402/next';
export type {
  PaymentRequired,
  PaymentRequirements,
  PaymentPayload,
  Network,
  SchemeNetworkServer,
} from '@t402/core/types';
export type { PaywallProvider, PaywallConfig, RouteConfig } from '@t402/core/server';
export { RouteConfigurationError } from '@t402/core/server';
 
// Adapter
export { NextAdapter } from '@t402/next';