846 lines
32 KiB
Markdown
846 lines
32 KiB
Markdown
# User Registration & Subscription Architecture Rearchitecture Proposal
|
|
|
|
## Executive Summary
|
|
|
|
This proposal outlines a comprehensive rearchitecture of the user registration, payment processing, and subscription management flow to address the current limitations and implement the requested multi-phase registration process.
|
|
|
|
## Current Architecture Analysis
|
|
|
|
### Current Flow Limitations
|
|
|
|
1. **Monolithic Registration Process**: The current flow combines user creation, payment processing, and subscription creation in a single step
|
|
2. **Tenant-Subscription Coupling**: Subscriptions are created and immediately linked to tenants during registration
|
|
3. **Payment Processing Timing**: Payment is processed before user creation is complete
|
|
4. **Onboarding Complexity**: The onboarding flow assumes immediate tenant creation with subscription
|
|
|
|
### Key Components Analysis
|
|
|
|
#### Frontend Components
|
|
- `RegisterForm.tsx`: Multi-step form handling basic info, subscription selection, and payment
|
|
- `PaymentForm.tsx`: Stripe payment processing component
|
|
- `RegisterTenantStep.tsx`: Tenant creation during onboarding
|
|
|
|
#### Backend Services
|
|
- **Auth Service**: User creation, authentication, and onboarding progress tracking
|
|
- **Tenant Service**: Tenant creation, subscription management, and payment processing
|
|
- **Shared Clients**: Inter-service communication between auth and tenant services
|
|
|
|
#### Current Data Flow
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[Frontend RegisterForm] -->|User Data + Payment| B[Auth Service Register]
|
|
B -->|Create User| C[User Created]
|
|
B -->|Call Tenant Service| D[Tenant Service Payment Customer]
|
|
D -->|Create Payment Customer| E[Payment Customer Created]
|
|
C -->|Return Tokens| F[User Authenticated]
|
|
F -->|Onboarding| G[RegisterTenantStep]
|
|
G -->|Create Tenant + Subscription| H[Tenant Service Create Tenant]
|
|
H -->|Create Subscription| I[Subscription Created]
|
|
```
|
|
|
|
## Proposed Architecture
|
|
|
|
### New Multi-Phase Registration Flow
|
|
|
|
```mermaid
|
|
graph TD
|
|
subgraph Frontend
|
|
A1[Basic Info Form] -->|Email + Password| A2[Subscription Selection]
|
|
A2 -->|Plan + Billing Cycle| A3[Payment Form]
|
|
A3 -->|Payment Method| A4[Process Payment]
|
|
end
|
|
|
|
subgraph Backend Services
|
|
A4 -->|User Data + Payment| B1[Auth Service Register]
|
|
B1 -->|Create User| B2[User Created with Payment ID]
|
|
B2 -->|Call Tenant Service| B3[Tenant Service Create Subscription]
|
|
B3 -->|Create Subscription| B4[Subscription Created]
|
|
B4 -->|Return Subscription ID| B2
|
|
B2 -->|Return Auth Tokens| A4
|
|
end
|
|
|
|
subgraph Onboarding
|
|
A4 -->|Success| C1[Onboarding Flow]
|
|
C1 -->|Tenant Creation| C2[RegisterTenantStep]
|
|
C2 -->|Tenant Data| C3[Tenant Service Create Tenant]
|
|
C3 -->|Link Subscription| C4[Link Subscription to Tenant]
|
|
C4 -->|Complete| C5[Onboarding Complete]
|
|
end
|
|
```
|
|
|
|
### Detailed Component Changes
|
|
|
|
#### 1. Frontend Changes
|
|
|
|
**RegisterForm.tsx Modifications:**
|
|
- **Phase 1**: Collect only email and password (basic info)
|
|
- **Phase 2**: Plan selection with billing cycle options
|
|
- **Phase 3**: Payment form with address and card details
|
|
- **Payment Processing**: Call new backend endpoint with complete registration data
|
|
|
|
**New Payment Flow:**
|
|
```typescript
|
|
// Current: handleRegistrationSubmit calls authService.register directly
|
|
// New: handleRegistrationSubmit calls new registration endpoint
|
|
const handleRegistrationSubmit = async (paymentMethodId?: string) => {
|
|
try {
|
|
const registrationData = {
|
|
email: formData.email,
|
|
password: formData.password,
|
|
full_name: formData.full_name,
|
|
subscription_plan: selectedPlan,
|
|
billing_cycle: billingCycle,
|
|
payment_method_id: paymentMethodId,
|
|
coupon_code: isPilot ? couponCode : undefined,
|
|
// Address and billing info
|
|
address: billingAddress,
|
|
postal_code: billingPostalCode,
|
|
city: billingCity,
|
|
country: billingCountry
|
|
};
|
|
|
|
// Call new registration endpoint
|
|
const response = await authService.registerWithSubscription(registrationData);
|
|
|
|
// Handle success and redirect to onboarding
|
|
onSuccess?.();
|
|
} catch (err) {
|
|
// Handle errors
|
|
}
|
|
};
|
|
```
|
|
|
|
#### 2. Auth Service Changes
|
|
|
|
**New Registration Endpoint:**
|
|
```python
|
|
@router.post("/api/v1/auth/register-with-subscription")
|
|
async def register_with_subscription(
|
|
user_data: UserRegistrationWithSubscription,
|
|
auth_service: EnhancedAuthService = Depends(get_auth_service)
|
|
):
|
|
"""Register user and create subscription in one call"""
|
|
|
|
# Step 1: Create user
|
|
user = await auth_service.register_user(user_data)
|
|
|
|
# Step 2: Create payment customer via tenant service
|
|
payment_result = await auth_service.create_payment_customer_via_tenant_service(
|
|
user_data,
|
|
user_data.payment_method_id
|
|
)
|
|
|
|
# Step 3: Create subscription via tenant service
|
|
subscription_result = await auth_service.create_subscription_via_tenant_service(
|
|
user.id,
|
|
user_data.subscription_plan,
|
|
user_data.payment_method_id,
|
|
user_data.billing_cycle,
|
|
user_data.coupon_code
|
|
)
|
|
|
|
# Step 4: Store subscription ID in user's onboarding progress
|
|
await auth_service.save_subscription_to_onboarding_progress(
|
|
user.id,
|
|
subscription_result.subscription_id,
|
|
user_data
|
|
)
|
|
|
|
return {
|
|
**user,
|
|
subscription_id: subscription_result.subscription_id
|
|
}
|
|
```
|
|
|
|
**Enhanced Auth Service Methods:**
|
|
```python
|
|
class EnhancedAuthService:
|
|
|
|
async def create_subscription_via_tenant_service(
|
|
self,
|
|
user_id: str,
|
|
plan_id: str,
|
|
payment_method_id: str,
|
|
billing_cycle: str,
|
|
coupon_code: Optional[str] = None
|
|
) -> Dict[str, Any]:
|
|
"""Create subscription via tenant service during registration"""
|
|
|
|
try:
|
|
from shared.clients.tenant_client import TenantServiceClient
|
|
from app.core.config import settings
|
|
|
|
tenant_client = TenantServiceClient(settings)
|
|
|
|
# Prepare user data for tenant service
|
|
user_data = await self.get_user_data_for_tenant_service(user_id)
|
|
|
|
# Call tenant service to create subscription
|
|
result = await tenant_client.create_subscription_for_registration(
|
|
user_data=user_data,
|
|
plan_id=plan_id,
|
|
payment_method_id=payment_method_id,
|
|
billing_cycle=billing_cycle,
|
|
coupon_code=coupon_code
|
|
)
|
|
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error("Failed to create subscription via tenant service",
|
|
user_id=user_id, error=str(e))
|
|
raise
|
|
|
|
async def save_subscription_to_onboarding_progress(
|
|
self,
|
|
user_id: str,
|
|
subscription_id: str,
|
|
registration_data: Dict[str, Any]
|
|
):
|
|
"""Store subscription info in onboarding progress for later tenant linking"""
|
|
|
|
try:
|
|
# Get or create onboarding progress
|
|
progress = await self.onboarding_repo.get_user_progress(user_id)
|
|
|
|
if not progress:
|
|
progress = await self.onboarding_repo.create_user_progress(user_id)
|
|
|
|
# Store subscription data in user_registered step
|
|
step_data = {
|
|
"subscription_id": subscription_id,
|
|
"subscription_plan": registration_data.subscription_plan,
|
|
"billing_cycle": registration_data.billing_cycle,
|
|
"coupon_code": registration_data.coupon_code,
|
|
"payment_method_id": registration_data.payment_method_id,
|
|
"payment_customer_id": registration_data.payment_customer_id,
|
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
"status": "pending_tenant_linking"
|
|
}
|
|
|
|
await self.onboarding_repo.upsert_user_step(
|
|
user_id=user_id,
|
|
step_name="user_registered",
|
|
completed=True,
|
|
step_data=step_data
|
|
)
|
|
|
|
logger.info("Subscription data saved to onboarding progress",
|
|
user_id=user_id,
|
|
subscription_id=subscription_id)
|
|
|
|
except Exception as e:
|
|
logger.error("Failed to save subscription to onboarding progress",
|
|
user_id=user_id, error=str(e))
|
|
raise
|
|
```
|
|
|
|
#### 3. Tenant Service Changes
|
|
|
|
**New Subscription Creation Endpoint:**
|
|
```python
|
|
@router.post("/api/v1/subscriptions/create-for-registration")
|
|
async def create_subscription_for_registration(
|
|
user_data: Dict[str, Any],
|
|
plan_id: str = Query(...),
|
|
payment_method_id: str = Query(...),
|
|
billing_cycle: str = Query("monthly"),
|
|
coupon_code: Optional[str] = Query(None),
|
|
payment_service: PaymentService = Depends(get_payment_service),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""
|
|
Create subscription during user registration (before tenant creation)
|
|
|
|
This endpoint creates a subscription that is not yet linked to any tenant.
|
|
The subscription will be linked to a tenant during the onboarding flow.
|
|
"""
|
|
|
|
try:
|
|
# Use orchestration service for complete workflow
|
|
orchestration_service = SubscriptionOrchestrationService(db)
|
|
|
|
# Create subscription without tenant_id (tenant-independent subscription)
|
|
result = await orchestration_service.create_tenant_independent_subscription(
|
|
user_data,
|
|
plan_id,
|
|
payment_method_id,
|
|
billing_cycle,
|
|
coupon_code
|
|
)
|
|
|
|
logger.info("Tenant-independent subscription created for registration",
|
|
user_id=user_data.get('user_id'),
|
|
subscription_id=result["subscription_id"])
|
|
|
|
return {
|
|
"success": True,
|
|
"subscription_id": result["subscription_id"],
|
|
"customer_id": result["customer_id"],
|
|
"status": result["status"],
|
|
"plan": result["plan"],
|
|
"billing_cycle": result["billing_cycle"]
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error("Failed to create tenant-independent subscription",
|
|
error=str(e),
|
|
user_id=user_data.get('user_id'))
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="Failed to create subscription"
|
|
)
|
|
```
|
|
|
|
**Enhanced Subscription Orchestration Service:**
|
|
```python
|
|
class SubscriptionOrchestrationService:
|
|
|
|
async def create_tenant_independent_subscription(
|
|
self,
|
|
user_data: Dict[str, Any],
|
|
plan_id: str,
|
|
payment_method_id: str,
|
|
billing_cycle: str = "monthly",
|
|
coupon_code: Optional[str] = None
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Create a subscription that is not linked to any tenant yet
|
|
|
|
This subscription will be linked to a tenant during onboarding
|
|
when the user creates their bakery/tenant.
|
|
"""
|
|
|
|
try:
|
|
logger.info("Creating tenant-independent subscription",
|
|
user_id=user_data.get('user_id'),
|
|
plan_id=plan_id)
|
|
|
|
# Step 1: Create customer in payment provider
|
|
customer = await self.payment_service.create_customer(user_data)
|
|
|
|
# Step 2: Handle coupon logic
|
|
trial_period_days = 0
|
|
coupon_discount = None
|
|
|
|
if coupon_code:
|
|
coupon_service = CouponService(self.db_session)
|
|
success, discount_applied, error = await coupon_service.redeem_coupon(
|
|
coupon_code,
|
|
None, # No tenant_id yet
|
|
base_trial_days=0
|
|
)
|
|
|
|
if success and discount_applied:
|
|
coupon_discount = discount_applied
|
|
trial_period_days = discount_applied.get("total_trial_days", 0)
|
|
|
|
# Step 3: Create subscription in payment provider
|
|
stripe_subscription = await self.payment_service.create_payment_subscription(
|
|
customer.id,
|
|
plan_id,
|
|
payment_method_id,
|
|
trial_period_days if trial_period_days > 0 else None,
|
|
billing_cycle
|
|
)
|
|
|
|
# Step 4: Create local subscription record WITHOUT tenant_id
|
|
subscription_record = await self.subscription_service.create_tenant_independent_subscription_record(
|
|
stripe_subscription.id,
|
|
customer.id,
|
|
plan_id,
|
|
stripe_subscription.status,
|
|
stripe_subscription.current_period_start,
|
|
stripe_subscription.current_period_end,
|
|
trial_period_days if trial_period_days > 0 else None,
|
|
billing_cycle,
|
|
user_data.get('user_id')
|
|
)
|
|
|
|
# Step 5: Store subscription in pending_tenant_linking state
|
|
await self.subscription_service.mark_subscription_as_pending_tenant_linking(
|
|
subscription_record.id,
|
|
user_data.get('user_id')
|
|
)
|
|
|
|
return {
|
|
"success": True,
|
|
"customer_id": customer.id,
|
|
"subscription_id": stripe_subscription.id,
|
|
"status": stripe_subscription.status,
|
|
"plan": plan_id,
|
|
"billing_cycle": billing_cycle,
|
|
"trial_period_days": trial_period_days,
|
|
"current_period_end": stripe_subscription.current_period_end.isoformat(),
|
|
"coupon_applied": bool(coupon_discount),
|
|
"user_id": user_data.get('user_id')
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error("Failed to create tenant-independent subscription",
|
|
error=str(e),
|
|
user_id=user_data.get('user_id'))
|
|
raise
|
|
```
|
|
|
|
**New Subscription Service Methods:**
|
|
```python
|
|
class SubscriptionService:
|
|
|
|
async def create_tenant_independent_subscription_record(
|
|
self,
|
|
subscription_id: str,
|
|
customer_id: str,
|
|
plan: str,
|
|
status: str,
|
|
current_period_start: datetime,
|
|
current_period_end: datetime,
|
|
trial_period_days: Optional[int] = None,
|
|
billing_cycle: str = "monthly",
|
|
user_id: Optional[str] = None
|
|
) -> Subscription:
|
|
"""Create subscription record without tenant_id"""
|
|
|
|
try:
|
|
subscription_data = {
|
|
"subscription_id": subscription_id,
|
|
"customer_id": customer_id,
|
|
"plan": plan,
|
|
"status": status,
|
|
"current_period_start": current_period_start,
|
|
"current_period_end": current_period_end,
|
|
"trial_period_days": trial_period_days,
|
|
"billing_cycle": billing_cycle,
|
|
"user_id": user_id,
|
|
"tenant_id": None, # No tenant linked yet
|
|
"is_tenant_linked": False,
|
|
"created_at": datetime.now(timezone.utc),
|
|
"updated_at": datetime.now(timezone.utc)
|
|
}
|
|
|
|
subscription = await self.subscription_repo.create(subscription_data)
|
|
|
|
logger.info("Tenant-independent subscription record created",
|
|
subscription_id=subscription.id,
|
|
user_id=user_id)
|
|
|
|
return subscription
|
|
|
|
except Exception as e:
|
|
logger.error("Failed to create tenant-independent subscription record",
|
|
error=str(e))
|
|
raise
|
|
|
|
async def mark_subscription_as_pending_tenant_linking(
|
|
self,
|
|
subscription_id: str,
|
|
user_id: str
|
|
):
|
|
"""Mark subscription as pending tenant linking"""
|
|
|
|
try:
|
|
await self.subscription_repo.update(
|
|
subscription_id,
|
|
{
|
|
"status": "pending_tenant_linking",
|
|
"tenant_linking_status": "pending",
|
|
"user_id": user_id
|
|
}
|
|
)
|
|
|
|
logger.info("Subscription marked as pending tenant linking",
|
|
subscription_id=subscription_id,
|
|
user_id=user_id)
|
|
|
|
except Exception as e:
|
|
logger.error("Failed to mark subscription as pending tenant linking",
|
|
error=str(e),
|
|
subscription_id=subscription_id)
|
|
raise
|
|
```
|
|
|
|
#### 4. Onboarding Flow Changes
|
|
|
|
**Enhanced RegisterTenantStep:**
|
|
```typescript
|
|
// When tenant is created, link the pending subscription
|
|
const handleSubmit = async () => {
|
|
if (!validateForm()) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
let tenant;
|
|
if (tenantId) {
|
|
// Update existing tenant
|
|
const updateData: TenantUpdate = { ... };
|
|
tenant = await updateTenant.mutateAsync({ tenantId, updateData });
|
|
} else {
|
|
// Create new tenant and link subscription
|
|
const registrationData: BakeryRegistrationWithSubscription = {
|
|
...formData,
|
|
// Include subscription linking data from onboarding progress
|
|
subscription_id: wizardContext.state.subscriptionId,
|
|
link_existing_subscription: true
|
|
};
|
|
|
|
tenant = await registerBakery.mutateAsync(registrationData);
|
|
}
|
|
|
|
// Continue with onboarding
|
|
onComplete({ tenant, tenantId: tenant.id });
|
|
|
|
} catch (error) {
|
|
console.error('Error registering bakery:', error);
|
|
setErrors({ submit: t('onboarding:steps.tenant_registration.errors.register') });
|
|
}
|
|
};
|
|
```
|
|
|
|
**Enhanced Tenant Creation Endpoint:**
|
|
```python
|
|
@router.post(route_builder.build_base_route("register", include_tenant_prefix=False))
|
|
async def register_bakery(
|
|
bakery_data: BakeryRegistrationWithSubscription,
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""Register a new bakery/tenant with subscription linking"""
|
|
|
|
try:
|
|
// Create tenant first
|
|
result = await tenant_service.create_bakery(bakery_data, current_user["user_id"])
|
|
tenant_id = result["tenant_id"]
|
|
|
|
// Check if we need to link an existing subscription
|
|
if bakery_data.link_existing_subscription and bakery_data.subscription_id:
|
|
// Link the pending subscription to this tenant
|
|
subscription_result = await tenant_service.link_subscription_to_tenant(
|
|
tenant_id,
|
|
bakery_data.subscription_id,
|
|
current_user["user_id"]
|
|
)
|
|
|
|
logger.info("Subscription linked to tenant during registration",
|
|
tenant_id=tenant_id,
|
|
subscription_id=bakery_data.subscription_id)
|
|
else:
|
|
// Fallback to current behavior for backward compatibility
|
|
// Create new subscription if needed
|
|
pass
|
|
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error("Failed to register bakery with subscription linking",
|
|
error=str(e),
|
|
user_id=current_user["user_id"])
|
|
raise
|
|
```
|
|
|
|
**New Tenant Service Method for Subscription Linking:**
|
|
```python
|
|
class EnhancedTenantService:
|
|
|
|
async def link_subscription_to_tenant(
|
|
self,
|
|
tenant_id: str,
|
|
subscription_id: str,
|
|
user_id: str
|
|
) -> Dict[str, Any]:
|
|
"""Link a pending subscription to a tenant"""
|
|
|
|
try:
|
|
async with self.database_manager.get_session() as db_session:
|
|
async with UnitOfWork(db_session) as uow:
|
|
# Register repositories
|
|
subscription_repo = uow.register_repository(
|
|
"subscriptions", SubscriptionRepository, Subscription
|
|
)
|
|
tenant_repo = uow.register_repository(
|
|
"tenants", TenantRepository, Tenant
|
|
)
|
|
|
|
# Get the subscription
|
|
subscription = await subscription_repo.get_by_id(subscription_id)
|
|
|
|
if not subscription:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Subscription not found"
|
|
)
|
|
|
|
# Verify subscription is in pending_tenant_linking state
|
|
if subscription.tenant_linking_status != "pending":
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Subscription is not in pending tenant linking state"
|
|
)
|
|
|
|
# Verify subscription belongs to this user
|
|
if subscription.user_id != user_id:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Subscription does not belong to this user"
|
|
)
|
|
|
|
# Update subscription with tenant_id
|
|
update_data = {
|
|
"tenant_id": tenant_id,
|
|
"is_tenant_linked": True,
|
|
"tenant_linking_status": "completed",
|
|
"linked_at": datetime.now(timezone.utc)
|
|
}
|
|
|
|
await subscription_repo.update(subscription_id, update_data)
|
|
|
|
# Update tenant with subscription information
|
|
tenant_update = {
|
|
"stripe_customer_id": subscription.customer_id,
|
|
"subscription_status": subscription.status,
|
|
"subscription_plan": subscription.plan,
|
|
"subscription_tier": subscription.plan,
|
|
"billing_cycle": subscription.billing_cycle,
|
|
"trial_period_days": subscription.trial_period_days
|
|
}
|
|
|
|
await tenant_repo.update_tenant(tenant_id, tenant_update)
|
|
|
|
# Commit transaction
|
|
await uow.commit()
|
|
|
|
logger.info("Subscription successfully linked to tenant",
|
|
tenant_id=tenant_id,
|
|
subscription_id=subscription_id,
|
|
user_id=user_id)
|
|
|
|
return {
|
|
"success": True,
|
|
"tenant_id": tenant_id,
|
|
"subscription_id": subscription_id,
|
|
"status": "linked"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error("Failed to link subscription to tenant",
|
|
error=str(e),
|
|
tenant_id=tenant_id,
|
|
subscription_id=subscription_id,
|
|
user_id=user_id)
|
|
raise
|
|
```
|
|
|
|
## Database Schema Changes
|
|
|
|
### New Subscription Table Structure
|
|
|
|
```sql
|
|
-- Add new columns to subscriptions table
|
|
ALTER TABLE subscriptions ADD COLUMN IF NOT EXISTS user_id UUID;
|
|
ALTER TABLE subscriptions ADD COLUMN IF NOT EXISTS is_tenant_linked BOOLEAN DEFAULT FALSE;
|
|
ALTER TABLE subscriptions ADD COLUMN IF NOT EXISTS tenant_linking_status VARCHAR(50);
|
|
ALTER TABLE subscriptions ADD COLUMN IF NOT EXISTS linked_at TIMESTAMP;
|
|
|
|
-- Add index for user-based subscription queries
|
|
CREATE INDEX IF NOT EXISTS idx_subscriptions_user_id ON subscriptions(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_subscriptions_linking_status ON subscriptions(tenant_linking_status);
|
|
|
|
-- Add constraint to ensure tenant_id is NULL when not linked
|
|
ALTER TABLE subscriptions ADD CONSTRAINT chk_tenant_linking
|
|
CHECK ((is_tenant_linked = FALSE AND tenant_id IS NULL) OR
|
|
(is_tenant_linked = TRUE AND tenant_id IS NOT NULL));
|
|
```
|
|
|
|
### Onboarding Progress Data Structure
|
|
|
|
```json
|
|
{
|
|
"user_id": "user-uuid",
|
|
"current_step": "user_registered",
|
|
"steps": [
|
|
{
|
|
"step_name": "user_registered",
|
|
"completed": true,
|
|
"completed_at": "2025-10-15T10:30:00Z",
|
|
"data": {
|
|
"subscription_id": "sub-uuid",
|
|
"subscription_plan": "professional",
|
|
"billing_cycle": "yearly",
|
|
"coupon_code": "PILOT2025",
|
|
"payment_method_id": "pm-123",
|
|
"payment_customer_id": "cus-456",
|
|
"status": "pending_tenant_linking",
|
|
"created_at": "2025-10-15T10:30:00Z"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## Error Handling & Recovery
|
|
|
|
### Error Scenarios and Recovery Strategies
|
|
|
|
1. **Payment Processing Failure**
|
|
- **Scenario**: Payment fails during registration
|
|
- **Recovery**: Rollback user creation, show error to user, allow retry
|
|
- **Implementation**: Transaction management in auth service
|
|
|
|
2. **Subscription Creation Failure**
|
|
- **Scenario**: Subscription creation fails after user creation
|
|
- **Recovery**: User created but marked as "registration_incomplete", allow retry in onboarding
|
|
- **Implementation**: Store registration state, provide recovery endpoint
|
|
|
|
3. **Tenant Linking Failure**
|
|
- **Scenario**: Tenant creation succeeds but subscription linking fails
|
|
- **Recovery**: Tenant created with default trial subscription, manual linking available
|
|
- **Implementation**: Fallback to current behavior, admin notification
|
|
|
|
4. **Orphaned Subscriptions**
|
|
- **Scenario**: User registers but never completes onboarding
|
|
- **Recovery**: Cleanup task to cancel subscriptions after 30 days
|
|
- **Implementation**: Background job to monitor pending subscriptions
|
|
|
|
### Monitoring and Alerts
|
|
|
|
```python
|
|
# Subscription linking monitoring
|
|
class SubscriptionMonitoringService:
|
|
|
|
async def monitor_pending_subscriptions(self):
|
|
"""Monitor subscriptions pending tenant linking"""
|
|
|
|
pending_subscriptions = await self.subscription_repo.get_pending_tenant_linking()
|
|
|
|
for subscription in pending_subscriptions:
|
|
created_days_ago = (datetime.now(timezone.utc) - subscription.created_at).days
|
|
|
|
if created_days_ago > 30:
|
|
# Cancel subscription and notify user
|
|
await self.cancel_orphaned_subscription(subscription.id)
|
|
await self.notify_user_about_cancellation(subscription.user_id)
|
|
elif created_days_ago > 7:
|
|
# Send reminder to complete onboarding
|
|
await self.send_onboarding_reminder(subscription.user_id)
|
|
```
|
|
|
|
## Migration Strategy
|
|
|
|
### Phase 1: Backend Implementation
|
|
1. **Database Migration**: Add new columns to subscriptions table
|
|
2. **Auth Service Updates**: Implement new registration endpoint
|
|
3. **Tenant Service Updates**: Implement tenant-independent subscription creation
|
|
4. **Shared Clients**: Update inter-service communication
|
|
|
|
### Phase 2: Frontend Implementation
|
|
1. **Registration Form**: Update to collect billing address
|
|
2. **Payment Flow**: Integrate with new backend endpoints
|
|
3. **Onboarding Flow**: Add subscription linking logic
|
|
|
|
### Phase 3: Testing and Validation
|
|
1. **Unit Tests**: Verify individual component behavior
|
|
2. **Integration Tests**: Test service-to-service communication
|
|
3. **End-to-End Tests**: Validate complete user journey
|
|
4. **Load Testing**: Ensure performance under load
|
|
|
|
### Phase 4: Deployment and Rollout
|
|
1. **Feature Flags**: Enable gradual rollout
|
|
2. **A/B Testing**: Compare with existing flow
|
|
3. **Monitoring**: Track key metrics and errors
|
|
4. **Rollback Plan**: Prepare for quick rollback if needed
|
|
|
|
## Benefits of the New Architecture
|
|
|
|
### 1. Improved User Experience
|
|
- **Clear Separation of Concerns**: Users understand each step of the process
|
|
- **Progressive Commitment**: Users can complete registration without immediate tenant creation
|
|
- **Flexible Onboarding**: Users can explore the platform before committing to a specific bakery
|
|
|
|
### 2. Better Error Handling
|
|
- **Isolated Failure Points**: Failures in one step don't cascade to others
|
|
- **Recovery Paths**: Clear recovery mechanisms for each failure scenario
|
|
- **Graceful Degradation**: System remains functional even with partial failures
|
|
|
|
### 3. Enhanced Business Flexibility
|
|
- **Multi-Tenant Support**: Users can create multiple tenants with the same subscription
|
|
- **Subscription Portability**: Subscriptions can be moved between tenants
|
|
- **Trial Management**: Better control over trial periods and conversions
|
|
|
|
### 4. Improved Security
|
|
- **Data Isolation**: Sensitive payment data handled separately from user data
|
|
- **Audit Trails**: Clear tracking of subscription lifecycle
|
|
- **Compliance**: Better support for GDPR and payment industry standards
|
|
|
|
### 5. Scalability
|
|
- **Microservice Alignment**: Better separation between auth and tenant services
|
|
- **Independent Scaling**: Services can be scaled independently
|
|
- **Future Extensibility**: Easier to add new features and integrations
|
|
|
|
## Implementation Timeline
|
|
|
|
| Phase | Duration | Key Activities |
|
|
|-------|----------|----------------|
|
|
| 1. Analysis & Design | 2 weeks | Architecture review, technical design, stakeholder approval |
|
|
| 2. Backend Implementation | 4 weeks | Database changes, service updates, API development |
|
|
| 3. Frontend Implementation | 3 weeks | Form updates, payment integration, onboarding changes |
|
|
| 4. Testing & QA | 3 weeks | Unit tests, integration tests, E2E tests, performance testing |
|
|
| 5. Deployment & Rollout | 2 weeks | Staging deployment, production rollout, monitoring setup |
|
|
| 6. Post-Launch | Ongoing | Bug fixes, performance optimization, feature enhancements |
|
|
|
|
## Risks and Mitigation
|
|
|
|
### Technical Risks
|
|
1. **Data Consistency**: Risk of inconsistent state between services
|
|
- *Mitigation*: Strong transaction management, idempotent operations, reconciliation jobs
|
|
|
|
2. **Performance Impact**: Additional service calls may impact performance
|
|
- *Mitigation*: Caching, async processing, performance optimization
|
|
|
|
3. **Complexity Increase**: More moving parts increase system complexity
|
|
- *Mitigation*: Clear documentation, comprehensive monitoring, gradual rollout
|
|
|
|
### Business Risks
|
|
1. **User Confusion**: Multi-step process may confuse some users
|
|
- *Mitigation*: Clear UI guidance, progress indicators, help documentation
|
|
|
|
2. **Conversion Impact**: Additional steps may reduce conversion rates
|
|
- *Mitigation*: A/B testing, user feedback, iterative improvements
|
|
|
|
3. **Support Burden**: New flow may require additional support
|
|
- *Mitigation*: Comprehensive documentation, self-service recovery, support training
|
|
|
|
## Success Metrics
|
|
|
|
### Key Performance Indicators
|
|
1. **Registration Completion Rate**: Percentage of users completing registration
|
|
2. **Onboarding Completion Rate**: Percentage of users completing onboarding
|
|
3. **Error Rates**: Frequency of errors in each step
|
|
4. **Conversion Rates**: Percentage of visitors becoming paying customers
|
|
5. **User Satisfaction**: Feedback and ratings from users
|
|
|
|
### Monitoring Dashboard
|
|
```
|
|
Registration Funnel:
|
|
- Step 1 (Basic Info): 100%
|
|
- Step 2 (Plan Selection): 85%
|
|
- Step 3 (Payment): 75%
|
|
- Onboarding Completion: 60%
|
|
|
|
Error Metrics:
|
|
- Registration Errors: < 1%
|
|
- Payment Errors: < 2%
|
|
- Subscription Linking Errors: < 0.5%
|
|
|
|
Performance Metrics:
|
|
- Registration Time: < 5s
|
|
- Payment Processing Time: < 3s
|
|
- Tenant Creation Time: < 2s
|
|
```
|
|
|
|
## Conclusion
|
|
|
|
This rearchitecture proposal addresses the current limitations by implementing a clear separation between user registration, payment processing, and tenant creation. The new multi-phase approach provides better user experience, improved error handling, and enhanced business flexibility while maintaining backward compatibility and providing clear migration paths.
|
|
|
|
The proposed solution aligns with modern microservice architectures and provides a solid foundation for future growth and feature enhancements. |