[backport core/1.48] feat(billing): disclose that changing plans on a cancelled subscription resumes it - #14284
Merged
Merged
Conversation
…on resumes it (#14222) Tells a user that changing plans while their subscription is cancelled will **resume it**. ## Why A subscription can be cancelled but still active — it runs to the end of the period already paid for. Backend work landing alongside this lets those users change plans, and doing so **clears the pending cancellation**: the subscription stops ending and starts renewing again. That is a bigger change than "swap my plan", triggered by what looks like an ordinary plan click. Without a disclosure, someone who deliberately cancelled ends up subscribed indefinitely and never told. ## What changes When the current subscription is cancelled, the transition preview gains a banner above the plan comparison and the primary button becomes **"Confirm & reactivate"**. The copy differs per case because the consequence does: | Case | Today | What the user needs to notice | |---|---|---| | **Upgrade** | prorated charge | charged now, and it renews instead of ending | | **Downgrade** | **nothing** | easiest to miss — no charge, but it now renews at the new price instead of ending | | **Monthly → annual** | full year | largest charge of the three | Because the downgrade case has no money attached, the copy leads with *reactivates*, not with a number. Above the current plan's monthly price the amount renders larger and higher-contrast, and the confirm button is **disabled** behind an explicit "I understand I'll be charged $X today" checkbox. Standard proration styling renders a $6 upgrade and a $336 annual charge identically, and only one of those deserves a second look. ## The load-bearing part `confirm_reactivation` is a **server-checked** flag — the API refuses the change without it, and records it for audit. An earlier commit on this branch derived it from `subscription.isCancelled`, which made it true in exactly the circumstances the server demanded it. The check could therefore never fail, and the audit record asserted consent for requests where no disclosure was ever shown. Consent is now computed by the component that actually rendered the banner and emitted on `confirm`; the composable only forwards it. That distinction is the entire point of the field, so it has a regression test that fails against the old wiring. ## Scope notes - `PricingTableWorkspace.vue` is untouched deliberately — a different-tier click while cancelled already routes through this preview, and the "Resubscribe to X" button hits a different endpoint whose label is already explicit. - Team and personal paths are both wired. - The pre-existing `SubscriptionTransitionPreviewWorkspace.test.ts` is untouched: its `vue-i18n` module mock is incompatible with `<i18n-t>`, so the new tests live in a sibling file. ## Verification `pnpm typecheck` clean, `pnpm lint` 0 errors, `pnpm knip` clean, `pnpm format` no diff. Consent is asserted on the **request payload**, not on component state — a test that only checked the checkbox toggles a local ref would pass with the bug fully intact. Six Storybook states cover the banner (baseline, upgrade, downgrade, monthly→annual, annual→monthly, above-threshold); rendered screenshots are in a comment below. ## Found while reviewing, not fixed here The credit line reads **"Credit from current monthly plan" on an annual→monthly switch**, where the current plan is annual. `creditFromPlanLabel` returns the monthly string for *any* cadence change and never reads the current subscription's duration. This is on `main` today, is not touched by this branch, and affects every cadence change rather than just the reactivation path — so it wants its own PR rather than widening this one. It is visible in the annual→monthly screenshot below. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
comfy-pr-bot
enabled auto-merge (squash)
July 29, 2026 18:36
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1740 passed, 0 failed📊 Browser Reports
|
wei-hai
approved these changes
Jul 29, 2026
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## core/1.48 #14284 +/- ##
=============================================
+ Coverage 79.09% 79.13% +0.04%
=============================================
Files 1743 1743
Lines 98454 98628 +174
Branches 32244 31570 -674
=============================================
+ Hits 77869 78048 +179
+ Misses 20182 20177 -5
Partials 403 403
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1 file with indirect coverage changes 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backport of #14222 to
core/1.48Automatically created by backport workflow.