A fully modular, framework-agnostic, easy-to-extend SDK for building complex X402 payment integrations.
| Crate | Description |
|---|---|
x402-kit |
Main SDK with network definitions, payment schemes, and facilitator client |
x402-core |
Core traits, types, and transport mechanisms for the X402 protocol |
x402-paywall |
Framework-agnostic HTTP paywall middleware |
Docs are available at docs.rs
X402-kit is not a facilitator β it's a composable SDK for buyers (signers) and sellers (servers) to build custom business logic. Future support for modular facilitator components is planned.
Existing X402 SDKs only support static prices per API route. X402-kit's fully modular architecture enables complex, dynamic pricing logic while maximizing code reuse.
All internal fields and methods are public by design. Compose and extend functionality freely without fighting the framework.
- Transport Layer: Uses generalized
Stringtypes to prevent serialization failures and ensure service availability - Network + Scheme Layer: Leverages traits and generics for compile-time type checking without runtime overhead
Implement a new asset, network, or scheme entirely in your codebase and plug it into the SDK immediatelyβno upstream pull request or waiting period required thanks to trait-driven extension points.
However, we still recommend contributing back any useful implementations to the main repository to help grow the ecosystem!
Minimize runtime errors through compile-time guarantees while maintaining the flexibility needed for real-world business logic.
The x402-paywall crate (re-exported via x402-kit) provides a composable PayWall that handles the complete X402 payment flow. Run the example:
FACILITATOR_URL=https://your-facilitator.example \
cargo run -p x402-kit --example axum_sellerThe handle_payment method provides a complete flow: update accepts from facilitator, verify payment, run handler, and settle on success.
use alloy::primitives::address;
use axum::{extract::{Request, State}, middleware::Next, response::{IntoResponse, Response}};
use url_macro::url;
use x402_kit::{
core::Resource,
facilitator_client::FacilitatorClient,
networks::evm::assets::UsdcBaseSepolia,
paywall::paywall::PayWall,
schemes::exact_evm::ExactEvm,
};
async fn paywall_middleware(State(state): State<AppState>, req: Request, next: Next) -> Response {
let paywall = PayWall::builder()
.facilitator(state.facilitator)
.accepts(
ExactEvm::builder()
.amount(1000)
.asset(UsdcBaseSepolia)
.pay_to(address!("0x3CB9B3bBfde8501f411bB69Ad3DC07908ED0dE20"))
.build(),
)
.resource(
Resource::builder()
.url(url!("https://example.com/resource"))
.description("X402 payment protected resource")
.mime_type("application/json")
.build(),
)
.build();
paywall
.handle_payment(req, |req| next.run(req))
.await
.unwrap_or_else(|err| err.into_response())
}Accept payments on multiple networks (EVM and SVM):
use x402_kit::{
networks::{evm::assets::UsdcBaseSepolia, svm::assets::UsdcSolanaDevnet},
schemes::{exact_evm::ExactEvm, exact_svm::ExactSvm},
transport::Accepts,
};
let paywall = PayWall::builder()
.facilitator(facilitator)
.accepts(
Accepts::new()
.push(
ExactEvm::builder()
.amount(1000)
.asset(UsdcBaseSepolia)
.pay_to(address!("0x3CB9B3bBfde8501f411bB69Ad3DC07908ED0dE20"))
.build(),
)
.push(
ExactSvm::builder()
.amount(1000)
.asset(UsdcSolanaDevnet)
.pay_to(pubkey!("Ge3jkza5KRfXvaq3GELNLh6V1pjjdEKNpEdGXJgjjKUR"))
.build(),
),
)
.resource(/* ... */)
.build();For fine-grained control, use the step-by-step API:
// Skip verification, settle before running handler
let response = paywall
.process_request(req)?
.settle()
.await?
.run_handler(|req| next.run(req))
.await?
.response();The PayWall injects PaymentState into request extensions:
use axum::{Extension, Json};
use x402_kit::paywall::processor::PaymentState;
async fn handler(Extension(payment_state): Extension<PaymentState>) -> Json<Value> {
Json(json!({
"message": "Premium content accessed!",
"payer": payment_state.verified.map(|v| v.payer),
"transaction": payment_state.settled.map(|s| s.transaction),
}))
}Customize request/response types for your facilitator.
Note: The default facilitator client uses
Url::join("verify")etc. to construct endpoint URLs from the base URL. This means trailing slashes matter. For example, when using the Coinbase facilitator:export FACILITATOR_URL=https://www.x402.org/facilitator/
use x402_kit::facilitator_client::{FacilitatorClient, IntoVerifyResponse, IntoSettleResponse};
#[derive(Serialize, Deserialize)]
struct CustomSettleRequest { /* ... */ }
#[derive(Deserialize)]
struct CustomSettleResponse { /* ... */ }
impl IntoSettleResponse for CustomSettleResponse {
fn into_settle_response(self) -> SettleResult { /* ... */ }
}
let facilitator = FacilitatorClient::from_url(facilitator_url)
.with_settle_request_type::<CustomSettleRequest>()
.with_settle_response_type::<CustomSettleResponse>();- Full buyer-side signer support
- More networks / assets / schemes
- MCP / A2A transport support
We welcome all contributions to x402-kit! Here's how you can get involved:
- β Star this repository
- π Open issues to report bugs or suggest features
- π§ Submit PRs to improve the codebase
Contributors will receive priority access and rewards at AIMO Network's Beta launch (coming soon)!