x402_kit/lib.rs
1//! # X402 Kit
2//!
3//! X402 Kit is a A fully modular, framework-agnostic, easy-to-extend SDK for building complex X402 payment integrations.
4//!
5//! X402-kit is **not a facilitator** — it's a composable SDK for buyers (signers) and sellers (servers) to build custom business logic.
6//! Future support for modular facilitator components is planned.
7//!
8//! ## Core Components Overview
9//!
10//! ### For the X402 Protocol
11//!
12//! - **[`concepts`]**: Core traits and types used across the X402 Kit.
13//! - **[`config`]**: Configuration types for defining resources and payment requirements.
14//! - **[`transport`]**: Types and traits for defining X402 transport mechanisms and facilitator interactions.
15//! - **[`types`]**: Common re-usable types for defining the X402 protocol.
16//!
17//! ### For Network-Specific Implementations
18//!
19//! - **[`networks`]**: Network-specific implementations, e.g., EVM / SVM assets and addresses.
20//! - **[`schemes`]**: Payment scheme implementations, e.g., Exact EVM / Exact SVM, and their signer logic.
21//!
22//! ### Commonly-Used Utilities
23//!
24//! - **[`seller`]**: Utilities for building X402 sellers, including an Axum integration.
25//! - **[`facilitator_client`]**: Utilities for building X402 facilitator clients.
26//!
27//! ## Seller Guide
28//!
29//! ### Configuring Payment Requirements
30//!
31//! #### Resources
32//!
33//! A [`config::Resource`] defines the details of what is being sold, including its URL, description, MIME type, and optional output schema.
34//!
35//! ```
36//! use url_macro::url;
37//! use x402_kit::config::Resource;
38//! use x402_kit::types::OutputSchema;
39//!
40//! let resource = Resource::builder()
41//! .url(url!("http://example.com/premium-content"))
42//! .description("Premium content access")
43//! .mime_type("application/json")
44//! // Make the endpoint discoverable with facilitators
45//! .output_schema(OutputSchema::discoverable_http_post())
46//! .build();
47//! ```
48//!
49//! See unit tests under [`types::OutputSchema`] in the [GitHub repo](https://github.com/AIMOverse/x402-kit/blob/main/crates/x402-kit/src/types/schema.rs) for complex examples of defining input/output schemas.
50//!
51//! #### Schemes
52//!
53//! Choose a scheme for building payment requirements. For example, using the `ExactEvm` scheme:
54//!
55//! ```
56//! use alloy_primitives::address;
57//! use url_macro::url;
58//! use x402_kit::{
59//! config::Resource,
60//! networks::evm::assets::UsdcBase,
61//! schemes::exact_evm::ExactEvm,
62//! transport::PaymentRequirements,
63//! };
64//!
65//! # fn build_payment_requirements() {
66//!
67//! let resource = Resource::builder()
68//! .url(url!("https://example.com/premium"))
69//! .description("Premium content access")
70//! .mime_type("application/json")
71//! .build();
72//!
73//! let payment_requirements = ExactEvm::builder()
74//! .asset(UsdcBase)
75//! .amount(1000) // Amount in smallest units (e.g., 1000 = 0.001 USDC)
76//! .pay_to(address!("0x3CB9B3bBfde8501f411bB69Ad3DC07908ED0dE20"))
77//! .resource(resource)
78//! .build();
79//!
80//! // Convert to PaymentRequirements for use with facilitator
81//! let requirements: PaymentRequirements = payment_requirements.into();
82//! # }
83//! ```
84//!
85//! ### Axum Integration
86//!
87//! As a seller, you might be interested in Axum integration for building an X402-enabled server.
88//! See [`seller::axum::PaymentHandler`] for more details.
89//!
90//! ```
91//! use alloy_primitives::address;
92//! use axum::{
93//! Router,
94//! extract::Request,
95//! middleware::{Next, from_fn},
96//! response::{IntoResponse, Response},
97//! routing::post,
98//! };
99//! use url_macro::url;
100//! use x402_kit::{
101//! config::Resource,
102//! facilitator_client::RemoteFacilitatorClient,
103//! networks::evm::assets::UsdcBase,
104//! schemes::exact_evm::ExactEvm,
105//! seller::axum::PaymentHandler,
106//! };
107//!
108//! async fn payment_middleware(req: Request, next: Next) -> Response {
109//! PaymentHandler::builder(RemoteFacilitatorClient::from_url(
110//! url!("https://facilitator.example.com"),
111//! ))
112//! .add_payment(
113//! ExactEvm::builder()
114//! .asset(UsdcBase)
115//! .amount(1000)
116//! .pay_to(address!("0x17d2e11d0405fa8d0ad2dca6409c499c0132c017"))
117//! .resource(
118//! Resource::builder()
119//! .url(url!("http://localhost:3000/premium"))
120//! .description("Premium content")
121//! .mime_type("application/json")
122//! .build(),
123//! )
124//! .build(),
125//! )
126//! .build()
127//! .handle_payment()
128//! .req(req)
129//! .next(next)
130//! .call()
131//! .await
132//! .map(|r| r.into_response())
133//! .unwrap_or_else(|err| err.into_response())
134//! }
135//!
136//! # async fn create_app() -> Router {
137//! Router::new()
138//! .route("/premium", post(premium_handler).layer(from_fn(payment_middleware)))
139//! # }
140//! # async fn premium_handler() {}
141//! ```
142//!
143//! ### The Seller Toolkit
144//!
145//! The seller toolkit provides utilities for building custom payment handling logic outside of specific frameworks.
146//!
147//! You might be interested in this if you are using a different web framework or have custom requirements.
148//!
149//! See [`seller::toolkit`] for more details.
150//!
151//! ## Extend X402 Kit As You Like
152//!
153//! The main idea is you don't need to wait for the upstream library to support the network or asset in your case.
154//! Adding a new network, asset, or scheme is as simple as implementing a few traits.
155//!
156//! However, we still recommend contributing back any useful implementations to the main repository to help grow the ecosystem!
157//!
158//! ### New Networks
159//!
160//! If you want support for new EVM / SVM networks or assets, just "declare" them anywhere in your codebase:
161//!
162//! #### Custom EVM Network
163//!
164//! ```
165//! use x402_kit::networks::evm::{ExplicitEvmNetwork, EvmNetwork};
166//!
167//! struct MyCustomEvmNetwork;
168//!
169//! impl ExplicitEvmNetwork for MyCustomEvmNetwork {
170//! const NETWORK: EvmNetwork = EvmNetwork {
171//! name: "my-custom-evm-network",
172//! chain_id: 12345,
173//! };
174//! }
175//!
176//! // Now you can use MyCustomEvmNetwork with any scheme that supports EVM
177//! ```
178//!
179//! #### Custom SVM Network
180//!
181//! ```
182//! use x402_kit::networks::svm::{ExplicitSvmNetwork, SvmNetwork};
183//!
184//! struct MyCustomSvmNetwork;
185//!
186//! impl ExplicitSvmNetwork for MyCustomSvmNetwork {
187//! const NETWORK: SvmNetwork = SvmNetwork("my-custom-svm-network");
188//! }
189//!
190//! // Now you can use MyCustomSvmNetwork with any scheme that supports SVM
191//! ```
192//!
193//! ### New Assets
194//!
195//! Similarly, you can define custom assets for your networks:
196//!
197//! #### Custom EVM Asset
198//!
199//! ```
200//! use alloy_primitives::address;
201//! use x402_kit::networks::evm::{
202//! ExplicitEvmAsset, ExplicitEvmNetwork, EvmNetwork, EvmAsset, EvmAddress, Eip712Domain
203//! };
204//!
205//! struct MyCustomNetwork;
206//! impl ExplicitEvmNetwork for MyCustomNetwork {
207//! const NETWORK: EvmNetwork = EvmNetwork {
208//! name: "my-network",
209//! chain_id: 12345,
210//! };
211//! }
212//!
213//! struct MyCustomToken;
214//! impl ExplicitEvmAsset for MyCustomToken {
215//! type Network = MyCustomNetwork;
216//!
217//! const ASSET: EvmAsset = EvmAsset {
218//! address: EvmAddress(address!("0x1234567890123456789012345678901234567890")),
219//! decimals: 18,
220//! name: "My Custom Token",
221//! symbol: "MCT",
222//! };
223//!
224//! const EIP712_DOMAIN: Option<Eip712Domain> = Some(Eip712Domain {
225//! name: "My Custom Token",
226//! version: "1",
227//! });
228//! }
229//!
230//! // Now you can use MyCustomToken with ExactEvm or other EVM schemes
231//! ```
232//!
233//! #### Custom SVM Asset
234//!
235//! ```
236//! use solana_pubkey::pubkey;
237//! use x402_kit::networks::svm::{
238//! ExplicitSvmAsset, ExplicitSvmNetwork, SvmNetwork, SvmAsset, SvmAddress
239//! };
240//!
241//! struct MyCustomSvmNetwork;
242//! impl ExplicitSvmNetwork for MyCustomSvmNetwork {
243//! const NETWORK: SvmNetwork = SvmNetwork("my-svm-network");
244//! }
245//!
246//! struct MyCustomSvmToken;
247//! impl ExplicitSvmAsset for MyCustomSvmToken {
248//! type Network = MyCustomSvmNetwork;
249//!
250//! const ASSET: SvmAsset = SvmAsset {
251//! address: SvmAddress(pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")),
252//! decimals: 9,
253//! name: "My Custom SVM Token",
254//! symbol: "MCST",
255//! };
256//! }
257//!
258//! // Now you can use MyCustomSvmToken with ExactSvm or other SVM schemes
259//! ```
260//!
261//! #### Using Custom Assets with Schemes
262//!
263//! Once you've defined your custom asset, you can use it with payment schemes just like built-in assets:
264//!
265//! ```no_run
266//! use alloy_primitives::address;
267//! use url_macro::url;
268//! use x402_kit::{
269//! config::Resource,
270//! networks::evm::{ExplicitEvmAsset, ExplicitEvmNetwork, EvmNetwork, EvmAsset, EvmAddress, Eip712Domain},
271//! schemes::exact_evm::ExactEvm,
272//! transport::PaymentRequirements,
273//! };
274//!
275//! // Define your custom network and asset
276//! struct Polygon;
277//! impl ExplicitEvmNetwork for Polygon {
278//! const NETWORK: EvmNetwork = EvmNetwork {
279//! name: "polygon",
280//! chain_id: 137,
281//! };
282//! }
283//!
284//! struct UsdcPolygon;
285//! impl ExplicitEvmAsset for UsdcPolygon {
286//! type Network = Polygon;
287//! const ASSET: EvmAsset = EvmAsset {
288//! address: EvmAddress(address!("0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174")),
289//! decimals: 6,
290//! name: "USD Coin",
291//! symbol: "USDC",
292//! };
293//! const EIP712_DOMAIN: Option<Eip712Domain> = Some(Eip712Domain {
294//! name: "USD Coin",
295//! version: "2",
296//! });
297//! }
298//!
299//! # fn use_custom_asset() {
300//! // Use it in payment requirements
301//! let resource = Resource::builder()
302//! .url(url!("https://example.com/api"))
303//! .description("API access")
304//! .mime_type("application/json")
305//! .build();
306//!
307//! let payment = ExactEvm::builder()
308//! .asset(UsdcPolygon)
309//! .amount(1000000) // 1 USDC
310//! .pay_to(address!("0x3CB9B3bBfde8501f411bB69Ad3DC07908ED0dE20"))
311//! .resource(resource)
312//! .build();
313//!
314//! let requirements: PaymentRequirements = payment.into();
315//! # }
316//! ```
317//!
318//! ### Defining New Network Families
319//!
320//! If you want to define an entirely new family of networks (beyond EVM or SVM), you need to implement the core traits under [`concepts`]:
321//!
322//! - [`concepts::NetworkFamily`]: Represents a blockchain network family
323//! - [`concepts::Address`]: Represents an address on that network
324//!
325//! The `Address` type should also implement `FromStr`, `Display`, `Copy`, `Debug`, `Clone`, `PartialEq`, `Eq`, and `Hash` for proper serialization/deserialization and usage throughout the SDK.
326//!
327//! Here's a complete example:
328//!
329//! ```
330//! use std::{fmt::Display, str::FromStr};
331//! use x402_kit::concepts::{Address, Asset, NetworkFamily};
332//!
333//! // Define your network family
334//! struct MyNetworkFamily {
335//! network_name: &'static str,
336//! network_id: u64,
337//! }
338//!
339//! impl NetworkFamily for MyNetworkFamily {
340//! fn network_name(&self) -> &str {
341//! self.network_name
342//! }
343//! }
344//!
345//! // Define an address type for your network
346//! #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
347//! struct MyAddress(u64);
348//!
349//! impl FromStr for MyAddress {
350//! type Err = std::num::ParseIntError;
351//!
352//! fn from_str(s: &str) -> Result<Self, Self::Err> {
353//! s.parse::<u64>().map(MyAddress)
354//! }
355//! }
356//!
357//! impl Display for MyAddress {
358//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359//! write!(f, "{}", self.0)
360//! }
361//! }
362//!
363//! impl Address for MyAddress {
364//! type Network = MyNetworkFamily;
365//! }
366//!
367//! // Define an asset type for your network
368//! type MyAsset = Asset<MyAddress>;
369//!
370//! # fn use_custom_network_family() {
371//! // Now you can use your custom network family
372//! let network = MyNetworkFamily {
373//! network_name: "my-custom-network",
374//! network_id: 42,
375//! };
376//!
377//! let address: MyAddress = "12345".parse().unwrap();
378//! assert_eq!(address.to_string(), "12345");
379//!
380//! let asset = MyAsset {
381//! address,
382//! decimals: 18,
383//! name: "My Token",
384//! symbol: "MTK",
385//! };
386//! # }
387//! ```
388//!
389//! Once you have these core types defined, you can build schemes and payment requirements for your custom network family by implementing the [`concepts::Scheme`] trait.
390//!
391//! ### Defining new Schemes
392//!
393//! To define a new payment scheme, implement the `Scheme` trait from the `concepts` module. This involves specifying the associated network and payload types.
394//!
395//! Just take how `ExactSvmScheme` is defined for example:
396//!
397//! ```
398//! use serde::{Deserialize, Serialize};
399//! use x402_kit::concepts::Scheme;
400//! use x402_kit::networks::svm::SvmNetwork;
401//!
402//! pub struct ExactSvmScheme(pub SvmNetwork);
403//!
404//! #[derive(Debug, Clone, Serialize, Deserialize)]
405//! #[serde(rename_all = "camelCase")]
406//! pub struct ExplicitSvmPayload {
407//! pub transaction: String,
408//! }
409//!
410//! impl Scheme for ExactSvmScheme {
411//! type Network = SvmNetwork;
412//! type Payload = ExplicitSvmPayload;
413//! const SCHEME_NAME: &'static str = "exact";
414//! fn network(&self) -> &Self::Network {
415//! &self.0
416//! }
417//! }
418//! ```
419//!
420//! Then you should make an entrypoint for sellers to convert the scheme into `PaymentRequirements`.
421//!
422//! Note that `PaymentRequirementsConfig` is a type-safe builder for constructing `PaymentRequirements` from schemes.
423//!
424//! ```
425//! use bon::Builder;
426//! use x402_kit::config::{PaymentRequirementsConfig, Resource, TransportConfig};
427//! use x402_kit::networks::svm::{ExplicitSvmAsset, ExplicitSvmNetwork, SvmAddress};
428//! use x402_kit::schemes::exact_svm::ExactSvmScheme;
429//! use x402_kit::transport::PaymentRequirements;
430//!
431//! #[derive(Builder, Debug, Clone)]
432//! pub struct ExactSvm<A: ExplicitSvmAsset> {
433//! pub asset: A,
434//! #[builder(into)]
435//! pub pay_to: SvmAddress,
436//! pub amount: u64,
437//! pub max_timeout_seconds_override: Option<u64>,
438//! pub resource: Resource,
439//! }
440//! impl<A: ExplicitSvmAsset> ExactSvm<A> {
441//! pub fn into_config(self) -> PaymentRequirementsConfig<ExactSvmScheme, SvmAddress> {
442//! PaymentRequirementsConfig {
443//! scheme: ExactSvmScheme(A::Network::NETWORK),
444//! transport: TransportConfig::builder()
445//! .amount(self.amount)
446//! .asset(A::ASSET)
447//! .pay_to(self.pay_to)
448//! .max_timeout_seconds(self.max_timeout_seconds_override.unwrap_or(60))
449//! .resource(self.resource)
450//! .build(),
451//! // Fee payer should be updated with facilitator's supported networks list
452//! extra: None,
453//! }
454//! }
455//! }
456//! impl<A: ExplicitSvmAsset> From<ExactSvm<A>> for PaymentRequirements {
457//! fn from(value: ExactSvm<A>) -> Self {
458//! value.into_config().into()
459//! }
460//! }
461//!
462//! ```
463//!
464
465pub mod concepts;
466pub mod config;
467pub mod errors;
468pub mod networks;
469pub mod schemes;
470pub mod transport;
471pub mod types;
472
473#[cfg(feature = "facilitator-client")]
474pub mod facilitator_client;
475
476#[cfg(feature = "seller")]
477pub mod seller;