#!/usr/bin/env python3 """ Payment Processing CLI Command-line interface for running payment processing in different modes. Usage: python payment_cli.py [mode] [live] Modes: batch, payintent, payplan, refund """ import sys import logging import argparse from config import Config from app import create_app from orchestration import PaymentOrchestrator # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('payment_processing.log'), logging.StreamHandler(sys.stdout) ] ) logger = logging.getLogger(__name__) def parse_arguments(): """ Parse command-line arguments. Returns: Namespace with parsed arguments """ parser = argparse.ArgumentParser( description='Payment Processing System - Process payments, payment plans, and follow-ups', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: python payment_cli.py batch test # Process batch payments in test mode python payment_cli.py batch live # Process batch payments in live mode python payment_cli.py payintent # Check payment intents (test mode) python payment_cli.py payplan live # Process payment plans in live mode python payment_cli.py refund # Check refund statuses (test mode) Modes: batch - Process Direct Debit and Card payments in batches payplan - Process recurring payment plans due today payintent - Follow up on pending payment intents refund - Follow up on pending refunds Environment: test - Use test Stripe API keys (default) live - Use live Stripe API keys and update Splynx """ ) parser.add_argument( 'mode', choices=['batch', 'payintent', 'payplan', 'refund'], help='Processing mode to run' ) parser.add_argument( 'environment', nargs='?', choices=['test', 'live'], default='test', help='Environment to run in (default: test)' ) parser.add_argument( '-v', '--verbose', action='store_true', help='Enable verbose logging' ) parser.add_argument( '--dry-run', action='store_true', help='Run in dry-run mode (no changes to Splynx)' ) return parser.parse_args() def main(): """ Main entry point for the payment processing CLI. """ args = parse_arguments() # Set logging level if args.verbose: logging.getLogger().setLevel(logging.DEBUG) logger.debug("Verbose logging enabled") # Determine if running in live mode process_live = (args.environment == 'live') and not args.dry_run if args.dry_run: logger.info("🔍 DRY-RUN MODE: No changes will be made to Splynx") # Log execution details env_display = "LIVE" if process_live else "TEST" logger.info("=" * 80) logger.info(f"Payment Processing System - Mode: {args.mode.upper()} - Environment: {env_display}") logger.info("=" * 80) try: # Create Flask application app = create_app() # Create orchestrator orchestrator = PaymentOrchestrator( app=app, config=Config, process_live=process_live ) # Run processing result = orchestrator.run(args.mode) # Display results if result.get('success'): logger.info("=" * 80) logger.info("PROCESSING COMPLETED SUCCESSFULLY") logger.info("=" * 80) # Display mode-specific results if args.mode == "batch": logger.info(f"Batch IDs: {result.get('batch_ids', [])}") logger.info(f"Successful: {result.get('success_count', 0)}") logger.info(f"Failed: {result.get('failed_count', 0)}") elif args.mode == "payplan": logger.info(f"Batch ID: {result.get('batch_id')}") logger.info(f"Successful: {result.get('success_count', 0)}") logger.info(f"Failed: {result.get('failed_count', 0)}") logger.info(f"Total Amount: ${result.get('total_amount', 0):.2f}") elif args.mode == "payintent": logger.info(f"Total Checked: {result.get('total_pending', 0)}") logger.info(f"Succeeded: {result.get('succeeded', 0)}") logger.info(f"Failed: {result.get('failed', 0)}") logger.info(f"Still Pending: {result.get('still_pending', 0)}") elif args.mode == "refund": logger.info(f"Total Checked: {result.get('total_pending', 0)}") logger.info(f"Completed: {result.get('completed', 0)}") logger.info(f"Failed: {result.get('failed', 0)}") logger.info(f"Still Pending: {result.get('still_pending', 0)}") logger.info(f"Duration: {result.get('duration', 0):.1f}s") logger.info("=" * 80) sys.exit(0) else: logger.error("=" * 80) logger.error("PROCESSING FAILED") logger.error("=" * 80) logger.error(f"Error: {result.get('error', 'Unknown error')}") logger.error("=" * 80) sys.exit(1) except KeyboardInterrupt: logger.warning("\n\nProcessing interrupted by user") sys.exit(130) except Exception as e: logger.error("=" * 80) logger.error("FATAL ERROR") logger.error("=" * 80) logger.error(f"Error: {e}", exc_info=True) logger.error("=" * 80) sys.exit(1) if __name__ == "__main__": main()