fix(billing): recover past-due plan changes (FE-1251) - #14513
Conversation
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1776 passed, 0 failed · 3 flaky📊 Browser Reports
📦 Bundle: 8.19 MB gzip 🔴 +693 BDetailsSummary
Category Glance App Entry Points — 3.62 kB (baseline 3.62 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.29 MB (baseline 1.29 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed / 1 unchanged Views & Navigation — 111 kB (baseline 111 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 12 added / 12 removed / 4 unchanged Panels & Settings — 551 kB (baseline 551 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 11 added / 11 removed / 15 unchanged User & Accounts — 28.7 kB (baseline 28.7 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 7 added / 7 removed / 3 unchanged Editors & Dialogs — 124 kB (baseline 124 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 6 added / 6 removed / 1 unchanged UI Components — 70 kB (baseline 70 kB) • ⚪ 0 BReusable component library chunks
Status: 6 added / 6 removed / 9 unchanged Data & Services — 3.45 MB (baseline 3.45 MB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 14 added / 14 removed / 3 unchanged Utilities & Hooks — 386 kB (baseline 386 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 19 added / 19 removed / 17 unchanged Vendor & Third-Party — 15.7 MB (baseline 15.7 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 12.7 MB (baseline 12.7 MB) • 🔴 +1.85 kBBundles that do not match a named category
Status: 78 added / 78 removed / 202 unchanged ⚡ Performance Report
✅ No regressions detected. All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-01T00:03:35.227Z",
"gitSha": "a4ba8341892165c66393b38332ef4dcdd404bc8d",
"branch": "agent/fe-1251-payment-recovery",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2071.2509999999893,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.257000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 467.87200000000007,
"heapDeltaBytes": 4534200,
"heapUsedBytes": 72478664,
"domNodes": 20,
"jsHeapTotalBytes": 20574208,
"scriptDurationMs": 17.674999999999997,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 1995.740000000012,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.628000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 365.9789999999999,
"heapDeltaBytes": 4647508,
"heapUsedBytes": 72610784,
"domNodes": 20,
"jsHeapTotalBytes": 20312064,
"scriptDurationMs": 12.878,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1727.6539999999727,
"styleRecalcs": 71,
"styleRecalcDurationMs": 26.962,
"layouts": 12,
"layoutDurationMs": 3.1069999999999998,
"taskDurationMs": 695.529,
"heapDeltaBytes": -18512224,
"heapUsedBytes": 49531804,
"domNodes": -280,
"jsHeapTotalBytes": 20701184,
"scriptDurationMs": 90.68,
"eventListeners": -146,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1741.5040000000204,
"styleRecalcs": 71,
"styleRecalcDurationMs": 29.103,
"layouts": 12,
"layoutDurationMs": 3.113,
"taskDurationMs": 698.902,
"heapDeltaBytes": -16369300,
"heapUsedBytes": 51435912,
"domNodes": -280,
"jsHeapTotalBytes": 20701184,
"scriptDurationMs": 90.694,
"eventListeners": -146,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1721.4419999999677,
"styleRecalcs": 32,
"styleRecalcDurationMs": 12.702,
"layouts": 6,
"layoutDurationMs": 0.569,
"taskDurationMs": 299.041,
"heapDeltaBytes": 7949460,
"heapUsedBytes": 75772112,
"domNodes": 77,
"jsHeapTotalBytes": 20312064,
"scriptDurationMs": 15.429000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1668.4079999999994,
"styleRecalcs": 31,
"styleRecalcDurationMs": 13.896,
"layouts": 6,
"layoutDurationMs": 0.5569999999999998,
"taskDurationMs": 319.67400000000004,
"heapDeltaBytes": 7963816,
"heapUsedBytes": 75868592,
"domNodes": 77,
"jsHeapTotalBytes": 20049920,
"scriptDurationMs": 17.659000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "dom-widget-clipping",
"durationMs": 478.6269999999604,
"styleRecalcs": 11,
"styleRecalcDurationMs": 6.7589999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 292.33500000000004,
"heapDeltaBytes": -12234836,
"heapUsedBytes": 55547892,
"domNodes": 18,
"jsHeapTotalBytes": 20836352,
"scriptDurationMs": 44.763,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "dom-widget-clipping",
"durationMs": 487.5179999999659,
"styleRecalcs": 13,
"styleRecalcDurationMs": 6.857,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 299.834,
"heapDeltaBytes": -12291872,
"heapUsedBytes": 55515364,
"domNodes": 22,
"jsHeapTotalBytes": 22147072,
"scriptDurationMs": 43.056000000000004,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "large-graph-idle",
"durationMs": 2020.2239999999847,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.176999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 554.44,
"heapDeltaBytes": 7785044,
"heapUsedBytes": 66986436,
"domNodes": -280,
"jsHeapTotalBytes": 5566464,
"scriptDurationMs": 79.85400000000001,
"eventListeners": -142,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2038.9369999999758,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.870999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 546.408,
"heapDeltaBytes": 8421264,
"heapUsedBytes": 69533704,
"domNodes": -277,
"jsHeapTotalBytes": 3731456,
"scriptDurationMs": 75.59,
"eventListeners": -142,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2103.5699999999906,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.536999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 986.947,
"heapDeltaBytes": 625088,
"heapUsedBytes": 60640212,
"domNodes": -280,
"jsHeapTotalBytes": 5267456,
"scriptDurationMs": 319.01099999999997,
"eventListeners": -144,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 2092.807999999991,
"styleRecalcs": 69,
"styleRecalcDurationMs": 13.265,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1001.0949999999998,
"heapDeltaBytes": 1225744,
"heapUsedBytes": 60912944,
"domNodes": -283,
"jsHeapTotalBytes": 4743168,
"scriptDurationMs": 326.818,
"eventListeners": -142,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3092.4479999999903,
"styleRecalcs": 67,
"styleRecalcDurationMs": 14.418000000000003,
"layouts": 60,
"layoutDurationMs": 6.691000000000001,
"taskDurationMs": 1174.264,
"heapDeltaBytes": 23138272,
"heapUsedBytes": 84461308,
"domNodes": 16,
"jsHeapTotalBytes": 8388608,
"scriptDurationMs": 412.859,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3134.8160000000007,
"styleRecalcs": 66,
"styleRecalcDurationMs": 13.746999999999998,
"layouts": 60,
"layoutDurationMs": 6.993,
"taskDurationMs": 1221.113,
"heapDeltaBytes": -6950952,
"heapUsedBytes": 56645932,
"domNodes": -285,
"jsHeapTotalBytes": 7401472,
"scriptDurationMs": 421.63300000000004,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2017.6220000000171,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.439,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 531.601,
"heapDeltaBytes": 7973692,
"heapUsedBytes": 70257580,
"domNodes": -279,
"jsHeapTotalBytes": 4780032,
"scriptDurationMs": 73.11599999999999,
"eventListeners": -144,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2018.0609999999888,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.324,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 539.904,
"heapDeltaBytes": 7372980,
"heapUsedBytes": 67872092,
"domNodes": -281,
"jsHeapTotalBytes": 5304320,
"scriptDurationMs": 75.783,
"eventListeners": -144,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 517.8270000000111,
"styleRecalcs": 47,
"styleRecalcDurationMs": 8.528999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 317.43499999999995,
"heapDeltaBytes": -11768832,
"heapUsedBytes": 56154816,
"domNodes": 20,
"jsHeapTotalBytes": 21098496,
"scriptDurationMs": 96.631,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 509.1229999999314,
"styleRecalcs": 47,
"styleRecalcDurationMs": 8.853000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 317.749,
"heapDeltaBytes": -12081512,
"heapUsedBytes": 55838116,
"domNodes": 20,
"jsHeapTotalBytes": 22409216,
"scriptDurationMs": 94.124,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-idle",
"durationMs": 2000.64500000002,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.290000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 397.49600000000004,
"heapDeltaBytes": -20928704,
"heapUsedBytes": 47092756,
"domNodes": -279,
"jsHeapTotalBytes": 20176896,
"scriptDurationMs": 11.126,
"eventListeners": -146,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-idle",
"durationMs": 2013.196999999991,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.278000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 386.10599999999994,
"heapDeltaBytes": 4345372,
"heapUsedBytes": 72321204,
"domNodes": 22,
"jsHeapTotalBytes": 21098496,
"scriptDurationMs": 11.024000000000001,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1682.9040000000077,
"styleRecalcs": 75,
"styleRecalcDurationMs": 27.165,
"layouts": 16,
"layoutDurationMs": 3.2770000000000006,
"taskDurationMs": 615.979,
"heapDeltaBytes": -19865084,
"heapUsedBytes": 47870276,
"domNodes": -280,
"jsHeapTotalBytes": 20963328,
"scriptDurationMs": 69.41,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1706.1150000000112,
"styleRecalcs": 78,
"styleRecalcDurationMs": 26.687,
"layouts": 16,
"layoutDurationMs": 3.2279999999999998,
"taskDurationMs": 623.7969999999999,
"heapDeltaBytes": -21059948,
"heapUsedBytes": 46910580,
"domNodes": -277,
"jsHeapTotalBytes": 20176896,
"scriptDurationMs": 70.21900000000001,
"eventListeners": -146,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-transition-enter",
"durationMs": 933.922999999993,
"styleRecalcs": 19,
"styleRecalcDurationMs": 22.98,
"layouts": 15,
"layoutDurationMs": 8.916,
"taskDurationMs": 743.8800000000001,
"heapDeltaBytes": 4803256,
"heapUsedBytes": 92250160,
"domNodes": 13673,
"jsHeapTotalBytes": 12320768,
"scriptDurationMs": 22.150000000000002,
"eventListeners": 2369,
"totalBlockingTimeMs": 96,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8103.064999999959,
"styleRecalcs": 250,
"styleRecalcDurationMs": 37.042,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3372.0540000000005,
"heapDeltaBytes": 9657204,
"heapUsedBytes": 68863952,
"domNodes": -279,
"jsHeapTotalBytes": 6840320,
"scriptDurationMs": 1023.1320000000002,
"eventListeners": -128,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "viewport-pan-sweep",
"durationMs": 8139.609000000008,
"styleRecalcs": 250,
"styleRecalcDurationMs": 36.167,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3471.854,
"heapDeltaBytes": 10691920,
"heapUsedBytes": 69397876,
"domNodes": -281,
"jsHeapTotalBytes": 6840320,
"scriptDurationMs": 1057.772,
"eventListeners": -128,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 13837.034000000018,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13815.219000000001,
"heapDeltaBytes": -47833176,
"heapUsedBytes": 167912156,
"domNodes": -8312,
"jsHeapTotalBytes": -14360576,
"scriptDurationMs": 419.82199999999995,
"eventListeners": -16391,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.220000000000073,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 14306.650999999987,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14263.838,
"heapDeltaBytes": -43692512,
"heapUsedBytes": 167268812,
"domNodes": -8312,
"jsHeapTotalBytes": -11472896,
"scriptDurationMs": 469.3930000000001,
"eventListeners": -16391,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.220000000000073,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 16525.97400000002,
"styleRecalcs": 101,
"styleRecalcDurationMs": 12.626999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16506.476,
"heapDeltaBytes": -57315372,
"heapUsedBytes": 164613980,
"domNodes": -8312,
"jsHeapTotalBytes": -15335424,
"scriptDurationMs": 669.574,
"eventListeners": -16389,
"totalBlockingTimeMs": 9,
"frameDurationMs": 17.779999999999927,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 16691.303999999946,
"styleRecalcs": 109,
"styleRecalcDurationMs": 12.405,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16647.788,
"heapDeltaBytes": -69345788,
"heapUsedBytes": 156611216,
"domNodes": -8312,
"jsHeapTotalBytes": -10428416,
"scriptDurationMs": 679.728,
"eventListeners": -16385,
"totalBlockingTimeMs": 1,
"frameDurationMs": 17.223333333333237,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 442.5909999999931,
"styleRecalcs": 13,
"styleRecalcDurationMs": 14.628,
"layouts": 3,
"layoutDurationMs": 0.5499999999999999,
"taskDurationMs": 90.807,
"heapDeltaBytes": -15904236,
"heapUsedBytes": 51168200,
"domNodes": 130,
"jsHeapTotalBytes": 7991296,
"scriptDurationMs": 8.16,
"eventListeners": 65,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 477.10399999994024,
"styleRecalcs": 14,
"styleRecalcDurationMs": 17.602,
"layouts": 3,
"layoutDurationMs": 0.49700000000000005,
"taskDurationMs": 119.946,
"heapDeltaBytes": -15990400,
"heapUsedBytes": 51197536,
"domNodes": 121,
"jsHeapTotalBytes": 7467008,
"scriptDurationMs": 11.805,
"eventListeners": 67,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
}
]
} |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughSubscription checkout now detects outstanding payment failures, validates Stripe billing-portal URLs, and routes personal or team recovery flows to the portal. It also handles blocked popups, stale requests, portal failures, and duplicate telemetry. ChangesWorkspace subscription payment recovery
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant PreviewLoader
participant useSubscriptionCheckout
participant workspaceApi
participant StripeBillingPortal
PreviewLoader->>useSubscriptionCheckout: report payment-related failure
useSubscriptionCheckout->>workspaceApi: retrieve billing status
workspaceApi-->>useSubscriptionCheckout: return billing status
useSubscriptionCheckout->>workspaceApi: retrieve payment portal URL
workspaceApi-->>useSubscriptionCheckout: return portal URL
useSubscriptionCheckout->>StripeBillingPortal: open validated portal URL
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 inconclusive)
✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
cf6f949 to
268ea28
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/platform/cloud/subscription/utils/teamSubscriptionCheckoutUtil.test.ts`:
- Around line 131-172: Add missing recovery-failure coverage: in
src/platform/cloud/subscription/utils/teamSubscriptionCheckoutUtil.test.ts lines
131-172, add a case where getPaymentPortalUrl resolves without a url and verify
the original checkout error is re-thrown; in
src/platform/workspace/composables/useSubscriptionCheckout.test.ts lines
389-435, add coverage for TRANSITION_NOT_ALLOWED with a non-payment_failed
billing status such as canceled, verifying recovery is skipped and the original
error is shown, and for manageSubscription(false) throwing during recovery.
In `@src/platform/cloud/subscription/utils/teamSubscriptionCheckoutUtil.ts`:
- Around line 14-44: Extract the shared hasErrorCode helper into a common
utility and import it in both
src/platform/cloud/subscription/utils/teamSubscriptionCheckoutUtil.ts:14-44 and
src/platform/workspace/composables/useSubscriptionCheckout.ts:272-336, removing
the duplicate implementation. Keep hasErrorMessage and the billingStatus-aware
fallback local to useSubscriptionCheckout.ts; preserve the standalone utility’s
existing recovery behavior unless the shared extraction requires documenting its
differing status-check failure fallback.
- Around line 68-88: Update the outstanding-payment recovery branch in
performTeamSubscriptionCheckout to catch failures from
workspaceApi.getPaymentPortalUrl and rethrow the original subscribe error.
Preserve the existing fallback when portal.url is missing, and only redirect
when the portal URL is successfully obtained.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 64790ff2-3316-496d-a00d-965b410b5e04
📒 Files selected for processing (11)
src/composables/billing/types.tssrc/composables/billing/useBillingContext.tssrc/composables/billing/useLegacyBilling.tssrc/locales/en/main.jsonsrc/platform/cloud/subscription/composables/useSubscription.tssrc/platform/cloud/subscription/utils/teamSubscriptionCheckoutUtil.test.tssrc/platform/cloud/subscription/utils/teamSubscriptionCheckoutUtil.tssrc/platform/workspace/composables/useSubscriptionCheckout.test.tssrc/platform/workspace/composables/useSubscriptionCheckout.tssrc/platform/workspace/composables/useWorkspaceBilling.test.tssrc/platform/workspace/composables/useWorkspaceBilling.ts
268ea28 to
3ba47f3
Compare
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/platform/workspace/composables/useSubscriptionCheckout.test.ts`:
- Around line 395-432: Add a test alongside the existing subscription checkout
recovery cases that invokes handleSubscribeTeamClick with a rejected team
preview, then assert previewData.value remains null and the confirm state is
display-only. Use the existing setup and mocks to verify the team fallback
behavior without changing the personal checkout tests.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: f8997b47-2a88-439d-9883-2af40f4e61f1
📒 Files selected for processing (2)
src/platform/workspace/composables/useSubscriptionCheckout.test.tssrc/platform/workspace/composables/useSubscriptionCheckout.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/platform/workspace/composables/useSubscriptionCheckout.ts`:
- Around line 281-303: Validate portal.url in
recoverOutstandingSubscriptionPayment before assigning globalThis.location.href,
requiring an https URL from the approved billing portal origin; reject invalid
schemes and unapproved origins. Route validation failures through
showSubscribeError with a translated, actionable message, and add coverage for
both invalid-scheme and unapproved-origin cases.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 6c8c0122-208c-417c-963d-a3edb34c3c9d
📒 Files selected for processing (2)
src/platform/workspace/composables/useSubscriptionCheckout.test.tssrc/platform/workspace/composables/useSubscriptionCheckout.ts
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #14513 +/- ##
==========================================
- Coverage 78.26% 78.26% -0.01%
==========================================
Files 1767 1767
Lines 110808 110908 +100
Branches 35366 35306 -60
==========================================
+ Hits 86719 86797 +78
- Misses 23540 23556 +16
- Partials 549 555 +6
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 8 files with indirect coverage changes 🚀 New features to boost your workflow:
|
3ba47f3 to
4a4aa15
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/platform/workspace/composables/useSubscriptionCheckout.ts`:
- Around line 302-305: Add a dedicated translation key for the invalid portal
URL message in main.json, then update the error construction in
useSubscriptionCheckout to pass that key’s t(...) result as the error value
instead of the raw English string. Keep the existing failedToAccessBillingPortal
translation flow unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: bb615bed-4e94-40ae-95bb-0328a2ae62f1
📒 Files selected for processing (2)
src/platform/workspace/composables/useSubscriptionCheckout.test.tssrc/platform/workspace/composables/useSubscriptionCheckout.ts
4a4aa15 to
ac81e64
Compare
ac81e64 to
2f4cc53
Compare
339e712 to
b522e83
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/platform/workspace/composables/useSubscriptionCheckout.ts`:
- Around line 394-395: Update the catch flow around recoverOutstandingPayment in
useSubscriptionCheckout so a 'failed' recovery result resets reactivation state
and previewData to the pricing state, matching the team flow around the
corresponding recovery handling. Preserve the existing early return for
successfully handled recovery outcomes and allow other errors to continue
through the current path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c251612a-aaa8-4210-8854-80b0a4ccb9b4
📒 Files selected for processing (3)
src/locales/en/main.jsonsrc/platform/workspace/composables/useSubscriptionCheckout.test.tssrc/platform/workspace/composables/useSubscriptionCheckout.ts
b522e83 to
abe8303
Compare
dante01yoon
left a comment
There was a problem hiding this comment.
Re-review complete. Both prior blockers are addressed: payment recovery now reads the authoritative workspace billing status for the production fallback, and team recovery re-checks request ownership after the awaited portal lookup. The expanded recovery and popup-safe behavior are covered by the focused checkout suite (96 tests).
e689073
abe8303 to
e689073
Compare
dante01yoon
left a comment
There was a problem hiding this comment.
Re-review complete. The new one-shot focus listener refreshes billing status after the Stripe portal opens, and its effect scope is cleaned up in the expanded tests. I did not find a new blocking issue; the focused checkout suite passes all 96 tests.
|
Why
Past-due Stripe subscriptions return
TRANSITION_NOT_ALLOWEDduring plan preview. The UI showed a generic error with no recovery path.Change
For preview failures only:
SUBSCRIPTION_PAYMENT_REQUIRED, or the current production response whenbilling_statusispayment_failedflowchart LR subgraph Before A[Change plan] --> B[Preview fails] B --> C[Generic error] C --> D[User blocked] end subgraph After E[Change plan] --> F[Preview fails] F --> G{Payment failed?} G -- No --> H[Existing error handling] G -- Yes --> I[Stripe billing portal in new tab] endTest