# Feature Gating System ## Overview The feature gating system provides tier-based access control for platform features. It allows restricting functionality based on store subscription tiers (Essential, Professional, Business, Enterprise) with contextual upgrade prompts when features are locked. **Implemented:** December 31, 2025 ## Architecture ### Database Models Located in `models/database/feature.py`: | Model | Purpose | |-------|---------| | `Feature` | Feature definitions with tier requirements | | `StoreFeatureOverride` | Per-store feature overrides (enable/disable) | ### Feature Model Structure ```python class Feature(Base): __tablename__ = "features" id: int # Primary key code: str # Unique feature code (e.g., "analytics_dashboard") name: str # Display name description: str # User-facing description category: str # Feature category minimum_tier_code: str # Minimum tier required (essential/professional/business/enterprise) minimum_tier_order: int # Tier order for comparison (1-4) is_active: bool # Whether feature is available created_at: datetime updated_at: datetime ``` ### Tier Ordering | Tier | Order | Code | |------|-------|------| | Essential | 1 | `essential` | | Professional | 2 | `professional` | | Business | 3 | `business` | | Enterprise | 4 | `enterprise` | ## Feature Categories 30 features organized into 8 categories: ### 1. Analytics | Feature Code | Name | Min Tier | |-------------|------|----------| | `basic_analytics` | Basic Analytics | Essential | | `analytics_dashboard` | Analytics Dashboard | Professional | | `advanced_analytics` | Advanced Analytics | Business | | `custom_reports` | Custom Reports | Enterprise | ### 2. Product Management | Feature Code | Name | Min Tier | |-------------|------|----------| | `basic_products` | Product Management | Essential | | `bulk_product_edit` | Bulk Product Edit | Professional | | `product_variants` | Product Variants | Professional | | `product_bundles` | Product Bundles | Business | | `inventory_alerts` | Inventory Alerts | Professional | ### 3. Order Management | Feature Code | Name | Min Tier | |-------------|------|----------| | `basic_orders` | Order Management | Essential | | `order_automation` | Order Automation | Professional | | `advanced_fulfillment` | Advanced Fulfillment | Business | | `multi_warehouse` | Multi-Warehouse | Enterprise | ### 4. Marketing | Feature Code | Name | Min Tier | |-------------|------|----------| | `discount_codes` | Discount Codes | Professional | | `abandoned_cart` | Abandoned Cart Recovery | Business | | `email_marketing` | Email Marketing | Business | | `loyalty_program` | Loyalty Program | Enterprise | ### 5. Support | Feature Code | Name | Min Tier | |-------------|------|----------| | `basic_support` | Email Support | Essential | | `priority_support` | Priority Support | Professional | | `phone_support` | Phone Support | Business | | `dedicated_manager` | Dedicated Account Manager | Enterprise | ### 6. Integration | Feature Code | Name | Min Tier | |-------------|------|----------| | `basic_api` | Basic API Access | Professional | | `advanced_api` | Advanced API Access | Business | | `webhooks` | Webhooks | Business | | `custom_integrations` | Custom Integrations | Enterprise | ### 7. Branding | Feature Code | Name | Min Tier | |-------------|------|----------| | `basic_theme` | Theme Customization | Essential | | `custom_domain` | Custom Domain | Professional | | `white_label` | White Label | Enterprise | | `custom_checkout` | Custom Checkout | Enterprise | ### 8. Team | Feature Code | Name | Min Tier | |-------------|------|----------| | `team_management` | Team Management | Professional | | `role_permissions` | Role Permissions | Business | | `audit_logs` | Audit Logs | Business | ## Services ### FeatureService Located in `app/services/feature_service.py`: ```python class FeatureService: """Service for managing tier-based feature access.""" # In-memory caching (refreshed every 5 minutes) _feature_cache: dict[str, Feature] = {} _cache_timestamp: datetime | None = None CACHE_TTL_SECONDS = 300 def has_feature(self, db: Session, store_id: int, feature_code: str) -> bool: """Check if store has access to a feature.""" def get_available_features(self, db: Session, store_id: int) -> list[str]: """Get list of feature codes available to store.""" def get_all_features_with_status(self, db: Session, store_id: int) -> list[dict]: """Get all features with availability status for store.""" def get_feature_info(self, db: Session, feature_code: str) -> dict | None: """Get full feature information including tier requirements.""" ``` ### UsageService Located in `app/services/usage_service.py`: ```python class UsageService: """Service for tracking and managing store usage against tier limits.""" def get_usage_summary(self, db: Session, store_id: int) -> dict: """Get comprehensive usage summary with limits and upgrade info.""" def check_limit(self, db: Session, store_id: int, limit_type: str) -> dict: """Check specific limit with detailed info.""" def get_upgrade_info(self, db: Session, store_id: int) -> dict: """Get upgrade recommendations based on current usage.""" ``` ## Backend Enforcement ### Decorator Pattern ```python from app.core.feature_gate import require_feature @router.get("/analytics/advanced") @require_feature("advanced_analytics") async def get_advanced_analytics( db: Session = Depends(get_db), store_id: int = Depends(get_current_store_id) ): # Only accessible if store has advanced_analytics feature pass ``` ### Dependency Pattern ```python from app.core.feature_gate import RequireFeature @router.get("/marketing/loyalty") async def get_loyalty_program( db: Session = Depends(get_db), _: None = Depends(RequireFeature("loyalty_program")) ): # Only accessible if store has loyalty_program feature pass ``` ### Exception Handling When a feature is not available, `FeatureNotAvailableException` is raised: ```python class FeatureNotAvailableException(Exception): def __init__(self, feature_code: str, required_tier: str): self.feature_code = feature_code self.required_tier = required_tier super().__init__(f"Feature '{feature_code}' requires {required_tier} tier") ``` HTTP Response (403): ```json { "detail": "Feature 'advanced_analytics' requires Professional tier or higher", "feature_code": "advanced_analytics", "required_tier": "Professional", "upgrade_url": "/store/orion/billing" } ``` ## API Endpoints ### Store Features API Base: `/api/v1/store/features` | Endpoint | Method | Description | |----------|--------|-------------| | `/features/available` | GET | List available feature codes | | `/features` | GET | All features with availability status | | `/features/{code}` | GET | Single feature info | | `/features/{code}/check` | GET | Quick availability check | ### Store Usage API Base: `/api/v1/store/usage` | Endpoint | Method | Description | |----------|--------|-------------| | `/usage` | GET | Full usage summary with limits | | `/usage/check/{limit_type}` | GET | Check specific limit (orders/products/team_members) | | `/usage/upgrade-info` | GET | Upgrade recommendations | ### Admin Features API Base: `/api/v1/admin/features` | Endpoint | Method | Description | |----------|--------|-------------| | `/features` | GET | List all features | | `/features/{id}` | GET | Get feature details | | `/features/{id}` | PUT | Update feature | | `/features/{id}/toggle` | POST | Toggle feature active status | | `/features/stores/{store_id}/overrides` | GET | Get store overrides | | `/features/stores/{store_id}/overrides` | POST | Create override | ## Frontend Integration ### Alpine.js Feature Store Located in `static/shared/js/feature-store.js`: ```javascript // Usage in templates $store.features.has('analytics_dashboard') // Check feature $store.features.loaded // Loading state $store.features.getFeature('advanced_api') // Get feature details ``` ### Alpine.js Upgrade Store Located in `static/shared/js/upgrade-prompts.js`: ```javascript // Usage in templates $store.upgrade.shouldShowLimitWarning('orders') $store.upgrade.getUsageString('products') $store.upgrade.hasUpgradeRecommendation ``` ### Jinja2 Macros Located in `app/templates/shared/macros/feature_gate.html`: #### Feature Gate Container ```jinja2 {% from "shared/macros/feature_gate.html" import feature_gate %} {% call feature_gate("analytics_dashboard") %}