Skip to main content
TypeScript SDK

Overview

The Prism Express Middleware is a drop-in solution for protecting your Node.js API routes with blockchain-based micropayments using the x402 protocol.

Zero Config

Works out of the box with Express.js

Type Safe

Full TypeScript support with IntelliSense

Flexible Pricing

Per-route pricing with wildcards

What’s New in v1.1

Latest Version: 1.1.0 - Enhanced error handling and Gateway integration
New error classes for better debugging and monitoring:
  • PrismGatewayError - Preserves Gateway status codes and trace IDs
  • PrismNetworkError - Network connectivity issues (503)
  • PrismConfigError - SDK misconfiguration (500)
  • PrismPaymentError - Invalid payment (402)
  • PrismValidationError - Request validation (400)
Benefits:
  • ✅ Type-safe error handling with instanceof checks
  • ✅ Gateway trace IDs for backend correlation
  • ✅ Timestamps for log searching
  • ✅ Original status codes preserved (not all converted to 500)
Debug production issues faster with Gateway trace IDs:
if (error instanceof PrismGatewayError) {
  console.log('Trace ID:', error.traceId);  // "0HNGT483NH6I8:00000001"
  console.log('Timestamp:', error.timestamp);
  console.log('Details:', error.details);   // Full error message from Gateway
}
Include trace IDs in support tickets for faster resolution.
Implemented verifyPayment() endpoint integration:
  • Cryptographic signature verification via Gateway
  • Payer address extracted and stored in res.locals.payer
  • Proper error handling for verification failures
Better error messages for end-users:
{
  "x402Version": 1,
  "error": "Internal Error",
  "details": "Database query failed: column p.ProviderId does not exist",
  "gateway": {
    "traceId": "0HNGT483NH6I8:00000001",
    "timestamp": "2025-11-06T13:13:00Z"
  }
}
Before: Generic “Failed to generate payment requirements”
After: Detailed error with trace ID and timestamp

Installation

npm install @1stdigital/prism-express
Package Registry: GitHub Packages Scope: @1stdigital Package: prism-express

Quick Start

1

Install Package

npm install @1stdigital/prism-express
2

Import Middleware

import express from 'express';
import { prismPaymentMiddleware } from '@1stdigital/prism-express';
3

Configure Middleware

const app = express();

app.use(
  prismPaymentMiddleware(
    {
      apiKey: process.env.PRISM_API_KEY },
    {
      '/api/premium': {
        price: 0.01,
        description: 'Premium API access'
      }
    }
  )
);
4

Add Protected Route

app.get('/api/premium', (req, res) => {
  res.json({ data: 'Premium content' });
});

app.listen(3000);

Basic Usage

import express from "express";
import { prismPaymentMiddleware } from "@1stdigital/prism-express";

const app = express();

// Configure payment middleware
app.use(
  prismPaymentMiddleware(
    {
      apiKey: "dev-key-123" },
    {
      "/api/weather": {
        price: 0.001,
        description: "Weather API access",
        mimeType: "application/json" },
      "/api/premium/*": {
        price: 0.01,
        description: "Premium API endpoints" } }
  )
);

// Public endpoint (no payment)
app.get("/", (req, res) => {
  res.json({ message: "Welcome to Prism API" });
});

// Protected endpoint (requires payment)
app.get("/api/weather", (req, res) => {
  res.json({
    location: "San Francisco",
    temperature: 72,
    condition: "Sunny" });
});

// Protected premium endpoint
app.get("/api/premium/data", (req, res) => {
  res.json({
    premium: true,
    data: "This is premium content" });
});

app.listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});

Configuration

Middleware Config

The first parameter to prismPaymentMiddleware is the global configuration:
interface PrismMiddlewareConfig {
  apiKey: string; // Required: Your Prism API key
  baseUrl?: string; // Optional: Gateway URL (defaults to test environment)
  timeout?: number; // Optional: Request timeout in ms (default: 10000)
  retries?: number; // Optional: Failed request retries (default: 3)
}
apiKey
string
required
Your Prism API key from Client Portal
Store in environment variable: process.env.PRISM_API_KEY
baseUrl
string
Prism Gateway URL. Defaults to test environment.
  • Test: https://prism-api.test.1stdigital.tech (default)
  • Production: https://prism-api.1stdigital.tech
{
  apiKey: 'key',
  baseUrl: 'https://prism-api.1stdigital.tech' // Production
}
timeout
number
default:"10000"
Request timeout in milliseconds
Gateway calls that exceed timeout will fail gracefully
retries
number
default:"3"
Number of retry attempts for failed Gateway requests

Route Configuration

The second parameter defines which routes require payment:
interface RoutePaymentConfig {
  price: number | string; // Required: Price per request
  description?: string; // Optional: Human-readable description
  mimeType?: string; // Optional: Response content type
  maxTimeoutSeconds?: number; // Optional: Payment timeout
  resource?: string; // Optional: Custom resource URL
}
price
number | string
required
Price to charge per request Formats: - Number: 0.001 (USDC) - String: "$0.001" (explicit USD) - String: "0.001 USDC" (explicit token)
Use small decimals for micropayments: 0.001 = $0.001 = 0.1¢
description
string
Human-readable description shown to users
{
  price: 0.01,
  description: 'Weather API - Current conditions'
}
mimeType
string
default:"application/json"
Response content type Common values: - application/json - text/html - image/png - video/mp4
maxTimeoutSeconds
number
default:"300"
Maximum time (seconds) to wait for payment
After timeout, payment authorization expires
resource
string
Custom resource URL (overrides request path)
{
  price: 0.01,
  resource: 'https://api.example.com/v2/premium'
}

Route Patterns

Exact Match

{
  '/api/weather': {
    price: 0.001,
    description: 'Weather data'
  }
}
Matches:
  • /api/weather
  • /api/weather/forecast
  • /api/weather-data

Wildcard Routes

{
  '/api/premium/*': {
    price: 0.01,
    description: 'All premium endpoints'
  }
}
Matches:
  • /api/premium/data
  • /api/premium/analytics
  • /api/premium/users/123
  • /api/public/data

Multiple Routes

{
  '/api/basic': {
    price: 0.001,
    description: 'Basic tier'
  },
  '/api/premium': {
    price: 0.01,
    description: 'Premium tier'
  },
  '/api/enterprise/*': {
    price: 0.1,
    description: 'Enterprise tier'
  }
}

Route Priority

When multiple patterns match, most specific wins:
{
  '/api/*': { price: 0.001 },           // Generic
  '/api/premium/*': { price: 0.01 },    // More specific
  '/api/premium/gold': { price: 0.05 }  // Most specific (used)
}
Request to /api/premium/gold → uses 0.05 price

Accessing Payment Info

Payment details are available in route handlers via res.locals.payment:
app.get("/api/premium", (req, res) => {
  const payment = res.locals.payment;

  console.log("Payment scheme:", payment.scheme);
  console.log("Network:", payment.network);
  console.log("Token:", payment.asset);
  console.log("Amount:", payment.amount);
  console.log("Transaction hash:", payment.txHash);

  res.json({ data: "Premium content" });
});

Payment Object Type

interface PaymentInfo {
  scheme: string; // 'exact' | 'range' | 'any'
  network: string; // 'base-sepolia' | 'ethereum' | 'bsc'
  asset: string; // Token contract address
  amount: string; // Amount in smallest unit
  payTo: string; // Provider wallet address
  txHash?: string; // Transaction hash (after settlement)
  validAfter: number; // Unix timestamp
  validBefore: number; // Unix timestamp
  nonce: string; // Unique payment nonce
}

Error Handling

The SDK provides structured error handling with specific error classes, detailed error messages, and Gateway tracing information.

Error Classes

All Prism errors extend the base PrismError class and include:
  • Status code - HTTP status code to return
  • Error code - Machine-readable error identifier
  • Message - Human-readable error description
  • Details - Additional context (varies by error type)
When: Prism Gateway API returns 4xx or 5xx responseProperties:
  • statusCode - Original HTTP status from Gateway (400, 401, 500, etc.)
  • message - Error title from Gateway
  • details - Detailed error description
  • traceId - Gateway trace ID for debugging
  • timestamp - Error timestamp from Gateway
Example Response:
{
  "x402Version": 1,
  "error": "Internal Error",
  "details": "Database query failed: column p.ProviderId does not exist",
  "gateway": {
    "traceId": "0HNGT483NH6I8:00000001",
    "timestamp": "2025-11-06T13:13:00.2646107Z"
  }
}
Usage:
import { PrismGatewayError } from '@1stdigital/prism-core';

app.use((err, req, res, next) => {
  if (err instanceof PrismGatewayError) {
    // Log with trace ID for backend correlation
    logger.error('Gateway error', {
      traceId: err.traceId,
      statusCode: err.statusCode,
      details: err.details
    });
    
    // Alert on 5xx errors
    if (err.statusCode >= 500) {
      alertOps('Prism Gateway error', { traceId: err.traceId });
    }
  }
  next(err);
});
When: Cannot reach Prism Gateway (timeout, DNS error, connection refused)Status Code: 503 Service UnavailableProperties:
  • message - Network error description
  • originalError - Original axios/network error
Example Response:
{
  "x402Version": 1,
  "error": "Payment service unavailable",
  "details": "Could not connect to Prism Gateway. Please try again later.",
  "retryAfter": 60
}
Usage (Retry Logic):
import { PrismNetworkError } from '@1stdigital/prism-core';

async function getPaymentRequirementsWithRetry(client, request, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await client.getPaymentRequirements(request);
    } catch (error) {
      if (error instanceof PrismNetworkError) {
        console.log(`Network error, retrying (${i + 1}/${maxRetries})...`);
        await sleep(1000 * Math.pow(2, i)); // Exponential backoff
        continue;
      }
      throw error; // Non-network error, don't retry
    }
  }
  throw new Error('Max retries exceeded');
}
When: SDK is misconfigured (invalid API key format, bad URL, etc.)Status Code: 500 Internal Server ErrorProperties:
  • message - Configuration error description
  • details - Configuration problem details
Example Response:
{
  "x402Version": 1,
  "error": "Payment service misconfigured",
  "details": "Please contact the administrator."
}
Usage:
import { PrismConfigError } from '@1stdigital/prism-core';

app.use((err, req, res, next) => {
  if (err instanceof PrismConfigError) {
    // Alert developers - this is a code/config issue
    alertDevs('Prism SDK misconfiguration', {
      error: err.message,
      details: err.details
    });
    
    // Don't expose config details to clients
    return res.status(500).json({
      error: 'Service temporarily unavailable'
    });
  }
  next(err);
});
When: Payment payload is malformed or invalidStatus Code: 402 Payment RequiredProperties:
  • message - Payment validation error
  • details - Why payment is invalid
Example Response:
{
  "x402Version": 1,
  "error": "Invalid payment authorization",
  "details": "Signature verification failed"
}
When: Request data fails validation (negative price, missing fields, etc.)Status Code: 400 Bad RequestProperties:
  • message - Validation error message
  • field - Field that failed validation
  • details - Validation error details
Example Response:
{
  "x402Version": 1,
  "error": "Validation failed",
  "details": "requestedAmount must be a positive number",
  "field": "requestedAmount"
}

Error Response Format

All error responses follow a consistent structure:
interface PrismErrorResponse {
  x402Version: number; // Always 1
  error: string; // Human-readable error message
  details?: string; // Additional error details
  gateway?: {
    // Gateway-specific info (if applicable)
    traceId?: string; // Trace ID for backend correlation
    timestamp?: string; // ISO 8601 timestamp
  };
  retryAfter?: number; // Seconds to wait before retry (503 errors)
}

Advanced Error Handling

1. Monitoring & Alerting with Trace IDs

import { PrismGatewayError, PrismNetworkError } from "@1stdigital/prism-core";

app.use((err, req, res, next) => {
  if (err instanceof PrismGatewayError) {
    // Log to monitoring service with Gateway trace ID
    logger.error("Prism Gateway Error", {
      traceId: err.traceId,
      timestamp: err.timestamp,
      statusCode: err.statusCode,
      message: err.message,
      details: err.details,
      endpoint: req.path,
      method: req.method });

    // Alert on critical errors (5xx)
    if (err.statusCode >= 500) {
      alerting.critical("Prism Gateway Internal Error", {
        traceId: err.traceId,
        endpoint: req.path,
        message: err.message });
    }

    // Return error with trace ID for support
    return res.status(err.statusCode).json({
      x402Version: 1,
      error: err.message,
      details: err.details,
      support: {
        traceId: err.traceId,
        message: "Please contact support with this trace ID" } });
  }

  if (err instanceof PrismNetworkError) {
    // Alert ops - Gateway is down
    alerting.critical("Prism Gateway Unreachable", {
      error: err.message,
      originalError: err.originalError?.code });

    return res.status(503).json({
      x402Version: 1,
      error: "Payment service temporarily unavailable",
      retryAfter: 60 });
  }

  next(err);
});

2. Retry Logic with Exponential Backoff

import {
  PrismClient,
  PrismNetworkError,
  PrismGatewayError } from "@1stdigital/prism-core";

async function makePaymentRequestWithRetry(
  client: PrismClient,
  request: any,
  maxRetries = 3
) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.getPaymentRequirements(request);
    } catch (error) {
      // Retry on network errors
      if (error instanceof PrismNetworkError) {
        const delay = 1000 * Math.pow(2, attempt); // 1s, 2s, 4s
        console.log(
          `Network error, retrying in ${delay}ms (${attempt + 1}/${maxRetries})`
        );
        await sleep(delay);
        continue;
      }

      // Retry on Gateway 5xx errors (server issues)
      if (error instanceof PrismGatewayError && error.statusCode >= 500) {
        if (attempt < maxRetries - 1) {
          const delay = 1000 * Math.pow(2, attempt);
          console.log(
            `Gateway error ${error.statusCode}, retrying in ${delay}ms`
          );
          await sleep(delay);
          continue;
        }
      }

      // Don't retry on 4xx errors (client errors)
      if (error instanceof PrismGatewayError && error.statusCode < 500) {
        throw error;
      }

      throw error;
    }
  }

  throw new Error("Max retries exceeded");
}

function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

3. User-Friendly Error Messages

import { PrismGatewayError } from "@1stdigital/prism-core";

app.use((err, req, res, next) => {
  if (err instanceof PrismGatewayError) {
    // Map Gateway errors to user-friendly messages
    const userMessages: Record<number, string> = {
      400: "Invalid payment request. Please check your payment details.",
      401: "Payment authentication failed. Please try again.",
      403: "Payment not authorized. Please contact support.",
      404: "Payment resource not found.",
      500: "Payment service is experiencing issues. Please try again later.",
      502: "Payment gateway temporarily unavailable.",
      503: "Payment service under maintenance. Please try again soon." };

    const userMessage =
      userMessages[err.statusCode] || "Payment processing failed.";

    return res.status(err.statusCode).json({
      error: userMessage,
      support: {
        message:
          "If the problem persists, contact support with this information:",
        traceId: err.traceId,
        timestamp: err.timestamp } });
  }

  next(err);
});

4. Error Metrics & Analytics

import { PrismGatewayError, PrismNetworkError } from "@1stdigital/prism-core";
import { Counter, Histogram } from "prom-client";

const paymentErrorCounter = new Counter({
  name: "prism_payment_errors_total",
  help: "Total payment errors by type and status",
  labelNames: ["error_type", "status_code"] });

const gatewayLatency = new Histogram({
  name: "prism_gateway_latency_seconds",
  help: "Prism Gateway API latency" });

app.use((err, req, res, next) => {
  if (err instanceof PrismGatewayError) {
    paymentErrorCounter.inc({
      error_type: "gateway",
      status_code: err.statusCode });

    // Track error by category
    if (err.statusCode >= 500) {
      metrics.increment("prism.gateway.server_error");
    } else if (err.statusCode >= 400) {
      metrics.increment("prism.gateway.client_error");
    }
  }

  if (err instanceof PrismNetworkError) {
    paymentErrorCounter.inc({
      error_type: "network",
      status_code: 503 });
    metrics.increment("prism.gateway.unreachable");
  }

  next(err);
});

Debugging with Trace IDs

When reporting issues to Prism support, always include the trace ID from error responses:
// Extract trace ID from error response
app.get("/api/paid", async (req, res) => {
  try {
    // ... payment logic
  } catch (error) {
    if (error instanceof PrismGatewayError) {
      console.error("Payment failed", {
        traceId: error.traceId, // ← Include in bug reports
        timestamp: error.timestamp,
        statusCode: error.statusCode,
        message: error.message });

      // Return trace ID to client for support tickets
      return res.status(error.statusCode).json({
        error: "Payment processing failed",
        supportInfo: {
          traceId: error.traceId,
          message: "Please contact support with this trace ID" } });
    }
  }
});
Example support ticket:
Subject: Payment Gateway Error - 500 Internal Server Error

Gateway Trace ID: 0HNGT483NH6I8:00000001
Timestamp: 2025-11-06T13:13:00.2646107Z
Endpoint: POST /api/v1/payment/requirements
Error: "42703: column p.ProviderId does not exist"

Advanced Usage

Environment-Based Configuration

const config = {
  apiKey: process.env.PRISM_API_KEY };

app.use(prismPaymentMiddleware(config, routes));
.env.development:
PRISM_API_KEY=dev-key-123
NODE_ENV=development
.env.production:
PRISM_API_KEY=prod-key-456
NODE_ENV=production

Accessing Payment Details

After successful payment verification, payment details are available in res.locals:
app.get("/api/premium", (req, res) => {
  // Payment payload (decoded from X-PAYMENT header)
  const payment = res.locals.payment;

  // Payer wallet address (from verification)
  const payerAddress = res.locals.payer;

  console.log("Payment received from:", payerAddress);
  console.log("Payment scheme:", payment.scheme);
  console.log("Network:", payment.network);

  res.json({
    data: "Premium content",
    paidBy: payerAddress });
});

Rate Limiting with Payments

Combine with rate limiting for hybrid monetization:
import rateLimit from "express-rate-limit";

// Free tier: 100 requests/hour
const freeLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,
  max: 100,
  message: "Free tier limit reached. Please upgrade or pay per request." });

app.use("/api/free", freeLimiter);

// Paid tier: No rate limit, but requires payment
app.use(
  "/api/paid",
  prismPaymentMiddleware(config, {
    "/api/paid/*": { price: 0.001 } })
);

TypeScript Support

Full Type Definitions

import {
  prismPaymentMiddleware,
  PrismMiddlewareConfig,
  RoutePaymentConfig,
  PaymentInfo,
  PrismError } from "@1stdigital/prism-express";

// Type-safe configuration
const config: PrismMiddlewareConfig = {
  apiKey: process.env.PRISM_API_KEY!,
  timeout: 15000 };

const routes: Record<string, RoutePaymentConfig> = {
  "/api/data": {
    price: 0.001,
    description: "Data access" } };

// Type-safe middleware
app.use(prismPaymentMiddleware(config, routes));

// Type-safe payment info
app.get("/api/data", (req, res) => {
  const payment: PaymentInfo | undefined = res.locals.payment;

  if (payment) {
    console.log(`Paid ${payment.amount} on ${payment.network}`);
  }

  res.json({ data: "Content" });
});

Express Types Extension

Add types to res.locals:
// types/express.d.ts
import { PaymentInfo } from "@1stdigital/prism-express";

declare global {
  namespace Express {
    interface Locals {
      payment?: PaymentInfo;
      prismPrice?: number;
    }
  }
}

Testing

Unit Tests

import request from "supertest";
import express from "express";
import { prismPaymentMiddleware } from "@1stdigital/prism-express";

describe("Prism Payment Middleware", () => {
  let app: express.Application;

  beforeEach(() => {
    app = express();
    app.use(
      prismPaymentMiddleware(
        { apiKey: "test-key" },
        { "/api/paid": { price: 0.01 } }
      )
    );

    app.get("/api/paid", (req, res) => {
      res.json({ data: "content" });
    });
  });

  it("should return 402 for unpaid request", async () => {
    const res = await request(app).get("/api/paid");

    expect(res.status).toBe(402);
    expect(res.headers["x-payment"]).toBeDefined();
  });

  it("should allow request with valid payment", async () => {
    const paymentHeader = generateValidPayment();

    const res = await request(app)
      .get("/api/paid")
      .set("X-PAYMENT-AUTHORIZATION", paymentHeader);

    expect(res.status).toBe(200);
    expect(res.body.data).toBe("content");
  });
});

Integration Tests with Sandbox

import { ethers } from "ethers";

describe("Payment Flow Integration", () => {
  it("should complete full payment cycle", async () => {
    // 1. Request protected resource
    const res1 = await request(app).get("/api/paid");
    expect(res1.status).toBe(402);

    // 2. Parse payment requirements
    const payment = JSON.parse(res1.headers["x-payment"]);

    // 3. Sign authorization (with test wallet)
    const wallet = new ethers.Wallet(process.env.TEST_PRIVATE_KEY!);
    const signature = await wallet.signTypedData(
      payment.typedData.domain,
      {
        TransferWithAuthorization:
          payment.typedData.types.TransferWithAuthorization },
      payment.typedData.message
    );

    // 4. Retry with signature
    const res2 = await request(app)
      .get("/api/paid")
      .set(
        "X-PAYMENT-AUTHORIZATION",
        JSON.stringify({
          signature,
          ...payment.typedData.message })
      );

    expect(res2.status).toBe(200);
    expect(res2.body.data).toBe("content");
  });
});

Performance Optimization

Caching Payment Requirements

import NodeCache from "node-cache";

const cache = new NodeCache({ stdTTL: 300 }); // 5 minutes

app.use((req, res, next) => {
  const cacheKey = `payment:${req.path}`;
  const cached = cache.get(cacheKey);

  if (cached) {
    res.set("X-PAYMENT", JSON.stringify(cached));
    return res.status(402).json({ error: "Payment required" });
  }

  next();
});

app.use(prismPaymentMiddleware(config, routes));

Connection Pooling

const config = {
  apiKey: process.env.PRISM_API_KEY,
  timeout: 5000,
  retries: 2,
  // Use HTTP keep-alive
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }) };

Migration Guide

From v0.x to v1.x

1

Update Package

npm install @1stdigital/prism-express@latest
2

Update Import

// Old
import { prismMiddleware } from '@prism/middleware';

// New
import { prismPaymentMiddleware } from '@1stdigital/prism-express';
3

Update Configuration

// Old
prismMiddleware({ key: 'xxx', sandbox: true }, routes)

// New
prismPaymentMiddleware({ apiKey: 'xxx' }, routes)

Troubleshooting

Symptoms: Routes always return 200, no payment requiredSolutions:
  • Verify middleware is registered before route handlers
  • Check route paths match exactly (case-sensitive)
  • Ensure apiKey is valid
  • Check Gateway connectivity (curl https://prism-api.test.1stdigital.tech/health)
Symptoms: 503 Service Unavailable or PrismNetworkErrorSolutions:
  • Check network connectivity to Gateway
  • Verify no firewall blocking outbound HTTPS to prism-api.test.1stdigital.tech
  • Check Gateway status page
  • Implement retry logic (see Error Handling section)
Example:
import { PrismNetworkError } from '@1stdigital/prism-core';

if (error instanceof PrismNetworkError) {
  console.error('Gateway unreachable:', error.message);
  // Retry or alert ops team
}
Symptoms: 401 Unauthorized or PrismGatewayError with status 401Solutions:
  • Verify API key in Client Portal
  • Check environment variable: echo $PRISM_API_KEY
  • Ensure key hasn’t been revoked
  • Use correct environment (sandbox vs production)
Check error details:
if (error instanceof PrismGatewayError && error.statusCode === 401) {
  console.error('Auth error:', {
    traceId: error.traceId,
    details: error.details
  });
}
Symptoms: PrismGatewayError with status 500 and trace IDSolutions:
  • This is a Gateway backend issue - not your code
  • Extract and log the traceId from error
  • Report to Prism support with trace ID
Example:
if (error instanceof PrismGatewayError && error.statusCode >= 500) {
  console.error('Gateway internal error:', {
    traceId: error.traceId,      // ← Include in support ticket
    timestamp: error.timestamp,
    details: error.details
  });
  
  // Alert ops to contact Prism support
  alertOps(`Gateway error - Trace ID: ${error.traceId}`);
}
Symptoms: Type errors during buildSolutions:
  • Install type definitions: npm install -D @types/express
  • Update tsconfig.json: "moduleResolution": "node"
  • Ensure TypeScript version >= 4.5

API Reference

prismPaymentMiddleware(config, routes)

Creates Express middleware for x402 payment protection. Parameters:
  • config (PrismMiddlewareConfig) - Global configuration
  • routes (Record<string, RoutePaymentConfig>) - Route payment settings
Returns: Express middleware function Example:
const middleware = prismPaymentMiddleware(
  { apiKey: "key" },
  { "/api/paid": { price: 0.01 } }
);

app.use(middleware);

Error Classes

PrismError (Base Class)

Base error class for all Prism SDK errors. Properties:
  • message (string) - Error message
  • code (string) - Error code (e.g., ‘GATEWAY_ERROR’, ‘NETWORK_ERROR’)
  • statusCode (number) - HTTP status code
  • details (any) - Additional error context
Methods:
  • toJSON() - Convert error to JSON representation
Example:
import { PrismError } from "@1stdigital/prism-core";

if (error instanceof PrismError) {
  console.log(error.code); // 'GATEWAY_ERROR'
  console.log(error.statusCode); // 500
  console.log(error.toJSON()); // { name, message, code, statusCode, details }
}

PrismGatewayError extends PrismError

Gateway returned an error response (4xx or 5xx). Additional Properties:
  • traceId (string | undefined) - Gateway trace ID for debugging
  • timestamp (string | undefined) - ISO 8601 timestamp from Gateway
Example:
import { PrismGatewayError } from "@1stdigital/prism-core";

app.use((err, req, res, next) => {
  if (err instanceof PrismGatewayError) {
    logger.error("Gateway error", {
      traceId: err.traceId,
      statusCode: err.statusCode,
      message: err.message,
      details: err.details });
  }
});

PrismNetworkError extends PrismError

Network connectivity issues (timeout, DNS error, connection refused). Additional Properties:
  • originalError (any) - Original network error from axios
Status Code: Always 503 Example:
import { PrismNetworkError } from "@1stdigital/prism-core";

if (error instanceof PrismNetworkError) {
  console.log("Gateway unreachable:", error.message);
  console.log("Original error:", error.originalError);
  // Implement retry logic or alert ops
}

PrismConfigError extends PrismError

SDK misconfiguration (invalid API key format, bad URL, etc.). Status Code: Always 500 Example:
import { PrismConfigError } from "@1stdigital/prism-core";

if (error instanceof PrismConfigError) {
  // Developer/ops issue - fix configuration
  alertDevs("Prism SDK misconfigured: " + error.message);
}

PrismPaymentError extends PrismError

Invalid payment payload or authorization. Status Code: Always 402 Example:
import { PrismPaymentError } from "@1stdigital/prism-core";

if (error instanceof PrismPaymentError) {
  // Client sent invalid payment
  return res.status(402).json({
    error: error.message,
    details: error.details });
}

PrismValidationError extends PrismError

Request validation failed. Additional Properties:
  • field (string | undefined) - Field that failed validation
Status Code: Always 400 Example:
import { PrismValidationError } from "@1stdigital/prism-core";

if (error instanceof PrismValidationError) {
  return res.status(400).json({
    error: "Validation failed",
    field: error.field,
    message: error.message });
}

Error Codes

CodeError ClassDescription
GATEWAY_ERRORPrismGatewayErrorGateway returned error (4xx/5xx)
NETWORK_ERRORPrismNetworkErrorCannot reach Gateway
CONFIG_ERRORPrismConfigErrorInvalid SDK configuration
PAYMENT_ERRORPrismPaymentErrorInvalid payment
VALIDATION_ERRORPrismValidationErrorRequest validation failed
UNKNOWN_ERRORPrismErrorUnclassified error

Resources

What’s Next?