Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13 KiB
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
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:
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:
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
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
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:
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):
{
"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:
// 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:
// 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
{% from "shared/macros/feature_gate.html" import feature_gate %}
{% call feature_gate("analytics_dashboard") %}
<div>Analytics content here - only visible if feature available</div>
{% endcall %}
Feature Locked Card
{% from "shared/macros/feature_gate.html" import feature_locked %}
{{ feature_locked("advanced_analytics", "Advanced Analytics", "Get deeper insights") }}
Upgrade Banner
{% from "shared/macros/feature_gate.html" import upgrade_banner %}
{{ upgrade_banner("custom_domain") }}
Usage Limit Warning
{% from "shared/macros/feature_gate.html" import limit_warning %}
{{ limit_warning("orders") }} {# Shows warning when approaching limit #}
Usage Progress Bar
{% from "shared/macros/feature_gate.html" import usage_bar %}
{{ usage_bar("products", "Products") }}
Tier Badge
{% from "shared/macros/feature_gate.html" import tier_badge %}
{{ tier_badge() }} {# Shows current tier as colored badge #}
Store Dashboard Integration
The store dashboard (/store/{code}/dashboard) now includes:
- Tier Badge: Shows current subscription tier in header
- Usage Bars: Visual progress bars for orders, products, team members
- Upgrade Prompts: Contextual upgrade recommendations when approaching limits
- Feature Gates: Locked sections for premium features
Admin Features Page
Located at /admin/features:
- View all 30 features in categorized table
- Toggle features on/off globally
- Filter by category
- Search by name/code
- View tier requirements
Admin Tier Management UI
Located at /admin/subscription-tiers:
Overview
The subscription tiers admin page provides full CRUD functionality for managing subscription tiers and their feature assignments.
Features
-
Stats Cards: Display total tiers, active tiers, public tiers, and estimated MRR
-
Tier Table: Sortable list of all tiers with:
- Display order
- Code (colored badge by tier)
- Name
- Monthly/Annual pricing
- Feature count
- Status (Active/Private/Inactive)
- Actions (Edit Features, Edit, Activate/Deactivate)
-
Create/Edit Modal: Form with all tier fields:
- Code and Name
- Monthly and Annual pricing (in cents)
- Display order
- Stripe IDs (optional)
- Description
- Active/Public toggles
-
Feature Assignment Slide-over Panel:
- Opens when clicking the puzzle-piece icon
- Shows all features grouped by category
- Binary features: checkbox selection with Select all/Deselect all per category
- Quantitative features: checkbox + numeric limit input for
limit_value - Feature count in footer
- Save to update tier's feature assignments via
TierFeatureLimitEntry[]
Files
| File | Purpose |
|---|---|
app/templates/admin/subscription-tiers.html |
Page template |
static/admin/js/subscription-tiers.js |
Alpine.js component |
app/routes/admin_pages.py |
Route registration |
API Endpoints Used
| Action | Method | Endpoint |
|---|---|---|
| Load tiers | GET | /api/v1/admin/subscriptions/tiers |
| Load stats | GET | /api/v1/admin/subscriptions/stats |
| Create tier | POST | /api/v1/admin/subscriptions/tiers |
| Update tier | PATCH | /api/v1/admin/subscriptions/tiers/{code} |
| Delete tier | DELETE | /api/v1/admin/subscriptions/tiers/{code} |
| Load feature catalog | GET | /api/v1/admin/subscriptions/features/catalog |
| Get tier feature limits | GET | /api/v1/admin/subscriptions/features/tiers/{code}/limits |
| Update tier feature limits | PUT | /api/v1/admin/subscriptions/features/tiers/{code}/limits |
Migration
The features are seeded via Alembic migration:
alembic/versions/n2c3d4e5f6a7_add_features_table.py
This creates:
featurestable with 30 default featuresstore_feature_overridestable for per-store exceptions
Testing
Unit tests located in:
tests/unit/services/test_feature_service.pytests/unit/services/test_usage_service.py
Run tests:
pytest tests/unit/services/test_feature_service.py -v
pytest tests/unit/services/test_usage_service.py -v
Architecture Compliance
All JavaScript files follow architecture rules:
- JS-003: Alpine components use
store*naming convention - JS-005: Init guards prevent duplicate initialization
- JS-006: Async operations have try/catch error handling
- JS-008: API calls use
apiClient(not rawfetch()) - JS-009: Notifications use
Utils.showToast()
Related Documentation
- Subscription Billing - Core subscription system
- Subscription Workflow Plan - Implementation roadmap