Skip to content

artlu99/x402-gateway-bun-lmdb

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

x402 Payment Gateway with Bun and lmdb

A production-ready, self-hosted payment gateway that implements the x402 protocol — HTTP-native micropayments for APIs. Accept stablecoin payments (USDC) per-request with no API keys, no subscriptions, and no intermediaries.

Forked from azep-ninja's x402-gateway-template to run as a microservice: Bun instead of Express; lmdb instead of Upstash Redis; Typescript and full test suite.

Fork this repo, configure your backend, and start accepting crypto micropayments in minutes.

What is x402?

x402 uses the HTTP 402 Payment Required status code to create a machine-readable payment flow:

1. Agent/client calls your API
2. Gateway returns 402 with payment requirements (chain, amount, token)
3. Agent signs a USDC transfer authorization
4. Agent retries with signed payment in header
5. Gateway verifies signature, settles on-chain, proxies to your backend
6. Client gets the API response + payment receipt

No wallets to integrate. No payment pages. Just HTTP headers.

Features

  • Multi-chain support — Accept USDC on Base, Ethereum, Arbitrum, Optimism, Polygon, Avalanche, Unichain, Linea, and Solana out of the box
  • MegaETH support — USDM (18 decimals) via Meridian facilitator
  • Hybrid settlement — Local on-chain settlement via viem + optional external facilitators
  • Solana support — SVM payments via @x402/svm facilitator pattern
  • Replay protection — nonce tracking prevents double-spending
  • Idempotencypayment-identifier extension for safe retries without double-charging
  • Agent discovery/accepted endpoint with full pricing, schemas, and network info
  • x402 well-known/.well-known/x402 discovery document
  • Zero lock-in — Your backend never knows about x402; it just gets authenticated requests
  • Deploy small — Bun microservice with LMDB

Quick Start

# Clone the template
git clone https://github.com/artlu99/x402-gateway-bun-lmdb.git
cd x402-gateway-bun-lmdb

# Install dependencies
bun install

# Configure
cp .env.example .env
# Edit .env with your values (see Configuration section)

# Run locally
bun dev

# Test
curl http://localhost:8080/health
curl http://localhost:8080/accepted

Architecture

Client/Agent request
  → x402 Gateway (this service)
    → Verify payment signature (EIP-712 / SVM)
    → Settle on-chain (USDC transfer)
    → Proxy to your backend API (with internal auth)
    → Return response + payment receipt header
┌─────────────────────────────────────────────────┐
│                  x402 Gateway                    │
│                                                  │
│  ┌──────────┐  ┌──────────┐  ┌───────────────┐ │
│  │ Payment  │→ │ On-chain │→ │ Backend Proxy │ │
│  │ Verify   │  │ Settle   │  │ (your API)    │ │
│  └──────────┘  └──────────┘  └───────────────┘ │
│       │              │                           │
│  ┌──────────┐  ┌──────────┐                     │
│  │  LMDB    │  │  RPC     │                     │
│  │ (nonces) │  │ (chains) │                     │
│  └──────────┘  └──────────┘                     │
└─────────────────────────────────────────────────┘

Configuration

1. Define Your Routes

Edit src/config/routes.ts to define your paid API endpoints:

export const ROUTE_CONFIG = {
  // Each key becomes a route prefix: /v1/{key}/*
  myapi: {
    path: '/v1/myapi/*',
    backendName: 'My API',
    get backendUrl() { return process.env.MY_BACKEND_URL; },
    backendApiKeyEnv: 'MY_BACKEND_API_KEY',
    backendApiKeyHeader: 'x-api-key',       // Header name your backend expects
    get price() { return process.env.MY_PRICE || '$0.01'; },
    get priceAtomic() { return process.env.MY_PRICE_ATOMIC || '10000'; }, // 0.01 USDC in 6-decimal units
    get payTo() { return process.env.MY_PAY_TO_ADDRESS || process.env.PAY_TO_ADDRESS; },
    get payToSol() { return process.env.MY_PAY_TO_ADDRESS_SOL; },
    description: 'Description of your API for agent discovery',
    mimeType: 'application/json',
  },
};

2. Environment Variables

Required

Variable Description
SETTLEMENT_PRIVATE_KEY Private key (0x hex) for the gas-paying settlement wallet
PAY_TO_ADDRESS Default EVM wallet that receives USDC payments
BASE_RPC_URL At least one RPC URL (Base recommended for low fees)

Backend (per route)

Variable Description
MY_BACKEND_URL Your backend API base URL
MY_BACKEND_API_KEY Internal API key for your backend

RPC URLs (only configured chains are advertised)

Variable Chain Required Gas Token
BASE_RPC_URL Base ETH (~$5)
ETHEREUM_RPC_URL Ethereum ETH (~$20-50)
ARBITRUM_RPC_URL Arbitrum ETH (~$5)
OPTIMISM_RPC_URL Optimism ETH (~$5)
POLYGON_RPC_URL Polygon POL (~$2)
AVALANCHE_RPC_URL Avalanche AVAX (~$2)
UNICHAIN_RPC_URL Unichain ETH (~$2)
LINEA_RPC_URL Linea ETH (~$2)
MEGAETH_RPC_URL MegaETH N/A (facilitator pays)
SOLANA_RPC_URL Solana SOL (~$2)

Solana (optional)

Variable Description
SOLANA_FACILITATOR_PRIVATE_KEY Base58 private key for Solana fee payer
MY_PAY_TO_ADDRESS_SOL Solana wallet to receive USDC payments

Optional Overrides

Variable Description
PORT Server port (default: 8080)
MY_PRICE Display price (e.g., "$0.05")
MY_PRICE_ATOMIC Price in token atomic units (e.g., "50000" for $0.05 USDC)

3. Settlement Wallet

The settlement wallet only pays gas to submit transferWithAuthorization on-chain. It never holds or receives stablecoins — payments flow directly from the payer to your payTo address.

Payer → (USDC) → Your payTo wallet     ← receives payment
Settlement wallet → (gas) → on-chain    ← only pays gas fees

Fund it with small amounts of native gas tokens on each chain you enable.

Adding Routes

Basic Route

  1. Add route config in src/config/routes.jt
  2. Register the route in src/index.ts:
// Paid route
app.all('/v1/myapi/{*path}', x402PaymentMiddleware('myapi'), async (req, res) => {
  const subpath = getSubpath(req.params);
  const route = ROUTE_CONFIG.myapi;
  await proxyToBackend({
    req, res,
    targetBase: route.backendUrl,
    targetPath: '/api/' + subpath,
    apiKey: process.env[route.backendApiKeyEnv],
    apiKeyHeader: route.backendApiKeyHeader,
  });
});

// Free route (no middleware)
app.get('/v1/myapi/health', async (req, res) => {
  // Proxy directly without payment
});

Path Aliases

Map user-friendly paths to your backend's actual routes:

const PATH_ALIASES = {
  'analyze': 'internal-analyze-endpoint',
  'report':  'generate-full-report',
};
const resolvedSubpath = PATH_ALIASES[subpath] || subpath;

Adding Chains

New EVM Chain

  1. Verify the chain has native Circle USDC with EIP-3009 support
  2. Add to src/config/routes.ts:
const MY_CHAIN = {
  vm: 'evm',
  caip2: 'eip155:CHAIN_ID',
  chainId: CHAIN_ID,
  rpcEnvVar: 'MY_CHAIN_RPC_URL',
  token: usdc('0xUSDC_CONTRACT_ADDRESS'),
};
  1. Register in ALL_NETWORKS
  2. Add RPC URL to .env
  3. Fund settlement wallet with gas on that chain
  4. Add the viem chain import in src/middleware/x402.js

New EVM Chain with Facilitator

For chains where USDC doesn't support EIP-3009 yet:

const MY_CHAIN = {
  vm: 'evm',
  caip2: 'eip155:CHAIN_ID',
  chainId: CHAIN_ID,
  rpcEnvVar: 'MY_CHAIN_RPC_URL',
  facilitator: {
    url: 'https://facilitator-api.example.com/v1',
    apiKeyEnv: 'FACILITATOR_API_KEY',
    networkName: 'mychain',              // If facilitator uses short names
    facilitatorContract: '0x...',        // Facilitator's contract address
    x402Version: 1,                      // Facilitator's x402 version
  },
  token: {
    address: '0x...',
    name: 'Token Name',
    version: '1',
    decimals: 18,
  },
};

Solana

Solana support uses the @x402/svm facilitator pattern where:

  • The client partially signs a transaction
  • Your facilitator wallet co-signs as fee payer and submits

Requirements:

  • SOLANA_FACILITATOR_PRIVATE_KEY — Base58 private key
  • SOLANA_RPC_URL — Solana RPC endpoint
  • *_PAY_TO_ADDRESS_SOL — Solana wallet per route

Deployment

Ask your LLM about running the Bun binary as a service or with pm2

API Endpoints

Method Path Auth Description
GET /health Free Gateway health + backend status
GET /accepted Free Agent discovery — pricing, networks, schemas
GET /.well-known/x402 Free x402 discovery document
ALL /v1/{route}/* Paid Your protected API routes

Agent Discovery

Agents call GET /accepted to discover your API before making paid requests:

curl https://your-gateway.com/accepted

Returns supported networks, pricing, and input/output schemas for each route. Compatible with Bazaar agent discovery protocol.

Compatible Agent Wallets

Any wallet that supports the x402 protocol works:

Wallet Description
@x402/fetch Official SDK — drop-in fetch replacement
OpenClaw / Lobster Agent framework with built-in x402
AgentWallet (frames.ag) x402 endpoint for agents
Vincent MPC wallet with policy controls
Sponge x402_fetch one-liner integration

Client Example

import { x402Fetch } from '@x402/fetch';

const res = await x402Fetch('https://your-gateway.com/v1/myapi/endpoint', {
  method: 'POST',
  body: JSON.stringify({ key: 'value' }),
  wallet  // any x402-compatible wallet
});

const data = await res.json();

Project Structure

├── src/
│   ├── index.ts              # app, route registration
│   ├── proxy.ts              # Backend proxy (injects internal auth)
│   ├── middleware/
│   │   └── x402.ts           # Payment verification + settlement
│   ├── config/
│   │   └── routes.ts         # Route definitions + network registry
│   └── utils/
│       ├── cors.ts           # cors headers
│       └── store.ts          # Nonce tracking + idempotency cache
├── public/
│   └── index.html            # Landing page (optional)
├── .env.example
└── package.json

Security Considerations

  • Settlement key — Store in a secrets manager (GCP Secret Manager, AWS Secrets Manager, etc.), never in env vars or code
  • Settlement wallet — Only holds gas tokens, never stablecoins. If compromised, attacker can only drain small gas balances
  • Pay-to address — This is YOUR wallet. Payments go directly from payer to you on-chain. The gateway never custodies funds
  • LMDB — Used for nonce tracking. If store is unavailable, gateway fails open on reads (settlement still checks on-chain) and fails closed on writes (rejects payment to be safe)
  • Backend API keys — The gateway injects these server-side. Your backend never sees x402 traffic directly

License

MIT

Links

About

self hosted x402 payment gateway, fork of azep-ninja's original for bun + lmdb

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages

  • TypeScript 67.6%
  • JavaScript 31.2%
  • HTML 1.2%