rust_x402/middleware/
config.rs1use crate::types::{FacilitatorConfig, Network, PaymentRequirements};
4use crate::{Result, X402Error};
5use rust_decimal::Decimal;
6
7#[derive(Debug, Clone)]
9pub struct PaymentMiddlewareConfig {
10 pub amount: Decimal,
12 pub pay_to: String,
14 pub description: Option<String>,
16 pub mime_type: Option<String>,
18 pub max_timeout_seconds: u32,
20 pub output_schema: Option<serde_json::Value>,
22 pub facilitator_config: FacilitatorConfig,
24 pub testnet: bool,
26 pub custom_paywall_html: Option<String>,
28 pub resource: Option<String>,
30 pub resource_root_url: Option<String>,
32}
33
34impl PaymentMiddlewareConfig {
35 pub fn new(amount: Decimal, pay_to: impl Into<String>) -> Self {
37 let pay_to_normalized = pay_to.into().to_lowercase();
39 Self {
40 amount,
41 pay_to: pay_to_normalized,
42 description: None,
43 mime_type: None,
44 max_timeout_seconds: 60,
45 output_schema: None,
46 facilitator_config: FacilitatorConfig::default(),
47 testnet: true,
48 custom_paywall_html: None,
49 resource: None,
50 resource_root_url: None,
51 }
52 }
53
54 pub fn with_description(mut self, description: impl Into<String>) -> Self {
56 self.description = Some(description.into());
57 self
58 }
59
60 pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
62 self.mime_type = Some(mime_type.into());
63 self
64 }
65
66 pub fn with_max_timeout_seconds(mut self, max_timeout_seconds: u32) -> Self {
68 self.max_timeout_seconds = max_timeout_seconds;
69 self
70 }
71
72 pub fn with_output_schema(mut self, output_schema: serde_json::Value) -> Self {
74 self.output_schema = Some(output_schema);
75 self
76 }
77
78 pub fn with_facilitator_config(mut self, facilitator_config: FacilitatorConfig) -> Self {
80 self.facilitator_config = facilitator_config;
81 self
82 }
83
84 pub fn with_testnet(mut self, testnet: bool) -> Self {
86 self.testnet = testnet;
87 self
88 }
89
90 pub fn with_custom_paywall_html(mut self, html: impl Into<String>) -> Self {
92 self.custom_paywall_html = Some(html.into());
93 self
94 }
95
96 pub fn with_resource(mut self, resource: impl Into<String>) -> Self {
98 self.resource = Some(resource.into());
99 self
100 }
101
102 pub fn with_resource_root_url(mut self, url: impl Into<String>) -> Self {
104 self.resource_root_url = Some(url.into());
105 self
106 }
107
108 pub fn create_payment_requirements(&self, request_uri: &str) -> Result<PaymentRequirements> {
110 let network = if self.testnet {
111 crate::types::networks::BASE_SEPOLIA
112 } else {
113 crate::types::networks::BASE_MAINNET
114 };
115
116 let usdc_address = crate::types::networks::get_usdc_address(network).ok_or_else(|| {
117 X402Error::NetworkNotSupported {
118 network: network.to_string(),
119 }
120 })?;
121
122 let resource = if let Some(ref resource_url) = self.resource {
123 resource_url.clone()
124 } else if let Some(ref root_url) = self.resource_root_url {
125 format!("{}{}", root_url, request_uri)
126 } else {
127 request_uri.to_string()
128 };
129
130 let max_amount_required = (self.amount * Decimal::from(1_000_000u64))
131 .normalize()
132 .to_string();
133
134 let pay_to_normalized = self.pay_to.to_lowercase();
136
137 let mut requirements = PaymentRequirements::new(
138 crate::types::schemes::EXACT,
139 network,
140 max_amount_required,
141 usdc_address,
142 &pay_to_normalized,
143 resource,
144 self.description.as_deref().unwrap_or("Payment required"),
145 );
146
147 requirements.mime_type = self.mime_type.clone();
148 requirements.output_schema = self.output_schema.clone();
149 requirements.max_timeout_seconds = self.max_timeout_seconds;
150
151 let network = if self.testnet {
152 Network::Testnet
153 } else {
154 Network::Mainnet
155 };
156 requirements.set_usdc_info(network)?;
157
158 Ok(requirements)
159 }
160}