Spaces:
Running
Running
| use crate::domain::error::{AppError, AppResult}; | |
| use crate::domain::models::{OrderRecord, ProductLink}; | |
| use crate::infrastructure::db::DbPool; | |
| use crate::infrastructure::repositories::{OrderRepository, ProductRepository}; | |
| use async_trait::async_trait; | |
| use std::sync::Arc; | |
| pub trait CheckoutService: Send + Sync { | |
| async fn get_checkout_view(&self, link_id: &str) -> AppResult<ProductLink>; | |
| async fn execute_checkout( | |
| &self, | |
| link_id: &str, | |
| buyer_phone: &str, | |
| buyer_name: &str, | |
| buyer_email: &str, | |
| shipping_pincode: &str, | |
| delivery_address: &str, | |
| coupon_code: Option<String>, | |
| request_id: Option<String>, | |
| client_ip: &str, | |
| lat: Option<f64>, | |
| lng: Option<f64>, | |
| device_fingerprint: Option<String>, | |
| ) -> AppResult<OrderRecord>; | |
| async fn execute_cart_checkout( | |
| &self, | |
| items: Vec<(String, u32)>, // (link_id, quantity) | |
| buyer_phone: &str, | |
| buyer_name: &str, | |
| buyer_email: &str, | |
| shipping_pincode: &str, | |
| delivery_address: &str, | |
| coupon_code: Option<String>, | |
| request_id: Option<String>, | |
| client_ip: &str, | |
| lat: Option<f64>, | |
| lng: Option<f64>, | |
| device_fingerprint: Option<String>, | |
| ) -> AppResult<OrderRecord>; | |
| async fn submit_delivery_proof( | |
| &self, | |
| transaction_id: &str, | |
| proof_data: &str, | |
| proof_token: &str, | |
| lat: Option<f64>, | |
| lng: Option<f64>, | |
| ) -> AppResult<()>; | |
| async fn estimate_delivery( | |
| &self, | |
| link_id: &str, | |
| pincode: &str, | |
| _buyer_phone: Option<String>, | |
| ) -> AppResult<(f64, f64)>; | |
| } | |
| pub struct RtixCheckoutService { | |
| product_repo: Arc<dyn ProductRepository>, | |
| merchant_repo: Arc<dyn crate::infrastructure::repositories::MerchantRepository>, | |
| order_repo: Arc<dyn OrderRepository>, | |
| assets: Arc<dyn crate::infrastructure::storage::assets::AssetProvider>, | |
| tx: tokio::sync::broadcast::Sender<crate::interfaces::http::api::RealtimeEvent>, | |
| pool: DbPool, | |
| } | |
| impl RtixCheckoutService { | |
| pub fn new( | |
| product_repo: Arc<dyn ProductRepository>, | |
| merchant_repo: Arc<dyn crate::infrastructure::repositories::MerchantRepository>, | |
| order_repo: Arc<dyn OrderRepository>, | |
| assets: Arc<dyn crate::infrastructure::storage::assets::AssetProvider>, | |
| tx: tokio::sync::broadcast::Sender<crate::interfaces::http::api::RealtimeEvent>, | |
| pool: DbPool, | |
| ) -> Self { | |
| Self { | |
| product_repo, | |
| merchant_repo, | |
| order_repo, | |
| assets, | |
| tx, | |
| pool, | |
| } | |
| } | |
| } | |
| impl CheckoutService for RtixCheckoutService { | |
| async fn get_checkout_view(&self, link_id: &str) -> AppResult<ProductLink> { | |
| let product = self.product_repo.find_by_id(link_id).await?; | |
| let mut product = | |
| product.ok_or_else(|| AppError::NotFound("Product link not found".to_string()))?; | |
| let _ = self.product_repo.increment_views(link_id).await; | |
| product.image_data = crate::core::utils::hydrate_file_to_base64(product.image_data).await; | |
| Ok(product) | |
| } | |
| async fn execute_checkout( | |
| &self, | |
| link_id: &str, | |
| buyer_phone: &str, | |
| buyer_name: &str, | |
| buyer_email: &str, | |
| shipping_pincode: &str, | |
| delivery_address: &str, | |
| coupon_code: Option<String>, | |
| request_id: Option<String>, | |
| client_ip: &str, | |
| lat: Option<f64>, | |
| lng: Option<f64>, | |
| device_fingerprint: Option<String>, | |
| ) -> AppResult<OrderRecord> { | |
| crate::application::services::checkout_impl::execute_checkout_helper( | |
| &self.product_repo, | |
| &self.merchant_repo, | |
| &self.order_repo, | |
| &self.pool, | |
| &self.tx, | |
| link_id, | |
| buyer_phone, | |
| buyer_name, | |
| buyer_email, | |
| shipping_pincode, | |
| delivery_address, | |
| coupon_code, | |
| request_id, | |
| client_ip, | |
| lat, | |
| lng, | |
| device_fingerprint, | |
| ) | |
| .await | |
| } | |
| async fn execute_cart_checkout( | |
| &self, | |
| items: Vec<(String, u32)>, | |
| buyer_phone: &str, | |
| buyer_name: &str, | |
| buyer_email: &str, | |
| shipping_pincode: &str, | |
| delivery_address: &str, | |
| coupon_code: Option<String>, | |
| request_id: Option<String>, | |
| client_ip: &str, | |
| lat: Option<f64>, | |
| lng: Option<f64>, | |
| device_fingerprint: Option<String>, | |
| ) -> AppResult<OrderRecord> { | |
| crate::application::services::checkout_impl::execute_cart_checkout_helper( | |
| &self.product_repo, | |
| &self.merchant_repo, | |
| &self.order_repo, | |
| &self.pool, | |
| &self.tx, | |
| items, | |
| buyer_phone, | |
| buyer_name, | |
| buyer_email, | |
| shipping_pincode, | |
| delivery_address, | |
| coupon_code, | |
| request_id, | |
| client_ip, | |
| lat, | |
| lng, | |
| device_fingerprint, | |
| ) | |
| .await | |
| } | |
| async fn submit_delivery_proof( | |
| &self, | |
| transaction_id: &str, | |
| proof_data: &str, | |
| proof_token: &str, | |
| lat: Option<f64>, | |
| lng: Option<f64>, | |
| ) -> AppResult<()> { | |
| crate::application::services::checkout_impl::execute_submit_delivery_proof_helper( | |
| &self.order_repo, | |
| &self.assets, | |
| &self.tx, | |
| transaction_id, | |
| proof_data, | |
| proof_token, | |
| lat, | |
| lng, | |
| ) | |
| .await | |
| } | |
| async fn estimate_delivery( | |
| &self, | |
| link_id: &str, | |
| pincode: &str, | |
| _buyer_phone: Option<String>, | |
| ) -> AppResult<(f64, f64)> { | |
| let product = self | |
| .product_repo | |
| .find_by_id(link_id) | |
| .await? | |
| .ok_or_else(|| AppError::NotFound("Product link not found".to_string()))?; | |
| let merchant = self | |
| .merchant_repo | |
| .find_by_id(&product.merchant_id) | |
| .await? | |
| .ok_or_else(|| AppError::NotFound("Merchant not found".to_string()))?; | |
| let distance_km = | |
| crate::domain::distance::estimate_distance_km(&merchant.base_pincode, pincode); | |
| let pricing_features = crate::application::services::pricing::PricingFeatures { | |
| distance_km, | |
| user_rate_per_km: merchant.delivery_rate_per_km, | |
| product_weight: product.expected_weight, | |
| base_charge: merchant.delivery_base_fee, | |
| config: serde_json::from_value(merchant.logistics_config.clone()).unwrap_or_default(), | |
| }; | |
| let fee = crate::application::services::pricing::PricingEngine::estimate_delivery_fee( | |
| pricing_features, | |
| ); | |
| Ok((fee, distance_km)) | |
| } | |
| } | |