Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesSubscriptions PlansSubscriptions & Plans — QA Plan

Subscriptions & Plans — QA Plan

Smoke

#ScenarioExpected
S1Create monthly subscription planRow created with interval='monthly', currency inherits from org.
S2Create class-pack plan without classCredits400 Class pack plans require classCredits.
S3Create subscription plan without interval400 Subscription plans require an interval.
S4Create class-pack on Lite tier403 — class_packs feature requires Pro.
S5Soft-delete planisActive=false. Existing subs continue to charge; new purchases blocked.
S6Member buys free planSub created active immediately; no paymentPageUrl; no payment_transactions row.
S7Member buys paid planSub pending + paymentPageUrl returned; on webhook, sub flips active.
S8Member retries abandoned checkoutSame pending sub row reused; fresh paymentPageUrl. No duplicate.
S9Admin enrolls member with skipPaymentSub active; no payment provider call.

Lifecycle

#ScenarioExpected
L1Freeze active substatus='paused', pausedAt=now. Renewal cron skips it. Bookings reject.
L2Resume paused sub after 7 daysstatus='active', pausedAt=null, currentPeriodEnd += 7d.
L3Cancel active sub (admin)status='cancelled', cancelledAt=now. If providerSubscriptionId was set, cancelRecurring is called.
L4Cancel already-cancelled sub400 Subscription is already cancelled.
L5Member self-cancels at period endcancelAtPeriodEnd=true, sub stays active. Email + low-priority task.
L6Member resumes before period endFlag cleared; reason/actor nulled.
L7Member tries to resume after cron sweeps400 Subscription is not scheduled to cancel.
L8Cancellation cron sweepsAt 02:30 UTC daily, all subs with cancelAtPeriodEnd=true and currentPeriodEnd ≤ now flip to cancelled; member email fires.

Renewal failures (FIT-136 surface)

See payments/qa-plan.md for the same scenarios. Subscription-side check:

#ScenarioExpected
RN1Past_due sub renews on retrystatus='active', attempts=0, period advanced. Membership flips to payment_status='current'.
RN2Sub enters debtstatus='debt', debt_since=now, debt_amount_in_cents=plan.price. Renewal cron will NOT pick it up (selector excludes debt).
RN3Clear debtOwner triggers DebtService.clearDebt. Charge fires for debt_amount_in_cents; on success, sub returns to active, debt zeroed.

Cancellation request workflow (mid-cycle refund)

#ScenarioExpected
CR1Member submits request with refundRow created, owner email, member email, urgent cancellation_review task.
CR2Duplicate request400 A cancellation request is already pending.
CR3Owner approves with refundSub canceled, refund flow runs (capability-aware), request → approved, refundTaskId filled if manual. Member email.
CR4Owner rejectsRequest → rejected. Sub stays active. Member email.
CR5Member cancels someone else’s requestService guard (cancellation-requests.service.ts:76): 403 You can only cancel your own subscription.
CR6Approve request whose sub has no completed chargeSub still cancels; refund skipped silently.
CR7Approve request whose linked refund task already existsRefused upstream by payment.service.ts:508 (Refund already in progress).

Credits

#ScenarioExpected
C1Booking deducts credit from class-pack subremaining_credits--.
C2Cancel booking refunds creditremaining_credits++.
C3Booking when credits = 0Service throws No credits remaining.
C4Booking on unlimited subNo-op deduct.
C5Admin adjusts credits by +5remaining_credits += 5.
C6Admin adjusts credits by −100 (more than balance)Clamped to 0.
C7Adjust credits on cancelled sub400 Subscription must be active or paused.
C8Adjust credits on unlimited plan400 This plan has unlimited credits.

Permissions

#ScenarioExpected
P1Member creates plan403
P2Coach creates plan403
P3Owner creates class_pack on Lite403 (feature-gated)
P4Member force-cancels another member’s sub403
P5Coach reads member’s subscriptions200 (allowed)
P6Owner reads cross-org sub404 (filtered out)

E2E

  • apps/web/e2e/specs/plans-purchase.spec.ts — full member purchase including Cardcom test payment.
  • apps/web/e2e/specs/cancellation-flow.spec.ts — member submit → owner approve → refund task.