Skip to main content
A payment handler is the bridge between a wallet and a payment scheme. When the fetch wrapper receives a 402 response, it passes the payment requirements to each registered handler. Handlers that can fulfill a requirement return a PaymentExecer — an object that can execute the payment when called.

The PaymentHandler interface

import type { RequestContext, PaymentExecer } from "@faremeter/types/client"
import type { x402PaymentRequirements } from "@faremeter/types/x402"

type PaymentHandler = (
  context: RequestContext,
  accepts: x402PaymentRequirements[]
) => Promise<PaymentExecer[]>
A handler receives the request context and a list of payment requirements. It returns an array of PaymentExecer objects for every requirement it can fulfill. If the handler cannot fulfill any requirement, it returns an empty array.

The PaymentExecer interface

type PaymentExecer = {
  requirements: x402PaymentRequirements
  exec(): Promise<PaymentExecResult>
}
Each execer is bound to a specific requirement. When exec() is called, it signs the payment using the associated wallet and returns the payload for the X-PAYMENT header.

How handlers compose

Multiple handlers can be registered simultaneously. The fetch wrapper collects all execers from all handlers and passes them to a payer chooser function.
import { wrap, chooseFirstAvailable } from "@faremeter/fetch"

const fetchWithPayment = wrap(fetch, {
  handlers: [solanaHandler, evmHandler],
  payerChooser: chooseFirstAvailable,
})
The default payer chooser, chooseFirstAvailable, picks the first handler that can fulfill a requirement. You can provide a custom chooser to implement logic like balance checking, fee comparison, or chain preference.

Handler selection flow

402 response received
    |
    v
For each registered handler:
    |--- Handler receives requirements
    |--- Handler checks scheme/network/asset against its wallet
    |--- Returns PaymentExecer[] for matches, [] for no match
    |
    v
All execers collected
    |
    v
payerChooser selects one execer
    |
    v
execer.exec() signs the payment
    |
    v
Payment attached to retry request

Built-in handlers

PackageHandlerSchemes
@faremeter/payment-solanacreatePaymentHandlerSOL, SPL tokens
@faremeter/payment-evmcreatePaymentHandlerEIP-3009 USDC

Building custom handlers

A custom handler follows the same interface. Return PaymentExecer objects for requirements you can handle, and an empty array for those you cannot.
import type { PaymentHandler, PaymentExecer } from "@faremeter/types/client"

const createCustomHandler = (wallet: CustomWallet): PaymentHandler => {
  return async (context, accepts) => {
    const execers: PaymentExecer[] = []

    for (const req of accepts) {
      if (req.scheme === "my-scheme" && req.network === "my-network") {
        execers.push({
          requirements: req,
          exec: async () => {
            const signature = await wallet.sign(req)
            return { payload: { signature } }
          },
        })
      }
    }

    return execers
  }
}
See Extensions for community-contributed handlers.

Further reading