# Subscription System Architecture Redesign ## Overview This document outlines a comprehensive redesign of the subscription system architecture to improve organization, maintainability, and separation of concerns while respecting the existing service-based client architecture. ## Current Issues 1. **Mixed Concerns**: Subscription endpoints are scattered across different API files 2. **Inconsistent URL Patterns**: Mixed use of path vs query parameters for tenant IDs 3. **Poor Organization**: Subscription operations mixed with tenant management operations 4. **Middleware Dependencies**: Hardcoded URLs that need updating 5. **Client Layer Confusion**: Tenant client contains many subscription-specific methods ## New Architecture Design ### API Structure & URL Patterns #### Public Endpoints (No Authentication) ``` GET /api/v1/plans - Get all available plans GET /api/v1/plans/{tier} - Get specific plan details GET /api/v1/plans/{tier}/features - Get plan features GET /api/v1/plans/{tier}/limits - Get plan limits GET /api/v1/plans/compare - Compare all plans ``` #### Registration Flow (No Tenant Context) ``` POST /api/v1/registration/payment-setup - Start registration with payment POST /api/v1/registration/complete - Complete registration after 3DS GET /api/v1/registration/state/{state_id} - Check registration state ``` #### Tenant-Dependent Subscription Endpoints ``` GET /api/v1/tenants/{tenant_id}/subscription/status - Get subscription status GET /api/v1/tenants/{tenant_id}/subscription/details - Get full subscription details GET /api/v1/tenants/{tenant_id}/subscription/tier - Get subscription tier (cached) GET /api/v1/tenants/{tenant_id}/subscription/limits - Get subscription limits GET /api/v1/tenants/{tenant_id}/subscription/usage - Get usage summary GET /api/v1/tenants/{tenant_id}/subscription/features/{feature} - Check feature access # Subscription Management POST /api/v1/tenants/{tenant_id}/subscription/cancel - Cancel subscription POST /api/v1/tenants/{tenant_id}/subscription/reactivate - Reactivate subscription GET /api/v1/tenants/{tenant_id}/subscription/validate-upgrade/{new_plan} - Validate upgrade POST /api/v1/tenants/{tenant_id}/subscription/upgrade - Upgrade subscription # Quota & Limit Checks GET /api/v1/tenants/{tenant_id}/subscription/limits/locations - Check location limits GET /api/v1/tenants/{tenant_id}/subscription/limits/products - Check product limits GET /api/v1/tenants/{tenant_id}/subscription/limits/users - Check user limits GET /api/v1/tenants/{tenant_id}/subscription/limits/recipes - Check recipe limits GET /api/v1/tenants/{tenant_id}/subscription/limits/suppliers - Check supplier limits # Payment Management GET /api/v1/tenants/{tenant_id}/subscription/payment-method - Get payment method POST /api/v1/tenants/{tenant_id}/subscription/payment-method - Update payment method GET /api/v1/tenants/{tenant_id}/subscription/invoices - Get invoices ``` ### Service Layer Organization **`subscription.py`** - All subscription-related endpoints - Registration flow endpoints - Tenant-dependent subscription endpoints - Subscription management endpoints - Quota and limit checks - Payment management endpoints **`tenant_operations.py`** - Only tenant-centric operations - Tenant creation/management - Tenant hierarchy operations - Tenant settings management - Tenant location management **`plans.py`** - Public plan information (unchanged) ## Implementation Plan ### Phase 1: Backend API Reorganization #### Step 1: Move Subscription Endpoints - Move all subscription endpoints from `tenant_operations.py` to `subscription.py` - Implement new URL patterns with consistent structure - Update all endpoint implementations to use new paths **Files to modify:** - `services/tenant/app/api/subscription.py` - Add all subscription endpoints - `services/tenant/app/api/tenant_operations.py` - Remove subscription endpoints #### Step 2: Update Tenant Service Main.py - Ensure proper router inclusion for new subscription endpoints - Remove old subscription endpoints from tenant operations router **Files to modify:** - `services/tenant/app/main.py` - Update router inclusion ### Phase 2: Update Tenant Client #### Step 3: Update Tenant Client URL Patterns Update all subscription-related methods in `shared/clients/tenant_client.py`: ```python async def get_subscription_status(self, tenant_id: str) -> Optional[Dict[str, Any]]: # Updated URL pattern result = await self.get(f"tenants/{tenant_id}/subscription/status") return result async def get_subscription_details(self, tenant_id: str) -> Optional[Dict[str, Any]]: # Updated URL pattern result = await self.get(f"tenants/{tenant_id}/subscription/details") return result # Update all other subscription methods similarly async def get_subscription_tier(self, tenant_id: str) -> Optional[str]: result = await self.get(f"tenants/{tenant_id}/subscription/tier") return result.get('tier') if result else None async def get_subscription_limits(self, tenant_id: str) -> Optional[Dict[str, Any]]: return await self.get(f"tenants/{tenant_id}/subscription/limits") async def get_usage_summary(self, tenant_id: str) -> Optional[Dict[str, Any]]: return await self.get(f"tenants/{tenant_id}/subscription/usage") async def has_feature(self, tenant_id: str, feature: str) -> bool: result = await self.get(f"tenants/{tenant_id}/subscription/features/{feature}") return result.get('has_feature', False) if result else False # Registration flow methods async def start_registration_payment_setup(self, user_data: Dict[str, Any]) -> Dict[str, Any]: result = await self.post("registration/payment-setup", user_data) return result async def complete_registration(self, setup_intent_id: str, user_data: Dict[str, Any]) -> Dict[str, Any]: result = await self.post("registration/complete", { "setup_intent_id": setup_intent_id, "user_data": user_data }) return result # Update quota check methods async def can_add_location(self, tenant_id: str) -> Dict[str, Any]: return await self.get(f"tenants/{tenant_id}/subscription/limits/locations") async def can_add_product(self, tenant_id: str) -> Dict[str, Any]: return await self.get(f"tenants/{tenant_id}/subscription/limits/products") async def can_add_user(self, tenant_id: str) -> Dict[str, Any]: return await self.get(f"tenants/{tenant_id}/subscription/limits/users") async def can_add_recipe(self, tenant_id: str) -> Dict[str, Any]: return await self.get(f"tenants/{tenant_id}/subscription/limits/recipes") async def can_add_supplier(self, tenant_id: str) -> Dict[str, Any]: return await self.get(f"tenants/{tenant_id}/subscription/limits/suppliers") # Update subscription management methods async def cancel_subscription(self, tenant_id: str, reason: str = "") -> Dict[str, Any]: return await self.post(f"tenants/{tenant_id}/subscription/cancel", {"reason": reason}) async def reactivate_subscription(self, tenant_id: str, plan: str = "starter") -> Dict[str, Any]: return await self.post(f"tenants/{tenant_id}/subscription/reactivate", {"plan": plan}) async def validate_plan_upgrade(self, tenant_id: str, new_plan: str) -> Dict[str, Any]: return await self.get(f"tenants/{tenant_id}/subscription/validate-upgrade/{new_plan}") async def upgrade_subscription_plan(self, tenant_id: str, new_plan: str) -> Dict[str, Any]: return await self.post(f"tenants/{tenant_id}/subscription/upgrade", {"new_plan": new_plan}) # Update payment management methods async def get_payment_method(self, tenant_id: str) -> Optional[Dict[str, Any]]: return await self.get(f"tenants/{tenant_id}/subscription/payment-method") async def update_payment_method(self, tenant_id: str, payment_method_id: str) -> Dict[str, Any]: return await self.post(f"tenants/{tenant_id}/subscription/payment-method", { "payment_method_id": payment_method_id }) async def get_invoices(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]: return await self.get(f"tenants/{tenant_id}/subscription/invoices") ``` **Files to modify:** - `shared/clients/tenant_client.py` - Update all subscription methods with new URL patterns ### Phase 3: Gateway Updates #### Step 4: Update Gateway Routes ```python # In gateway/app/routes/subscription.py # Public endpoints @router.get("/plans") @router.get("/plans/{tier}") @router.get("/plans/{tier}/features") @router.get("/plans/{tier}/limits") @router.get("/plans/compare") # Registration flow @router.post("/registration/payment-setup") @router.post("/registration/complete") @router.get("/registration/state/{state_id}") # Tenant subscription endpoints @router.get("/tenants/{tenant_id}/subscription/status") @router.get("/tenants/{tenant_id}/subscription/details") @router.get("/tenants/{tenant_id}/subscription/tier") @router.get("/tenants/{tenant_id}/subscription/limits") @router.get("/tenants/{tenant_id}/subscription/usage") @router.get("/tenants/{tenant_id}/subscription/features/{feature}") @router.post("/tenants/{tenant_id}/subscription/cancel") @router.post("/tenants/{tenant_id}/subscription/reactivate") @router.get("/tenants/{tenant_id}/subscription/validate-upgrade/{new_plan}") @router.post("/tenants/{tenant_id}/subscription/upgrade") @router.get("/tenants/{tenant_id}/subscription/limits/locations") @router.get("/tenants/{tenant_id}/subscription/limits/products") @router.get("/tenants/{tenant_id}/subscription/limits/users") @router.get("/tenants/{tenant_id}/subscription/limits/recipes") @router.get("/tenants/{tenant_id}/subscription/limits/suppliers") @router.get("/tenants/{tenant_id}/subscription/payment-method") @router.post("/tenants/{tenant_id}/subscription/payment-method") @router.get("/tenants/{tenant_id}/subscription/invoices") ``` **Files to modify:** - `gateway/app/routes/subscription.py` - Update all gateway routes with new patterns #### Step 5: Update Middleware URL Patterns ```python # In gateway/app/middleware/auth.py async def _get_tenant_subscription_tier(self, tenant_id: str, request: Request) -> Optional[str]: try: # Updated URL pattern response = await self._make_request( "GET", f"{settings.TENANT_SERVICE_URL}/api/v1/tenants/{tenant_id}/subscription/tier" ) # ... rest of method remains the same ``` ```python # In gateway/app/middleware/read_only_mode.py async def check_subscription_status(self, tenant_id: str, authorization: str) -> dict: try: # Updated URL pattern response = await self._make_request( "GET", f"{settings.TENANT_SERVICE_URL}/api/v1/tenants/{tenant_id}/subscription/status" ) # ... rest of method remains the same ``` **Files to modify:** - `gateway/app/middleware/subscription.py` - Update URL patterns - `gateway/app/middleware/auth.py` - Update subscription tier lookup URL - `gateway/app/middleware/read_only_mode.py` - Update subscription status check URL ### Phase 4: Frontend Updates #### Step 6: Update Frontend Subscription Service ```typescript // In frontend/src/api/services/subscription.ts export class SubscriptionService { // Update all methods to use new URL patterns async getSubscriptionStatus(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/status`); } async getSubscriptionDetails(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/details`); } async getSubscriptionTier(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/tier`); } async getUsageSummary(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/usage`); } async checkFeatureAccess(tenantId: string, featureName: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/features/${featureName}`); } async cancelSubscription(tenantId: string, reason?: string): Promise { return apiClient.post(`/tenants/${tenantId}/subscription/cancel`, { reason }); } async reactivateSubscription(tenantId: string, plan: string = 'starter'): Promise { return apiClient.post(`/tenants/${tenantId}/subscription/reactivate`, { plan }); } async validatePlanUpgrade(tenantId: string, newPlan: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/validate-upgrade/${newPlan}`); } async upgradePlan(tenantId: string, newPlan: string): Promise { return apiClient.post(`/tenants/${tenantId}/subscription/upgrade`, { new_plan: newPlan }); } async canAddLocation(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/limits/locations`); } async canAddProduct(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/limits/products`); } async canAddUser(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/limits/users`); } async canAddRecipe(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/limits/recipes`); } async canAddSupplier(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/limits/suppliers`); } async getPaymentMethod(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/payment-method`); } async updatePaymentMethod(tenantId: string, paymentMethodId: string): Promise { return apiClient.post(`/tenants/${tenantId}/subscription/payment-method`, { payment_method_id: paymentMethodId }); } async getInvoices(tenantId: string): Promise { return apiClient.get(`/tenants/${tenantId}/subscription/invoices`); } // Registration flow methods async startRegistrationPaymentSetup(userData: any): Promise { return apiClient.post('/registration/payment-setup', userData); } async completeRegistration(setupIntentId: string, userData: any): Promise { return apiClient.post('/registration/complete', { setup_intent_id: setupIntentId, user_data: userData }); } } ``` **Files to modify:** - `frontend/src/api/services/subscription.ts` - Update all URL patterns ## Benefits of New Architecture 1. **Clear Separation of Concerns**: Subscription operations are properly separated from tenant management operations 2. **Consistent URL Patterns**: All subscription endpoints follow a logical, standardized structure 3. **Better Organization**: Easier to find and maintain subscription-related code 4. **Service-Based Architecture**: Maintains the constraint of using tenant client only 5. **Improved Maintainability**: Changes to subscription logic are localized to one API file 6. **Better Performance**: Clear caching strategies for subscription data 7. **Easier Scaling**: Subscription endpoints can be scaled independently if needed 8. **Cleaner Codebase**: No mixed concerns between tenant and subscription operations ## Implementation Summary ### Files to Modify **Backend Services:** - `services/tenant/app/api/subscription.py` - Add all subscription endpoints - `services/tenant/app/api/tenant_operations.py` - Remove subscription endpoints - `services/tenant/app/main.py` - Update router inclusion **Shared Client Layer:** - `shared/clients/tenant_client.py` - Update all subscription methods with new URL patterns **Gateway Layer:** - `gateway/app/routes/subscription.py` - Update all gateway routes with new patterns - `gateway/app/middleware/subscription.py` - Update URL patterns - `gateway/app/middleware/auth.py` - Update subscription tier lookup URL - `gateway/app/middleware/read_only_mode.py` - Update subscription status check URL **Frontend Layer:** - `frontend/src/api/services/subscription.ts` - Update all URL patterns ### Implementation Order 1. **Backend API Reorganization** (Phase 1) 2. **Update Tenant Client** (Phase 2) 3. **Gateway Updates** (Phase 3) 4. **Frontend Updates** (Phase 4) This comprehensive redesign creates a clean, modern subscription system with proper separation of concerns while respecting the existing service-based client architecture constraint.