> ## Documentation Index
> Fetch the complete documentation index at: https://docs.faremeter.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Dynamic Pricing

> Vary payment requirements per request using lower-level middleware primitives.

There are two ways to implement dynamic pricing depending on your setup.

## With in-process handlers

When using in-process handlers (`x402Handlers` + `pricing`), handlers receive the resource URL as context and can adjust pricing per request. This is the simplest path -- configure base pricing and let the handler do the work.

```typescript theme={null}
import { createMiddleware } from "@faremeter/middleware/hono"

app.get(
  "/api/:tier",
  await createMiddleware({
    x402Handlers: [solanaHandler],
    pricing: [
      { amount: "10000", asset: "USDC", recipient: "7xKX...", network: "solana:devnet" },
    ],
  }),
  (c) => c.json({ tier: c.req.param("tier") }),
)
```

The handler's `getRequirements` method receives the resource URL (e.g. `/api/premium`) and can return different amounts based on the path, query parameters, or any other aspect of the request.

## With `handleMiddlewareRequest`

When you need to vary pricing from the middleware layer itself, use `handleMiddlewareRequest` from `@faremeter/middleware/common`. The tradeoff is that you wire up the framework integration yourself: reading headers, sending JSON responses, and calling your route handler.

Unlike fixed pricing per route, dynamic pricing lets you derive payment requirements from any aspect of the incoming request: headers, query parameters, path, or body. You can also vary:

* The networks, schemes, and assets accepted
* The `recipient` wallet address
* The facilitator URL (via `createRemoteX402Handlers`)

<Note>
  Dynamic payment requirements **must** be idempotent. The same request must produce the same requirements every time, because the client may retry payment against the same requirements.
</Note>

### Server

```typescript server.ts theme={null}
import { Hono, type MiddlewareHandler } from "hono"
import { HTTPException } from "hono/http-exception"
import { serve } from "@hono/node-server"
import { x402Exact as solanaX402Exact } from "@faremeter/info/solana"
import { x402Exact } from "@faremeter/info/evm"
import {
  handleMiddlewareRequest,
  resolveSupportedVersions,
} from "@faremeter/middleware/common"
import { createRemoteX402Handlers } from "@faremeter/middleware"

const createDynamicPricingMiddleware = (): MiddlewareHandler => {
  return async (c, next) => {
    const amount = c.req.query("amount")
    const asset = "USDC"

    // In production, validate that amount is a numeric string.
    if (amount === undefined) {
      throw new HTTPException(400, {
        message: "you need to provide an 'amount' querystring parameter",
      })
    }

    const x402Handlers = createRemoteX402Handlers({
      facilitatorURL: "https://facilitator.corbits.dev",
      accepts: [
        ...solanaX402Exact({
          network: "devnet",
          asset,
          amount,
          payTo: process.env.MERCHANT_ADDRESS,
        }),
        x402Exact({
          network: "base-sepolia",
          asset,
          amount,
          payTo: process.env.EVM_MERCHANT_ADDRESS,
        }),
      ],
    })

    return await handleMiddlewareRequest({
      x402Handlers,
      pricing: [
        { amount, asset, recipient: process.env.MERCHANT_ADDRESS, network: "solana:devnet" },
        { amount, asset, recipient: process.env.EVM_MERCHANT_ADDRESS, network: "eip155:84532" },
      ],
      supportedVersions: resolveSupportedVersions(),
      resource: c.req.url,
      getHeader: (key) => c.req.header(key),
      sendJSONResponse: (status, body, headers) => {
        if (headers) {
          for (const [key, value] of Object.entries(headers)) {
            c.header(key, value)
          }
        }
        c.status(status)
        return c.json(body)
      },
      body: async (context) => {
        const captureResult = await context.capture()
        if (!captureResult.success) {
          return captureResult.errorResponse
        }

        // On success, call the next handler and let Hono set the response.
        await next()
      },
    })
  }
}

const app = new Hono()

app.get("/protected", createDynamicPricingMiddleware(), (c) => {
  return c.json({ msg: "success" })
})

// @hono/node-server is required to run Hono as a standalone Node.js server.
serve(app, (info) => {
  console.log(`Listening on http://localhost:${info.port}`)
})
```

### Client

```typescript client.ts theme={null}
import "dotenv/config"
import { payer } from "@faremeter/rides"
import { getLogger } from "@faremeter/logs"

const logger = await getLogger(["client"])

await payer.addLocalWallet(process.env.PAYER_KEYPAIR_PATH)
await payer.addLocalWallet(process.env.EVM_PRIVATE_KEY)

const response = await payer.fetch("http://localhost:3000/protected?amount=1000")
logger.info(JSON.stringify(await response.json()))
```

### Running the example

```bash theme={null}
# Terminal 1
MERCHANT_ADDRESS=7xKXwxRPMo2sUAT5... EVM_MERCHANT_ADDRESS=0x1234... pnpm tsx server.ts

# Terminal 2
PAYER_KEYPAIR_PATH=~/.config/solana/id.json EVM_PRIVATE_KEY=0xabc... pnpm tsx client.ts
```

The client requests `/protected?amount=1000`, and the server returns payment requirements for 1000 units (0.001 USDC). Changing the `amount` query parameter changes the price charged.
