Subscription Lifecycle
State machine and sequence diagrams for subscription management
Subscription Lifecycle
This page details the complete subscription lifecycle from initial signup through renewal, pause, and cancellation.
Subscription State Machine
stateDiagram-v2
[*] --> PENDING: Create Subscription
PENDING --> TRIALING: Start Trial
PENDING --> ACTIVE: Payment Success (no trial)
PENDING --> CANCELLED: Payment Failed / User Cancels
TRIALING --> ACTIVE: Trial Ends + Payment Success
TRIALING --> CANCELLED: Trial Ends + Payment Failed
TRIALING --> CANCELLED: User Cancels During Trial
ACTIVE --> PAUSED: User Pauses
ACTIVE --> PAST_DUE: Payment Failed
ACTIVE --> CANCELLED: User Cancels
ACTIVE --> EXPIRED: End Date Reached
PAUSED --> ACTIVE: Resume
PAUSED --> CANCELLED: Cancel While Paused
PAST_DUE --> ACTIVE: Payment Retry Success
PAST_DUE --> CANCELLED: Max Retries Exceeded
EXPIRED --> ACTIVE: Renew
EXPIRED --> CANCELLED: No Renewal
CANCELLED --> [*]
EXPIRED --> [*]
note right of TRIALING
Free trial period
No payment yet
end note
note right of PAST_DUE
Payment failed
Retry in progress
end note
note right of PAUSED
Temporary hold
Can resume
end noteStatus Descriptions
| Status | Description | Actions Available |
|---|---|---|
PENDING | Created, awaiting initial payment | Pay, Cancel |
TRIALING | In free trial period | Use service, Cancel |
ACTIVE | Paid and active | Use service, Pause, Cancel, Upgrade |
PAUSED | Temporarily suspended | Resume, Cancel |
PAST_DUE | Payment failed, retrying | Update payment, Cancel |
CANCELLED | Terminated by user or system | Resubscribe |
EXPIRED | End date reached | Renew, Resubscribe |
Subscription Signup Sequence
sequenceDiagram
participant User
participant UI as Frontend
participant API as TRPC Router
participant Service as Subscription Service
participant Stripe as Stripe API
participant DB as Database
participant Email as Email Service
User->>UI: Select subscription plan
UI->>API: createSubscription(planId, paymentMethodId)
API->>Service: initiateSubscription(ctx, input)
Service->>DB: Get plan details
DB-->>Service: Plan data
Service->>DB: Get or create contact
DB-->>Service: Contact
alt Plan has trial
Service->>Stripe: Create subscription (trial)
Stripe-->>Service: Subscription (trialing)
Service->>DB: Create subscription (TRIALING)
else No trial
Service->>Stripe: Create subscription
Stripe-->>Service: Subscription (active)
Service->>DB: Create subscription (ACTIVE)
end
DB-->>Service: Subscription created
Service->>Email: sendWelcomeEmail(subscription)
Email-->>Service: Queued
Service-->>API: Subscription result
API-->>UI: Success
UI-->>User: Welcome screenTrial to Active Transition
sequenceDiagram
participant Stripe as Stripe
participant Webhook as Webhook Handler
participant Service as Subscription Service
participant DB as Database
participant Email as Email Service
participant User
Note over Stripe: Trial period ends
Stripe->>Stripe: Attempt first charge
Stripe->>Webhook: customer.subscription.updated
Webhook->>Service: handleSubscriptionUpdate(event)
Service->>DB: Find subscription by stripeId
DB-->>Service: Subscription
alt Payment successful
Service->>DB: Update status (ACTIVE)
Service->>DB: Set nextBillingDate
Service->>Email: sendTrialEndedSuccess(subscription)
Email->>User: "Your subscription is now active"
else Payment failed
Service->>DB: Update status (PAST_DUE)
Service->>Email: sendPaymentFailed(subscription)
Email->>User: "Payment failed - please update"
end
Service-->>Webhook: HandledPause Subscription Flow
flowchart TD
REQUEST[User Requests Pause] --> VALIDATE{Validate Request}
VALIDATE -->|Invalid| REJECT[Reject - Show Reason]
VALIDATE -->|Valid| CHECK_LIMITS{Within Pause Limits?}
CHECK_LIMITS -->|No| REJECT_LIMIT[Reject - Max Pauses Reached]
CHECK_LIMITS -->|Yes| SELECT_DATES[Select Pause Period]
SELECT_DATES --> CONFIRM[Confirm Pause]
CONFIRM --> STRIPE[Update Stripe Subscription]
STRIPE --> DB[Create Pause Record]
DB --> ADJUST[Adjust Next Billing Date]
ADJUST --> NOTIFY[Send Confirmation Email]
NOTIFY --> PAUSED[Subscription Paused]
style PAUSED fill:#fff3e0Pause Sequence
sequenceDiagram
participant User
participant API as TRPC Router
participant Service as Subscription Service
participant Stripe as Stripe API
participant DB as Database
User->>API: pauseSubscription(subscriptionId, dates)
API->>Service: pauseSubscription(id, pauseStart, pauseEnd)
Service->>DB: Get subscription
DB-->>Service: Subscription
Service->>Service: Validate pause request
Note over Service: Check: status, limits, dates
Service->>Stripe: Update subscription (pause collection)
Stripe-->>Service: Updated
Service->>DB: Create SubscriptionPause record
Service->>DB: Update subscription status (PAUSED)
Service->>DB: Calculate new billing date
DB-->>Service: Updated
Service-->>API: Pause confirmed
API-->>User: SuccessResume Subscription Flow
sequenceDiagram
participant User
participant API as TRPC Router
participant Service as Subscription Service
participant Stripe as Stripe API
participant DB as Database
participant Email as Email Service
User->>API: resumeSubscription(subscriptionId)
API->>Service: resumeSubscription(id)
Service->>DB: Get subscription + pause record
DB-->>Service: Data
Service->>Service: Calculate billing adjustment
Note over Service: Extend end date by pause duration
Service->>Stripe: Resume collection
Stripe-->>Service: Updated
Service->>DB: Update pause record (ended)
Service->>DB: Update subscription (ACTIVE)
Service->>DB: Update billing dates
Service->>Email: sendResumeConfirmation(subscription)
Service-->>API: Resume confirmed
API-->>User: SuccessUpgrade/Downgrade Flow
flowchart TD
REQUEST[Change Plan Request] --> COMPARE{Compare Plans}
COMPARE -->|Upgrade| PRORATE_UP[Calculate Prorated Charge]
COMPARE -->|Downgrade| PRORATE_DOWN[Calculate Credit]
COMPARE -->|Same Tier| REJECT[No Change Needed]
PRORATE_UP --> CHARGE[Charge Difference]
CHARGE --> UPDATE[Update Plan]
PRORATE_DOWN --> CREDIT[Apply Credit]
CREDIT --> UPDATE
UPDATE --> STRIPE[Update Stripe]
STRIPE --> DB[Update Database]
DB --> EMAIL[Send Confirmation]
EMAIL --> COMPLETE[Plan Changed]
style COMPLETE fill:#e8f5e9Plan Change Sequence
sequenceDiagram
participant User
participant API as TRPC Router
participant Service as Subscription Service
participant Stripe as Stripe API
participant DB as Database
User->>API: changePlan(subscriptionId, newPlanId)
API->>Service: changePlan(id, newPlanId)
Service->>DB: Get current subscription
Service->>DB: Get new plan
DB-->>Service: Both records
Service->>Service: Calculate proration
alt Upgrade (higher price)
Service->>Stripe: Update subscription (prorate)
Note over Stripe: Creates prorated invoice
Stripe-->>Service: Invoice + updated subscription
Service->>Stripe: Pay invoice immediately
else Downgrade (lower price)
Service->>Stripe: Update subscription (credit)
Note over Stripe: Applies credit to account
Stripe-->>Service: Updated subscription
end
Service->>DB: Update subscription (new plan)
Service->>DB: Log plan change
Service-->>API: Plan changed
API-->>User: New plan activeSubscription Timeline
gantt
title Subscription Lifecycle Example
dateFormat YYYY-MM-DD
axisFormat %b %d
section Trial
Trial Period :active, trial, 2024-01-01, 14d
section Active
First Billing Cycle :active, cycle1, after trial, 30d
Second Billing Cycle :cycle2, after cycle1, 30d
Third Billing Cycle :cycle3, after cycle2, 30d
section Pause
Paused Period :crit, pause, after cycle3, 14d
section Resume
Fourth Billing Cycle :cycle4, after pause, 30dGrace Period Handling
stateDiagram-v2
ACTIVE --> PAYMENT_FAILED: Invoice fails
PAYMENT_FAILED --> GRACE_PERIOD: Enter grace period
state GRACE_PERIOD {
[*] --> DAY_1: Start
DAY_1 --> DAY_3: Retry 1 fails
DAY_3 --> DAY_7: Retry 2 fails
DAY_7 --> DAY_14: Retry 3 fails
DAY_14 --> END: Final retry
}
GRACE_PERIOD --> ACTIVE: Payment succeeds
GRACE_PERIOD --> CANCELLED: All retries fail