Reference@t402/express

@t402/express

Express.js middleware for protecting routes with t402 payment requirements. Automatically handles payment verification, settlement, and paywall rendering.

Installation

npm install @t402/express @t402/core

Quick Start

import express from 'express';
import { paymentMiddleware } from '@t402/express';
import { t402ResourceServer, createFacilitatorClient } from '@t402/core/server';
import { registerExactEvmScheme } from '@t402/evm/exact/server';
 
const app = express();
 
// Create and configure the t402 server
const facilitator = createFacilitatorClient({ url: 'https://facilitator.t402.io' });
const server = new t402ResourceServer(facilitator);
registerExactEvmScheme(server, {});
 
// Define protected routes
const routes = {
  '/api/premium/*': {
    accepts: {
      scheme: 'exact',
      network: 'eip155:8453',
      payTo: '0xYourAddress',
      price: '$0.01',
    },
    description: 'Premium API access',
  },
};
 
// Apply middleware
app.use(paymentMiddleware(routes, server));
 
// Protected route
app.get('/api/premium/data', (req, res) => {
  res.json({ data: 'Premium content' });
});
 
app.listen(3000);

API Reference

paymentMiddleware

Creates Express middleware with a pre-configured server instance.

function paymentMiddleware(
  routes: RoutesConfig,
  server: t402ResourceServer,
  paywallConfig?: PaywallConfig,
  paywall?: PaywallProvider,
  syncFacilitatorOnStart?: boolean
): RequestHandler

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)

Example

import { paymentMiddleware } from '@t402/express';
import { t402ResourceServer } from '@t402/core/server';
 
const server = new t402ResourceServer(facilitatorClient);
registerExactEvmScheme(server, {});
 
app.use(paymentMiddleware(routes, server, {
  title: 'Premium Content',
  description: 'Pay to access this resource',
}));

paymentMiddlewareFromConfig

Creates Express middleware with simple configuration (server created internally).

function paymentMiddlewareFromConfig(
  routes: RoutesConfig,
  facilitatorClients?: FacilitatorClient | FacilitatorClient[],
  schemes?: SchemeRegistration[],
  paywallConfig?: PaywallConfig,
  paywall?: PaywallProvider,
  syncFacilitatorOnStart?: boolean
): RequestHandler

Parameters

ParameterTypeDescription
routesRoutesConfigRoute configurations for protected endpoints
facilitatorClientsFacilitatorClient | FacilitatorClient[]Facilitator client(s) for payment processing
schemesSchemeRegistration[]Array of scheme registrations
paywallConfigPaywallConfigOptional paywall UI configuration
paywallPaywallProviderOptional custom paywall provider
syncFacilitatorOnStartbooleanSync with facilitator on startup (default: true)

Example

import { paymentMiddlewareFromConfig } from '@t402/express';
import { createExactEvmServer } from '@t402/evm/exact/server';
 
app.use(paymentMiddlewareFromConfig(
  routes,
  facilitatorClient,
  [{ network: 'eip155:8453', server: createExactEvmServer({}) }],
  { title: 'Premium Content' }
));

Route Configuration

RoutesConfig

Define which routes require payment and their pricing:

const routes: RoutesConfig = {
  // Single price for route
  '/api/premium/*': {
    accepts: {
      scheme: 'exact',
      network: 'eip155:8453',
      payTo: '0xRecipient',
      price: '$0.01',
    },
    description: 'Premium API access',
  },
 
  // Multiple payment options
  '/api/multi-chain/*': {
    accepts: [
      {
        scheme: 'exact',
        network: 'eip155:8453',
        payTo: '0xRecipient',
        price: '$0.01',
      },
      {
        scheme: 'exact',
        network: 'ton:mainnet',
        payTo: 'EQRecipientAddress',
        price: '$0.01',
      },
    ],
    description: 'Multi-chain payment support',
  },
 
  // With extensions
  '/api/bazaar/*': {
    accepts: {
      scheme: 'exact',
      network: 'eip155:8453',
      payTo: '0xRecipient',
      price: '$0.01',
    },
    description: 'Dynamic pricing endpoint',
    extensions: {
      bazaar: {
        endpoint: 'https://api.example.com/bazaar',
      },
    },
  },
};

PaywallConfig

Configure the built-in paywall UI:

interface PaywallConfig {
  title?: string;
  description?: string;
  logoUrl?: string;
  theme?: 'light' | 'dark' | 'auto';
  customCss?: string;
}

Example

const paywallConfig: PaywallConfig = {
  title: 'Premium API',
  description: 'Pay once to access this endpoint',
  logoUrl: 'https://example.com/logo.png',
  theme: 'dark',
};
 
app.use(paymentMiddleware(routes, server, paywallConfig));

Multi-Network Support

Accept payments from multiple blockchain networks:

import { registerExactEvmScheme } from '@t402/evm/exact/server';
import { registerExactTonScheme } from '@t402/ton/exact/server';
import { registerExactSvmScheme } from '@t402/svm/exact/server';
 
const server = new t402ResourceServer(facilitator);
 
// Register multiple networks
registerExactEvmScheme(server, {}); // All EVM chains
registerExactTonScheme(server, {}); // TON
registerExactSvmScheme(server, {}); // Solana
 
const routes = {
  '/api/data': {
    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 endpoint',
  },
};
 
app.use(paymentMiddleware(routes, server));

Settlement Behavior

The middleware handles settlement atomically:

  1. Request received - Middleware checks if route requires payment
  2. Payment verified - Signature is validated before proceeding
  3. Handler executes - Your route handler runs
  4. Settlement on success - Payment is settled only if handler returns status < 400
  5. Response sent - Settlement confirmation headers are added
app.get('/api/premium/data', (req, res) => {
  try {
    const data = fetchPremiumData();
    res.json({ data }); // Status 200 -> Payment settles
  } catch (error) {
    res.status(500).json({ error: 'Failed' }); // Status 500 -> No settlement
  }
});

Settlement only occurs after your handler returns a successful response (status < 400). If your handler throws an error or returns a 4xx/5xx status, the payment is not settled.

Error Handling

The middleware returns structured error responses:

// 402 - Payment Required
{
  "error": "Payment required",
  "paymentRequirements": { ... }
}
 
// 402 - Settlement Failed
{
  "error": "Settlement failed",
  "details": "Insufficient balance"
}

Custom Paywall Provider

Implement a custom paywall for branded experiences:

import { PaywallProvider } from '@t402/core/server';
 
const customPaywall: PaywallProvider = {
  render: (paymentRequired, config) => {
    return `
      <!DOCTYPE html>
      <html>
        <head><title>${config?.title || 'Payment Required'}</title></head>
        <body>
          <h1>Custom Paywall</h1>
          <p>Amount: ${paymentRequired.requirements[0].maxAmountRequired}</p>
          <button onclick="handlePayment()">Pay Now</button>
        </body>
      </html>
    `;
  },
};
 
app.use(paymentMiddleware(routes, server, paywallConfig, customPaywall));

Type Exports

// Server types
export { t402ResourceServer, t402HTTPResourceServer } from '@t402/core/server';
export type { PaywallProvider, PaywallConfig } from '@t402/core/server';
export { RouteConfigurationError } from '@t402/core/server';
export type { RouteValidationError } from '@t402/core/server';
 
// Protocol types
export type {
  PaymentRequired,
  PaymentRequirements,
  PaymentPayload,
  Network,
  SchemeNetworkServer,
} from '@t402/core/types';
 
// Adapter
export { ExpressAdapter } from '@t402/express';