""" Payment Orchestrator Module This module coordinates the execution of different payment processing modes. """ import logging from datetime import datetime from typing import Dict, Any, Optional from flask import Flask logger = logging.getLogger(__name__) class PaymentOrchestrator: """ Orchestrator for payment processing operations. This class coordinates the execution of different payment processing modes (batch, payment plan, payment intent follow-up, refund follow-up). """ VALID_MODES = ["batch", "payplan", "payintent", "refund"] def __init__(self, app: Flask, config, process_live: bool = False): """ Initialize the payment orchestrator. Args: app: Flask application instance config: Configuration object process_live: Whether to process in live mode """ self.app = app self.config = config self.process_live = process_live self.logger = logging.getLogger(self.__class__.__name__) # Initialize components (deferred to run()) self.stripe_processor = None self.splynx_repo = None self.payment_repo = None def run(self, mode: str) -> Dict[str, Any]: """ Run payment processing for the specified mode. Args: mode: Processing mode ("batch", "payplan", "payintent", "refund") Returns: Dictionary with processing results """ if mode not in self.VALID_MODES: error_msg = f"Invalid mode '{mode}'. Valid modes: {', '.join(self.VALID_MODES)}" self.logger.error(error_msg) return { 'success': False, 'error': error_msg, 'mode': mode } start_time = datetime.now() # Log script start try: from services import log_script_start environment = "live" if self.process_live else "sandbox" log_script_start("query_mysql_new.py", mode, environment) except Exception as e: self.logger.warning(f"Failed to log script start: {e}") # Initialize components within Flask app context with self.app.app_context(): self._initialize_components() try: # Execute the appropriate processing mode if mode == "batch": result = self._process_batch() elif mode == "payplan": result = self._process_payment_plans() elif mode == "payintent": result = self._process_payment_intents() elif mode == "refund": result = self._process_refunds() # Calculate duration end_time = datetime.now() duration = (end_time - start_time).total_seconds() result['duration'] = duration # Log script completion self._log_completion(mode, result, duration) return result except Exception as e: self.logger.error(f"Error during {mode} processing: {e}", exc_info=True) duration = (datetime.now() - start_time).total_seconds() return { 'success': False, 'error': str(e), 'mode': mode, 'duration': duration } def _initialize_components(self): """Initialize all required components within Flask app context.""" from app import db from stripe_payment_processor import StripePaymentProcessor from splynx import Splynx, SPLYNX_URL, SPLYNX_KEY, SPLYNX_SECRET from repositories import PaymentRepository, SplynxRepository # Initialize Stripe processor api_key = self.config.STRIPE_LIVE_API_KEY if self.process_live else self.config.STRIPE_TEST_API_KEY self.stripe_processor = StripePaymentProcessor(api_key=api_key, enable_logging=True) # Initialize Splynx splynx_client = Splynx(url=SPLYNX_URL, key=SPLYNX_KEY, secret=SPLYNX_SECRET) self.splynx_repo = SplynxRepository(splynx_client) # Initialize payment repository self.payment_repo = PaymentRepository(db.session) self.logger.info("Components initialized successfully") def _process_batch(self) -> Dict[str, Any]: """ Process batch payments (Direct Debit and Card). Returns: Dictionary with processing results """ from app import db from payment_processors import BatchPaymentProcessor self.config.MODE = 'batch' processor = BatchPaymentProcessor( db_session_factory=db.session, stripe_processor=self.stripe_processor, splynx_repository=self.splynx_repo, payment_repository=self.payment_repo, config=self.config, process_live=self.process_live ) result = processor.process() result['mode'] = self.config.MODE return result def _process_payment_plans(self) -> Dict[str, Any]: """ Process payment plans. Returns: Dictionary with processing results """ from app import db from payment_processors import PaymentPlanProcessor self.config.MODE = 'payplan' processor = PaymentPlanProcessor( db_session_factory=db.session, stripe_processor=self.stripe_processor, splynx_repository=self.splynx_repo, payment_repository=self.payment_repo, config=self.config, process_live=self.process_live ) result = processor.process() result['mode'] = self.config.MODE return result def _process_payment_intents(self) -> Dict[str, Any]: """ Process payment intent follow-ups. Returns: Dictionary with processing results """ from app import db from payment_processors import FollowUpProcessor self.config.MODE = 'payintent' processor = FollowUpProcessor( db_session_factory=db.session, stripe_processor=self.stripe_processor, splynx_repository=self.splynx_repo, payment_repository=self.payment_repo, config=self.config, process_live=self.process_live ) result = processor.process_payment_intents() result['mode'] = self.config.MODE return result def _process_refunds(self) -> Dict[str, Any]: """ Process refund follow-ups. Returns: Dictionary with processing results """ from app import db from payment_processors import FollowUpProcessor self.config.MODE = 'refund' processor = FollowUpProcessor( db_session_factory=db.session, stripe_processor=self.stripe_processor, splynx_repository=self.splynx_repo, payment_repository=self.payment_repo, config=self.config, process_live=self.process_live ) result = processor.process_refunds() result['mode'] = self.config.MODE return result def _log_completion(self, mode: str, result: Dict[str, Any], duration: float): """ Log script completion using services module. Args: mode: Processing mode result: Processing result dictionary duration: Execution duration in seconds """ try: from services import log_script_completion success_count = result.get('success_count', result.get('succeeded', result.get('completed', 0))) failed_count = result.get('failed_count', result.get('failed', 0)) total_amount = result.get('total_amount', 0.0) batch_ids = result.get('batch_ids') or ([result.get('batch_id')] if result.get('batch_id') else None) errors = [result.get('error')] if result.get('error') else None log_script_completion( script_name="query_mysql.py", mode=mode, success_count=success_count, failed_count=failed_count, total_amount=total_amount, batch_ids=batch_ids, duration_seconds=duration, errors=errors ) self.logger.info(f"Script completed in {duration:.1f}s: {success_count} successful, {failed_count} failed") except Exception as e: self.logger.warning(f"Failed to log script completion: {e}")