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.
 
 
 

198 lines
6.9 KiB

"""
Base Payment Processor Module
This module provides the abstract base class for all payment processors.
It defines the common interface and shared functionality.
"""
import logging
import json
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional
from sqlalchemy.orm import scoped_session
logger = logging.getLogger(__name__)
class BasePaymentProcessor(ABC):
"""
Abstract base class for payment processors.
This class defines the common interface and shared functionality
for all payment processing modes.
"""
def __init__(self, db_session_factory, stripe_processor, splynx_repository,
payment_repository, config, process_live: bool = False):
"""
Initialize the base payment processor.
Args:
db_session_factory: Factory for creating database sessions (for thread safety)
stripe_processor: StripePaymentProcessor instance
splynx_repository: SplynxRepository instance
payment_repository: PaymentRepository instance
config: Configuration object
process_live: Whether to process in live mode
"""
self.db_session_factory = db_session_factory
self.stripe_processor = stripe_processor
self.splynx_repo = splynx_repository
self.payment_repo = payment_repository
self.config = config
self.process_live = process_live
self.logger = logging.getLogger(self.__class__.__name__)
def get_db_session(self):
"""
Get a database session for the current thread.
Returns:
Database session
"""
if isinstance(self.db_session_factory, scoped_session):
return self.db_session_factory()
else:
return self.db_session_factory
@abstractmethod
def process(self) -> Dict[str, Any]:
"""
Process payments for this processor's mode.
This method must be implemented by all subclasses.
Returns:
Dictionary with processing results (success_count, failed_count, etc.)
"""
pass
def handle_database_operation(self, operation_func, operation_name: str) -> Any:
"""
Execute a database operation with consistent error handling.
Args:
operation_func: Function that performs the database operation
operation_name: Description of the operation for logging
Returns:
Result of operation_func or None if failed
"""
try:
result = operation_func()
self.payment_repo.commit()
self.logger.debug(f"{operation_name} completed successfully")
return result
except Exception as e:
self.payment_repo.rollback()
self.logger.error(f"{operation_name} failed: {e}")
return None
def log_processing_start(self, mode: str, details: str = ""):
"""
Log the start of payment processing.
Args:
mode: Processing mode name
details: Additional details to log
"""
env = "LIVE" if self.process_live else "SANDBOX"
self.logger.info(f"Starting {mode} processing ({env})")
if details:
self.logger.info(details)
def log_processing_complete(self, mode: str, success_count: int, failed_count: int,
duration: float, additional_info: Dict[str, Any] = None):
"""
Log the completion of payment processing.
Args:
mode: Processing mode name
success_count: Number of successful operations
failed_count: Number of failed operations
duration: Processing duration in seconds
additional_info: Additional information to log
"""
self.logger.info(f"{mode} processing completed in {duration:.1f}s")
self.logger.info(f"Results: {success_count} successful, {failed_count} failed")
if additional_info:
for key, value in additional_info.items():
self.logger.info(f"{key}: {value}")
def create_payment_data(self, payment_record, payment_id: int, description: str = None) -> Dict[str, Any]:
"""
Create standardized payment data dictionary for Stripe processing.
Args:
payment_record: Database payment record
payment_id: Payment ID
description: Payment description override
Returns:
Dictionary with payment data for Stripe API
"""
if not description:
description = f"Payment ID: {payment_id} - Splynx ID: {payment_record.Splynx_ID}"
return {
'payment_id': payment_id,
'customer_id': payment_record.Stripe_Customer_ID,
'amount': payment_record.Payment_Amount,
'currency': "aud",
'description': description,
'stripe_pm': payment_record.Stripe_Payment_Method
}
def handle_payment_result(self, payment_id: int, result: Dict[str, Any],
payment_type: str = "pay") -> bool:
"""
Handle payment processing result and update database.
Args:
payment_id: Payment record ID
result: Payment processing result from Stripe
payment_type: "pay" or "singlepay"
Returns:
True if successful, False otherwise
"""
from payment_services.payment_service import processPaymentResult
#print(f"\n\nhandle_payment_result - result: {json.dumps(result,indent=2)}\n\n")
cust_stripe_details = self.stripe_processor.get_customer_info(customer_id=result.get('customer_id'))
#print(f"\ncust_stripe_details ({result.get('customer_id')}) - result: {json.dumps(cust_stripe_details,indent=2)}\n\n")
try:
# Get notification handler
notification_handler = self._get_notification_handler()
# Process the result
processPaymentResult(
db=self.payment_repo.db,
splynx=self.splynx_repo.splynx,
notification_handler=notification_handler,
pay_id=payment_id,
result=result,
key=payment_type,
process_live=self.process_live,
cust_stripe_details=cust_stripe_details,
mode=self.config.MODE
)
return True
except Exception as e:
self.logger.error(f"Error handling payment result for payment {payment_id}: {e}")
return False
def _get_notification_handler(self):
"""
Get the notification handler function for failed payments.
Returns:
Notification handler function or None
"""
from .notification_handler import handle_failed_payment_notification
#return handle_failed_payment_notification if self.process_live else None
return handle_failed_payment_notification