x402 Facilitator
Overview
x402 Facilitator is a plugin for OpenZeppelin Relayer that enables payment verification and settlement for x402 payments on Stellar. The plugin implements the X402 payment facilitation protocol, allowing applications to accept payments in Stellar assets (like USDC) for API access or content.
The plugin features:
- Payment verification: Validates payment payloads against payment requirements
- Transaction settlement: Submits verified payments on-chain via the relayer or an optional channel service
- Auth entry validation: Verifies authorization entries are properly signed
- Multi-network support: Configure multiple Stellar networks and assets
- Channel service integration: Optional integration with the Channels plugin for high-throughput settlement
Prerequisites
- Node.js >= 18
- pnpm >= 10
- OpenZeppelin Relayer (installed and configured)
- Stellar relayer account (for transaction submission)
Example Setup
For a complete working example with Docker Compose, refer to the x402 Facilitator plugin example in the OpenZeppelin Relayer repository:
- Location: examples/x402-facilitator-plugin
- Documentation: README.md
Installation
x402 Facilitator can be added to any OpenZeppelin Relayer installation.
Resources:
- npm package: @openzeppelin/relayer-plugin-x402-facilitator
- GitHub repository: https://github.com/OpenZeppelin/relayer-plugin-x402-facilitator
- Example setup: x402-facilitator-plugin
Install from npm
# From the root of your Relayer repository
cd plugins
pnpm add @openzeppelin/relayer-plugin-x402-facilitatorCreate the plugin wrapper
Inside your Relayer, create a directory for the plugin and expose its handler:
mkdir -p plugins/x402-facilitatorCreate plugins/x402-facilitator/index.ts:
export { handler } from "@openzeppelin/relayer-plugin-x402-facilitator";Configuration
Plugin Registration
Register the plugin in your config/config.json file:
{
"plugins": [
{
"id": "x402-facilitator",
"path": "x402-facilitator/index.ts",
"timeout": 30,
"emit_logs": false,
"emit_traces": false,
"forward_logs": true,
"raw_response": true,
"allow_get_invocation": true,
"config": {
"networks": [
{
"network": "stellar:testnet",
"type": "stellar",
"relayer_id": "stellar-example",
"assets": [
"CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA"
]
}
]
}
}
]
}Note on path: this is the plugin entrypoint path as seen by the running Relayer process.
- If you run Relayer locally, ensure the file exists at that path under your
plugins/directory. - If you run Relayer in Docker, ensure the file exists at that path inside the container (for example by mounting your plugin folder into
/app/plugins/x402-facilitatorin the container).
Configuration Options:
id: Unique identifier for the pluginpath: Path to the plugin file (relative toplugins/directory)timeout: Maximum execution time in seconds (default: 30)emit_logs: Whether to include plugin logs in API responses (default: false)emit_traces: Whether to include plugin traces in API responses (default: false)raw_response: Whether to return raw plugin response without ApiResponse wrapper (default: false, recommended for x402 compatibility)config.networks: Array of network configurationsnetwork: Network identifier (e.g., "stellar:testnet", "stellar:pubnet")type: Network type (must be "stellar")relayer_id: ID of the relayer to use for this networkassets: Array of supported asset contract addresseschannel_service_api_url(optional): Channel service API URL for settlementchannel_service_api_key(optional): Channel service API key
Relayer Configuration
The plugin requires at least one Stellar relayer configured for transaction submission:
{
"relayers": [
{
"id": "stellar-example",
"name": "Stellar Example",
"network": "testnet",
"paused": false,
"network_type": "stellar",
"signer_id": "local-signer",
"policies": {
"fee_payment_strategy": "relayer",
"min_balance": 0
}
}
],
"signers": [
{
"id": "local-signer",
"type": "local",
"config": {
"path": "config/keys/local-signer.json",
"passphrase": {
"type": "env",
"value": "KEYSTORE_PASSPHRASE"
}
}
}
]
}Important configuration notes:
- Relayer account: Must be configured with
network_type: "stellar"and a valid signer - Network: Use
testnetfor testing ormainnetfor production - Signers: Each relayer references a signer by
signer_id; signers are defined separately with keystore paths - Keystore files: Create keystore files for each account; see the OpenZeppelin Relayer documentation for key management
- Assets: Configure the contract addresses of assets you want to accept payments in (for example, a USDC contract address)
Channel Service Integration (Optional)
For high-throughput settlement, you can configure the plugin to use the Channels plugin:
{
"plugins": [
{
"id": "x402-facilitator",
"path": "x402-facilitator/index.ts",
"timeout": 30,
"raw_response": true,
"config": {
"networks": [
{
"network": "stellar:testnet",
"type": "stellar",
"relayer_id": "stellar-example",
"assets": [
"CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA"
],
"channel_service_api_url": "http://localhost:8080/api/v1/plugins/channels-plugin/call",
"channel_service_api_key": "YOUR_CHANNELS_API_KEY"
}
]
}
}
]
}When channel_service_api_url and channel_service_api_key are configured, the plugin will use the Channels service for settlement instead of the relayer API, enabling parallel transaction processing.
API Usage
The x402 Facilitator plugin implements the X402 v2 specification API, providing three main endpoints:
/verify: Verifies a payment payload against payment requirements/settle: Settles a verified payment by submitting the transaction on-chain/supported: Discovery of supported payment kinds
Invocation
The plugin is invoked via the standard Relayer plugin endpoint with route-based routing:
POST /api/v1/plugins/{plugin-id}/call/{route}Where {route} is one of:
verify- Payment verificationsettle- Payment settlementsupported- Supported payment kinds
Integration with x402 Ecosystem
The plugin implements the API defined by the x402 v2 spec, making it compatible with any packages in the x402 ecosystem. For more information on available packages, see https://github.com/coinbase/x402.
x402-express Example
To use OpenZeppelin Relayer and its x402-facilitator plugin with x402-express (and similar packages), point the facilitator to your Relayer plugin URL and pass the Relayer API key via createAuthHeaders:
.env
STELLAR_ADDRESS=
FACILITATOR_URL=http://localhost:8080/api/v1/plugins/x402-facilitator/callimport { config } from "dotenv";
import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactStellarScheme } from "@x402/stellar/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
config();
const stellarAddress = process.env.STELLAR_ADDRESS as string | undefined;
// Validate stellar address is provided
if (!stellarAddress) {
console.error("❌ STELLAR_ADDRESS is required");
process.exit(1);
}
const facilitatorUrl = process.env.FACILITATOR_URL;
if (!facilitatorUrl) {
console.error("❌ FACILITATOR_URL environment variable is required");
process.exit(1);
}
const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl, createAuthHeaders: async () => ({
// Use your Relayer API key for the plugin
verify: { Authorization: "Bearer RELAYER_API_KEY" },
settle: { Authorization: "Bearer RELAYER_API_KEY" },
supported: { Authorization: "Bearer RELAYER_API_KEY" },
})});
const app = express();
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: [
{
scheme: "exact",
price: "$0.001",
network: "stellar:testnet",
payTo: stellarAddress,
},
],
description: "Weather data",
mimeType: "application/json",
},
},
new x402ResourceServer(facilitatorClient)
.register("stellar:testnet", new ExactStellarScheme())
),
);
app.get("/weather", (req, res) => {
res.send({
report: {
weather: "sunny",
temperature: 70,
},
});
});
app.listen(4021, () => {
console.log(`Server listening at http://localhost:${4021}`);
});Server Setup (Express)
Set up an Express server with x402 payment middleware. For detailed instructions, see the x402 Express server guide.
Client Setup (Fetch)
Make requests to x402-protected endpoints using the fetch client. For detailed instructions, see the x402 Fetch client guide.
How It Works
Verification Flow
- Protocol Validation: Validates X402 version, scheme, and network
- Transaction Parsing: Decodes transaction XDR and extracts operation details
- Operation Validation: Ensures it's an
invokeHostFunctioncallingtransfer - Amount & Recipient Validation: Validates transfer amount and recipient match requirements
- Auth Entry Validation: Verifies auth entries are present and signed by the payer
- Envelope Signature Check: Ensures transaction envelope has no signatures (for relayer rebuild)
- Simulation: Simulates transaction to ensure it will succeed
- Security Checks: Validates transaction source is not the relayer
Settlement Flow
- Verification: Verifies payment before settlement
- Operation Extraction: Extracts operation details and signed auth entries from transaction
- Submission:
- If channel service configured: Submits via channel service API with
funcandauthXDRs - Otherwise: Submits via relayer API with operations format
- If channel service configured: Submits via channel service API with
- Confirmation: Waits for transaction confirmation
Channel Service vs Relayer API
The plugin supports two settlement methods:
- Relayer API (default): Uses the relayer's
sendTransactionwith operations format - Channel Service API (optional): Uses external channel service when
channel_service_api_urlandchannel_service_api_keyare configured
Additional Resources
- OpenZeppelin Relayer Documentation: https://docs.openzeppelin.com/relayer
- Stellar SDK Documentation: https://stellar.github.io/js-stellar-sdk/
- Coinbase x402 GitHub Repository: https://github.com/coinbase/x402
- x402 Facilitator Plugin Repository: https://github.com/OpenZeppelin/relayer-plugin-x402-facilitator