Skip to content

Repository files navigation

@oviato/x402-facilitator-hono

Mountable Hono sub-app implementing the x402 facilitator protocol.

npm version License

What is this?

In the x402 protocol, a facilitator is the service that verifies payment signatures and settles transactions on-chain. Resource servers delegate payment verification and settlement to a facilitator instead of interacting with blockchains directly.

This package wraps the official Coinbase x402 packages (@x402/core, @x402/evm, @x402/svm) into a Hono sub-app with three endpoints. Mount it on any Hono app and you have a working facilitator — on Cloudflare Workers, Node.js, Bun, Deno, or any other Hono-supported runtime.

Install

pnpm add @oviato/x402-facilitator-hono

Quick Start

import { Hono } from "hono";
import { x402Facilitator } from "@oviato/x402-facilitator-hono";

const app = new Hono();

app.route("/facilitator", await x402Facilitator({
  evm: {
    privateKey: env.EVM_PRIVATE_KEY,
    rpcUrls: {
      "eip155:84532": env.RPC_URL_BASE_SEPOLIA,
    },
  },
}));

export default app;

Three endpoints are now live: GET /facilitator/supported, POST /facilitator/verify, POST /facilitator/settle.

Configuration

interface FacilitatorConfig {
  evm?: {
    privateKey: string;              // Hex, 0x-prefixed. Gas wallet.
    rpcUrls: Record<string, string>; // CAIP-2 network ID -> RPC URL
  };
  svm?: {
    privateKey: string;              // Base58. Fee payer wallet.
    rpcUrls: Record<string, string>; // CAIP-2 network ID -> RPC URL
  };
  nonceStore?: NonceStore;           // Optional. Defaults to in-memory.
}

At least one of evm or svm must be provided. Only configured chains appear in /supported.

Network identifiers

Use CAIP-2 identifiers:

Chain CAIP-2 ID
Base Sepolia eip155:84532
Base eip155:8453
Ethereum eip155:1
Solana Devnet solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1
Solana solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp

With EVM + Solana

app.route("/facilitator", await x402Facilitator({
  evm: {
    privateKey: env.EVM_PRIVATE_KEY,
    rpcUrls: {
      "eip155:84532": env.RPC_URL_BASE_SEPOLIA,
      "eip155:8453": env.RPC_URL_BASE,
    },
  },
  svm: {
    privateKey: env.SVM_PRIVATE_KEY,
    rpcUrls: {
      "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": env.RPC_URL_SOLANA_DEVNET,
    },
  },
}));

Nonce Stores

The facilitator uses a nonce store for replay protection. Two implementations are included:

Cloudflare KV (distributed deployments)

import { x402Facilitator, kvNonceStore } from "@oviato/x402-facilitator-hono";

app.route("/facilitator", await x402Facilitator({
  evm: { ... },
  nonceStore: kvNonceStore(env.NONCE_KV),
}));

In-memory (single-instance deployments)

import { x402Facilitator, memoryNonceStore } from "@oviato/x402-facilitator-hono";

app.route("/facilitator", await x402Facilitator({
  evm: { ... },
  nonceStore: memoryNonceStore(), // this is the default
}));

Custom implementation

Implement the NonceStore interface for Redis, D1, DynamoDB, etc.:

interface NonceStore {
  has(nonce: string): Promise<boolean>;
  set(nonce: string, ttlSeconds?: number): Promise<void>;
}

Adding Middleware

This package has zero opinions on auth, rate limiting, logging, or CORS. Add whatever middleware you need before the mount:

const app = new Hono();

app.use("/facilitator/*", rateLimiter({ max: 100 }));
app.use("/facilitator/*", apiKeyAuth({ header: "X-API-Key" }));

app.route("/facilitator", await x402Facilitator({ ... }));

Connecting Resource Servers

Resource servers using @x402/hono or @x402/express point their facilitatorUrl at this facilitator:

// On the resource server
import { paymentMiddleware } from "@x402/hono";

app.use("/api/*", paymentMiddleware({
  facilitatorUrl: "https://your-facilitator.example.com/facilitator",
  // ...
}));

API Reference

GET /supported

Returns which networks and schemes this facilitator supports.

Response:

{
  "kinds": [
    { "x402Version": 2, "scheme": "exact", "network": "eip155:84532" }
  ],
  "extensions": [],
  "signers": {
    "eip155:*": ["0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf"]
  }
}

POST /verify

Verifies a payment payload against payment requirements. Checks nonce replay protection.

Request body: Standard x402 verify request ({ paymentPayload, paymentRequirements })

Response: Standard x402 verify response ({ isValid, invalidReason?, payer? })

POST /settle

Settles a payment on-chain. Stores the nonce after successful settlement.

Request body: Standard x402 settle request ({ paymentPayload, paymentRequirements })

Response: Standard x402 settle response ({ success, transaction, network, payer? })

Examples

  • Cloudflare Worker — Minimal wrangler project with KV nonce store
  • Node.js — Minimal Node.js server with @hono/node-server

Gas Costs

The facilitator's private key is used as a gas wallet to broadcast settlement transactions. On Base, gas costs are approximately $0.001 per transaction. Fund the gas wallet with a small amount of ETH (or SOL for Solana).

The gas wallet only pays gas fees. It does not hold or custody user funds. Payment tokens (e.g., USDC) are transferred directly from payer to payee via signed authorizations (EIP-3009 / Permit2 on EVM, pre-signed transactions on Solana).

Security

Private key handling

  • Never commit private keys to source control
  • Use environment variables or secret managers (Cloudflare Secrets, AWS Secrets Manager, etc.)
  • The private key is used solely to submit settlement transactions — it cannot access or redirect user funds

What the gas wallet can do

  • Submit pre-authorized token transfers on behalf of payers
  • Pay gas fees for settlement transactions

What the gas wallet cannot do

  • Transfer tokens without a valid signed authorization from the payer
  • Change the recipient of a payment
  • Modify the payment amount

Nonce replay protection

Every payment nonce is checked against the NonceStore before verification. After successful settlement, the nonce is stored to prevent the same payment from being settled twice.

About Oviato

This package is maintained by Oviato — passkey-native, chain-agnostic wallet infrastructure. Oviato lets developers spin up self-custody embedded wallets in seconds using Face ID, Touch ID, or passkeys instead of seed phrases. One SDK, one identity (Ovi.id), across Bitcoin, EVM, Solana, and more.

Backed by Draper VC, Boost VC, CoinGecko, and Etherscan. Try the live demo.

Contributing

See CONTRIBUTING.md.

Related

License

Apache 2.0

About

A mountable Hono sub-app for self-hosting an x402 payment facilitator. Wraps official Coinbase x402 packages. Deploy to Cloudflare Workers, Node, Bun or Deno.

Resources

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages