Skip to content

fix(migrate): answer a past_due subscription with its own provision code - #412

Merged
ABB65 merged 1 commit into
mainfrom
fix/provision-past-due-code
Oct 4, 2026
Merged

ABB65 merged 1 commit into
mainfrom
fix/provision-past-due-code

Conversation

@ABB65

@ABB65 ABB65 commented Oct 4, 2026

Copy link
Copy Markdown
Member

What

When every workspace the customer owns already holds a subscription, POST /api/migrate/provision answered 409 subscription_exists for a plan that is ending and for one whose last payment failed alike, so Migrate showed "Your Studio plan is ending" for a past_due plan (found in the E2E runbook review).

A blocked workspace whose subscription is past_due now answers 409 with data.code: 'subscription_past_due' (message migrate.attach_past_due, the key the attach route already uses) and the same workspace_slug. Ending and other live subscriptions keep subscription_exists.

Evidence

migrate-provision.test.ts: new test (past_due → key + data code/slug, nothing claimed); the existing "ending" and "already pays" tests unchanged and green. migrate-grant-routes tests green, eslint clean.

Migrate half: migrate PR follows (its own panel for the new code).

Reviewer: t6

When every owned workspace holds a subscription, the provision 409 carried
subscription_exists for a plan that is ending and for one whose payment
failed alike, so Migrate showed the "ending" screen for both. A workspace
whose subscription is past_due now answers subscription_past_due (and the
attach_past_due message); the way out there is to fix the payment.
@ABB65

ABB65 commented Oct 4, 2026

Copy link
Copy Markdown
Member Author

ONAY on cf403a9 (local checks).

  • Distinction: the 409 still happens exactly where it did (every owned workspace holds a subscription), before any grant claim or checkout, so no second subscription can be created; only data.code and the message differ. subscription_past_due is chosen from the same workspace whose slug is returned (ordered[0]), so code and workspace_slug agree. It reads the raw subscription_status === 'past_due', while attach uses billing.state === 'past_due' (which excludes an expired grace period): after grace the answer here stays subscription_past_due, which is right (the way out is still to fix the payment), and every other live state (active, ending, trial) stays subscription_exists. Account-state is unchanged (past_due still counts as no plan for pricing).
  • The code is a fixed machine string in data.code; only message goes through errorMessage (migrate.attach_past_due already exists in error-messages en.json).
  • Test covers code, workspace_slug, message key and that claimMigrateGrant is not called.
  • pnpm lint 0 errors, pnpm typecheck clean; unit 2038 passed (one first run had 6 load flakes in agent/brain-query files this PR does not touch, all green on rerun), integration 487 passed, nuxt 274 passed.

@ABB65
ABB65 merged commit 1db3e1f into main Oct 4, 2026
2 checks passed
@ABB65
ABB65 deleted the fix/provision-past-due-code branch October 4, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant