Modular Rust SDK for the x402 payment protocol — client signing, server gating, and facilitator settlement over HTTP 402.
Protocol version 2 only. Schemes exact / upto. Deployments: EVM, SVM (Solana), NEAR, XRPL, Hedera, Algorand, Aptos, Keeta, TON, Stellar, Concordium. Tron is experimental / not an x402 protocol mechanism. Casper is extra / not an x402 protocol mechanism.
See also
facilitator— a production-ready facilitator server built on r402.
[dependencies]
r402 = { version = "0.20", features = ["evm", "http"] }
# SVM + MCP, same crate:
# r402 = { version = "0.20", features = ["evm", "svm", "http", "mcp"] }
# Every production chain + HTTP + MCP:
# r402 = { version = "0.20", features = ["full"] }default = ["evm", "http"] so a first r402 dep does not compile Solana, protobuf, or gRPC. Enable chains as features (svm, near, xrpl, hedera, avm, aptos, keeta, tvm, stellar, concordium). tron and casper are opt-in and not in full. Individual r402-* crates remain for binaries that want one crate and no facade.
Full feature matrix and crate list: crates/README.md.
use alloy_primitives::address;
use axum::{Router, routing::get};
use r402::evm::{Eip155Exact, USDC};
use r402::http::server::X402Middleware;
let x402 = X402Middleware::try_new("https://facilitator.example.com")?
.with_base_url("https://api.example.com".parse()?)
.with_scheme("eip155:*".parse()?, Eip155Exact);
let app = Router::new().route(
"/paid-content",
get(handler).layer(
x402.with_price_tag(Eip155Exact::price_tag(
address!("0xYourPayToAddress"),
USDC::base().amount(1_000_000u64),
None,
))?,
),
);use alloy_signer_local::PrivateKeySigner;
use r402::evm::Eip155ExactClient;
use r402::http::{WithPayments, X402Client};
use std::sync::Arc;
let signer = Arc::new("0x...".parse::<PrivateKeySigner>()?);
let client = reqwest::Client::new().with_payments(
X402Client::new().register(Eip155ExactClient::new(signer)),
);
let res = client.get("https://api.example.com/paid").send().await?;SettlementMode is not a paymentFlow. Spec paymentFlow (authorization / upfront / escrow) is the on-wire ordering of verify and settle around the handler. Sequential / Concurrent / Background are a resource-server scheduler for the after-handler settle of authorization: whether this HTTP response waits for that settle. Facilitators and clients never see the knob; it is not written into PAYMENT-REQUIRED.
- Sequential (default) is spec
authorization: verify → handler → settle → respond withPayment-Response. - Concurrent and Background overlap that after-handler settle with the handler (async I/O). They are not a fourth flow.
upfront/escrowreject them (IncompatibleSettlementMode) because those flows already settle before the handler.
Configurable via with_settlement_mode() after with_price_tag.
Verify → execute → settle. Spec authorization. On-chain settlement only after the handler succeeds; Payment-Response is on the same HTTP response.
sequenceDiagram
participant C as Client
participant S as Server
participant F as Facilitator
participant H as Handler
C->>S: HTTP Request + Payment-Signature
S->>F: verify(payment)
F-->>S: VerifyResponse ✓
S->>H: execute request
Note over S,H: Balance verified but NOT locked —<br/>handler executing (variable latency)
H-->>S: response body
S->>F: settle(payment)
Note over S,F: On-chain transfer (2–5 s)
F-->>S: SettleResponse (tx_hash)
S-->>C: 200 OK + Payment-Response header
Verify → (settle ∥ execute) → await both. Still authorization on the wire. Overlaps the after-handler settle RPC with the handler so total latency is verify + max(handler, settle). The response still waits and still carries Payment-Response. On handler error the settlement task is detached — the payer may be charged even if the handler failed.
sequenceDiagram
participant C as Client
participant S as Server
participant F as Facilitator
participant H as Handler
C->>S: HTTP Request + Payment-Signature
S->>F: verify(payment)
F-->>S: VerifyResponse ✓
par settle ∥ execute
S->>F: settle(payment)
Note over S,F: On-chain transfer
F-->>S: SettleResponse (tx_hash)
and
S->>H: execute request
H-->>S: response body
end
S-->>C: 200 OK + Payment-Response header
Verify → spawn settle (fire-and-forget) → execute → return. Still authorization on the wire. The HTTP response does not wait for on-chain confirmation, so SSE / LLM streams can start before settle finishes. Settlement errors are logged and do not fail the request. Trade-off: Payment-Response is omitted — headers are already on the way out — and the payer may be charged if the handler later fails.
sequenceDiagram
participant C as Client
participant S as Server
participant F as Facilitator
participant H as Handler
C->>S: HTTP Request + Payment-Signature
S->>F: verify(payment)
F-->>S: VerifyResponse ✓
S-)F: settle(payment) [fire-and-forget]
S->>H: execute request
H-->>S: response body (or stream)
S-->>C: 200 OK (no Payment-Response header)
Note over S,F: Settlement completes asynchronously
F-)S: SettleResponse (logged)
Applies only to paymentFlow = authorization. upfront / escrow stay Sequential.
| Mode | Total latency | Safety | Payment-Response |
Best for |
|---|---|---|---|---|
| Sequential | verify + handler + settle | Settlement only on handler success | Included | Spec authorization; buffered responses |
| Concurrent | verify + max(handler, settle) | Settlement may occur on handler failure | Included | Overlap settle with a slow handler |
| Background | verify + handler | Settlement errors are non-fatal (logged) | Not attached | SSE / LLM streaming (headers must leave before the body) |
Note:
UptoActualAmountis honoured only bySettlementMode::Sequential. Concurrent and Background start settlement before the handler returns and therefore charge the signed maximum. They cannot meter a stream.
- x402 Protocol Specification — protocol design by Coinbase
- coinbase/x402 — official reference implementations (TypeScript, Python, Go)
- x402-rs/x402-rs — community Rust implementation
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project shall be dual-licensed as above, without any additional terms or conditions.