Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developers.fd.xyz/llms.txt

Use this file to discover all available pages before exploring further.

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 install @1stdigital/prism-nextjs next

The canonical SDK config is identifyToken (PRISM_IDENTIFY_TOKEN env var) — formerly apiKey / PRISM_API_KEY. SDKs accept the legacy names as fallback during the migration window; new code should use the canonical names.

Quick Start (App Router)

// 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, {
  identifyToken: process.env.PRISM_IDENTIFY_TOKEN || "dev-key-123",
  baseUrl: "https://prism-gw.fd.xyz",
  price: 0.01, // $0.01 USD
  description: "Premium API",
});

Quick Start (Pages Router)

// pages/api/premium.ts
import { NextApiRequest, NextApiResponse } from "next";
import { createPrismMiddleware } from "@1stdigital/prism-nextjs";

const prismMiddleware = createPrismMiddleware({
  identifyToken: process.env.PRISM_IDENTIFY_TOKEN || "dev-key-123",
  baseUrl: "https://prism-gw.fd.xyz",
});

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

interface PrismRouteConfig {
  identifyToken: string;
  baseUrl?: string;
  debug?: boolean;
  price: number | string; // USD price
  description: string;
  mimeType?: string;
}

Pages Router Options

interface PrismMiddlewareConfig {
  identifyToken: string;
  baseUrl?: string;
  debug?: boolean;
}

interface RoutePaymentConfig {
  price: number | string;
  description: string;
  mimeType?: string;
}

App Router Routes

Single Route

// 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, {
  identifyToken: process.env.PRISM_IDENTIFY_TOKEN!,
  price: 0.01,
  description: "Premium API",
});

Multiple Methods

// 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, {
  identifyToken: process.env.PRISM_IDENTIFY_TOKEN!,
  price: 0.01,
  description: "Get data",
});

export const POST = withPrismPayment(postHandler, {
  identifyToken: process.env.PRISM_IDENTIFY_TOKEN!,
  price: 0.02,
  description: "Post data",
});

Accessing Payment Info

App Router

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

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

Coming soon: transaction hash in the X-PAYMENT-RESPONSE response header. The Next.js SDK currently confirms settlement internally before returning the response, but does not yet surface the transaction hash to the client. If your application needs the on-chain transaction hash on the client side, use the Express SDK — it returns this header today.
Next.js wraps the Response object to validate settlement:
// 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

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

// next.config.js
module.exports = {
  env: {
    PRISM_IDENTIFY_TOKEN: process.env.PRISM_IDENTIFY_TOKEN,
    PRISM_BASE_URL: process.env.PRISM_BASE_URL
  },

  // Enable edge runtime (optional)
  experimental: {
    runtime: 'edge'
  }
};

// Vercel deployment
// vercel.json
{
  "env": {
    "PRISM_IDENTIFY_TOKEN": "@prism-identify-token",
    "PRISM_BASE_URL": "@prism-base-url"
  }
}
Last modified on May 15, 2026