rust_x402/template/
config.rs1use super::PaywallConfig;
6use crate::types::PaymentRequirements;
7
8#[derive(Debug, Clone)]
10pub struct PaywallConfigBuilder {
11 app_name: Option<String>,
12 app_logo: Option<String>,
13 cdp_client_key: Option<String>,
14 session_token_endpoint: Option<String>,
15}
16
17impl PaywallConfigBuilder {
18 pub fn new() -> Self {
20 Self {
21 app_name: None,
22 app_logo: None,
23 cdp_client_key: None,
24 session_token_endpoint: None,
25 }
26 }
27
28 pub fn app_name(mut self, name: impl Into<String>) -> Self {
30 self.app_name = Some(name.into());
31 self
32 }
33
34 pub fn app_logo(mut self, logo: impl Into<String>) -> Self {
36 self.app_logo = Some(logo.into());
37 self
38 }
39
40 pub fn cdp_client_key(mut self, key: impl Into<String>) -> Self {
42 self.cdp_client_key = Some(key.into());
43 self
44 }
45
46 pub fn session_token_endpoint(mut self, endpoint: impl Into<String>) -> Self {
48 self.session_token_endpoint = Some(endpoint.into());
49 self
50 }
51
52 pub fn build(self) -> PaywallConfig {
54 PaywallConfig {
55 app_name: self.app_name,
56 app_logo: self.app_logo,
57 cdp_client_key: self.cdp_client_key,
58 session_token_endpoint: self.session_token_endpoint,
59 custom_css: None,
60 custom_js: None,
61 theme: None,
62 branding: None,
63 }
64 }
65}
66
67impl Default for PaywallConfigBuilder {
68 fn default() -> Self {
69 Self::new()
70 }
71}
72
73pub fn default_config() -> PaywallConfig {
75 PaywallConfigBuilder::new().build()
76}
77
78pub fn branded_config(app_name: &str, app_logo: Option<&str>) -> PaywallConfig {
80 let mut builder = PaywallConfigBuilder::new().app_name(app_name);
81
82 if let Some(logo) = app_logo {
83 builder = builder.app_logo(logo);
84 }
85
86 builder.build()
87}
88
89pub fn cdp_config(
91 app_name: &str,
92 cdp_client_key: &str,
93 session_token_endpoint: Option<&str>,
94) -> PaywallConfig {
95 let mut builder = PaywallConfigBuilder::new()
96 .app_name(app_name)
97 .cdp_client_key(cdp_client_key);
98
99 if let Some(endpoint) = session_token_endpoint {
100 builder = builder.session_token_endpoint(endpoint);
101 }
102
103 builder.build()
104}
105
106pub fn validate_payment_requirements(requirements: &[PaymentRequirements]) -> Result<(), String> {
108 if requirements.is_empty() {
109 return Err("No payment requirements provided".to_string());
110 }
111
112 for req in requirements {
113 if req.scheme.is_empty() {
114 return Err("Payment scheme cannot be empty".to_string());
115 }
116
117 if req.network.is_empty() {
118 return Err("Payment network cannot be empty".to_string());
119 }
120
121 if req.max_amount_required.is_empty() {
122 return Err("Payment amount cannot be empty".to_string());
123 }
124
125 if req.pay_to.is_empty() {
126 return Err("Payment recipient cannot be empty".to_string());
127 }
128 }
129
130 Ok(())
131}
132
133pub fn format_amount(amount: &str, decimals: u8) -> Result<String, String> {
135 let amount_num: u64 = amount
136 .parse()
137 .map_err(|_| "Invalid amount format".to_string())?;
138
139 let divisor = 10_u64.pow(decimals as u32);
140 let display_amount = amount_num as f64 / divisor as f64;
141
142 Ok(format!("${:.6}", display_amount)
143 .trim_end_matches('0')
144 .trim_end_matches('.')
145 .to_string())
146}
147
148pub fn get_network_display_name(network: &str) -> String {
150 match network {
151 "base" => "Base".to_string(),
152 "base-sepolia" => "Base Sepolia".to_string(),
153 "ethereum" => "Ethereum".to_string(),
154 "ethereum-mainnet" => "Ethereum Mainnet".to_string(),
155 "polygon" => "Polygon".to_string(),
156 "polygon-mumbai" => "Polygon Mumbai".to_string(),
157 "avalanche" => "Avalanche".to_string(),
158 "avalanche-fuji" => "Avalanche Fuji".to_string(),
159 _ => network.to_string(),
160 }
161}
162
163pub fn is_testnet(network: &str) -> bool {
165 matches!(
166 network,
167 "base-sepolia" | "ethereum-sepolia" | "polygon-mumbai" | "avalanche-fuji"
168 )
169}