Skip to main content

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

See Billing Architecture


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
  • 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_id
    • stripe_subscription_id
    • current_period_start
    • current_period_end
    • plan_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 immediately
    • unused_credit - Credit from current plan
    • new_charge - Prorated charge for new plan
    • days_remaining - Days until next billing cycle
    • new_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 invoice
    • billing_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 ID
    • subscription - 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
  • 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

ErrorCauseResponseRecovery
400 Bad RequestMissing fields"Missing user_id or new_plan"User resubmits request
400 Bad RequestInvalid plan"Invalid plan specified"User selects valid plan
400 Bad RequestDowngrade attempt"Use /downgrade-subscription"User uses correct endpoint
402 Payment RequiredPayment failed"Payment method declined"User updates payment method
404 Not FoundNo active subscription"No active subscription found"User contacts support
500 InternalStripe 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

MetricTargetActual
Total Latency< 15 secondsP95: 11s
Stripe API Call< 3 secondsP95: 2.1s
Proration Calculation< 100msP95: 45ms
Workstation Resize< 5 secondsP95: 4.2s
Success Rate> 98%99.1%

Business Impact

MetricValue
Upgrade Conversion Rate12% of Starter users upgrade within 90 days
Average Upgrade Revenue$840/year per upgraded user
Proration Accuracy100% (prevents billing disputes)
Support Tickets Reduced60% 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


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