Skip to main content
A facilitator verifies and settles payments on-chain. By default, faremeter uses the Corbits facilitator (https://facilitator.corbits.dev). If you need to run your own, use @faremeter/facilitator.
pnpm add @faremeter/facilitator hono

When to run your own

  • You need to support custom payment schemes not available on the Corbits facilitator.
  • You want full control over payment verification and settlement.
  • You are running in an environment that cannot reach the Corbits facilitator.
For most development and production use cases, the Corbits facilitator is sufficient.
adaptSettleResponseV2ToV1Legacy is deprecated. Use adaptSettleResponseV2ToV1 for spec-compliant v1 output. See the Facilitator API reference for details on version adapters.

Basic setup

import { Hono } from "hono"
import { createFacilitatorRoutes } from "@faremeter/facilitator"

const handlers = [
  // Chain-specific handlers
]

const app = new Hono()
app.route("/", createFacilitatorRoutes({ handlers }))

export { app }
createFacilitatorRoutes returns a Hono router with the standard facilitator endpoints: /accepts, /verify, /settle, and /supported.

Creating handlers

Each chain has its own createFacilitatorHandler function that returns a FacilitatorHandler. Install the payment package for the chain you want to support.

Solana handler

pnpm add @faremeter/payment-solana @solana/kit @solana/web3.js @solana-program/node-helpers
import { createFacilitatorHandler } from "@faremeter/payment-solana/exact"
import { createSolanaRpc } from "@solana/kit"
import { getKeypairFromFile } from "@solana-program/node-helpers"
import { PublicKey } from "@solana/web3.js"

const rpc = createSolanaRpc(process.env.SOLANA_RPC_URL!)
const feePayerKeypair = await getKeypairFromFile(process.env.SOLANA_PAYER_KEYPAIR!)
const mint = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") // USDC

const solanaHandler = await createFacilitatorHandler(
  "devnet",
  rpc,
  feePayerKeypair,
  mint,
)
Signature: createFacilitatorHandler(network, rpc, feePayerKeypair, mint, config?) The optional config parameter accepts:
OptionTypeDefaultDescription
maxRetriesnumber30Maximum retries for transaction submission.
retryDelayMsnumber1000Delay between retries in milliseconds.
maxPriorityFeenumber100000Maximum priority fee in lamports.
maxTransactionAgenumber150Maximum age of a transaction (in slots) before it expires.
features.enableSettlementAccountsbooleanEnable settlement account support.
features.enableDuplicateCheckbooleanPrevent duplicate transaction submission.
hooksFacilitatorHooks[]Lifecycle hooks for afterVerify and afterSettle.
const solanaHandler = await createFacilitatorHandler(
  "devnet",
  rpc,
  feePayerKeypair,
  mint,
  {
    maxRetries: 30,
    retryDelayMs: 1000,
    features: { enableDuplicateCheck: true },
    hooks: [
      {
        afterSettle: async ({ response, requirements, payment, logger }) => {
          logger.info("Settlement complete", { transaction: response.transaction })
        },
      },
    ],
  },
)

EVM handler

pnpm add @faremeter/payment-evm viem
import { createFacilitatorHandler } from "@faremeter/payment-evm/exact"

const evmHandler = await createFacilitatorHandler(
  { id: 84532, name: "base-sepolia", rpcUrls: { default: { http: [process.env.EVM_RPC_URL!] } } },
  process.env.EVM_PRIVATE_KEY!,
  "USDC",
)
Signature: createFacilitatorHandler(chain, privateKey, assetNameOrInfo, opts?)
ParameterTypeDescription
chainChainInfoWithRPCObject with id, name, and rpcUrls: { default: { http: [url] } }.
privateKeystringHex-encoded private key for transaction submission.
assetNameOrInfoAssetNameOrContractInfoAsset name (e.g., "USDC") or contract info object.
optsobjectOptional. network (KnownX402Network) and transport (viem Transport).

Multi-chain configuration

Wire handlers for each chain together and pass them to createFacilitatorRoutes:
import { Hono } from "hono"
import { createFacilitatorRoutes } from "@faremeter/facilitator"
import { createFacilitatorHandler as createSolanaHandler } from "@faremeter/payment-solana/exact"
import { createFacilitatorHandler as createEvmHandler } from "@faremeter/payment-evm/exact"
import { createSolanaRpc } from "@solana/kit"
import { getKeypairFromFile } from "@solana-program/node-helpers"
import { PublicKey } from "@solana/web3.js"

// Solana
const rpc = createSolanaRpc(process.env.SOLANA_RPC_URL!)
const feePayerKeypair = await getKeypairFromFile(process.env.SOLANA_PAYER_KEYPAIR!)
const solanaMint = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")

// EVM
const chain = { id: 84532, name: "base-sepolia", rpcUrls: { default: { http: [process.env.EVM_RPC_URL!] } } }

const handlers = [
  await createSolanaHandler("devnet", rpc, feePayerKeypair, solanaMint),
  await createEvmHandler(chain, process.env.EVM_PRIVATE_KEY!, "USDC"),
]

const app = new Hono()
app.route("/", createFacilitatorRoutes({ handlers }))

export { app }
Each handler implements the FacilitatorHandler interface:
import type { FacilitatorHandler, GetRequirementsArgs } from "@faremeter/types/facilitator"

interface FacilitatorHandler {
  getSupported?: () => Promise<x402SupportedKind>[]
  getRequirements: (args: GetRequirementsArgs) => Promise<x402PaymentRequirements[]>
  handleVerify?: (requirements, payment) => Promise<x402VerifyResponse | null>
  handleSettle: (requirements, payment) => Promise<x402SettleResponse | null>
}
Handlers return null for payment schemes they do not support, allowing the facilitator to route to the correct handler.

Environment configuration

Facilitator handlers typically need:
  • RPC endpoint URLs for each chain
  • Private keys for transaction submission (the facilitator submits transactions on behalf of clients)
  • Token contract addresses
Configure these via environment variables:
SOLANA_RPC_URL=https://api.devnet.solana.com
SOLANA_PAYER_KEYPAIR=~/.config/solana/facilitator.json
EVM_RPC_URL=https://sepolia.base.org
EVM_PRIVATE_KEY=0x...

Facilitator endpoints

EndpointMethodPurpose
/acceptsPOSTEnriches payment requirements with facilitator-specific fields.
/verifyPOSTVerifies a payment without settling.
/settlePOSTVerifies and settles a payment on-chain. Returns transaction hash.
/supportedGETLists supported schemes, networks, and assets.

Further reading