# Pricing & gates

> Declare what a route costs — inline prices, named gate catalogues, fees and splits, and dynamic per-request pricing.

A **gate** is a protected unit: an amount, an ordered list of accepted protocols, and zero or more named fees. You declare gates inline in `createPayKit`'s `pricing` option; gate names are inferred, so referencing them is typed.

## Prices

A price is a value object: a USD amount and the stablecoins it may settle in. Build one with `usd`.

```ts
import { usd } from '@solana/pay-kit';

usd('0.10'); // $0.10, default settlement stablecoins
usd('0.10', 'USDC', 'PYUSD'); // restrict/order settlement to USDC then PYUSD
```

The customer is quoted in USD; the **settlement** stablecoin (`USDC`, `USDT`, `PYUSD`, `USDG`, `CASH`) is what actually transfers.

## Inline price vs. catalogue

For a single route, pass a price straight to the middleware — an inline gate:

```ts
app.get('/report', pay.express(usd('0.25')), (_req, res) => res.json({ ok: true }));
```

When more than one route is paid, lift the prices into the `pricing` catalogue and reference gates by name:

```ts
import { createPayKit, usd } from '@solana/pay-kit';

const pay = await createPayKit({
  rpcUrl: 'https://402.surfnet.dev:8899',
  pricing: {
    report: { amount: usd('0.10'), description: 'Premium report' },
    apiCall: { amount: usd('0.001') },
  },
});

app.get('/report', pay.express('report'), (_req, res) => res.json({ ok: true }));
app.get('/api/data', pay.express('apiCall'), (_req, res) => res.json({ data: [] }));
```

## Fees and splits

Two fee shapes route part of a payment to another address. `feeWithin` carves the fee out of the amount (the recipient nets less); `feeOnTop` adds it on top (the customer pays more, the recipient nets full).

```ts
const SELLER = 'Ay…';
const PLATFORM = 'CX…';

const pay = await createPayKit({
  rpcUrl: 'https://402.surfnet.dev:8899',
  pricing: {
    // Stripe-Connect "application fee": customer pays $10.00,
    // SELLER nets $9.70, PLATFORM nets $0.30.
    marketplaceSale: {
      amount: usd('10.00'),
      payTo: SELLER,
      feeWithin: { [PLATFORM]: usd('0.30') },
    },

    // Surcharge: customer pays $10.50, SELLER nets $10.00, PLATFORM nets $0.50.
    ticket: {
      amount: usd('10.00'),
      payTo: SELLER,
      feeOnTop: { [PLATFORM]: usd('0.50') },
    },
  },
});
```

## Dynamic pricing

A gate can be a function of the request — return a price per call:

```ts
const pay = await createPayKit({
  rpcUrl: 'https://402.surfnet.dev:8899',
  pricing: {
    tiered: (request) => usd(new URL(request.url).searchParams.get('tier') === 'premium' ? '5.00' : '0.10'),
  },
});
```

## Boot-time validation

Gates are validated when `createPayKit` runs, so configuration mistakes throw at boot — before any traffic — as a [`ConfigurationError`](/docs/sdk/typescript/errors) or subtype:

- `payTo` resolves from the gate or `operator.recipient`.
- A fee recipient must differ from `payTo` — fold the fee into the amount instead.
- All fee prices share one currency with the amount.
- `sum(feeWithin)` must be less than the amount.
- `accept: ['x402']` on a fee-bearing gate throws (`ProtocolIncompatibleError`) — fees need MPP.
