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;