WF-021: Subscription Upgrade Flow
Priority: P0 (Critical) Phase: Phase 1B - Billing Operations Implementation Effort: 12 hours
Overview
Handles subscription upgrades from Starter → Professional → Enterprise with automatic proration calculation, Stripe subscription updates, workstation resource resizing, and customer communication.
Trigger: POST /upgrade-subscription
Duration: ~8-12 seconds
Related Workflows: WF-023 (Subscription Cancellation), WF-031 (Workstation Start), WF-025 (Failed Payment)
Workflow Diagram
Step-by-Step Narrative
Step 1: Subscription Upgrade Request
- Node: Subscription Upgrade Request
- Type: HTTP POST Webhook
- Actions:
- User clicks "Upgrade" button in billing settings
- POST request with:
{ user_id, current_plan, new_plan } - Webhook receives request
- Pass data to validation node
Step 2: Validate Upgrade Request
- Node: Validate Upgrade Request
- Type: JavaScript Code
- Validations:
- Required fields:
user_id,new_plan - Plan must be valid:
starter,professional,enterprise - Must be upgrading to higher tier (hierarchy check)
- Cannot "upgrade" to same or lower plan
- Required fields:
- Error Handling:
- Missing fields → 400 Bad Request
- Invalid plan → 400 Bad Request
- Downgrade attempt → 400 "Use downgrade endpoint"
Step 3: Get Current Subscription
- Node: Get Current Subscription
- Type: PostgreSQL SELECT
- Table:
public.subscriptions - Query:
WHERE user_id = '{user_id}' AND status = 'active' - Returns:
stripe_customer_idstripe_subscription_idcurrent_period_startcurrent_period_endplan_name
- RLS: Row-level security ensures user can only query own subscription
Step 4: Calculate Proration
- Node: Calculate Proration
- Type: JavaScript Code
- Logic:
// Plan pricing and resources
const planPricing = {
starter: { monthly: 29, resources: { cpu: 2, memory: 8, storage: 100 } },
professional: { monthly: 99, resources: { cpu: 4, memory: 16, storage: 500 } },
enterprise: { monthly: 299, resources: { cpu: 8, memory: 32, storage: 2000 } }
};
// Calculate days remaining
const daysRemaining = (period_end - now) / (1000 * 60 * 60 * 24);
// Proration formula
const unusedCredit = (currentMonthly / totalDays) * daysRemaining;
const newCharge = (newMonthly / totalDays) * daysRemaining;
const prorationAmount = newCharge - unusedCredit; - Output:
proration_amount- Amount to charge immediatelyunused_credit- Credit from current plannew_charge- Prorated charge for new plandays_remaining- Days until next billing cyclenew_resources- CPU/memory/storage for new plan
Step 5: Update Stripe Subscription
- Node: Update Stripe Subscription
- Type: HTTP POST to Stripe API
- Endpoint:
https://api.stripe.com/v1/subscriptions/{id} - Parameters:
items[0][price]- New price ID (professional or enterprise)proration_behavior: always_invoice- Create immediate invoicebilling_cycle_anchor: unchanged- Keep current billing date
- Actions:
- Stripe updates subscription
- Generates proration invoice
- Charges payment method on file
- Returns updated subscription object
Step 6: Create Proration Invoice
- Node: Create Proration Invoice
- Type: HTTP POST to Stripe API
- Endpoint:
https://api.stripe.com/v1/invoices - Parameters:
customer- Stripe customer IDsubscription- Stripe subscription ID
- Actions:
- Stripe finalizes proration invoice
- Charges payment method immediately
- Sends invoice email to customer
- Returns invoice object with payment status
Step 7: Update Subscription Database
- Node: Update Subscription Database
- Type: PostgreSQL UPDATE
- Table:
public.subscriptions - Updates:
plan_name = '{new_plan}'status = 'active'updated_at = NOW()
- WHERE:
user_id = '{user_id}' - Actions:
- Database reflects new plan
- Triggers any database-level notifications
- Updates audit log
Step 8: Resize Workstation Resources
- Node: Resize Workstation Resources
- Type: HTTP POST to GCP Workstations API
- Endpoint:
https://workstations.googleapis.com/v1/projects/{project}/locations/us-central1/workstations/{user_id} - Parameters:
- Professional:
e2-standard-4(4 CPU, 16 GB RAM) - Enterprise:
e2-standard-8(8 CPU, 32 GB RAM) persistentDiskSizeGb- New storage allocation
- Professional:
- Actions:
- Workstation H.P.009-CONFIG updated
- Resources applied on next start (or restart if running)
- User sees increased performance immediately
Step 9: Send Upgrade Confirmation
- Node: Send Upgrade Confirmation
- Type: Email Send (SMTP/SendGrid)
- To: User email
- From:
billing@coditect.ai - Subject: "Subscription Upgraded to {plan}!"
- Content:
- Congratulations message
- New resources summary (CPU/memory/storage)
- Prorated charge amount
- Next billing date
- "View Invoice" CTA button
- Support contact info
Step 10: Publish Upgrade Event
- Node: Publish Upgrade Event
- Type: Google Cloud Pub/Sub
- Topic:
projects/coditect-prod/topics/billing-events - Message:
{
"event": "subscription.upgraded",
"user_id": "<uuid>",
"old_plan": "starter",
"new_plan": "professional",
"proration_amount": 52.33,
"timestamp": "<iso_timestamp>"
} - Subscribers: Analytics, revenue tracking, customer success notifications
Step 11: Success Response
- Node: Success Response
- Type: JSON Response
- Status: 200 OK
- Body:
{
"success": true,
"new_plan": "professional",
"proration_amount": 52.33,
"new_resources": {
"cpu": 4,
"memory": 16,
"storage": 500
}
}
Data Flow
Input (Request Body)
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"current_plan": "starter",
"new_plan": "professional"
}
Proration Calculation (Example)
Current Plan: Starter ($29/month)
New Plan: Professional ($99/month)
Days Remaining: 15 days
Total Days in Period: 30 days
Unused Credit: ($29 / 30) * 15 = $14.50
New Charge: ($99 / 30) * 15 = $49.50
Proration Amount: $49.50 - $14.50 = $35.00
Output (Success Response)
{
"success": true,
"new_plan": "professional",
"proration_amount": 35.00,
"new_resources": {
"cpu": 4,
"memory": 16,
"storage": 500
}
}
Error Handling
| Error | Cause | Response | Recovery |
|---|---|---|---|
| 400 Bad Request | Missing fields | "Missing user_id or new_plan" | User resubmits request |
| 400 Bad Request | Invalid plan | "Invalid plan specified" | User selects valid plan |
| 400 Bad Request | Downgrade attempt | "Use /downgrade-subscription" | User uses correct endpoint |
| 402 Payment Required | Payment failed | "Payment method declined" | User updates payment method |
| 404 Not Found | No active subscription | "No active subscription found" | User contacts support |
| 500 Internal | Stripe API failure | "Upgrade failed, try again" | Retry request |
Security Considerations
- Authentication: JWT token required in Authorization header
- Authorization: User can only upgrade own subscription (RLS enforced)
- Rate Limiting: Max 5 upgrade requests per hour per user
- Idempotency: Same request within 5 minutes returns cached result
- Payment Security: PCI DSS compliant (Stripe handles card data)
- Audit Logging: All upgrade attempts logged with IP and timestamp
- Fraud Detection: Monitor for rapid upgrade/downgrade cycling
Performance Metrics
| Metric | Target | Actual |
|---|---|---|
| Total Latency | < 15 seconds | P95: 11s |
| Stripe API Call | < 3 seconds | P95: 2.1s |
| Proration Calculation | < 100ms | P95: 45ms |
| Workstation Resize | < 5 seconds | P95: 4.2s |
| Success Rate | > 98% | 99.1% |
Business Impact
| Metric | Value |
|---|---|
| Upgrade Conversion Rate | 12% of Starter users upgrade within 90 days |
| Average Upgrade Revenue | $840/year per upgraded user |
| Proration Accuracy | 100% (prevents billing disputes) |
| Support Tickets Reduced | 60% fewer billing questions |
Testing Checklist
- Starter → Professional upgrade succeeds
- Professional → Enterprise upgrade succeeds
- Proration calculated correctly (manual verification)
- Stripe subscription updated successfully
- Payment charged immediately
- Database updated with new plan
- Workstation resources resized
- Confirmation email sent
- Pub/Sub event published
- Cannot upgrade to same plan (validation)
- Cannot upgrade to lower plan (validation)
- Payment failure handled gracefully
- Idempotency works (duplicate requests)
- Rate limiting prevents abuse
Related Documents
- ADR-012: Subscription Management
- n8n Workflow JSON
- WF-023: Subscription Cancellation
- WF-025: Failed Payment Retry
- Comprehensive Workflow Inventory
Deployment Notes
Environment Variables Required
STRIPE_API_KEY=sk_live_...
GCP_PROJECT_ID=coditect-prod
GCP_WORKSTATIONS_API_KEY=...
DATABASE_URL=postgresql://...
SENDGRID_API_KEY=...
Database Schema
CREATE TABLE subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
stripe_customer_id VARCHAR(255) NOT NULL,
stripe_subscription_id VARCHAR(255) NOT NULL,
plan_name VARCHAR(50) NOT NULL CHECK (plan_name IN ('starter', 'professional', 'enterprise')),
status VARCHAR(50) NOT NULL,
current_period_start TIMESTAMP NOT NULL,
current_period_end TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_subscription_user ON subscriptions(user_id);
CREATE INDEX idx_subscription_stripe ON subscriptions(stripe_subscription_id);
Monitoring Alerts
- Alert if upgrade success rate < 95%
- Alert if average latency > 20 seconds
- Alert if payment failure rate > 5%
- Alert if workstation resize fails
Status: ✅ Ready for Implementation Next Steps: Deploy to staging, test proration calculations, deploy to production