rust_x402/template/
config.rs

1//! Configuration utilities for paywall templates
2//!
3//! This module provides utilities for creating and managing paywall configuration.
4
5use super::PaywallConfig;
6use crate::types::PaymentRequirements;
7
8/// Paywall configuration builder
9#[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    /// Create a new builder
19    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    /// Set the app name
29    pub fn app_name(mut self, name: impl Into<String>) -> Self {
30        self.app_name = Some(name.into());
31        self
32    }
33
34    /// Set the app logo
35    pub fn app_logo(mut self, logo: impl Into<String>) -> Self {
36        self.app_logo = Some(logo.into());
37        self
38    }
39
40    /// Set the CDP client key
41    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    /// Set the session token endpoint
47    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    /// Build the configuration
53    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
73/// Create a default paywall configuration
74pub fn default_config() -> PaywallConfig {
75    PaywallConfigBuilder::new().build()
76}
77
78/// Create a configuration with app branding
79pub 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
89/// Create a configuration with CDP integration
90pub 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
106/// Validate payment requirements
107pub 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
133/// Format amount for display
134pub 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
148/// Get network display name
149pub 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
163/// Check if network is testnet
164pub fn is_testnet(network: &str) -> bool {
165    matches!(
166        network,
167        "base-sepolia" | "ethereum-sepolia" | "polygon-mumbai" | "avalanche-fuji"
168    )
169}