//! Pricing Engine //! //! Service pricing, tier management, and discount system. mod discounts; mod tiers; pub use discounts::{Discount, DiscountType}; pub use tiers::PricingTier; use crate::error::{EconomicsError, Result}; use crate::{ResourceUnit, ServiceType, SynorDecimal}; use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// Service pricing configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServicePricing { /// Base prices by resource unit pub base_prices: HashMap, /// Minimum billable amount pub minimum_charge: SynorDecimal, /// Free tier included pub free_tier: Option, } /// Free tier allocation #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FreeTier { /// Free allocation per resource pub allocations: HashMap, /// Monthly reset pub monthly_reset: bool, } /// Pricing engine managing all pricing logic pub struct PricingEngine { /// Pricing by service type service_pricing: HashMap, /// Available pricing tiers tiers: Vec, /// Active discounts discounts: Vec, } impl PricingEngine { /// Create a new pricing engine with default prices pub fn new() -> Self { let mut engine = Self { service_pricing: HashMap::new(), tiers: Vec::new(), discounts: Vec::new(), }; engine.initialize_default_pricing(); engine.initialize_default_tiers(); engine } /// Initialize default service pricing fn initialize_default_pricing(&mut self) { // Storage L2 pricing let mut storage_prices = HashMap::new(); storage_prices.insert(ResourceUnit::GbMonth, Decimal::new(2, 2)); // 0.02 SYNOR/GB/month storage_prices.insert(ResourceUnit::BandwidthGb, Decimal::new(1, 2)); // 0.01 SYNOR/GB retrieval storage_prices.insert(ResourceUnit::Bytes, Decimal::new(2, 11)); // For small files self.service_pricing.insert( ServiceType::Storage, ServicePricing { base_prices: storage_prices, minimum_charge: Decimal::new(1, 4), // 0.0001 SYNOR free_tier: Some(FreeTier { allocations: [(ResourceUnit::GbMonth, Decimal::new(5, 1))].into(), // 0.5 GB free monthly_reset: true, }), }, ); // Hosting pricing let mut hosting_prices = HashMap::new(); hosting_prices.insert(ResourceUnit::BandwidthGb, Decimal::new(5, 2)); // 0.05 SYNOR/GB hosting_prices.insert(ResourceUnit::Domains, Decimal::new(50, 2)); // 0.50 SYNOR/domain/month self.service_pricing.insert( ServiceType::Hosting, ServicePricing { base_prices: hosting_prices, minimum_charge: Decimal::ZERO, free_tier: Some(FreeTier { allocations: [ (ResourceUnit::BandwidthGb, Decimal::new(1, 0)), // 1 GB free ] .into(), monthly_reset: true, }), }, ); // Database L2 pricing let mut db_prices = HashMap::new(); db_prices.insert(ResourceUnit::GbMonth, Decimal::new(10, 2)); // 0.10 SYNOR/GB/month db_prices.insert(ResourceUnit::Queries, Decimal::new(1, 8)); // 0.00000001 SYNOR/query (0.01/million) db_prices.insert(ResourceUnit::VectorSearches, Decimal::new(5, 8)); // 0.00000005 SYNOR/search (0.05/million) self.service_pricing.insert( ServiceType::Database, ServicePricing { base_prices: db_prices, minimum_charge: Decimal::new(1, 4), free_tier: Some(FreeTier { allocations: [ (ResourceUnit::GbMonth, Decimal::new(5, 1)), // 0.5 GB free (ResourceUnit::Queries, Decimal::new(1_000_000, 0)), // 1M queries free (ResourceUnit::VectorSearches, Decimal::new(100_000, 0)), // 100K vector searches free ] .into(), monthly_reset: true, }), }, ); // Compute L2 pricing let mut compute_prices = HashMap::new(); compute_prices.insert(ResourceUnit::CpuCoreHours, Decimal::new(2, 2)); // 0.02 SYNOR/core-hour compute_prices.insert(ResourceUnit::GpuHours, Decimal::new(50, 2)); // 0.50 SYNOR/GPU-hour (RTX 4090) compute_prices.insert(ResourceUnit::MemoryGbHours, Decimal::new(5, 3)); // 0.005 SYNOR/GB-hour compute_prices.insert(ResourceUnit::Invocations, Decimal::new(2, 7)); // 0.0000002 SYNOR/invocation (0.20/million) self.service_pricing.insert( ServiceType::Compute, ServicePricing { base_prices: compute_prices, minimum_charge: Decimal::new(1, 4), free_tier: Some(FreeTier { allocations: [ (ResourceUnit::CpuCoreHours, Decimal::new(100, 0)), // 100 core-hours free (ResourceUnit::MemoryGbHours, Decimal::new(200, 0)), // 200 GB-hours free (ResourceUnit::Invocations, Decimal::new(1_000_000, 0)), // 1M invocations free ] .into(), monthly_reset: true, }), }, ); // Network pricing let mut network_prices = HashMap::new(); network_prices.insert(ResourceUnit::BandwidthGb, Decimal::new(1, 2)); // 0.01 SYNOR/GB self.service_pricing.insert( ServiceType::Network, ServicePricing { base_prices: network_prices, minimum_charge: Decimal::ZERO, free_tier: Some(FreeTier { allocations: [(ResourceUnit::BandwidthGb, Decimal::new(10, 0))].into(), // 10 GB free monthly_reset: true, }), }, ); // Contract execution (gas pricing) let mut contract_prices = HashMap::new(); contract_prices.insert(ResourceUnit::Gas, Decimal::new(1, 9)); // 0.000000001 SYNOR/gas self.service_pricing.insert( ServiceType::Contract, ServicePricing { base_prices: contract_prices, minimum_charge: Decimal::ZERO, free_tier: None, }, ); } /// Initialize default pricing tiers fn initialize_default_tiers(&mut self) { self.tiers = vec![ PricingTier::free(), PricingTier::standard(), PricingTier::premium(), PricingTier::enterprise(), ]; } /// Calculate cost for resource usage pub fn calculate_cost( &self, service_type: ServiceType, resource_unit: ResourceUnit, amount: Decimal, ) -> Result { let pricing = self .service_pricing .get(&service_type) .ok_or_else(|| EconomicsError::ServiceNotConfigured(service_type.to_string()))?; let unit_price = pricing .base_prices .get(&resource_unit) .ok_or_else(|| { EconomicsError::ServiceNotConfigured(format!( "{} - {}", service_type, resource_unit )) })?; let cost = amount * unit_price; // Apply minimum charge Ok(cost.max(pricing.minimum_charge)) } /// Calculate cost with tier discount applied pub fn calculate_cost_with_tier( &self, service_type: ServiceType, resource_unit: ResourceUnit, amount: Decimal, tier_name: &str, ) -> Result { let base_cost = self.calculate_cost(service_type, resource_unit, amount)?; let tier = self.tiers.iter().find(|t| t.name == tier_name); if let Some(tier) = tier { let discount_rate = tier.discount_percentage / Decimal::ONE_HUNDRED; Ok(base_cost * (Decimal::ONE - discount_rate)) } else { Ok(base_cost) } } /// Calculate tier discount amount pub fn calculate_tier_discount( &self, tier_name: &str, subtotal: SynorDecimal, ) -> Result { let tier = self.tiers.iter().find(|t| t.name == tier_name); if let Some(tier) = tier { let discount_rate = tier.discount_percentage / Decimal::ONE_HUNDRED; Ok(subtotal * discount_rate) } else { Ok(Decimal::ZERO) } } /// Get free tier allocation for a resource pub fn get_free_allocation( &self, service_type: ServiceType, resource_unit: ResourceUnit, ) -> Decimal { self.service_pricing .get(&service_type) .and_then(|p| p.free_tier.as_ref()) .and_then(|f| f.allocations.get(&resource_unit)) .copied() .unwrap_or(Decimal::ZERO) } /// Get base price for a resource pub fn get_base_price( &self, service_type: ServiceType, resource_unit: ResourceUnit, ) -> Option { self.service_pricing .get(&service_type) .and_then(|p| p.base_prices.get(&resource_unit)) .copied() } /// Get pricing tier by name pub fn get_tier(&self, name: &str) -> Option<&PricingTier> { self.tiers.iter().find(|t| t.name == name) } /// Get all tiers pub fn get_all_tiers(&self) -> &[PricingTier] { &self.tiers } /// Add a custom discount pub fn add_discount(&mut self, discount: Discount) { self.discounts.push(discount); } /// Apply applicable discounts to an amount pub fn apply_discounts( &self, amount: SynorDecimal, account_id: &str, service_type: Option, ) -> (SynorDecimal, Vec) { let mut discounted = amount; let mut applied = Vec::new(); for discount in &self.discounts { if discount.is_applicable(account_id, service_type) { let discount_amount = discount.calculate_discount(discounted); discounted -= discount_amount; applied.push(discount.clone()); } } (discounted, applied) } /// Get pricing summary for display pub fn get_pricing_summary(&self) -> PricingSummary { PricingSummary { storage: ServicePricingSummary { gb_month: self.get_base_price(ServiceType::Storage, ResourceUnit::GbMonth), retrieval_gb: self.get_base_price(ServiceType::Storage, ResourceUnit::BandwidthGb), free_storage_gb: self.get_free_allocation(ServiceType::Storage, ResourceUnit::GbMonth), }, hosting: HostingPricingSummary { bandwidth_gb: self.get_base_price(ServiceType::Hosting, ResourceUnit::BandwidthGb), domain_month: self.get_base_price(ServiceType::Hosting, ResourceUnit::Domains), free_bandwidth_gb: self .get_free_allocation(ServiceType::Hosting, ResourceUnit::BandwidthGb), }, database: DatabasePricingSummary { storage_gb_month: self.get_base_price(ServiceType::Database, ResourceUnit::GbMonth), queries_million: self .get_base_price(ServiceType::Database, ResourceUnit::Queries) .map(|p| p * Decimal::from(1_000_000)), vector_searches_million: self .get_base_price(ServiceType::Database, ResourceUnit::VectorSearches) .map(|p| p * Decimal::from(1_000_000)), free_queries: self .get_free_allocation(ServiceType::Database, ResourceUnit::Queries), }, compute: ComputePricingSummary { cpu_core_hour: self.get_base_price(ServiceType::Compute, ResourceUnit::CpuCoreHours), gpu_hour: self.get_base_price(ServiceType::Compute, ResourceUnit::GpuHours), memory_gb_hour: self .get_base_price(ServiceType::Compute, ResourceUnit::MemoryGbHours), invocations_million: self .get_base_price(ServiceType::Compute, ResourceUnit::Invocations) .map(|p| p * Decimal::from(1_000_000)), free_cpu_hours: self .get_free_allocation(ServiceType::Compute, ResourceUnit::CpuCoreHours), }, } } } impl Default for PricingEngine { fn default() -> Self { Self::new() } } /// Pricing summary for display #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PricingSummary { pub storage: ServicePricingSummary, pub hosting: HostingPricingSummary, pub database: DatabasePricingSummary, pub compute: ComputePricingSummary, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServicePricingSummary { pub gb_month: Option, pub retrieval_gb: Option, pub free_storage_gb: SynorDecimal, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HostingPricingSummary { pub bandwidth_gb: Option, pub domain_month: Option, pub free_bandwidth_gb: SynorDecimal, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DatabasePricingSummary { pub storage_gb_month: Option, pub queries_million: Option, pub vector_searches_million: Option, pub free_queries: SynorDecimal, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComputePricingSummary { pub cpu_core_hour: Option, pub gpu_hour: Option, pub memory_gb_hour: Option, pub invocations_million: Option, pub free_cpu_hours: SynorDecimal, } #[cfg(test)] mod tests { use super::*; use rust_decimal_macros::dec; #[test] fn test_storage_pricing() { let engine = PricingEngine::new(); // 10 GB storage for a month let cost = engine .calculate_cost(ServiceType::Storage, ResourceUnit::GbMonth, dec!(10)) .unwrap(); assert_eq!(cost, dec!(0.20)); // 10 * 0.02 } #[test] fn test_compute_pricing() { let engine = PricingEngine::new(); // 5 GPU hours let cost = engine .calculate_cost(ServiceType::Compute, ResourceUnit::GpuHours, dec!(5)) .unwrap(); assert_eq!(cost, dec!(2.50)); // 5 * 0.50 } #[test] fn test_database_queries() { let engine = PricingEngine::new(); // 10 million queries let cost = engine .calculate_cost(ServiceType::Database, ResourceUnit::Queries, dec!(10_000_000)) .unwrap(); assert_eq!(cost, dec!(0.10)); // 10M * 0.00000001 } #[test] fn test_tier_discount() { let engine = PricingEngine::new(); // Premium tier gets 20% discount let base_cost = dec!(100); let discount = engine.calculate_tier_discount("premium", base_cost).unwrap(); assert_eq!(discount, dec!(20)); // 20% } #[test] fn test_free_allocation() { let engine = PricingEngine::new(); let free_storage = engine.get_free_allocation(ServiceType::Storage, ResourceUnit::GbMonth); assert_eq!(free_storage, dec!(0.5)); let free_queries = engine.get_free_allocation(ServiceType::Database, ResourceUnit::Queries); assert_eq!(free_queries, dec!(1_000_000)); } }