qntx
r402
Rust

Rust SDK for the x402 payment protocol.

Last updated Jun 17, 2026
148
Stars
28
Forks
4
Issues
0
Stars/day
Attention Score
86
Language breakdown
Rust 99.8%
Makefile 0.2%
โ–ธ Files click to expand
README

R402

[![CI][ci-badge]][ci-url] [![License][license-badge]][license-url] [![Rust][rust-badge]][rust-url]

[ci-badge]: https://github.com/qntx/r402/actions/workflows/ci.yml/badge.svg [ci-url]: https://github.com/qntx/r402/actions/workflows/ci.yml [license-badge]: https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg [license-url]: LICENSE-MIT [rust-badge]: https://img.shields.io/badge/rust-edition%202024-orange.svg [rust-url]: https://doc.rust-lang.org/edition-guide/

Modular Rust SDK for the x402 payment protocol โ€” client signing, server gating, and facilitator settlement over HTTP 402.

r402 provides a production-grade, multi-chain implementation of the x402 protocol with dual-path ERC-3009 / Permit2 transfers, the exact and upto (usage-based) schemes, composable lifecycle hooks, and 44 built-in chain deployments across EVM and Solana.

Crates

| Crate | | Description | | --- | --- | --- | | r402 | [![crates.io][r402-crate]][r402-crate-url] [![docs.rs][r402-doc]][r402-doc-url] | Core library โ€” protocol types, scheme traits, facilitator abstractions, and hook system | | r402-evm | [![crates.io][r402-evm-crate]][r402-evm-crate-url] [![docs.rs][r402-evm-doc]][r402-evm-doc-url] | EVM (EIP-155) โ€” ERC-3009 transfer authorization, multi-signer management, nonce tracking | | r402-svm | [![crates.io][r402-svm-crate]][r402-svm-crate-url] [![docs.rs][r402-svm-doc]][r402-svm-doc-url] | Solana (SVM) โ€” SPL token transfers, program-derived addressing | | r402-http | [![crates.io][r402-http-crate]][r402-http-crate-url] [![docs.rs][r402-http-doc]][r402-http-doc-url] | HTTP transport โ€” Axum payment gate middleware, reqwest client middleware, facilitator client |

[r402-crate]: https://img.shields.io/crates/v/r402.svg [r402-crate-url]: https://crates.io/crates/r402 [r402-evm-crate]: https://img.shields.io/crates/v/r402-evm.svg [r402-evm-crate-url]: https://crates.io/crates/r402-evm [r402-svm-crate]: https://img.shields.io/crates/v/r402-svm.svg [r402-svm-crate-url]: https://crates.io/crates/r402-svm [r402-http-crate]: https://img.shields.io/crates/v/r402-http.svg [r402-http-crate-url]: https://crates.io/crates/r402-http [r402-doc]: https://img.shields.io/docsrs/r402.svg [r402-doc-url]: https://docs.rs/r402 [r402-evm-doc]: https://img.shields.io/docsrs/r402-evm.svg [r402-evm-doc-url]: https://docs.rs/r402-evm [r402-svm-doc]: https://img.shields.io/docsrs/r402-svm.svg [r402-svm-doc-url]: https://docs.rs/r402-svm [r402-http-doc]: https://img.shields.io/docsrs/r402-http.svg [r402-http-doc-url]: https://docs.rs/r402-http

See also facilitator โ€” a production-ready facilitator server built on r402.

Quick Start

Protect a Route (Server)

use alloy_primitives::address;
use axum::{Router, routing::get};
use r402_evm::{Eip155Exact, USDC};
use r402_http::server::X402Middleware;

let x402 = X402Middleware::new("https://facilitator.example.com");

let app = Router::new().route( "/paid-content", get(handler).layer( x402.withpricetag(Eip155Exact::price_tag( address!("0xYourPayToAddress"), USDC::base().amount(1000000u64), // 1 USDC (6 decimals) )) ), );

Send Payments (Client)

use alloysignerlocal::PrivateKeySigner;
use r402_evm::Eip155ExactClient;
use r402_http::client::{WithPayments, X402Client};
use std::sync::Arc;

let signer = Arc::new("0x...".parse::<PrivateKeySigner>()?); let x402 = X402Client::new().register(Eip155ExactClient::new(signer));

let client = reqwest::Client::new().with_payments(x402);

let res = client.get("https://api.example.com/paid").send().await?;

Usage-Based Pricing (upto Scheme)

The upto scheme lets the buyer sign a maximum while the resource server picks the final charge at request time (meter reads, token usage, dynamic tiers). The facilitator settles for any value in [0, max]; a final amount of 0 returns no on-chain transaction.

use alloy_primitives::address;
use axum::{Router, response::IntoResponse, routing::post};
use r402_evm::{Eip155Upto, USDC};
use r402_http::server::{UptoActualAmount, X402Middleware};

async fn meter(/ ... /) -> impl IntoResponse { let mut response = "result".into_response(); // Charge 0.125 USDC for this call. response.extensions_mut().insert(UptoActualAmount::new("125000")); response }

let layer = X402Middleware::new("https://facilitator.example.com") .withpricetag(Eip155Upto::price_tag( address!("0xYourPayToAddress"), USDC::base().amount(1000000u64), // up to 1 USDC )); let app = Router::new().route("/meter", post(meter).layer(layer));

Handlers opt in by inserting UptoActualAmount into the response extensions; the middleware patches paymentRequirements.amount before forwarding the settle request. Buyers sign with Eip155UptoClient (shares the Permit2 auto-approve plumbing with Eip155ExactClient).

Note: UptoActualAmount is honoured only by SettlementMode::Sequential.
Concurrent and background modes start settlement before the handler returns and therefore charge the signed maximum.

Settlement Modes

X402Middleware supports three settlement strategies, configurable via withsettlementmode():

Sequential (default)

Verify โ†’ execute โ†’ settle. The safest mode โ€” on-chain settlement only occurs after the handler succeeds, and the Payment-Response header is included in 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

Concurrent

Verify โ†’ (settle โˆฅ execute) โ†’ await both. Reduces total latency by overlapping on-chain settlement with handler execution, saving one facilitator round-trip. On handler error the settlement task is detached (fire-and-forget).

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

Background

Verify โ†’ spawn settle (fire-and-forget) โ†’ execute โ†’ return. Settlement runs entirely in the background โ€” the response is returned to the client as soon as the handler completes, without waiting for on-chain confirmation. Ideal for streaming responses (SSE, LLM token streams) where the client should start receiving data immediately. Settlement errors are logged but do not propagate to the caller. Trade-off: the Payment-Response header is not attached since settlement may still be in progress when the response is sent.

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)

Comparison

| Mode | Total latency | Safety | Payment-Response | Best for | | --- | --- | --- | --- | --- | | Sequential | verify + handler + settle | Settlement only on handler success | โœ… Included | Standard request/response APIs | | Concurrent | verify + max(handler, settle) | Settlement may occur on handler failure | โœ… Included | Latency-sensitive endpoints | | Background | verify + handler | Settlement errors are non-fatal (logged) | โŒ Not attached | SSE / LLM streaming responses |

For full manual control over settlement timing, use the composable Paygate API directly with verify_only() + VerifiedPayment::settle().

Design

| Aspect | Details | | --- | --- | | Built-in chains | 44 โ€” 42 EVM (EIP-155) + 2 Solana | | Schemes | exact (fixed amount, ERC-3009 + Permit2) + upto (usage-based, Permit2-only, EVM) | | Transfer methods | Dual path โ€” ERC-3009 transferWithAuthorization + Permit2 proxy | | Lifecycle hooks | FacilitatorHooks (verify/settle) + ClientHooks (payment creation) | | Async model | Zero async_trait in core โ€” RPITIT / Pin<Box<dyn Future>> | | Facilitator trait | Unified โ€” dyn-compatible Box<dyn Facilitator> across all schemes | | Wire format | V2-only server (CAIP-2 chain IDs, Payment-Signature header) | | Settlement errors | Explicit โ€” failed settle returns 402 with structured error | | Network definitions | Decoupled โ€” per-chain crate (r402-evm, r402-svm) | | Smart wallets | EIP-6492 (counterfactual) + EIP-1271 (deployed) + ERC-2098 (compact signatures) | | Linting | pedantic + nursery + correctness (deny) |

Feature Flags

Each chain and transport crate uses feature flags to minimize compile-time dependencies:

| Crate | server | client | facilitator | telemetry | | --- | --- | --- | --- | --- | | r402-http | Axum payment gate + facilitator client | Reqwest middleware | โ€” | tracing spans | | r402-evm | Price tag generation | EIP-712 / EIP-3009 / Permit2 signing | On-chain verify & settle | tracing spans | | r402-svm | Price tag generation | SPL token signing | On-chain verify & settle | tracing spans |

Security

See SECURITY.md for disclaimers, supported versions, and vulnerability reporting.

Acknowledgments

License

Licensed under either of:

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.


A QNTX open-source project.

QNTX

Code is law. We write both.

๐Ÿ”— More in this category

ยฉ 2026 GitRepoTrend ยท qntx/r402 ยท Updated daily from GitHub