Skip to main content
Faremeter’s plugin architecture allows community developers to create custom wallet adapters, payment schemes, and middleware integrations. Every layer of the stack is replaceable.

What can be extended

Plugin typeInterfaceUse case
Wallet adaptercreateLocalWallet() patternSupport a new wallet type (hardware, custodial, multisig)
Payment handlerPaymentHandlerSupport a new blockchain or payment scheme on the client
Facilitator handlerFacilitatorHandlerSupport a new blockchain or scheme on the facilitator
Middleware adapterhandleMiddlewareRequest()Integrate with a framework beyond Express and Hono

Building a custom wallet adapter

A wallet adapter is a factory function that returns an object capable of signing payment proofs. The exact shape depends on the chain, but the pattern is the same: accept credentials, return a signer.
import type { Keypair } from "some-chain-sdk"

interface MyWallet {
  address: string
  network: string
  sign(data: Uint8Array): Promise<Uint8Array>
}

export async function createMyWallet(
  network: string,
  keypair: Keypair,
): Promise<MyWallet> {
  return {
    address: keypair.publicKey.toString(),
    network,
    sign: async (data) => {
      return keypair.sign(data)
    },
  }
}
The wallet is then passed to a payment handler. See Wallets & Signing for the full adapter pattern.

Building a custom payment handler

A payment handler bridges a wallet and a payment scheme. It receives payment requirements from a 402 response and returns PaymentExecer objects for any requirements it can fulfill.
import type { PaymentHandler, PaymentExecer } from "@faremeter/types/client"

export function createMyPaymentHandler(wallet: MyWallet): PaymentHandler {
  return async (context, accepts) => {
    const execers: PaymentExecer[] = []

    for (const req of accepts) {
      // Only handle requirements this wallet can fulfill
      if (req.scheme !== "my-scheme" || req.network !== wallet.network) {
        continue
      }

      execers.push({
        requirements: req,
        exec: async () => {
          // Build and sign the payment proof
          const proof = buildProof(req, wallet)
          const signature = await wallet.sign(proof)

          return {
            payload: {
              signature: Buffer.from(signature).toString("base64"),
              address: wallet.address,
            },
          }
        },
      })
    }

    return execers
  }
}
Register the handler with the fetch wrapper:
import { wrap } from "@faremeter/fetch"

const wallet = await createMyWallet("my-network", keypair)
const handler = createMyPaymentHandler(wallet)

const fetchWithPayment = wrap(fetch, {
  handlers: [handler],
})
See Payment Handlers for the full interface and composition model.

Building a custom facilitator handler

To support a new chain on the facilitator side, implement the FacilitatorHandler interface:
import type { FacilitatorHandler } from "@faremeter/types/facilitator"

export async function createMyFacilitatorHandler(
  network: string,
  rpcUrl: string,
  signerKey: string,
): Promise<FacilitatorHandler> {
  const client = createChainClient(rpcUrl, signerKey)

  return {
    getSupported: () => [
      Promise.resolve({
        x402Version: 2,
        scheme: "my-scheme",
        network,
      }),
    ],

    getRequirements: async ({ accepts }) => {
      // Enrich requirements with chain-specific fields
      return accepts
        .filter((r) => r.scheme === "my-scheme" && r.network === network)
        .map((r) => ({
          ...r,
          // Add facilitator-specific enrichment here
          extra: { signerAddress: client.address },
        }))
    },

    handleSettle: async (requirements, payment) => {
      if (requirements.scheme !== "my-scheme") return null

      // Verify and submit the transaction on-chain
      const txHash = await client.submitTransaction(payment.payload)

      return {
        success: true,
        transaction: txHash,
        network,
      }
    },
  }
}
Wire it into a facilitator:
import { createFacilitatorRoutes } from "@faremeter/facilitator"
import { Hono } from "hono"

const handler = await createMyFacilitatorHandler("my-network", rpcUrl, signerKey)

const app = new Hono()
app.route("/", createFacilitatorRoutes({ handlers: [handler] }))
Handlers return null from handleSettle and handleVerify for schemes they don’t support, allowing multiple handlers to coexist. See Facilitator for the full setup.

Building a custom middleware adapter

If you use a framework other than Express or Hono, use handleMiddlewareRequest from @faremeter/middleware/common to wire up the payment flow manually. You provide functions to read headers, send responses, and handle the request body.
import {
  handleMiddlewareRequest,
  createPaymentRequiredResponseCache,
  resolveSupportedVersions,
} from "@faremeter/middleware/common"

const { getPaymentRequiredResponse } = createPaymentRequiredResponseCache({})

// Example: Fastify integration
fastify.addHook("preHandler", async (request, reply) => {
  return await handleMiddlewareRequest({
    facilitatorURL: "https://facilitator.corbits.dev",
    accepts,
    supportedVersions: resolveSupportedVersions(),
    resource: request.url,
    getHeader: (key) => request.headers[key] as string | undefined,
    getPaymentRequiredResponse,
    sendJSONResponse: (status, body, headers) => {
      if (headers) {
        for (const [key, value] of Object.entries(headers)) {
          reply.header(key, value)
        }
      }
      reply.status(status).send(body)
    },
    body: async ({ settle }) => {
      const result = await settle()
      if (!result.success) return result.errorResponse
      // Request continues to the route handler
    },
  })
})
See Middleware Overview for the full API reference.

Publishing plugins

When publishing a faremeter plugin:
  1. Use the naming convention faremeter-* or @your-scope/faremeter-* for discoverability.
  2. Declare @faremeter/types as a peer dependency for type compatibility.
  3. Export your factory functions as the package’s public API.
  4. Include a README with a working example.

Plugin registry

Visit the faremeter plugin registry to browse community-contributed packages.

Further reading