Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.skale.space/llms.txt

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

Protect your HTTP endpoints with x402 payments using middleware. The middleware enforces the x402 handshake, forwards payment requirements to a facilitator, inspects payment headers from clients, and handles settlement before allowing requests to proceed.

Prerequisites

  • Node.js and npm installed
  • A SKALE Chain endpoint
  • Understanding of x402 protocol
  • A facilitator service (see Run a Facilitator)

Overview

When a client reaches a paywalled endpoint, the middleware:
  1. Returns a 402 Payment Required response with payment requirements if the incoming request lacks a valid payment header
  2. When a payment header is present, forwards it to the facilitator’s /verify and /settle endpoints
  3. If settlement succeeds, the request continues; otherwise another 402 is returned

Environment Setup

Create a .env file with the following variables:
# Facilitator URL
FACILITATOR_URL=https://facilitator.dirtroad.dev

# Your wallet address to receive payments
FACILITATOR_RECEIVING_ADDRESS=0xfacilitator_receiving_address

# USDC token contract on SKALE
PAYMENT_TOKEN_ADDRESS=0x2e08028E3C4c2356572E096d8EF835cD5C6030bD
PAYMENT_TOKEN_NAME="Bridged USDC (SKALE Bridge)"

# SKALE Chain ID
NETWORK_CHAIN_ID=324705682

# Server port
PORT=3000

Implementation

Step 1: Install Dependencies

npm install hono @hono/node-server @x402/hono @x402/evm @x402/core dotenv

Step 2: Create the Server

import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { paymentMiddleware, x402ResourceServer } from "@x402/hono";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
import type { Network } from "@x402/core/types";
import "dotenv/config";

const app = new Hono();

async function main() {
    const facilitatorUrl = process.env.FACILITATOR_URL!;
    const receivingAddress = process.env.RECEIVING_ADDRESS as `0x${string}`;
    const paymentTokenAddress = process.env.PAYMENT_TOKEN_ADDRESS as `0x${string}`;
    const paymentTokenName = process.env.PAYMENT_TOKEN_NAME || "Bridged USDC (SKALE Bridge)";
    const networkChainId = process.env.NETWORK_CHAIN_ID || "324705682";
    const network: Network = `eip155:${networkChainId}`;

    // Setup facilitator client and resource server
    const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
    const resourceServer = new x402ResourceServer(facilitatorClient);
    
    // Register the exact scheme for EVM networks
    resourceServer.register("eip155:*", new ExactEvmScheme());

    // Public endpoint
    app.get("/", (c) => {
        return c.json({ message: "Welcome! Access /premium/* endpoints for paid content" });
    });

    // Apply payment middleware to protected routes
    app.use(
        paymentMiddleware(
            {
                "GET /premium/data": {
                    accepts: [
                        {
                            scheme: "exact",
                            network: network,
                            payTo: receivingAddress,
                            price: {
                                amount: "10000",
                                asset: paymentTokenAddress,
                                extra: {
                                    name: paymentTokenName,
                                    version: "1",
                                },
                            },
                        },
                    ],
                    description: "Premium data access",
                    mimeType: "application/json",
                },
            },
            resourceServer,
        ),
    );

    // Protected endpoint
    app.get("/premium/data", (c) => {
        return c.json({
            secret: "Premium data unlocked!",
            timestamp: new Date().toISOString(),
        });
    });

    const port = Number(process.env.PORT) || 3000;
    serve({ fetch: app.fetch, port }, () => {
        console.log(`Server running on http://localhost:${port}`);
    });
}

main().catch(console.error);

Step 3: Run the Server

npx tsx server.ts

Multiple Protected Routes

Add multiple routes with different pricing:
app.use(
    paymentMiddleware(
        {
            "GET /premium/data": {
                accepts: [
                    {
                        scheme: "exact",
                        network: network,
                        payTo: receivingAddress,
                        price: {
                            amount: "10000",
                            asset: paymentTokenAddress,
                            extra: { name: paymentTokenName, version: "1" },
                        },
                    },
                ],
                description: "Basic premium data",
                mimeType: "application/json",
            },
            "GET /premium/advanced": {
                accepts: [
                    {
                        scheme: "exact",
                        network: network,
                        payTo: receivingAddress,
                        price: {
                            amount: "50000",
                            asset: paymentTokenAddress,
                            extra: { name: paymentTokenName, version: "1" },
                        },
                    },
                ],
                description: "Advanced premium data",
                mimeType: "application/json",
            },
        },
        resourceServer,
    ),
);

Testing Your Server

Once your server is running, you can test it:
# Public endpoint (no payment required)
curl http://localhost:3000/

# Protected endpoint (returns 402 Payment Required)
curl http://localhost:3000/premium/data
The protected endpoint will return a 402 Payment Required response with payment requirements that a client can use to make a payment.

Error Handling

  • Invalid facilitator responses throw an exception (HTTP 500)
  • When settlement fails, the middleware returns a 402 status with error details
  • Missing configuration returns a 503 Service Unavailable

Resources