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
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()
|
|
|