# app/modules/payments/exceptions.py """ Payment-related exceptions. Includes: - Webhook verification exceptions - Payment processing exceptions """ from app.exceptions.base import ( BusinessLogicException, ExternalServiceException, ResourceNotFoundException, ValidationException, ) # ============================================================================= # Webhook Exceptions # ============================================================================= class InvalidWebhookSignatureException(BusinessLogicException): """Raised when Stripe webhook signature verification fails.""" def __init__(self, message: str = "Invalid webhook signature"): super().__init__( message=message, error_code="INVALID_WEBHOOK_SIGNATURE", ) class WebhookMissingSignatureException(BusinessLogicException): """Raised when Stripe webhook is missing the signature header.""" def __init__(self): super().__init__( message="Missing Stripe-Signature header", error_code="WEBHOOK_MISSING_SIGNATURE", ) class WebhookVerificationException(BusinessLogicException): """Raised when webhook signature verification fails.""" def __init__(self, message: str = "Invalid webhook signature"): super().__init__( message=message, error_code="WEBHOOK_VERIFICATION_FAILED", ) # ============================================================================= # Payment Exceptions # ============================================================================= class PaymentException(BusinessLogicException): # noqa: MOD025 """Base exception for payment-related errors.""" def __init__( self, message: str, error_code: str = "PAYMENT_ERROR", details: dict | None = None, ): super().__init__(message=message, error_code=error_code, details=details) class PaymentNotFoundException(ResourceNotFoundException): # noqa: MOD025 """Raised when a payment is not found.""" def __init__(self, payment_id: str): super().__init__( resource_type="Payment", identifier=payment_id, message=f"Payment with ID '{payment_id}' not found", ) self.payment_id = payment_id class PaymentFailedException(PaymentException): # noqa: MOD025 """Raised when payment processing fails.""" def __init__(self, message: str, stripe_error: str | None = None): super().__init__( message=message, error_code="PAYMENT_FAILED", details={"stripe_error": stripe_error} if stripe_error else None, ) self.stripe_error = stripe_error class PaymentRefundException(PaymentException): # noqa: MOD025 """Raised when a refund fails.""" def __init__(self, message: str, payment_id: str | None = None): super().__init__( message=message, error_code="REFUND_FAILED", details={"payment_id": payment_id} if payment_id else None, ) self.payment_id = payment_id class InsufficientFundsException(PaymentException): # noqa: MOD025 """Raised when there are insufficient funds for payment.""" def __init__(self, required_amount: float, available_amount: float | None = None): message = f"Insufficient funds. Required: {required_amount}" if available_amount is not None: message += f", Available: {available_amount}" super().__init__( message=message, error_code="INSUFFICIENT_FUNDS", details={ "required_amount": required_amount, "available_amount": available_amount, }, ) self.required_amount = required_amount self.available_amount = available_amount class PaymentGatewayException(ExternalServiceException): # noqa: MOD025 """Raised when payment gateway fails.""" def __init__(self, gateway: str, message: str): super().__init__( service_name=gateway, message=f"Payment gateway error: {message}", ) self.gateway = gateway class InvalidPaymentMethodException(ValidationException): # noqa: MOD025 """Raised when an invalid payment method is provided.""" def __init__(self, method: str): super().__init__( message=f"Invalid payment method: {method}", details={"method": method}, ) self.method = method