Files
orion/app/exceptions/billing.py
Samir Boulahtit 9d8d5e7138 feat: add subscription and billing system with Stripe integration
- Add database models for subscription tiers, vendor subscriptions,
  add-ons, billing history, and webhook events
- Implement BillingService for subscription operations
- Implement StripeService for Stripe API operations
- Implement StripeWebhookHandler for webhook event processing
- Add vendor billing API endpoints for subscription management
- Create vendor billing page with Alpine.js frontend
- Add limit enforcement for products and team members
- Add billing exceptions for proper error handling
- Create comprehensive unit tests (40 tests passing)
- Add subscription billing documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 20:29:44 +01:00

96 lines
2.9 KiB
Python

# app/exceptions/billing.py
"""
Billing and subscription related exceptions.
This module provides exceptions for:
- Payment system configuration issues
- Subscription management errors
- Tier-related errors
"""
from typing import Any
from .base import BusinessLogicException, ResourceNotFoundException, ServiceUnavailableException
class PaymentSystemNotConfiguredException(ServiceUnavailableException):
"""Raised when the payment system (Stripe) is not configured."""
def __init__(self):
super().__init__(message="Payment system not configured")
class TierNotFoundException(ResourceNotFoundException):
"""Raised when a subscription tier is not found."""
def __init__(self, tier_code: str):
super().__init__(
resource_type="SubscriptionTier",
identifier=tier_code,
message=f"Subscription tier '{tier_code}' not found",
error_code="TIER_NOT_FOUND",
)
self.tier_code = tier_code
class StripePriceNotConfiguredException(BusinessLogicException):
"""Raised when Stripe price is not configured for a tier."""
def __init__(self, tier_code: str):
super().__init__(
message=f"Stripe price not configured for tier '{tier_code}'",
error_code="STRIPE_PRICE_NOT_CONFIGURED",
details={"tier_code": tier_code},
)
self.tier_code = tier_code
class NoActiveSubscriptionException(BusinessLogicException):
"""Raised when no active subscription exists for an operation that requires one."""
def __init__(self, message: str = "No active subscription found"):
super().__init__(
message=message,
error_code="NO_ACTIVE_SUBSCRIPTION",
)
class SubscriptionNotCancelledException(BusinessLogicException):
"""Raised when trying to reactivate a subscription that is not cancelled."""
def __init__(self):
super().__init__(
message="Subscription is not cancelled",
error_code="SUBSCRIPTION_NOT_CANCELLED",
)
class SubscriptionAlreadyCancelledException(BusinessLogicException):
"""Raised when trying to cancel an already cancelled subscription."""
def __init__(self):
super().__init__(
message="Subscription is already cancelled",
error_code="SUBSCRIPTION_ALREADY_CANCELLED",
)
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",
)