use crate::models::order::{Order, OrderItem, OrderStatus, Cart, CartItem, PaymentDetails, PaymentMethod}; use crate::models::product::Product; use crate::services::{currency::CurrencyService, product::ProductService, user_persistence::UserPersistence}; use rust_decimal::Decimal; use rust_decimal::prelude::ToPrimitive; use rust_decimal_macros::dec; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use chrono::Utc; use uuid::Uuid; use actix_session::Session; use serde::Serialize; /// Service for handling order and cart operations pub struct OrderService { currency_service: CurrencyService, product_service: ProductService, } /// Singleton order storage for demo purposes pub struct OrderStorage { orders: HashMap, } impl OrderStorage { fn new() -> Self { Self { orders: HashMap::default(), } } /// Get singleton instance of order storage pub fn instance() -> Arc> { use std::sync::Once; static mut INSTANCE: Option>> = None; static ONCE: Once = Once::new(); unsafe { ONCE.call_once(|| { INSTANCE = Some(Arc::new(Mutex::new(OrderStorage::new()))); }); INSTANCE.as_ref().unwrap().clone() } } pub fn insert_order(&mut self, order_id: String, order: Order) { self.orders.insert(order_id, order); } pub fn get_order(&self, order_id: &str) -> Option<&Order> { self.orders.get(order_id) } pub fn get_user_orders(&self, user_id: &str) -> Vec<&Order> { self.orders.values() .filter(|order| order.user_id == user_id) .collect() } pub fn update_order_status(&mut self, order_id: &str, status: OrderStatus) -> Result<(), String> { if let Some(order) = self.orders.get_mut(order_id) { order.update_status(status); Ok(()) } else { Err("Order not found".to_string()) } } pub fn get_order_mut(&mut self, order_id: &str) -> Option<&mut Order> { self.orders.get_mut(order_id) } pub fn get_orders_count(&self) -> usize { self.orders.len() } pub fn get_all_orders(&self) -> Vec<&Order> { self.orders.values().collect() } } /// Mock payment gateway for simulating payment processing pub struct MockPaymentGateway { pub success_rate: f32, // Configurable for testing } /// Payment request structure #[derive(Debug, Clone)] pub struct PaymentRequest { pub order_id: String, pub amount: Decimal, pub currency: String, pub payment_method: PaymentMethod, pub user_id: String, } /// Payment result #[derive(Debug, Clone)] pub struct PaymentResult { pub success: bool, pub transaction_id: Option, pub error_message: Option, pub payment_details: Option, } impl OrderService { pub fn new() -> Self { Self { currency_service: CurrencyService::new(), product_service: ProductService::new(), } } pub fn new_with_config( currency_service: CurrencyService, product_service: ProductService, _auto_save: bool, ) -> Self { Self { currency_service, product_service, } } pub fn builder() -> crate::models::builders::OrderServiceBuilder { crate::models::builders::OrderServiceBuilder::new() } // Cart Operations /// Get or create cart for user from session pub fn get_or_create_cart(&self, user_id: &str, session: &Session) -> Cart { let cart_key = if user_id.is_empty() { "guest_cart" } else { "user_cart" }; // For logged-in users, ALWAYS load from persistent storage first if !user_id.is_empty() { let persistent_cart = self.load_user_cart_from_storage(user_id); // Update session with persistent cart data to ensure consistency if let Err(e) = self.save_cart_to_session(user_id, session, &persistent_cart) { } return persistent_cart; } // For guest users, try to get cart from session if let Ok(Some(cart_json)) = session.get::(cart_key) { if let Ok(cart) = serde_json::from_str::(&cart_json) { return cart; } } // Create new cart for guest users Cart::new(user_id.to_string()) } /// Load user cart from persistent storage fn load_user_cart_from_storage(&self, user_id: &str) -> Cart { // Sanitize user ID for filesystem compatibility let sanitized_id = user_id.replace('@', "_at_").replace('.', "_"); let cart_file = format!("user_data/{}_cart.json", sanitized_id); if let Ok(cart_json) = std::fs::read_to_string(&cart_file) { match serde_json::from_str::(&cart_json) { Ok(cart) => { return cart; }, Err(e) => { // Return empty cart instead of corrupted data return Cart::new(user_id.to_string()); } } } else { } Cart::new(user_id.to_string()) } /// Save user cart to persistent storage fn save_user_cart_to_storage(&self, user_id: &str, cart: &Cart) -> Result<(), String> { // Sanitize user ID for filesystem compatibility let sanitized_id = user_id.replace('@', "_at_").replace('.', "_"); let cart_file = format!("user_data/{}_cart.json", sanitized_id); // Ensure user_data directory exists let _ = std::fs::create_dir_all("user_data"); let cart_json = serde_json::to_string_pretty(cart) .map_err(|e| format!("Failed to serialize cart: {}", e))?; std::fs::write(&cart_file, cart_json) .map_err(|e| format!("Failed to save cart: {}", e)) } /// Save cart to session fn save_cart_to_session(&self, user_id: &str, session: &Session, cart: &Cart) -> Result<(), String> { let cart_key = if user_id.is_empty() { "guest_cart" } else { "user_cart" }; let cart_json = serde_json::to_string(cart) .map_err(|e| format!("Failed to serialize cart: {}", e))?; session.insert(cart_key, cart_json) .map_err(|e| format!("Failed to save cart to session: {}", e))?; Ok(()) } /// Add item to cart pub fn add_to_cart( &self, user_id: &str, session: &Session, product_id: String, quantity: u32, specifications: Option>, ) -> Result<(), String> { // Debug: Log available products let all_products = self.product_service.get_all_products(); // Validate product exists if let Some(found_product) = self.product_service.get_product_by_id(&product_id) { } else { return Err("Product not found".to_string()); } eprintln!("[ORDER SERVICE] Getting or creating cart for user_id: '{}'", user_id); let mut cart = self.get_or_create_cart(user_id, session); eprintln!("[ORDER SERVICE] Cart retrieved/created - current items: {}", cart.items.len()); let cart_item = if let Some(specs) = specifications { CartItem::with_specifications(product_id.clone(), quantity, specs) } else { CartItem::new(product_id.clone(), quantity) }; eprintln!("[ORDER SERVICE] Created cart item for product: {}", product_id); cart.add_item(cart_item); eprintln!("[ORDER SERVICE] Added item to cart - total items now: {}", cart.items.len()); eprintln!("[ORDER SERVICE] Saving cart to session..."); match self.save_cart_to_session(user_id, session, &cart) { Ok(()) => eprintln!("[ORDER SERVICE] Cart saved to session successfully"), Err(e) => { eprintln!("[ORDER SERVICE ERROR] Failed to save cart to session: {}", e); return Err(e); } } // Save to persistent storage for logged-in users if !user_id.is_empty() { eprintln!("[ORDER SERVICE] Saving cart to persistent storage for user: {}", user_id); match self.save_user_cart_to_storage(user_id, &cart) { Ok(()) => eprintln!("[ORDER SERVICE] Cart saved to persistent storage successfully"), Err(e) => { eprintln!("[ORDER SERVICE ERROR] Failed to save cart to persistent storage: {}", e); return Err(e); } } } else { eprintln!("[ORDER SERVICE] Guest user - skipping persistent storage"); } eprintln!("[ORDER SERVICE] add_to_cart completed successfully"); Ok(()) } /// Remove item from cart pub fn remove_from_cart( &self, user_id: &str, session: &Session, product_id: &str, ) -> Result { let mut cart = self.get_or_create_cart(user_id, session); let removed = cart.remove_item(product_id); if removed { // Save to session first self.save_cart_to_session(user_id, session, &cart)?; // For logged-in users, also save to persistent storage if !user_id.is_empty() { self.save_user_cart_to_storage(user_id, &cart)?; } } Ok(removed) } /// Update cart item quantity pub fn update_cart_item_quantity( &self, user_id: &str, session: &Session, product_id: &str, quantity: u32, ) -> Result { let mut cart = self.get_or_create_cart(user_id, session); let result = cart.update_item_quantity(product_id, quantity); self.save_cart_to_session(user_id, session, &cart)?; // Save to persistent storage for logged-in users if !user_id.is_empty() { self.save_user_cart_to_storage(user_id, &cart)?; } Ok(result) } /// Get cart contents with product details and pricing pub fn get_cart_with_details( &self, user_id: &str, session: &Session, display_currency: &str, ) -> Result { let mut cart = self.get_or_create_cart(user_id, session); let mut cart_details = CartDetails { cart: cart.clone(), items: Vec::default(), subtotal: dec!(0), total: dec!(0), currency: display_currency.to_string(), item_count: 0, }; let mut removed_invalid = false; for cart_item in &cart.items { if let Some(product) = self.product_service.get_product_by_id(&cart_item.product_id) { let price = self.currency_service.create_price( product.base_price, &product.base_currency, display_currency, )?; let item_total = price.display_amount * Decimal::from(cart_item.quantity); cart_details.subtotal += item_total; cart_details.items.push(CartItemDetails { cart_item: cart_item.clone(), product: product.clone(), unit_price: price, total_price: item_total, }); } else { // Product no longer exists; mark for cleanup and exclude from details removed_invalid = true; } } // Compute item_count based on valid items only cart_details.item_count = cart_details .items .iter() .map(|i| i.cart_item.quantity) .sum(); cart_details.total = cart_details.subtotal; // Add taxes, fees, etc. here // If invalid items were found, clean them from the persisted cart and session if removed_invalid { // Retain only items that still have valid products cart.items.retain(|ci| self.product_service.get_product_by_id(&ci.product_id).is_some()); // Update metadata cart.updated_at = chrono::Utc::now(); // Persist the cleaned cart let _ = self.save_cart_to_session(user_id, session, &cart); if !user_id.is_empty() { let _ = self.save_user_cart_to_storage(user_id, &cart); } // Ensure returned details reflect cleaned cart structure cart_details.cart = cart.clone(); } Ok(cart_details) } /// Clear cart pub fn clear_cart(&self, user_id: &str, session: &Session) -> Result<(), String> { let mut cart = self.get_or_create_cart(user_id, session); cart.clear(); // Save cleared cart to session self.save_cart_to_session(user_id, session, &cart)?; // For logged-in users, also clear persistent storage if !user_id.is_empty() { self.save_user_cart_to_storage(user_id, &cart)?; } Ok(()) } /// Transfer guest cart items to user cart during login pub fn transfer_guest_cart_to_user(&self, user_id: &str, session: &Session) -> Result { // Get guest cart if it exists let guest_cart = if let Ok(Some(guest_cart_json)) = session.get::("guest_cart") { if let Ok(guest_cart) = serde_json::from_str::(&guest_cart_json) { guest_cart } else { return Ok(0); } } else { return Ok(0); }; // If guest cart is empty, nothing to transfer if guest_cart.items.is_empty() { return Ok(0); } // Load user's existing persistent cart from user data let mut user_cart = self.load_user_cart_from_storage(user_id); let items_transferred = guest_cart.items.len(); // Transfer all items from guest cart to user cart for guest_item in guest_cart.items { // Update the cart item to reflect the new user let mut user_item = guest_item; user_item.updated_at = chrono::Utc::now(); // Check if item already exists in user cart if let Some(existing_item) = user_cart.items.iter_mut().find(|item| item.product_id == user_item.product_id) { // Merge quantities if same product exists let old_qty = existing_item.quantity; existing_item.quantity += user_item.quantity; existing_item.updated_at = chrono::Utc::now(); } else { // Add new item to user cart user_cart.items.push(user_item.clone()); } } // Update user cart metadata user_cart.user_id = user_id.to_string(); user_cart.updated_at = chrono::Utc::now(); // Save updated user cart to persistent storage FIRST match self.save_user_cart_to_storage(user_id, &user_cart) { Ok(()) => { // Cart saved successfully to storage }, Err(e) => { return Err(format!("Failed to save cart to storage: {}", e)); } } // Then save to session match self.save_cart_to_session(user_id, session, &user_cart) { Ok(()) => { // Don't fail the transfer if session save fails, persistent storage is more important }, Err(_) => { // Don't fail the transfer if session save fails, persistent storage is more important } } // Clear guest cart after successful transfer session.remove("guest_cart"); Ok(items_transferred) } // Order Operations /// Create order from cart pub fn create_order_from_cart( &self, user_id: &str, session: &Session, payment_currency: &str, ) -> Result { let cart_details = self.get_cart_with_details(user_id, session, payment_currency)?; if cart_details.items.is_empty() { return Err("Cart is empty".to_string()); } let order_id = Uuid::new_v4().to_string(); // Use CurrencyService base currency (USD) rather than mock data let base_currency = self.currency_service.get_base_currency().code.clone(); let conversion_rate = if payment_currency == base_currency { dec!(1.0) } else { self.currency_service.get_exchange_rate_to_base(payment_currency)? }; // Normalize user ID for consistent guest user handling let normalized_user_id = if user_id.is_empty() { "guest".to_string() } else { user_id.to_string() }; let mut order = Order::new( order_id.clone(), normalized_user_id, base_currency.clone(), payment_currency.to_string(), conversion_rate, ); // Convert cart items to order items for item_detail in cart_details.items { let order_item = OrderItem::new( item_detail.product.id, item_detail.product.name, item_detail.product.category_id, item_detail.cart_item.quantity, item_detail.product.base_price, item_detail.product.provider_id, item_detail.product.provider_name, ); order.add_item(order_item); } // Store order in singleton storage { let order_storage = OrderStorage::instance(); let mut storage = order_storage.lock().unwrap(); storage.insert_order(order_id.clone(), order); } // Clear the cart after creating order self.clear_cart(user_id, session)?; Ok(order_id) } /// Get order by ID pub fn get_order(&self, order_id: &str) -> Option { let order_storage = OrderStorage::instance(); let storage = order_storage.lock().unwrap(); storage.get_order(order_id).cloned() } /// Get orders for user (sorted by creation date, latest first) pub fn get_user_orders(&self, user_id: &str) -> Vec { // Load user's persistent data and return their orders if let Some(user_data) = crate::services::user_persistence::UserPersistence::load_user_data(user_id) { let mut orders = user_data.orders; // Sort by created_at in descending order (latest first) orders.sort_by(|a, b| b.created_at.cmp(&a.created_at)); orders } else { Vec::new() } } /// Update order status pub fn update_order_status(&self, order_id: &str, status: OrderStatus) -> Result<(), String> { let order_storage = OrderStorage::instance(); let mut storage = order_storage.lock().unwrap(); storage.update_order_status(order_id, status) } /// Process payment for order pub fn process_payment( &self, order_id: &str, payment_method: PaymentMethod, ) -> Result { let order = { let order_storage = OrderStorage::instance(); let storage = order_storage.lock().unwrap(); storage.get_order(order_id).cloned() .ok_or("Order not found")? }; // Check if this is a wallet payment and handle it with persistent data let is_wallet_payment = matches!(payment_method, PaymentMethod::Token { ref token_type, .. } if token_type == "USD" ); let payment_result = if is_wallet_payment { // Handle wallet payment with persistent data self.process_wallet_payment_with_persistent_data(&order, &payment_method)? } else { // Use MockPaymentGateway for other payment methods let payment_request = PaymentRequest { order_id: order_id.to_string(), amount: order.currency_total, currency: order.currency_used.clone(), payment_method: payment_method.clone(), user_id: order.user_id.clone(), }; let gateway = MockPaymentGateway { success_rate: 0.95 }; gateway.process_payment(payment_request) }; // Update order with payment details { let order_storage = OrderStorage::instance(); let mut storage = order_storage.lock().unwrap(); if let Some(order) = storage.get_order_mut(order_id) { if payment_result.success { order.update_status(OrderStatus::Completed); if let Some(payment_details) = &payment_result.payment_details { order.set_payment_details(payment_details.clone()); } // PHASE 1: Create application deployments for successful app orders if let Err(e) = self.create_application_deployments_from_order(&order) { } // PHASE 2: Create service bookings for successful service orders if let Err(e) = self.create_service_bookings_from_order(&order) { } } else { order.update_status(OrderStatus::Failed); } } } Ok(payment_result) } /// Process wallet payment using persistent data (no mock code) fn process_wallet_payment_with_persistent_data( &self, order: &Order, payment_method: &PaymentMethod, ) -> Result { use crate::services::user_persistence::UserPersistence; // Extract user email from user_id (assuming user_id is email) let user_email = &order.user_id; // Load user data let mut persistent_data = UserPersistence::load_user_data(user_email) .ok_or_else(|| "User data not found".to_string())?; // Check if user has sufficient balance if persistent_data.wallet_balance_usd < order.currency_total { let mut payment_details = PaymentDetails::new( Uuid::new_v4().to_string(), payment_method.clone(), ); payment_details.mark_failed(format!( "Insufficient balance. Required: ${:.2}, Available: ${:.2}", order.currency_total, persistent_data.wallet_balance_usd )); return Ok(PaymentResult { success: false, transaction_id: None, error_message: Some(format!( "Insufficient balance. Required: ${:.2}, Available: ${:.2}", order.currency_total, persistent_data.wallet_balance_usd )), payment_details: Some(payment_details), }); } // Deduct balance persistent_data.wallet_balance_usd -= order.currency_total; // Create transaction record let transaction_id = Uuid::new_v4().to_string(); // Use the first product ID for the transaction (TransactionType::Purchase only supports single product_id) let product_id = order.items.first() .map(|item| item.product_id.clone()) .unwrap_or_else(|| "unknown".to_string()); let transaction = crate::models::user::Transaction { id: transaction_id.clone(), user_id: user_email.clone(), transaction_type: crate::models::user::TransactionType::Purchase { product_id, }, amount: order.currency_total, currency: Some("USD".to_string()), exchange_rate_usd: Some(rust_decimal::Decimal::ONE), amount_usd: Some(order.currency_total), description: Some(format!("Order {} payment", order.id)), reference_id: Some(order.id.clone()), metadata: None, timestamp: chrono::Utc::now(), status: crate::models::user::TransactionStatus::Completed, }; // Add transaction to history persistent_data.transactions.push(transaction); // Build payment details now so we can persist them with the order let mut payment_details = PaymentDetails::new( transaction_id.clone(), payment_method.clone(), ); payment_details.mark_completed(transaction_id.clone()); // Persist the order as Completed with payment details for dashboard views let mut order_to_persist = order.clone(); order_to_persist.update_status(OrderStatus::Completed); order_to_persist.set_payment_details(payment_details.clone()); persistent_data.orders.push(order_to_persist); // Save updated user data (includes transactions and updated order) UserPersistence::save_user_data(&persistent_data) .map_err(|e| format!("Failed to save user data: {}", e))?; // Create successful payment result Ok(PaymentResult { success: true, transaction_id: Some(transaction_id), error_message: None, payment_details: Some(payment_details), }) } /// Get order statistics pub fn get_order_statistics(&self) -> HashMap { let order_storage = OrderStorage::instance(); let storage = order_storage.lock().unwrap(); let mut stats = HashMap::default(); stats.insert("total_orders".to_string(), serde_json::Value::Number(serde_json::Number::from(storage.get_orders_count()))); // Orders by status let mut status_counts: HashMap = HashMap::default(); for order in storage.get_all_orders() { let status_str = format!("{:?}", order.status); *status_counts.entry(status_str).or_insert(0) += 1; } let status_stats: Vec = status_counts.iter() .map(|(status, count)| { serde_json::json!({ "status": status, "count": count }) }) .collect(); stats.insert("orders_by_status".to_string(), serde_json::Value::Array(status_stats)); // Revenue calculation let total_revenue: Decimal = storage.get_all_orders().iter() .filter(|o| matches!(o.status, OrderStatus::Completed | OrderStatus::Confirmed)) .map(|o| o.total_base) .sum(); // Use CurrencyService base currency (USD) rather than mock data let currency = self.currency_service.get_base_currency().code.clone(); stats.insert("total_revenue".to_string(), serde_json::json!({ "amount": total_revenue.to_string(), "currency": currency })); stats } /// PHASE 1: Create application deployments when apps are successfully ordered fn create_application_deployments_from_order(&self, order: &Order) -> Result<(), String> { use crate::services::user_persistence::{UserPersistence, AppDeployment}; use crate::models::user::ResourceUtilization; use chrono::Utc; // Get customer information from order let customer_email = order.user_id.clone(); let customer_name = if customer_email == "guest" { "Guest User".to_string() } else { // Try to get customer name from persistent data if let Some(customer_data) = UserPersistence::load_user_data(&customer_email) { customer_data.name.unwrap_or_else(|| customer_email.clone()) } else { customer_email.clone() } }; // Process each order item for item in &order.items { // Only create deployments for application products if item.product_category == "application" { // Find the application provider by looking up who published this app if let Some(application_provider_email) = self.find_application_provider(&item.product_id) { // Create deployment for each quantity ordered for _i in 0..item.quantity { let deployment_id = format!("dep-{}-{}", &order.id[..8], &uuid::Uuid::new_v4().to_string()[..8] ); let deployment = AppDeployment::builder() .id(&deployment_id) .app_id(&item.product_id) .app_name(&item.product_name) .customer_name(&customer_name) .customer_email(&customer_email) .deployed_date(&Utc::now().format("%Y-%m-%d").to_string()) .status("Active") .health_score(98.0 + (rand::random::() * 2.0)) // 98-100% .region(&self.select_deployment_region()) .instances(1) // Default to 1 instance per deployment .resource_usage(ResourceUtilization { cpu: 15 + (rand::random::() % 20), // 15-35% memory: 25 + (rand::random::() % 30), // 25-55% storage: 10 + (rand::random::() % 15), // 10-25% network: 5 + (rand::random::() % 10), // 5-15% }) .monthly_revenue_usd((item.unit_price_base.to_f64().unwrap_or(0.0) * 0.8) as i32) // 80% to provider .last_updated(&Utc::now().format("%Y-%m-%d %H:%M:%S").to_string()) .auto_healing(true) // Default to enabled for new deployments .build() .unwrap(); // Add deployment to application provider's data if let Err(e) = UserPersistence::add_user_application_deployment(&application_provider_email, deployment.clone()) { } else { } // Also add deployment to customer's data (for future user dashboard) if customer_email != "guest" { if let Err(e) = UserPersistence::add_user_application_deployment(&customer_email, deployment) { } else { } } } } else { } } } Ok(()) } /// Find the application provider (user who published the app) by product ID fn find_application_provider(&self, product_id: &str) -> Option { // Get all user data files and search for the app let user_data_dir = std::path::Path::new("user_data"); if !user_data_dir.exists() { return None; } if let Ok(entries) = std::fs::read_dir(user_data_dir) { for entry in entries.flatten() { if let Some(file_name) = entry.file_name().to_str() { if file_name.ends_with(".json") && file_name.contains("_at_") && !file_name.contains("_cart") && file_name != "session_data.json" { // Extract email from filename let user_email = file_name .trim_end_matches(".json") .replace("_at_", "@") .replace("_", "."); // Check if this user has the app let user_apps = UserPersistence::get_user_apps(&user_email); for app in user_apps { if app.id == product_id { return Some(user_email); } } } } } } None } /// Select a deployment region (simple round-robin for demo) fn select_deployment_region(&self) -> String { let regions = vec![ "US-East-1", "US-West-2", "EU-Central-1", "Asia-Pacific-1", "Canada-Central-1" ]; let index = (rand::random::()) % regions.len(); regions[index].to_string() } /// PHASE 2: Create service bookings when services are successfully ordered pub fn create_service_bookings_from_order(&self, order: &Order) -> Result<(), String> { use crate::services::user_persistence::{UserPersistence}; use crate::models::user::{ServiceRequest}; use chrono::Utc; // Get customer information from order let customer_email = order.user_id.clone(); let customer_name = if customer_email == "guest" { "Guest User".to_string() } else { // Try to get customer name from persistent data if let Some(customer_data) = UserPersistence::load_user_data(&customer_email) { customer_data.name.unwrap_or_else(|| customer_email.clone()) } else { customer_email.clone() } }; log::info!( target: "order_service", "create_service_bookings_from_order:start order_id={} customer_email={}", order.id, customer_email ); // Process each order item for item in &order.items { // Only create bookings for service products if item.product_category == "service" { // Find the service provider by looking up who published this service if let Some(service_provider_email) = self.find_service_provider(&item.product_id) { log::info!( target: "order_service", "provider_found order_id={} product_id={} provider_email={}", order.id, item.product_id, service_provider_email ); // Create service request for provider (same as existing pattern) for _i in 0..item.quantity { let request_id = format!("req-{}-{}", &order.id[..8], &uuid::Uuid::new_v4().to_string()[..8] ); let service_request = ServiceRequest { id: request_id.clone(), customer_email: customer_email.clone(), service_id: item.product_id.clone(), description: Some(format!("Service booking from marketplace order {}", order.id)), status: "Pending".to_string(), estimated_hours: Some((item.unit_price_base.to_f64().unwrap_or(0.0) / 75.0) as i32), // Assume $75/hour hourly_rate_usd: Some(rust_decimal::Decimal::from(75)), total_cost_usd: Some(item.unit_price_base), progress_percentage: Some(0.0), created_date: Some(Utc::now().format("%Y-%m-%d").to_string()), completed_date: None, progress: None, priority: "Medium".to_string(), hours_worked: None, notes: None, service_name: item.product_name.clone(), budget: item.unit_price_base, requested_date: Utc::now().format("%Y-%m-%d").to_string(), client_phone: None, client_name: Some(customer_name.clone()), client_email: Some(customer_email.clone()), }; // Add request to service provider's data if let Err(e) = UserPersistence::add_user_service_request(&service_provider_email, service_request.clone()) { log::error!( target: "order_service", "add_user_service_request:failed order_id={} provider_email={} request_id={} err={}", order.id, service_provider_email, request_id, e ); return Err(format!( "Failed to persist service request for provider {}: {}", service_provider_email, e )); } else { log::info!( target: "order_service", "add_user_service_request:succeeded order_id={} provider_email={} request_id={}", order.id, service_provider_email, request_id ); } // Create service booking for customer let service_booking = UserPersistence::create_service_booking_from_request( &service_request, &customer_email, &service_provider_email ); // Add booking to customer's data if customer_email != "guest" { if let Err(e) = UserPersistence::add_user_service_booking(&customer_email, service_booking.clone()) { log::error!( target: "order_service", "add_user_service_booking:failed order_id={} customer_email={} booking_id={} err={}", order.id, customer_email, service_booking.id, e ); return Err(format!( "Failed to persist service booking for customer {}: {}", customer_email, e )); } else { log::info!( target: "order_service", "add_user_service_booking:succeeded order_id={} customer_email={} booking_id={}", order.id, customer_email, service_booking.id ); } } } } else { log::warn!( target: "order_service", "provider_not_found order_id={} product_id={} product_name={}", order.id, item.product_id, item.product_name ); } } } log::info!( target: "order_service", "create_service_bookings_from_order:success order_id={}", order.id ); Ok(()) } /// Find the service provider (user who published the service) by product ID fn find_service_provider(&self, product_id: &str) -> Option { // Search per-user persisted data first let user_data_dir = std::path::Path::new("user_data"); if user_data_dir.exists() { if let Ok(entries) = std::fs::read_dir(user_data_dir) { for entry in entries.flatten() { if let Some(file_name) = entry.file_name().to_str() { if file_name.ends_with(".json") && file_name.contains("_at_") && !file_name.contains("_cart") && file_name != "session_data.json" { // Extract email from filename let user_email = file_name .trim_end_matches(".json") .replace("_at_", "@") .replace("_", "."); // Check if this user has the service let user_services = UserPersistence::get_user_services(&user_email); for service in user_services { if service.id == product_id { return Some(user_email); } } // Also check if this user has the product (published services live under products) let user_products = UserPersistence::get_user_products(&user_email); for product in user_products { if product.id == product_id { return Some(user_email); } } } } } } } // Fallback: look up in the global product catalog to resolve provider let all_products = self.product_service.get_all_products(); if let Some(p) = all_products.into_iter().find(|p| p.id == product_id) { return Some(p.provider_id); } // If nothing matched, return None None } } /// Cart details with product information and pricing #[derive(Debug, Clone, Serialize)] pub struct CartDetails { pub cart: Cart, pub items: Vec, pub subtotal: Decimal, pub total: Decimal, pub currency: String, pub item_count: u32, } /// Cart item with product details and pricing #[derive(Debug, Clone, Serialize)] pub struct CartItemDetails { pub cart_item: CartItem, pub product: Product, pub unit_price: crate::models::currency::Price, pub total_price: Decimal, } impl MockPaymentGateway { pub fn process_payment(&self, request: PaymentRequest) -> PaymentResult { // Simulate payment processing delay std::thread::sleep(std::time::Duration::from_millis(100)); // Simulate success/failure based on success rate let success = rand::random::() < self.success_rate; if success { let transaction_id = Uuid::new_v4().to_string(); let mut payment_details = PaymentDetails::new( Uuid::new_v4().to_string(), request.payment_method, ); payment_details.mark_completed(transaction_id.clone()); PaymentResult { success: true, transaction_id: Some(transaction_id), error_message: None, payment_details: Some(payment_details), } } else { let mut payment_details = PaymentDetails::new( Uuid::new_v4().to_string(), request.payment_method, ); payment_details.mark_failed("Payment processing failed".to_string()); PaymentResult { success: false, transaction_id: None, error_message: Some("Payment processing failed. Please try again.".to_string()), payment_details: Some(payment_details), } } } } impl Default for OrderService { fn default() -> Self { Self::new() } } /// Utility functions for order operations pub mod utils { use super::*; /// Calculate order total with taxes and fees pub fn calculate_order_total( subtotal: Decimal, tax_rate: Option, shipping_fee: Option, discount: Option, ) -> Decimal { let mut total = subtotal; // Apply tax if let Some(rate) = tax_rate { total += subtotal * Decimal::from_f32_retain(rate).unwrap_or(dec!(0)); } // Add shipping if let Some(shipping) = shipping_fee { total += shipping; } // Apply discount if let Some(discount_amount) = discount { total -= discount_amount; } total.max(dec!(0)) // Ensure total is not negative } /// Generate order confirmation number pub fn generate_order_confirmation(order_id: &str) -> String { let timestamp = Utc::now().timestamp(); let short_id = &order_id[..8]; format!("TF-{}-{}", timestamp, short_id.to_uppercase()) } /// Validate payment method pub fn validate_payment_method(payment_method: &PaymentMethod) -> Result<(), String> { match payment_method { PaymentMethod::CreditCard { last_four, .. } => { if last_four.len() != 4 || !last_four.chars().all(|c| c.is_ascii_digit()) { return Err("Invalid credit card last four digits".to_string()); } }, PaymentMethod::BankTransfer { account_last_four, .. } => { if account_last_four.len() != 4 || !account_last_four.chars().all(|c| c.is_ascii_digit()) { return Err("Invalid bank account last four digits".to_string()); } }, PaymentMethod::Cryptocurrency { wallet_address, .. } => { if wallet_address.is_empty() { return Err("Wallet address is required".to_string()); } }, PaymentMethod::Token { wallet_address, .. } => { if wallet_address.is_empty() { return Err("Wallet address is required".to_string()); } }, PaymentMethod::Mock { .. } => { // Mock payment method is always valid }, } Ok(()) } }