15 min
Building Your Own Facilitator
Learning Objectives
- Understand the facilitator interface
- Learn the implementation steps
- Recognize key security considerations
- Know how to integrate custom facilitators
Facilitator Interface
A facilitator must implement three core methods:
1.acceptPayment() - Verify and accept payment requests
2.settlePayment() - Submit transaction on-chain
3.getPaymentStatus() - Check payment status
typescript
interface Facilitator {
// Accept payment request from client
acceptPayment(payment: SignedPayment): Promise<PaymentAcceptance>;
// Settle payment on-chain
settlePayment(paymentId: string): Promise<SettlementProof>;
// Check payment status
getPaymentStatus(paymentId: string): Promise<PaymentStatus>;
}
interface SignedPayment {
from: string; // Payer address
to: string; // Receiver address
amount: string; // Amount in USDC
network: string; // 'base'
signature: string; // Signed by payer
}typescript
class CustomFacilitator implements Facilitator {
async acceptPayment(payment: SignedPayment): Promise<PaymentAcceptance> {
// 1. Verify signature
const isValid = await this.verifySignature(payment);
if (!isValid) {
return { accepted: false, reason: 'Invalid signature' };
}
// 2. Check payer has sufficient funds (optional)
const balance = await this.getBalance(payment.from);
if (balance < parseFloat(payment.amount)) {
return { accepted: false, reason: 'Insufficient funds' };
}
// 3. Store payment request
const paymentId = await this.storePayment(payment);
return { accepted: true, paymentId };
}
async settlePayment(paymentId: string): Promise<SettlementProof> {
// 1. Retrieve payment
const payment = await this.getPayment(paymentId);
// 2. Submit to blockchain
const tx = await this.submitTransaction(payment);
// 3. Wait for confirmation
await this.waitForConfirmation(tx.hash);
return {
success: true,
transaction: tx.hash,
network: payment.network,
payer: payment.from,
};
}
}Key Considerations
When building a custom facilitator:
•Security: Verify all signatures, never trust client input
•Gas fees: Decide who pays (facilitator, payer, receiver?)
•Retries: Handle failed transactions, network issues
•Monitoring: Log all payments for debugging
•Rate limiting: Prevent spam/abuse
•Database: Store payment records for auditing
typescript
// Using your custom facilitator
import { CustomFacilitator } from './facilitator';
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import { paymentMiddleware } from '@x402/express';
import { x402ResourceServer } from '@x402/core/server';
import { registerExactEvmScheme as registerServerScheme } from '@x402/evm/exact/server';
import express from 'express';
const myFacilitator = new CustomFacilitator({
rpcUrl: process.env.BASE_RPC_URL,
privateKey: process.env.FACILITATOR_PRIVATE_KEY,
});
// Use in client
const evmSigner = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
registerExactEvmScheme(client, { signer: evmSigner });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
// Use in server
const server = new x402ResourceServer(myFacilitator);
registerServerScheme(server);
const app = express();
app.use(
paymentMiddleware(
{
'GET /api/data': {
accepts: [{
scheme: 'exact',
price: '$0.001',
network: 'eip155:8453',
payTo: '0xYourWallet',
}],
description: 'Data endpoint',
mimeType: 'application/json',
},
},
server
)
);Implement the verifySignature() method
Complete the signature verification logic
Requirements:
Verifies valid signatures
Returns true for correctly signed payments
Rejects invalid signatures
Returns false for incorrectly signed payments
Your Solution