Trial Flow
Current plan-based trial workflow, eligibility rules, entitlement creation, and conversion behavior.
Trial Flow
Audience: backend, QA, frontend Scope:
POST /subscriptions/trial/:planIdand related lifecycle behavior
Key Change From Older Docs
Trials are now configured on the plan, not on the module.
- old assumption: module-level
trialDurationDays - current implementation: plan-level
trialDaysandtrialRequiresCc
The trial endpoint also takes a planId, not a moduleId.
Endpoint
POST /subscriptions/trial/:planId
Authentication:
- requires
JwtAuthGuard
Eligibility Checks
SubscriptionService.startTrial() performs these checks:
- the plan exists and is active
plan.trialDays > 0- the user has no
user_trialrecord for the same module - the user has no live
activeortrialsubscription for the same module
The module is derived from the selected plan's tier.
What Gets Written
On success, one transaction creates:
-
subscriptionstatus = "trial"planId = selected planplanPriceId = nullpriceSnapshotNpr = 0startDate = nowendDate = now + plan.trialDays
-
user_trial- one row per
userId + moduleId - stores
startedAt - later stores
convertedAtif the trial becomes paid
- one row per
-
subscription_history- action
trial_started
- action
-
user_accessgrantType = "trial"expiresAt = endDate
Why Trial Uses user_access
The trial subscription row is not used directly for authorization.
Authorization depends on the user_access row that is created during trial start. That keeps trials aligned with admin grants and paid access.
Trial Conversion
If a pending paid purchase is activated while a live trial exists for the same module, activateSubscription() converts the trial.
Conversion behavior:
- existing subscription changes from
trialtoactive planIdandplanPriceIdare updatedstartDateis reset tonowendDateis recalculated from the selected plan priceuser_trial.convertedAtis setuser_accesschanges fromgrantType = "trial"tograntType = "subscription"- a
trial_convertedhistory row is recorded
Cancellation and Expiry
If a user cancels during trial:
- subscription status becomes
cancelled - access is retained only until the current
endDate
If the trial reaches its end date:
AccessExpirationTaskrevokes the expireduser_access- the corresponding subscription is marked
expired
Flow Summary
Paid Activation Flow
Current paid subscription lifecycle in the service layer, including pending purchase creation, activation, extension, and failure cleanup.
Admin Subscription Workflow
Current admin catalog and subscription operations, including routes, permissions, grants, extensions, and revocations.