npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@shade402/express

v0.0.1

Published

Express.js middleware and decorators for X402 payment protocol

Downloads

6

Readme

@shade402/express

Express.js middleware and utilities for implementing the X402 payment protocol in your web applications.

Overview

The Express package provides middleware and helper functions to easily add X402 payment requirements to your Express.js routes. It handles payment verification, 402 response generation, and integrates seamlessly with Solana blockchain payments.

Features

  • Payment middleware for protecting routes with automatic verification
  • Centralized error handling middleware for X402 payment errors
  • Automatic payment verification using Solana blockchain
  • Flexible configuration with global or per-route options
  • 402 response builder for creating proper payment requests
  • Full TypeScript support with Express request extensions
  • Integration with Express error handling

Installation

npm install @shade402/express
# or
pnpm add @shade402/express
# or
yarn add @shade402/express

Dependencies

  • @shade402/core: Core X402 protocol implementation
  • express: Express.js web framework

Usage

Basic Setup with Global Configuration

import express from 'express';
import { initX402, paymentRequired } from '@shade402/express';

const app = express();
app.use(express.json());

// Initialize X402 with global configuration
initX402({
  paymentAddress: process.env.PAYMENT_WALLET_ADDRESS!,
  tokenMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
  network: 'solana-devnet',
  rpcUrl: process.env.SOLANA_RPC_URL,
  autoVerify: true
});

// Protect a route with payment requirement
app.get('/premium-content',
  paymentRequired({
    amount: '1000000', // 1 USDC (6 decimals)
    description: 'Access to premium content'
  }),
  (req, res) => {
    // This code only runs after payment is verified
    res.json({
      content: 'Premium content here',
      paidBy: req.payment?.publicKey
    });
  }
);

app.listen(3000, () => console.log('Server running on port 3000'));

Per-Route Configuration

You can override global configuration for specific routes:

import { paymentRequired } from '@shade402/express';

// Override global config for specific routes
app.get('/api/data',
  paymentRequired({
    amount: '500000',
    paymentAddress: 'SpecificWalletAddress',
    tokenMint: 'So11111111111111111111111111111111111111112', // Native SOL
    network: 'solana-mainnet',
    description: 'API data access',
    expiresIn: 300, // 5 minutes
    autoVerify: true
  }),
  (req, res) => {
    res.json({ data: 'Your data here' });
  }
);

Error Handling Middleware

Add the error middleware at the end of your middleware stack to handle X402 payment errors:

import express from 'express';
import { x402ErrorMiddleware } from '@shade402/express';

const app = express();

// ... your routes ...

// Add error middleware at the end (after all routes)
app.use(x402ErrorMiddleware({
  logErrors: true,
  includeStack: process.env.NODE_ENV === 'development'
}));

The error middleware automatically handles:

  • PaymentRequiredError: Returns 402 status with payment request
  • PaymentExpiredError: Returns 410 Gone status
  • InsufficientFundsError: Returns 402 status
  • PaymentVerificationError: Returns 403 Forbidden status
  • TransactionBroadcastError: Returns 502 Bad Gateway status
  • InvalidPaymentRequestError: Returns 400 Bad Request status
  • Generic errors: Returns 500 Internal Server Error

Manual Payment Verification

For custom verification logic, disable automatic verification:

import { paymentRequired } from '@shade402/express';
import { X402Request } from '@shade402/express';

app.post('/custom-endpoint',
  paymentRequired({
    amount: '2000000',
    autoVerify: false // Disable automatic verification
  }),
  (req: X402Request, res) => {
    // Payment authorization is available but not verified
    const payment = req.payment;

    // Do custom verification logic
    if (payment && customVerificationLogic(payment)) {
      res.json({ success: true });
    } else {
      res.status(403).json({ error: 'Payment verification failed' });
    }
  }
);

Building 402 Responses Manually

For custom payment flow control, build 402 responses manually:

import { build402Response } from '@shade402/express';

app.get('/custom-protected', (req, res) => {
  // Check some condition
  if (!userHasPaid) {
    const response = build402Response({
      amount: '1000000',
      paymentAddress: process.env.PAYMENT_WALLET_ADDRESS!,
      tokenMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
      network: 'solana-devnet',
      resource: req.path,
      description: 'Custom payment required'
    });

    // Optional: Set X402 headers for better client compatibility
    return res
      .status(402)
      .set({
        'X-Payment-Required': 'true',
        'X-Payment-Protocol': 'x402',
        'X-Payment-Amount': '1000000',
        'X-Payment-Asset': 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
      })
      .json(response);
  }

  res.json({ content: 'Protected content' });
});

Configuration Options

import { initX402, X402ConfigOptions } from '@shade402/express';

const config: X402ConfigOptions = {
  paymentAddress: string;      // Your Solana wallet address
  tokenMint: string;            // Token mint address (SPL token or SOL)
  network: string;              // 'solana-devnet' or 'solana-mainnet'
  rpcUrl?: string;              // Optional custom RPC endpoint
  autoVerify?: boolean;         // Auto-verify payments (default: true)
  expiresIn?: number;           // Payment expiration in seconds (default: 600)
};

initX402(config);

Accessing Payment Information

Payment information is available on the request object after verification:

import { X402Request } from '@shade402/express';

app.get('/payment-info',
  paymentRequired({ amount: '1000000' }),
  (req: X402Request, res) => {
    const payment = req.payment;

    if (!payment) {
      return res.status(402).json({ error: 'Payment required' });
    }

    res.json({
      paymentId: payment.paymentId,
      amount: payment.actualAmount,
      payer: payment.publicKey,
      transactionHash: payment.transactionHash,
      timestamp: payment.timestamp
    });
  }
);

Multiple Payment Tiers

You can implement different payment tiers for different routes:

// Basic tier
app.get('/basic',
  paymentRequired({ amount: '100000', description: 'Basic access' }),
  handler
);

// Premium tier
app.get('/premium',
  paymentRequired({ amount: '1000000', description: 'Premium access' }),
  handler
);

// Enterprise tier
app.get('/enterprise',
  paymentRequired({ amount: '10000000', description: 'Enterprise access' }),
  handler
);

API Reference

Middleware

paymentRequired(options: PaymentRequiredOptions)

Express middleware that requires payment to access a route.

Options:

interface PaymentRequiredOptions {
  amount: string;                    // Required payment amount in token base units
  paymentAddress?: string;           // Override global payment address
  tokenMint?: string;                // Override global token mint
  network?: string;                  // Override global network
  description?: string;              // Payment description
  expiresIn?: number;                // Payment expiration in seconds
  autoVerify?: boolean;              // Enable automatic verification
}

Returns: Express middleware function

Configuration

initX402(config: X402ConfigOptions)

Initialize global X402 configuration. Must be called before using payment middleware.

getConfig()

Get current global configuration.

Returns: X402ConfigOptions | undefined

isInitialized()

Check if X402 is initialized.

Returns: boolean

Error Middleware

x402ErrorMiddleware(options?: X402ErrorMiddlewareOptions)

Error handling middleware for X402 payment errors and general application errors.

Options:

interface X402ErrorMiddlewareOptions {
  includeStack?: boolean;  // Include stack traces (default: NODE_ENV === 'development')
  logErrors?: boolean;     // Log errors to console (default: true)
  onError?: (error: Error, req: Request, res: Response) => void; // Custom error handler
}

Note: This middleware should be added after all your routes and middleware.

Response Builders

build402Response(options: Build402ResponseOptions)

Build a 402 Payment Required response object.

Options:

interface Build402ResponseOptions {
  amount: string;
  paymentAddress: string;
  tokenMint: string;
  network: string;
  resource: string;
  description?: string;
  expiresIn?: number;
}

Returns: Payment request object that should be sent with status 402.

Type Definitions

X402Request

Extended Express Request interface with payment information:

interface X402Request extends Request {
  payment?: PaymentAuthorization;
}

TypeScript Support

The package includes full TypeScript definitions. Use the X402Request type for request objects in protected routes:

import { X402Request } from '@shade402/express';

app.get('/protected',
  paymentRequired({ amount: '1000000' }),
  (req: X402Request, res: Response) => {
    // req.payment is available here
    const payment = req.payment;
    // ...
  }
);

Error Responses

The error middleware returns standardized error responses:

402 Payment Required

{
  "error": "Payment required",
  "code": "PAYMENT_REQUIRED",
  "payment_request": { ... }
}

403 Forbidden (Payment Verification Failed)

{
  "error": "Payment verification failed",
  "code": "PAYMENT_VERIFICATION_FAILED"
}

410 Gone (Payment Expired)

{
  "error": "Payment request expired",
  "code": "PAYMENT_EXPIRED"
}

Best Practices

  1. Initialize X402 configuration once at application startup
  2. Store sensitive configuration in environment variables
  3. Use appropriate payment amounts for different tiers
  4. Set reasonable expiration times for payment requests
  5. Enable automatic verification in production
  6. Add error middleware after all routes
  7. Use TypeScript types for better type safety
  8. Monitor payment verification failures
  9. Consider rate limiting for payment-protected endpoints
  10. Log payment transactions for audit purposes

Examples

Complete Express Server

import express from 'express';
import { initX402, paymentRequired, x402ErrorMiddleware } from '@shade402/express';

const app = express();
app.use(express.json());

// Initialize X402
initX402({
  paymentAddress: process.env.PAYMENT_WALLET_ADDRESS!,
  tokenMint: process.env.TOKEN_MINT!,
  network: 'solana-devnet',
  rpcUrl: process.env.SOLANA_RPC_URL,
  autoVerify: true,
  expiresIn: 600
});

// Public route
app.get('/health', (req, res) => {
  res.json({ status: 'ok' });
});

// Protected route
app.get('/api/data',
  paymentRequired({
    amount: '1000000',
    description: 'API data access'
  }),
  (req, res) => {
    res.json({ data: 'Protected data' });
  }
);

// Error handling
app.use(x402ErrorMiddleware({
  logErrors: true,
  includeStack: process.env.NODE_ENV === 'development'
}));

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Custom Payment Verification

import { paymentRequired, X402Request } from '@shade402/express';
import { verifyPaymentInDatabase } from './database';

app.post('/api/process',
  paymentRequired({
    amount: '1000000',
    autoVerify: false
  }),
  async (req: X402Request, res) => {
    const payment = req.payment;
    
    if (!payment) {
      return res.status(402).json({ error: 'Payment required' });
    }

    // Custom verification against database
    const isVerified = await verifyPaymentInDatabase(payment.transactionHash);
    
    if (!isVerified) {
      return res.status(403).json({ error: 'Payment not verified' });
    }

    // Process request
    res.json({ success: true });
  }
);

Testing

pnpm test

Contributing

See CONTRIBUTING.md

License

MIT - See LICENSE