Migrates billing module to self-contained structure: - Create app/modules/billing/services/ with subscription, stripe, admin services - Create app/modules/billing/models/ re-exporting from central location - Create app/modules/billing/schemas/ re-exporting from central location - Create app/modules/billing/tasks/ with 4 scheduled Celery tasks - Create app/modules/billing/exceptions.py with module-specific exceptions - Update definition.py with is_self_contained=True and scheduled_tasks Celery task migration: - reset_period_counters -> billing module - check_trial_expirations -> billing module - sync_stripe_status -> billing module - cleanup_stale_subscriptions -> billing module - capture_capacity_snapshot remains in legacy (will go to monitoring) Backward compatibility: - Create re-exports in app/services/ for subscription, stripe, admin services - Old import paths continue to work - Update celery_config.py to use module-defined schedules Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
84 lines
2.4 KiB
Python
84 lines
2.4 KiB
Python
# app/modules/billing/exceptions.py
|
|
"""
|
|
Billing module exceptions.
|
|
|
|
Custom exceptions for subscription, billing, and payment operations.
|
|
"""
|
|
|
|
from app.exceptions import BusinessLogicException, ResourceNotFoundException
|
|
|
|
|
|
class BillingException(BusinessLogicException):
|
|
"""Base exception for billing module errors."""
|
|
|
|
pass
|
|
|
|
|
|
class SubscriptionNotFoundException(ResourceNotFoundException):
|
|
"""Raised when a subscription is not found."""
|
|
|
|
def __init__(self, vendor_id: int):
|
|
super().__init__("Subscription", str(vendor_id))
|
|
|
|
|
|
class TierNotFoundException(ResourceNotFoundException):
|
|
"""Raised when a subscription tier is not found."""
|
|
|
|
def __init__(self, tier_code: str):
|
|
super().__init__("SubscriptionTier", tier_code)
|
|
|
|
|
|
class TierLimitExceededException(BillingException):
|
|
"""Raised when a tier limit is exceeded."""
|
|
|
|
def __init__(self, message: str, limit_type: str, current: int, limit: int):
|
|
super().__init__(message)
|
|
self.limit_type = limit_type
|
|
self.current = current
|
|
self.limit = limit
|
|
|
|
|
|
class FeatureNotAvailableException(BillingException):
|
|
"""Raised when a feature is not available in current tier."""
|
|
|
|
def __init__(self, feature: str, current_tier: str, required_tier: str):
|
|
message = f"Feature '{feature}' requires {required_tier} tier (current: {current_tier})"
|
|
super().__init__(message)
|
|
self.feature = feature
|
|
self.current_tier = current_tier
|
|
self.required_tier = required_tier
|
|
|
|
|
|
class StripeNotConfiguredException(BillingException):
|
|
"""Raised when Stripe is not configured."""
|
|
|
|
def __init__(self):
|
|
super().__init__("Stripe is not configured")
|
|
|
|
|
|
class PaymentFailedException(BillingException):
|
|
"""Raised when a payment fails."""
|
|
|
|
def __init__(self, message: str, stripe_error: str | None = None):
|
|
super().__init__(message)
|
|
self.stripe_error = stripe_error
|
|
|
|
|
|
class WebhookVerificationException(BillingException):
|
|
"""Raised when webhook signature verification fails."""
|
|
|
|
def __init__(self, message: str = "Invalid webhook signature"):
|
|
super().__init__(message)
|
|
|
|
|
|
__all__ = [
|
|
"BillingException",
|
|
"SubscriptionNotFoundException",
|
|
"TierNotFoundException",
|
|
"TierLimitExceededException",
|
|
"FeatureNotAvailableException",
|
|
"StripeNotConfiguredException",
|
|
"PaymentFailedException",
|
|
"WebhookVerificationException",
|
|
]
|