Skip to content

[backport core/1.48] feat(billing): disclose that changing plans on a cancelled subscription resumes it - #14284

Merged
comfy-pr-bot merged 1 commit into
core/1.48from
backport-14222-to-core-1.48
Jul 29, 2026
Merged

[backport core/1.48] feat(billing): disclose that changing plans on a cancelled subscription resumes it#14284
comfy-pr-bot merged 1 commit into
core/1.48from
backport-14222-to-core-1.48

Conversation

@comfy-pr-bot

Copy link
Copy Markdown
Member

Backport of #14222 to core/1.48

Automatically created by backport workflow.

…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 comfy-pr-bot added the backport Backporting a PR onto a release candidate label Jul 29, 2026
@comfy-pr-bot
comfy-pr-bot enabled auto-merge (squash) July 29, 2026 18:36
@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Jul 29, 2026
@dosubot

dosubot Bot commented Jul 29, 2026

Copy link
Copy Markdown

📄 Knowledge review

Dosu skipped reviewing this PR because your organization has used its 200 included credits for the month. Your usage will reset on 2026-08-01. To have Dosu review this PR before then, ask your organization admin to upgrade to a pro account.


Leave Feedback Ask Dosu about ComfyUI_frontend Add Dosu to your team

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 07/29/2026, 06:38:16 PM UTC

Links

🎭 Playwright: ✅ 1740 passed, 0 failed

📊 Browser Reports
  • chromium: View Report (✅ 1719 / ❌ 0 / ⚠️ 0 / ⏭️ 5)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 18 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

@comfy-pr-bot
comfy-pr-bot merged commit cf7ea44 into core/1.48 Jul 29, 2026
74 checks passed
@comfy-pr-bot
comfy-pr-bot deleted the backport-14222-to-core-1.48 branch July 29, 2026 18:49
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.57282% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ponents/SubscriptionTransitionPreviewWorkspace.vue 95.69% 4 Missing ⚠️
...m/workspace/composables/useSubscriptionCheckout.ts 98.24% 1 Missing ⚠️
@@              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              
Flag Coverage Δ
unit 68.74% <97.57%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/platform/telemetry/types.ts 100.00% <ø> (ø)
src/platform/workspace/api/workspaceApi.ts 47.66% <ø> (+0.15%) ⬆️
...ts/dialogs/DowngradeRemoveMembersDialogContent.vue 100.00% <100.00%> (ø)
...rm/workspace/composables/useDowngradeToPersonal.ts 99.03% <100.00%> (+0.30%) ⬆️
src/services/dialogService.ts 58.62% <100.00%> (+2.84%) ⬆️
...m/workspace/composables/useSubscriptionCheckout.ts 97.44% <98.24%> (+1.17%) ⬆️
...ponents/SubscriptionTransitionPreviewWorkspace.vue 97.58% <95.69%> (+<0.01%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport Backporting a PR onto a release candidate size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants