Add subcription feature 7
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user