# Payment schemes

> The four ways a pay-kit gate can charge — fixed charge, metered usage, subscription, and session — and how each maps to MPP and x402.

A gate's `kind` decides how it charges. The default is a one-time fixed charge; `usage`, `subscription`, and `session` are gate factories you wrap the price in. Protocol stays invisible to your handler — the only protocol knob is `accept`.

| Scheme        | Factory                   | Protocol · sub-form         | What it does                                                                             |
| ------------- | ------------------------- | --------------------------- | ---------------------------------------------------------------------------------------- |
| Fixed charge  | _(default)_               | mpp `charge` · x402 `exact` | Charge a fixed amount once, settled immediately.                                         |
| Metered usage | `usage(max)`              | x402 `upto`                 | Authorize a ceiling, meter actual consumption, refund the remainder.                     |
| Subscription  | `subscription(amount, …)` | mpp `subscription`          | Recurring on-chain authorization; the first call activates a plan, later calls reuse it. |
| Session       | `session(cap, …)`         | mpp `session`               | Open a payment channel, stream metered deliveries, settle out-of-band on close.          |

## Fixed charge

The default gate: a fixed price the client settles over **MPP or x402** (whichever it picks from `accept`). Settlement runs before your handler.

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

<Snippet name="charge.server" path="/api/v1/quote/:symbol" />

The client side is a single `fetch` — pay-kit pays the 402 and retries:

<Snippet name="charge.client" url="https://api.example.com/api/v1/quote/AAPL" />

## Metered usage

A `usage` gate authorizes a **ceiling** (x402 `upto`), then bills what your handler actually consumed and refunds the rest. Record consumption with `pay.charge(request)`, in base units of the gate's currency.

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

const pay = await createPayKit({
  accept: ['x402'], // upto is an x402 scheme
  network: 'mainnet',
  operator: { recipient: RECIPIENT, signer },
  // Authorize up to $0.10; settle the actual usage, refund the remainder.
  pricing: { summarize: usage(usd('0.10'), { description: 'Summarize text, billed per token' }) },
  rpcUrl,
});

app.post('/api/v1/summarize', pay.express('summarize'), (req, res) => {
  const summary = summarize(req.body.text);
  // Meter what was consumed; pay.express settles the actual draw after the handler.
  pay.charge(req)?.charge(BigInt(summary.tokens) * 100_000n);
  res.json({ summary: summary.text });
});
```

## Subscription

A `subscription` gate is a recurring on-chain authorization (MPP). The first call activates the plan; subsequent calls within the period reuse it for free. The plan PDA and the puller (the entity allowed to debit renewals) are created ahead of time.

<Snippet name="subscription.server" path="/api/v1/feed" />

From the client, the same `fetch` activates the subscription on first use:

<Snippet name="subscription.client" url="https://api.example.com/api/v1/feed" />

## Session

A `session` gate opens a payment channel (MPP). You stream metered deliveries — billed per unit against the channel cap — and settlement runs out-of-band when the channel idle-closes. Unlike the other schemes, session gates need their side-channel and receipt routes mounted explicitly; pay-kit hands them over via `pay.sessionRoutes(gate)` rather than auto-injecting them.

<Snippet name="session.server" path="/api/v1/stream" />

The client opens one channel and consumes the stream chunk by chunk:

<Snippet name="session.client" url="https://api.example.com/api/v1/stream" />

## MPP or x402?

`accept` is an ordered preference list. With both protocols enabled (`accept: ['mpp', 'x402']`) a fixed-charge gate advertises both and the client chooses; `usage` is x402-only, while `subscription` and `session` are MPP-only. To force a rail from the client when an endpoint offers both, pass the protocol as the third `fetch` argument:

<Snippet name="x402.client" url="https://api.example.com/api/v1/quote/AAPL" />
