x402-facilitator in golang
x402-facilitator is an early-stage Go project in the AI payments / x402 ecosystem. It currently has 0 GitHub stars and 0 forks.
A production-ready x402 facilitator in Go — implements the x402 HTTP payment protocol with multi-chain, multi-ERC20 token support.
x402 is a protocol for machine-native payments over HTTP. When a client requests a resource that costs money, the server responds with 402 Payment Required and a description of what it accepts. The client creates a cryptographically signed payment authorization and retries the request with it in a header. The facilitator is a third-party service that:
transferWithAuthorization — used by USDC, EURC, and other Circle tokensconfig.yaml| Method | Path | Description |
|---|---|---|
POST |
/verify |
Verify a payment without settling |
POST |
/settle |
Verify and settle a payment on-chain |
GET |
/supported |
List supported chains/tokens |
GET |
/health |
Liveness probe |
// Request
{
"x402Version": 2,
"paymentPayload": {
"x402Version": 2,
"payload": {
"signature": "0x...",
"authorization": {
"from": "0xPAYER",
"to": "0xRECIPIENT",
"value": "1000000",
"validAfter": "1700000000",
"validBefore": "1700000060",
"nonce": "0xRANDOM32BYTES"
}
},
"accepted": {
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "1000000",
"payTo": "0xRECIPIENT",
"maxTimeoutSeconds": 60,
"extra": {
"name": "USD Coin",
"version": "2",
"assetTransferMethod": "eip3009"
}
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "1000000",
"payTo": "0xRECIPIENT",
"maxTimeoutSeconds": 60
}
}
// Response (valid)
{
"isValid": true,
"payer": "0xPAYER"
}
// Response (invalid)
{
"isValid": false,
"invalidReason": "insufficient_balance",
"invalidMessage": "have 500000, need 1000000"
}
Same request shape as /verify, including the top-level x402Version field. On success:
{
"success": true,
"payer": "0xPAYER",
"transaction": "0xTX_HASH",
"network": "eip155:8453"
}
{
"kinds": [
{
"x402Version": 2,
"scheme": "exact",
"network": "eip155:8453",
"extra": {
"assetTransferMethod": "eip3009",
"name": "USD Coin",
"version": "2"
}
}
],
"signers": {
"eip155:*": ["0xFACILITATOR_ADDRESS"]
}
}
Edit config.yaml. Every field in extra of paymentRequirements is driven by your config.
server:
host: "0.0.0.0"
port: 8080
facilitator:
# Set via env var instead of storing here:
private_key: "${FACILITATOR_PRIVATE_KEY}"
chains:
- network: "eip155:8453" # CAIP-2 identifier
rpc_url: "https://mainnet.base.org"
tokens:
- name: "USD Coin" # EIP-712 domain name
symbol: "USDC"
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
decimals: 6
eip712_version: "2" # EIP-712 domain version
transfer_method: "eip3009" # or "permit2"
- name: "Wrapped Ether"
symbol: "WETH"
address: "0x4200000000000000000000000000000000000006"
decimals: 18
eip712_version: "1"
transfer_method: "permit2"
Simply add an entry under the relevant chain's tokens array. The only requirements:
transfer_method must be "eip3009" (token has transferWithAuthorization) or "permit2" (any ERC-20 with Permit2 approval).eip712_version must match the token contract's EIP-712 domain version — check the token's contract or Circle docs. USDC v2 = "2".name must exactly match the token's EIP-712 domain name (e.g. "USD Coin" not "USDC").Add a new entry to chains:
- network: "eip155:10" # Optimism
rpc_url: "https://mainnet.optimism.io"
tokens:
- name: "USD Coin"
symbol: "USDC"
address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"
decimals: 6
eip712_version: "2"
transfer_method: "eip3009"
The facilitator wallet must hold native gas tokens (ETH, MATIC, etc.) on each chain where settlement is needed.
# Install dependencies
go mod tidy
# Build
go build -o facilitator ./cmd/facilitator
# Run with config
FACILITATOR_PRIVATE_KEY=0xYOUR_PRIVATE_KEY ./facilitator -config config.yaml
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod tidy && go build -o facilitator ./cmd/facilitator
FROM alpine:3.19
COPY --from=builder /app/facilitator /facilitator
COPY config.yaml /config.yaml
ENV FACILITATOR_PRIVATE_KEY=""
EXPOSE 8080
ENTRYPOINT ["/facilitator", "-config", "/config.yaml"]
If you don't set FACILITATOR_PRIVATE_KEY, the /verify endpoint works fully but /settle returns:
{
"success": false,
"errorReason": "no_signer_configured"
}
Client Resource Server Facilitator
│ │ │
│──── GET /resource ─────────▶│ │
│ │ │
│◀─── 402 Payment Required ───│ │
│ PAYMENT-REQUIRED: b64 │ │
│ │ │
│ [client signs EIP-712 │ │
│ TransferWithAuthorization]│ │
│ │ │
│──── GET /resource ─────────▶│ │
│ PAYMENT-SIGNATURE: b64 │ │
│ │──── POST /verify ────────▶│
│ │◀─── {isValid:true} ───────│
│ │──── POST /settle ────────▶│
│ │ │──▶ on-chain tx
│ │◀─── {success:true, tx} ───│
│◀─── 200 OK ─────────────────│ │
│ PAYMENT-RESPONSE: b64 │ │
For each incoming payment the facilitator checks (in order):
x402Version == 2 and scheme == "exact"authorization.value == requirements.amount (exact match)authorization.to == requirements.payTo (case-insensitive)validAfter <= now (not used before window)now + 6s < validBefore (not expired; 6s buffer for tx landing)authorization.frombalanceOf(from) >= amount (on-chain RPC call)Tokens without native EIP-3009 support use Uniswap Permit2. The user:
0x000000000022D473030F116dFC393fb4147B33FF) to spend their tokens.PermitTransferFrom message.The facilitator verifies the signature and checks both the ERC-20 allowance to Permit2 and the user's balance.
Note: Permit2 settlement (
/settle) is not yet implemented — the signed authorization must be submitted via thex402ExactPermit2Proxycontract. Verify works fully.
| Code | Meaning |
|---|---|
invalid_payload |
Malformed JSON payload |
unsupported_version |
x402Version ≠ 2 |
unsupported_scheme |
scheme ≠ "exact" |
no_facilitator_for_network |
Chain not in config |
unsupported_asset |
Token not in config |
invalid_signature |
Signature verification failed |
amount_mismatch |
Payment amount doesn't match requirement |
recipient_mismatch |
authorization.to ≠ requirements.payTo |
payment_expired |
validBefore has passed |
payment_not_yet_valid |
validAfter is in the future |
insufficient_balance |
Payer lacks token balance |
no_signer_configured |
Settlement called without private key |
transaction_failed |
On-chain tx reverted or failed to send |
Your AI trading terminal assistant for US stocks, commodities, forex, and crypto.
Golang SDK for A2A Protocol
Publishes localhost services to the agentic web through self-hostable, trustless relays with x402 payments.
Building blocks for Agentic payments (x402, MPP, AP2) for TypeScript, Rust, Go, Python, Ruby, PHP, Lua, Kotlin and Swift.
Building blocks for Agentic payments (x402, MPP, AP2) for TypeScript, Rust, Go, Python, Ruby, PHP, Lua, Kotlin and Swift.
Go implementation of the x402 payment protocol