Overview
The @1stdigital/prism-nextjs package provides middleware for Next.js API routes and App Router route handlers. Supports both Pages Router and App Router architectures.App Router
Support for Next.js 13+ App Router
Pages Router
Works with traditional API routes
Edge Runtime
Compatible with Edge Runtime
Installation
- npm
- yarn
- pnpm
Copy
npm install @1stdigital/prism-nextjs next
bash yarn add @1stdigital/prism-nextjs next Copy
pnpm add @1stdigital/prism-nextjs next
Quick Start (App Router)
Copy
// app/api/premium/route.ts
import { NextRequest, NextResponse } from "next/server";
import { withPrismPayment } from "@1stdigital/prism-nextjs";
async function handler(request: NextRequest) {
// Access payer from request
const payer = (request as any).payer;
return NextResponse.json({
message: "Premium content",
payer,
});
}
// Wrap handler with payment middleware
export const GET = withPrismPayment(handler, {
apiKey: process.env.PRISM_API_KEY || "dev-key-123",
baseUrl: "https://prism-api.test.1stdigital.tech",
price: 0.01, // $0.01 USD
description: "Premium API",
});
Quick Start (Pages Router)
Copy
// pages/api/premium.ts
import { NextApiRequest, NextApiResponse } from "next";
import { createPrismMiddleware } from "@1stdigital/prism-nextjs";
const prismMiddleware = createPrismMiddleware({
apiKey: process.env.PRISM_API_KEY || "dev-key-123",
baseUrl: "https://prism-api.test.1stdigital.tech",
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Apply middleware
const paymentResult = await prismMiddleware(req, res, {
price: 0.01, // $0.01 USD
description: "Premium API",
});
if (!paymentResult) return; // Middleware handled response
// Payment verified
res.status(200).json({
message: "Premium content",
payer: (req as any).payer,
});
}
Configuration
App Router Options
Copy
interface PrismRouteConfig {
apiKey: string;
baseUrl?: string;
debug?: boolean;
price: number | string; // USD price
description: string;
mimeType?: string;
}
Pages Router Options
Copy
interface PrismMiddlewareConfig {
apiKey: string;
baseUrl?: string;
debug?: boolean;
}
interface RoutePaymentConfig {
price: number | string;
description: string;
mimeType?: string;
}
App Router Routes
Single Route
Copy
// app/api/premium/route.ts
import { withPrismPayment } from "@1stdigital/prism-nextjs";
async function handler(request: NextRequest) {
return NextResponse.json({ data: "Premium" });
}
export const GET = withPrismPayment(handler, {
apiKey: process.env.PRISM_API_KEY!,
price: 0.01,
description: "Premium API",
});
Multiple Methods
Copy
// app/api/data/route.ts
import { withPrismPayment } from "@1stdigital/prism-nextjs";
async function getHandler(request: NextRequest) {
return NextResponse.json({ data: "GET data" });
}
async function postHandler(request: NextRequest) {
const body = await request.json();
return NextResponse.json({ received: body });
}
export const GET = withPrismPayment(getHandler, {
apiKey: process.env.PRISM_API_KEY!,
price: 0.01,
description: "Get data",
});
export const POST = withPrismPayment(postHandler, {
apiKey: process.env.PRISM_API_KEY!,
price: 0.02,
description: "Post data",
});
Accessing Payment Info
App Router
Copy
async function handler(request: NextRequest) {
// Payer address attached to request
const payer = (request as any).payer;
// '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
// Payment object in custom property
const payment = (request as any).prismPayment;
return NextResponse.json({
message: "Data",
payer,
network: payment?.network,
});
}
Pages Router
Copy
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const paymentResult = await prismMiddleware(req, res, {
price: 0.01,
description: "Premium",
});
if (!paymentResult) return;
// Access payer
const payer = (req as any).payer;
const payment = (res as any).locals?.payment;
res.json({ payer, network: payment?.network });
}
Settlement Validation
Next.js wraps the Response object to validate settlement:Copy
// Internal implementation (automatic!)
async function withPrismPayment(handler, config) {
return async (request: NextRequest) => {
// ... payment validation ...
// Execute handler
const response = await handler(request);
// Validate settlement before returning
const settlementResult = await core.settlementCallback(...);
if (!settlementResult || !settlementResult.success) {
// ❌ Settlement failed
return NextResponse.json(
{ error: 'Payment settlement failed' },
{ status: 402 }
);
}
// ✅ Settlement succeeded
response.headers.set('X-PAYMENT-RESPONSE', settlementResult.transaction);
return response;
};
}
Testing
Copy
import { NextRequest } from "next/server";
import { GET } from "./app/api/premium/route";
describe("Next.js Payment Routes", () => {
it("returns 402 without payment", async () => {
const request = new NextRequest("http://localhost:3000/api/premium");
const response = await GET(request);
expect(response.status).toBe(402);
const body = await response.json();
expect(body.paymentRequired).toBe(true);
});
it("returns 200 with valid payment", async () => {
const payment = JSON.stringify({
scheme: "eip3009",
signature: "0x" + "0".repeat(130),
});
const request = new NextRequest("http://localhost:3000/api/premium", {
headers: { "X-PAYMENT": payment },
});
const response = await GET(request);
expect(response.status).toBe(200);
const body = await response.json();
expect(body.message).toBe("Premium content");
});
});
Production Deployment
Copy
// next.config.js
module.exports = {
env: {
PRISM_API_KEY: process.env.PRISM_API_KEY,
PRISM_BASE_URL: process.env.PRISM_BASE_URL
},
// Enable edge runtime (optional)
experimental: {
runtime: 'edge'
}
};
// Vercel deployment
// vercel.json
{
"env": {
"PRISM_API_KEY": "@prism-api-key",
"PRISM_BASE_URL": "@prism-base-url"
}
}