Add subcription feature 7

This commit is contained in:
Urtzi Alfaro
2026-01-16 15:21:11 +01:00
parent 4bafceed0d
commit 5e01b34cc0
5 changed files with 0 additions and 1154 deletions

View File

@@ -1,396 +0,0 @@
# 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<UsageSummary> {
return apiClient.get<UsageSummary>(`/tenants/${tenantId}/subscription/status`);
}
async getSubscriptionDetails(tenantId: string): Promise<any> {
return apiClient.get(`/tenants/${tenantId}/subscription/details`);
}
async getSubscriptionTier(tenantId: string): Promise<string> {
return apiClient.get(`/tenants/${tenantId}/subscription/tier`);
}
async getUsageSummary(tenantId: string): Promise<UsageSummary> {
return apiClient.get<UsageSummary>(`/tenants/${tenantId}/subscription/usage`);
}
async checkFeatureAccess(tenantId: string, featureName: string): Promise<FeatureCheckResponse> {
return apiClient.get<FeatureCheckResponse>(`/tenants/${tenantId}/subscription/features/${featureName}`);
}
async cancelSubscription(tenantId: string, reason?: string): Promise<any> {
return apiClient.post(`/tenants/${tenantId}/subscription/cancel`, { reason });
}
async reactivateSubscription(tenantId: string, plan: string = 'starter'): Promise<any> {
return apiClient.post(`/tenants/${tenantId}/subscription/reactivate`, { plan });
}
async validatePlanUpgrade(tenantId: string, newPlan: string): Promise<PlanUpgradeValidation> {
return apiClient.get<PlanUpgradeValidation>(`/tenants/${tenantId}/subscription/validate-upgrade/${newPlan}`);
}
async upgradePlan(tenantId: string, newPlan: string): Promise<PlanUpgradeResult> {
return apiClient.post<PlanUpgradeResult>(`/tenants/${tenantId}/subscription/upgrade`, { new_plan: newPlan });
}
async canAddLocation(tenantId: string): Promise<any> {
return apiClient.get(`/tenants/${tenantId}/subscription/limits/locations`);
}
async canAddProduct(tenantId: string): Promise<any> {
return apiClient.get(`/tenants/${tenantId}/subscription/limits/products`);
}
async canAddUser(tenantId: string): Promise<any> {
return apiClient.get(`/tenants/${tenantId}/subscription/limits/users`);
}
async canAddRecipe(tenantId: string): Promise<any> {
return apiClient.get(`/tenants/${tenantId}/subscription/limits/recipes`);
}
async canAddSupplier(tenantId: string): Promise<any> {
return apiClient.get(`/tenants/${tenantId}/subscription/limits/suppliers`);
}
async getPaymentMethod(tenantId: string): Promise<any> {
return apiClient.get(`/tenants/${tenantId}/subscription/payment-method`);
}
async updatePaymentMethod(tenantId: string, paymentMethodId: string): Promise<any> {
return apiClient.post(`/tenants/${tenantId}/subscription/payment-method`, { payment_method_id: paymentMethodId });
}
async getInvoices(tenantId: string): Promise<any[]> {
return apiClient.get(`/tenants/${tenantId}/subscription/invoices`);
}
// Registration flow methods
async startRegistrationPaymentSetup(userData: any): Promise<any> {
return apiClient.post('/registration/payment-setup', userData);
}
async completeRegistration(setupIntentId: string, userData: any): Promise<any> {
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.