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