1#![doc = include_str!("../README.md")]
2
3pub mod blockchain;
4pub mod blockchain_facilitator;
5pub mod client;
6pub mod crypto;
7pub mod error;
8pub mod facilitator;
9pub mod facilitator_storage;
10pub mod template;
11pub mod types;
12pub mod wallet;
13
14#[cfg(feature = "http3")]
16pub mod http3;
17
18#[cfg(feature = "axum")]
20pub mod server;
21
22#[cfg(feature = "axum")]
24pub mod middleware;
25
26#[cfg(feature = "axum")]
28pub mod proxy;
29
30pub use blockchain::{BlockchainClient, BlockchainClientFactory};
32pub use blockchain_facilitator::{
33 BlockchainFacilitatorClient, BlockchainFacilitatorConfig, BlockchainFacilitatorFactory,
34};
35pub use client::X402Client;
36pub use error::{Result, X402Error};
37pub use types::*;
38pub use wallet::{Wallet, WalletFactory};
39
40#[cfg(feature = "axum")]
42pub mod axum;
43
44#[cfg(feature = "actix-web")]
45pub mod actix_web;
46
47#[cfg(feature = "warp")]
48pub mod warp;
49
50pub const VERSION: &str = env!("CARGO_PKG_VERSION");
52
53pub const X402_VERSION: u32 = 1;
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn test_version_constants() {
62 assert_eq!(X402_VERSION, 1);
63 assert!(VERSION.contains('.') || VERSION.chars().any(|c| c.is_ascii_digit()));
66 }
67
68 #[test]
69 fn test_payment_requirements_creation() {
70 let requirements = PaymentRequirements::new(
71 "exact",
72 "base-sepolia",
73 "1000000",
74 "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
75 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
76 "https://example.com/test",
77 "Test payment",
78 );
79
80 assert_eq!(requirements.scheme, "exact");
81 assert_eq!(requirements.network, "base-sepolia");
82 assert_eq!(requirements.max_amount_required, "1000000");
83 assert_eq!(
84 requirements.asset,
85 "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
86 );
87 assert_eq!(
88 requirements.pay_to,
89 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C"
90 );
91 assert_eq!(requirements.resource, "https://example.com/test");
92 assert_eq!(requirements.description, "Test payment");
93 }
94
95 #[test]
96 fn test_payment_requirements_usdc_info() {
97 let mut requirements = PaymentRequirements::new(
98 "exact",
99 "base-sepolia",
100 "1000000",
101 "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
102 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
103 "https://example.com/test",
104 "Test payment",
105 );
106
107 requirements
108 .set_usdc_info(crate::types::Network::Testnet)
109 .unwrap();
110 assert!(requirements.extra.is_some());
111
112 let extra = requirements.extra.as_ref().unwrap();
113 assert_eq!(extra["name"], "USDC");
114 assert_eq!(extra["version"], "2");
115 }
116
117 #[test]
118 fn test_payment_payload_creation() {
119 let authorization = ExactEvmPayloadAuthorization::new(
120 "0x857b06519E91e3A54538791bDbb0E22373e36b66",
121 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
122 "1000000",
123 "1745323800",
124 "1745323985",
125 "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
126 );
127
128 let payload = ExactEvmPayload {
129 signature: "0x2d6a7588d6acca505cbf0d9a4a227e0c52c6c34008c8e8986a1283259764173608a2ce6496642e377d6da8dbbf5836e9bd15092f9ecab05ded3d6293af148b571c".to_string(),
130 authorization,
131 };
132
133 let payment_payload = PaymentPayload::new("exact", "base-sepolia", payload);
134
135 assert_eq!(payment_payload.x402_version, X402_VERSION);
136 assert_eq!(payment_payload.scheme, "exact");
137 assert_eq!(payment_payload.network, "base-sepolia");
138 }
139
140 #[test]
141 fn test_payment_payload_base64_encoding() {
142 let authorization = ExactEvmPayloadAuthorization::new(
143 "0x857b06519E91e3A54538791bDbb0E22373e36b66",
144 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
145 "1000000",
146 "1745323800",
147 "1745323985",
148 "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
149 );
150
151 let payload = ExactEvmPayload {
152 signature: "0x2d6a7588d6acca505cbf0d9a4a227e0c52c6c34008c8e8986a1283259764173608a2ce6496642e377d6da8dbbf5836e9bd15092f9ecab05ded3d6293af148b571c".to_string(),
153 authorization,
154 };
155
156 let payment_payload = PaymentPayload::new("exact", "base-sepolia", payload);
157 let encoded = payment_payload.to_base64().unwrap();
158 let decoded = PaymentPayload::from_base64(&encoded).unwrap();
159
160 assert_eq!(payment_payload.x402_version, decoded.x402_version);
161 assert_eq!(payment_payload.scheme, decoded.scheme);
162 assert_eq!(payment_payload.network, decoded.network);
163 }
164
165 #[test]
166 fn test_authorization_validity() {
167 let now = chrono::Utc::now().timestamp();
168 let valid_after = (now - 100).to_string();
169 let valid_before = (now + 100).to_string();
170
171 let authorization = ExactEvmPayloadAuthorization::new(
172 "0x857b06519E91e3A54538791bDbb0E22373e36b66",
173 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
174 "1000000",
175 valid_after,
176 valid_before,
177 "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
178 );
179
180 assert!(authorization.is_valid_now().unwrap());
181 }
182
183 #[test]
184 fn test_authorization_expired() {
185 let now = chrono::Utc::now().timestamp();
186 let valid_after = (now - 200).to_string();
187 let valid_before = (now - 100).to_string(); let authorization = ExactEvmPayloadAuthorization::new(
190 "0x857b06519E91e3A54538791bDbb0E22373e36b66",
191 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
192 "1000000",
193 valid_after,
194 valid_before,
195 "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
196 );
197
198 assert!(!authorization.is_valid_now().unwrap());
199 }
200
201 #[test]
202 fn test_facilitator_config() {
203 let config = FacilitatorConfig {
204 url: "https://example.com/facilitator".to_string(),
205 timeout: Some(std::time::Duration::from_secs(30)),
206 create_auth_headers: None,
207 };
208
209 assert_eq!(config.url, "https://example.com/facilitator".to_string());
210 assert_eq!(config.timeout, Some(std::time::Duration::from_secs(30)));
211 }
212
213 #[test]
214 fn test_blockchain_facilitator_config() {
215 let config = BlockchainFacilitatorConfig {
216 rpc_url: Some("https://example.com/facilitator".to_string()),
217 network: "base-sepolia".to_string(),
218 verification_timeout: std::time::Duration::from_secs(30),
219 confirmation_blocks: 1,
220 max_retries: 3,
221 retry_delay: std::time::Duration::from_secs(1),
222 };
223
224 assert_eq!(
225 config.rpc_url,
226 Some("https://example.com/facilitator".to_string())
227 );
228 assert_eq!(
229 config.verification_timeout,
230 std::time::Duration::from_secs(30)
231 );
232 }
233
234 #[test]
235 fn test_networks() {
236 assert_eq!(networks::BASE_MAINNET, "base");
237 assert_eq!(networks::BASE_SEPOLIA, "base-sepolia");
238 assert_eq!(networks::AVALANCHE_MAINNET, "avalanche");
239 assert_eq!(networks::AVALANCHE_FUJI, "avalanche-fuji");
240
241 assert!(networks::is_supported("base-sepolia"));
242 assert!(networks::is_supported("base"));
243 assert!(!networks::is_supported("unsupported-network"));
244
245 assert_eq!(
246 networks::get_usdc_address("base-sepolia"),
247 Some("0x036CbD53842c5426634e7929541eC2318f3dCF7e")
248 );
249 assert_eq!(
250 networks::get_usdc_address("base"),
251 Some("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913")
252 );
253 }
254
255 #[test]
256 fn test_schemes() {
257 assert_eq!(schemes::EXACT, "exact");
258 }
259}