Skip to main content
The quickest path to a working h402 integration is a thin gateway that sits in front of your HTTP service. This guide shows how to implement both client and server components using the @bit-gpt/h402 package.

Client and Server quickstart

1

Installation

npm install @bit-gpt/h402
2

Server implementation (to h402 gate your resources)

Express code

Explore the Express server implementation for h402 integration.

All examples

Browse all h402 integration examples available in our repository.
import { config } from "dotenv";
import express from "express";
import { paymentMiddleware, Resource, createRouteConfigFromPrice, Network } from "h402-express";

config();

const facilitatorUrl = process.env.FACILITATOR_URL as Resource;
const payTo = process.env.ADDRESS as `0x${string}`;
const network = process.env.NETWORK as Network;

if (!facilitatorUrl || !payTo || !network) {
  console.error("Missing required environment variables");
  process.exit(1);
}

const app = express();

console.log("Server is running");

app.use(
  paymentMiddleware(
    payTo,
    {
      // Use createRouteConfigFromPrice to construct the RouteConfig
      "/weather": createRouteConfigFromPrice("$0.001", network),
      // Example of advanced configuration with multiple payment options
      "/premium/*": {
        paymentRequirements: [
          {
            scheme: "exact",
            namespace: "evm",
            tokenAddress: "0x55d398326f99059ff775485246999027b3197955", // USDT on BSC
            amountRequired: 0.01,
            amountRequiredFormat: "humanReadable",
            networkId: "56",
            payToAddress: payTo,
            description: "Premium content access with USDT on BSC",
            tokenDecimals: 18,
          },
          {
            scheme: "exact",
            namespace: "solana",
            tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC on Solana
            amountRequired: 0.01,
            amountRequiredFormat: "humanReadable",
            networkId: "mainnet",
            payToAddress: "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", // Example Solana address
            description: "Premium content access with USDC on Solana",
            tokenDecimals: 6,
          },
        ],
      },
    },
    {
      url: facilitatorUrl,
    },
  ),
);

app.get("/weather", (req, res) => {
  res.send({
    report: {
      weather: "sunny",
      temperature: 70,
    },
  });
});

app.get("/premium/content", (req, res) => {
  res.send({
    content: "This is premium content accessible via multiple payment methods",
    supportedPayments: ["USDT on BSC", "USDC on Solana"],
  });
});

app.listen(4021, () => {
  console.log(`Server listening at http://localhost:${4021}`);
});
3

Client implementation (to interact with h402-gated servers)

Axios client code

Discover how to implement the Axios client for h402 integration.

All examples

Browse all h402 integration examples available in our repository.
Client.ts
import axios from "axios";
import { config } from "dotenv";
import { createWalletClient, http, publicActions, type Hex } from "viem";
import { withPaymentInterceptor, decodeXPaymentResponse } from "h402-axios";
import { Keypair } from "@solana/web3.js";
import bs58 from "bs58";
import { createKeyPairSignerFromBytes } from "@solana/signers";
import type { TransactionModifyingSigner } from "@solana/signers";
import type { Transaction } from "@solana/transactions";
import { privateKeyToAccount } from "viem/accounts";
import { bsc } from "viem/chains";

config();

const evmPrivateKey = process.env.EVM_PRIVATE_KEY as Hex;
const solanaPrivateKey = process.env.SOLANA_PRIVATE_KEY as Hex;
const baseURL = process.env.RESOURCE_SERVER_URL as string; // e.g. http://localhost:3000
const endpointPath = process.env.ENDPOINT_PATH as string; // e.g. /image

if (!baseURL || !evmPrivateKey || !endpointPath) {
  console.error("Missing required environment variables");
  process.exit(1);
}

// EVM client
const evmAccount = privateKeyToAccount(evmPrivateKey);
const evmClient = createWalletClient({
  account: evmAccount,
  transport: http(),
  chain: bsc,
}).extend(publicActions);

// Solana client
const solanaKeypair = Keypair.fromSecretKey(bs58.decode(solanaPrivateKey));
const solanaClient = {
  publicKey: solanaKeypair.publicKey.toBase58(),
  signTransaction: async <T extends Transaction>(
    transactions: readonly T[],
  ): Promise<readonly T[]> => {
    const signer = await createKeyPairSignerFromBytes(solanaKeypair.secretKey);
    const signatures = await signer.signTransactions(transactions);
    const modifiedTransactions = transactions.map((transaction, index) => {
      const signature = signatures[index];
      if (!signature || Object.keys(signature).length === 0) {
        throw new Error(`Failed to sign transaction at index ${index}`);
      }
      return {
        ...transaction,
        signatures: {
          ...transaction.signatures,
          ...signature,
        },
      } as T;
    });
    return modifiedTransactions;
  },
} satisfies {
  publicKey: string;
  signTransaction: TransactionModifyingSigner["modifyAndSignTransactions"];
};

// Create the API client with payment interceptor
// If multiple clients are provided, the payment interceptor will use the first one that is available according to payment requirements
// You can comment out the evmClient to test the solana client
const api = withPaymentInterceptor(
  axios.create({
    baseURL,
  }),
  {
    evmClient,
    solanaClient,
  },
);

api
  .get(endpointPath)
  .then(response => {
    console.log(response.data);

    const paymentResponse = decodeXPaymentResponse(response.headers["x-payment-response"]);
    console.log(paymentResponse);
  })
  .catch(error => {
    console.error("example axios error", error);
  });

Key Points

  • The resource server remains completely stateless - all necessary information is encoded in the payment header
  • No hidden session state or database required
  • Payment verification is deterministic and based on cryptographic proofs
  • The same pattern works for any HTTP service, from API endpoints to static files
  • Support for multiple payment networks through the same interface
For production deployments, consider:
  • Adding proper error handling
  • Implementing retry logic
  • Setting up monitoring and logging
  • Using a production-grade facilitator (or use ours https://facilitator.bitgpt.xyz/)