You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

183 lines
5.7 KiB

#!/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 os
import logging
import argparse
# Add parent directory to Python path to allow imports
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
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()