pay.sh docs
SDKRust

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.

SchemeGate functionWhat it does
Fixed chargepaid_get / paid_postCharge a fixed amount once, settled before the handler runs.
Metered usagepaid_upto_get / paid_upto_postAuthorize a ceiling, meter actual usage, settle the real amount and refund the rest.
Batch-settlementpaid_batch_get / paid_batch_postOne 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));

On this page