""" Enhanced Subscription Error Responses Provides detailed, conversion-optimized error responses when users hit subscription tier restrictions (HTTP 402 Payment Required). """ from typing import List, Dict, Optional, Any from pydantic import BaseModel class UpgradeBenefit(BaseModel): """A single benefit of upgrading""" text: str icon: str # Icon name (e.g., 'zap', 'trending-up', 'shield') class ROIEstimate(BaseModel): """ROI estimate for upgrade""" monthly_savings_min: int monthly_savings_max: int currency: str = "€" payback_period_days: int class FeatureRestrictionDetail(BaseModel): """Detailed error response for feature restrictions""" error: str = "subscription_tier_insufficient" code: str = "SUBSCRIPTION_UPGRADE_REQUIRED" status_code: int = 402 message: str details: Dict[str, Any] # Feature-specific upgrade messages FEATURE_MESSAGES = { 'analytics': { 'title': 'Unlock Advanced Analytics', 'description': 'Get deeper insights into your bakery performance with advanced analytics dashboards.', 'benefits': [ UpgradeBenefit(text='90-day forecast horizon (vs 7 days)', icon='calendar'), UpgradeBenefit(text='Weather & traffic integration', icon='cloud'), UpgradeBenefit(text='What-if scenario modeling', icon='trending-up'), UpgradeBenefit(text='Custom reports & dashboards', icon='bar-chart'), UpgradeBenefit(text='Profitability analysis by product', icon='dollar-sign') ], 'roi': ROIEstimate( monthly_savings_min=800, monthly_savings_max=1200, payback_period_days=7 ) }, 'multi_location': { 'title': 'Scale to Multiple Locations', 'description': 'Manage up to 3 bakery locations with centralized inventory and analytics.', 'benefits': [ UpgradeBenefit(text='Up to 3 locations (vs 1)', icon='map-pin'), UpgradeBenefit(text='Inventory transfer between locations', icon='arrow-right'), UpgradeBenefit(text='Location comparison analytics', icon='bar-chart'), UpgradeBenefit(text='Centralized reporting', icon='file-text'), UpgradeBenefit(text='500 products (vs 50)', icon='package') ], 'roi': ROIEstimate( monthly_savings_min=1000, monthly_savings_max=2000, payback_period_days=10 ) }, 'pos_integration': { 'title': 'Integrate Your POS System', 'description': 'Automatically sync sales data from your point-of-sale system.', 'benefits': [ UpgradeBenefit(text='Automatic sales import', icon='refresh-cw'), UpgradeBenefit(text='Real-time inventory sync', icon='zap'), UpgradeBenefit(text='Save 10+ hours/week on data entry', icon='clock'), UpgradeBenefit(text='Eliminate manual errors', icon='check-circle'), UpgradeBenefit(text='Faster, more accurate forecasts', icon='trending-up') ], 'roi': ROIEstimate( monthly_savings_min=600, monthly_savings_max=1000, payback_period_days=5 ) }, 'advanced_forecasting': { 'title': 'Unlock Advanced AI Forecasting', 'description': 'Get more accurate predictions with weather, traffic, and seasonal patterns.', 'benefits': [ UpgradeBenefit(text='Weather-based demand predictions', icon='cloud'), UpgradeBenefit(text='Traffic & event impact analysis', icon='activity'), UpgradeBenefit(text='Seasonal pattern detection', icon='calendar'), UpgradeBenefit(text='15% more accurate forecasts', icon='target'), UpgradeBenefit(text='Reduce waste by 7+ percentage points', icon='trending-down') ], 'roi': ROIEstimate( monthly_savings_min=800, monthly_savings_max=1500, payback_period_days=7 ) }, 'scenario_modeling': { 'title': 'Plan with What-If Scenarios', 'description': 'Model different business scenarios before making decisions.', 'benefits': [ UpgradeBenefit(text='Test menu changes before launch', icon='beaker'), UpgradeBenefit(text='Optimize pricing strategies', icon='dollar-sign'), UpgradeBenefit(text='Plan seasonal inventory', icon='calendar'), UpgradeBenefit(text='Risk assessment tools', icon='shield'), UpgradeBenefit(text='Data-driven decision making', icon='trending-up') ], 'roi': ROIEstimate( monthly_savings_min=500, monthly_savings_max=1000, payback_period_days=10 ) }, 'api_access': { 'title': 'Integrate with Your Tools', 'description': 'Connect bakery.ai with your existing business systems via API.', 'benefits': [ UpgradeBenefit(text='Full REST API access', icon='code'), UpgradeBenefit(text='1,000 API calls/hour (vs 100)', icon='zap'), UpgradeBenefit(text='Webhook support for real-time events', icon='bell'), UpgradeBenefit(text='Custom integrations', icon='link'), UpgradeBenefit(text='API documentation & support', icon='book') ], 'roi': None # ROI varies by use case } } def create_upgrade_required_response( feature: str, current_tier: str, required_tier: str = 'professional', allowed_tiers: Optional[List[str]] = None, custom_message: Optional[str] = None ) -> FeatureRestrictionDetail: """ Create an enhanced 402 error response with upgrade suggestions Args: feature: Feature key (e.g., 'analytics', 'multi_location') current_tier: User's current subscription tier required_tier: Minimum tier required for this feature allowed_tiers: List of tiers that have access (defaults to [required_tier, 'enterprise']) custom_message: Optional custom message (overrides default) Returns: FeatureRestrictionDetail with upgrade information """ if allowed_tiers is None: allowed_tiers = [required_tier, 'enterprise'] if required_tier != 'enterprise' else ['enterprise'] # Get feature-specific messaging feature_info = FEATURE_MESSAGES.get(feature, { 'title': f'Upgrade to {required_tier.capitalize()}', 'description': f'This feature requires a {required_tier.capitalize()} subscription.', 'benefits': [], 'roi': None }) # Build detailed response message = custom_message or feature_info['title'] details = { 'required_feature': feature, 'minimum_tier': required_tier, 'allowed_tiers': allowed_tiers, 'current_tier': current_tier, # Upgrade messaging 'title': feature_info['title'], 'description': feature_info['description'], 'benefits': [b.dict() for b in feature_info['benefits']], # ROI information 'roi_estimate': feature_info['roi'].dict() if feature_info['roi'] else None, # Call-to-action 'upgrade_url': f'/app/settings/subscription?upgrade={required_tier}&from={current_tier}&feature={feature}', 'preview_url': f'/app/{feature}?demo=true' if feature in ['analytics'] else None, # Suggested tier 'suggested_tier': required_tier, 'suggested_tier_display': required_tier.capitalize(), # Additional context 'can_preview': feature in ['analytics'], 'has_free_trial': True, 'trial_days': 0, # Social proof 'social_proof': get_social_proof_message(required_tier), # Pricing context 'pricing_context': get_pricing_context(required_tier) } return FeatureRestrictionDetail( message=message, details=details ) def create_quota_exceeded_response( metric: str, current: int, limit: int, current_tier: str, upgrade_tier: str = 'professional', upgrade_limit: Optional[int] = None, reset_at: Optional[str] = None ) -> Dict[str, Any]: """ Create an enhanced 429 error response for quota limits Args: metric: The quota metric (e.g., 'training_jobs', 'forecasts') current: Current usage limit: Quota limit current_tier: User's current subscription tier upgrade_tier: Suggested upgrade tier upgrade_limit: Limit in upgraded tier (None = unlimited) reset_at: When the quota resets (ISO datetime string) Returns: Error response with upgrade suggestions """ metric_labels = { 'training_jobs': 'Training Jobs', 'forecasts': 'Forecasts', 'api_calls': 'API Calls', 'products': 'Products', 'users': 'Users', 'locations': 'Locations' } label = metric_labels.get(metric, metric.replace('_', ' ').title()) return { 'error': 'quota_exceeded', 'code': 'QUOTA_LIMIT_REACHED', 'status_code': 429, 'message': f'Daily quota exceeded for {label.lower()}', 'details': { 'metric': metric, 'label': label, 'current': current, 'limit': limit, 'reset_at': reset_at, 'quota_type': metric, # Upgrade suggestion 'can_upgrade': True, 'upgrade_tier': upgrade_tier, 'upgrade_limit': upgrade_limit, 'upgrade_benefit': f'{upgrade_limit}x more capacity' if upgrade_limit and limit else 'Unlimited capacity', # Call-to-action 'upgrade_url': f'/app/settings/subscription?upgrade={upgrade_tier}&from={current_tier}&reason=quota_exceeded&metric={metric}', # ROI context 'roi_message': get_quota_roi_message(metric, current_tier, upgrade_tier) } } def get_social_proof_message(tier: str) -> str: """Get social proof message for a tier""" messages = { 'professional': '87% of growing bakeries choose Professional', 'enterprise': 'Trusted by multi-location bakery chains across Europe' } return messages.get(tier, '') def get_pricing_context(tier: str) -> Dict[str, Any]: """Get pricing context for a tier""" pricing = { 'professional': { 'monthly_price': 149, 'yearly_price': 1490, 'per_day_cost': 4.97, 'currency': '€', 'savings_yearly': 596, 'value_message': 'Only €4.97/day for unlimited growth' }, 'enterprise': { 'monthly_price': 499, 'yearly_price': 4990, 'per_day_cost': 16.63, 'currency': '€', 'savings_yearly': 1998, 'value_message': 'Complete solution for €16.63/day' } } return pricing.get(tier, {}) def get_quota_roi_message(metric: str, current_tier: str, upgrade_tier: str) -> str: """Get ROI-focused message for quota upgrades""" messages = { 'training_jobs': 'More training = better predictions = less waste', 'forecasts': 'Run forecasts for all products daily to optimize inventory', 'products': 'Expand your menu without limits', 'users': 'Give your entire team access to real-time data', 'locations': 'Manage all your bakeries from one platform' } return messages.get(metric, 'Unlock more capacity to grow your business') # Example usage function for gateway middleware def handle_feature_restriction( feature: str, current_tier: str, required_tier: str = 'professional' ) -> tuple[int, Dict[str, Any]]: """ Handle feature restriction in gateway middleware Returns: (status_code, response_body) """ response = create_upgrade_required_response( feature=feature, current_tier=current_tier, required_tier=required_tier ) return response.status_code, response.dict()