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.
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
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
Signature: createFacilitatorHandler(network, rpc, feePayerKeypair, mint, config?)
The optional config parameter accepts:
| Option | Type | Default | Description |
|---|
maxRetries | number | 30 | Maximum retries for transaction submission. |
retryDelayMs | number | 1000 | Delay between retries in milliseconds. |
maxPriorityFee | number | 100000 | Maximum priority fee in lamports. |
maxTransactionAge | number | 150 | Maximum age of a transaction (in slots) before it expires. |
features.enableSettlementAccounts | boolean | — | Enable settlement account support. |
features.enableDuplicateCheck | boolean | — | Prevent duplicate transaction submission. |
hooks | FacilitatorHooks[] | — | Lifecycle hooks for afterVerify and afterSettle. |
EVM handler
Signature: createFacilitatorHandler(chain, privateKey, assetNameOrInfo, opts?)
| Parameter | Type | Description |
|---|
chain | ChainInfoWithRPC | Object with id, name, and rpcUrls: { default: { http: [url] } }. |
privateKey | string | Hex-encoded private key for transaction submission. |
assetNameOrInfo | AssetNameOrContractInfo | Asset name (e.g., "USDC") or contract info object. |
opts | object | Optional. network (KnownX402Network) and transport (viem Transport). |
Multi-chain configuration
Wire handlers for each chain together and pass them to createFacilitatorRoutes:
Each handler implements the FacilitatorHandler interface:
Handlers return null for payment schemes they do not support, allowing the facilitator to route to the correct handler.
The optional capabilities field declares the networks and assets a handler supports, and the separate optional schemes field declares the x402 schemes the handler can settle. The middleware uses both to route ResourcePricing entries to the right handler without calling it. While optional on the interface, both are effectively required for in-process usage — the middleware skips handlers that don’t declare them on its dispatch path.
Using handlers in-process
The same handlers you pass to createFacilitatorRoutes can be passed directly to the middleware via x402Handlers, eliminating the need for a separate facilitator process. See the Middleware Overview for details.
If you have an existing remote facilitator and want to use it through the in-process interface, wrap it with createHTTPFacilitatorHandler:
Lifecycle hooks
Hooks let you run custom logic after the facilitator verifies or settles a payment. Use them for logging, analytics, webhooks, or downstream side effects.
Pass an array of hook objects to the handler’s hooks option. Each object can define afterVerify, afterSettle, or both:
Hook parameters
Both hooks receive a context object:
| Parameter | Available in | Description |
|---|
requirements | afterVerify, afterSettle | The matched payment requirements. |
payment | afterVerify, afterSettle | The decoded payment payload from the client. |
logger | afterVerify, afterSettle | A structured logger instance from @faremeter/logs. |
response | afterVerify, afterSettle | The operation result. In afterVerify: { isValid, payer?, invalidReason? }. In afterSettle: { success, transaction, network, payer?, errorReason?, extensions? }. |
Multiple hooks
You can pass multiple hook objects. They run in order:
Hook behavior
Hooks can optionally replace the response by returning a new value. If a hook returns a non-undefined value, that value becomes the response passed to subsequent hooks and returned to the client.
Hook errors are not caught by the facilitator — a throwing hook will cause the request to fail with a 500 error, even though the payment may have already been verified or settled on-chain. Ensure your hooks handle their own errors internally if you want settlement to proceed regardless.
Settlement accounts
The enableSettlementAccounts feature (Solana only) enables an intermediate settlement account flow. When enabled, payments are first settled into a facilitator-controlled settlement account rather than directly to the merchant’s payTo address. This is useful when the facilitator needs to hold funds temporarily before distributing them — for example, to support refunds, escrow patterns, or fee splitting.
Settlement accounts are an advanced feature. For most integrations, leave this disabled and payments will settle directly to the payTo address.
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:
Facilitator endpoints
| Endpoint | Method | Purpose |
|---|
/accepts | POST | Enriches payment requirements with facilitator-specific fields. |
/verify | POST | Verifies a payment without settling. |
/settle | POST | Verifies and settles a payment on-chain. Returns transaction hash. |
/supported | GET | Lists supported schemes, networks, and assets. |
Further reading