Payment schemes
The three ways a solana-pay-kit gate can charge — fixed charge, metered usage (upto), and batch-settlement — and which gate function each uses.
A gated route advertises both protocols at once; the scheme is decided by which gate function you mount. All three return an axum MethodRouter.
| Scheme | Gate function | What it does |
|---|---|---|
| Fixed charge | paid_get / paid_post | Charge a fixed amount once, settled before the handler runs. |
| Metered usage | paid_upto_get / paid_upto_post | Authorize a ceiling, meter actual usage, settle the real amount and refund the rest. |
| Batch-settlement | paid_batch_get / paid_batch_post | One channel, many cheap requests verified off-chain; redeem vouchers on-chain later. |
The upto and batch-settlement schemes settle with operator-signed vouchers, so they require a fee_payer_signer
on PayKitConfig (see Signers). MPP session and subscription exist at the protocol
layer (solana_pay_kit::mpp) but are not yet exposed as paid_* gate functions.
Fixed charge
The default gate: a fixed price the client settles over MPP or x402 (its choice). Settlement runs before your handler.
let pay = PayKit::new(PayKitConfig { recipient: "CXhrFZJLKqjzmP3sjYLcF4dTeXWKCy9e2SXXZ2Yo6MPY".to_string(), network: "localnet".to_string(), rpc_url: Some("https://402.surfnet.dev:8899".to_string()), ..Default::default()}).expect("valid config");// `paid_get(handler, price, &pay)` settles the 402 (MPP or x402, the// client's choice) before the handler runs.let app = Router::new().route("/quote", paid_get(quote, "0.01", &pay));The client side reads the challenge and pays — see Client:
// 1. Parse the MPP charge challenge from the 402 `WWW-Authenticate` header.let challenge = parse_challenge(www_authenticate)?;// 2. Sign a payment credential and replay the request with it.let authorization = build_credential_header(signer, rpc, &challenge).await?;let res = reqwest::Client::new() .get("https://api.example.com/quote") .header("Authorization", authorization) .send() .await?;println!("{}", res.text().await?);Metered usage (upto)
A paid_upto_* gate advertises a maximum price. The client deposits that ceiling; the handler meters real usage and reports it via the Charge extractor; the gate settles the actual amount and refunds the remainder. This is the shape for LLM-token billing or per-byte metering, where the final cost is unknown until the work runs.
let pay = PayKit::new(PayKitConfig { recipient: "CXhrFZJLKqjzmP3sjYLcF4dTeXWKCy9e2SXXZ2Yo6MPY".to_string(), network: "localnet".to_string(), rpc_url: Some("https://402.surfnet.dev:8899".to_string()), // `upto` settlement vouchers are operator-signed, so a signer is required. fee_payer_signer: Some(Arc::new(operator)), ..Default::default()}).expect("valid config");// Maximum $1.00; the handler bills only for actual usage via `charge.charge`,// and the gate refunds the remainder.let app = Router::new().route("/summarize", paid_upto_post(summarize, "1.00", &pay));Batch-settlement
A paid_batch_* gate is for high-throughput APIs: the client opens one payment channel and signs a cumulative voucher per request, which the gate verifies off-chain and serves immediately — no on-chain transaction per request. The operator redeems vouchers on-chain later, in batches, via pay.x402_batch().
let pay = PayKit::new(PayKitConfig { recipient: "CXhrFZJLKqjzmP3sjYLcF4dTeXWKCy9e2SXXZ2Yo6MPY".to_string(), network: "localnet".to_string(), rpc_url: Some("https://402.surfnet.dev:8899".to_string()), // batch-settlement transactions are operator-signed. fee_payer_signer: Some(Arc::new(operator)), ..Default::default()}).expect("valid config");// The client opens one channel and signs a cumulative voucher per request;// the gate verifies it off-chain and serves immediately. Redeem vouchers// on-chain later, in batches, via `pay.x402_batch()`.let app = Router::new().route("/quote", paid_batch_get(quote, "0.001", &pay));Rust SDK
Gate any HTTP route for stablecoin payments with solana-pay-kit — one config, a dual-protocol axum gate that advertises both MPP and x402.
Pricing
Price a route in solana-pay-kit — a fixed dollar string or a per-request closure. There is no catalogue type; each route names its own price inline.