Skip to content

fix(billing): send the plan name from route params under Next 16#245

Merged
vernu merged 1 commit into
mainfrom
fix/checkout-plan-params-next16
Jul 20, 2026
Merged

fix(billing): send the plan name from route params under Next 16#245
vernu merged 1 commit into
mainfrom
fix/checkout-plan-params-next16

Conversation

@vernu

@vernu vernu commented Jul 20, 2026

Copy link
Copy Markdown
Owner

/checkout/pro and /checkout/scale failed with "Plan cannot be purchased" for every user since the Next 16 upgrade (ea1b00c).

The checkout page still typed params as a plain object and read params.planName synchronously. Next 16 removed the sync compat shim, so the value was undefined, axios dropped the key, and the API ran findOne({ name: undefined }). Mongoose 9 returns null for that where older versions stripped the key, so the guard threw a message about a misconfigured plan for a request that never named one. Plan data and Polar product ids were correct throughout.

  • unwrap params with React's use() and drop the as string cast that suppressed the type error
  • split the conflated guard into PLAN_NAME_REQUIRED, PLAN_NOT_FOUND and the existing unpurchasable case, each logged server side
  • stop retrying a rejected checkout twice: a 400 is not transient
  • mark the Polar product id indexes sparse, they have been failing to build with E11000 on every boot because most plans leave them unset

Checkout now defaults to yearly. A URL that names an interval still redirects straight through, so the marketing funnel is unchanged; the in-app CTAs that name none get an interval chooser with yearly preselected instead of a silent monthly default.

Also redesigns the checkout states, which were the only place in the dashboard hardcoding gray-100/white and so rendering a light slab in dark mode, and the plan picker, which painted the accent on every tier.

Tests: a checkout e2e spec asserting the request body carries planName, which is what would have caught this, plus API cases pinning the three guard failures apart.

/checkout/pro and /checkout/scale failed with "Plan cannot be purchased"
for every user since the Next 16 upgrade (ea1b00c).

The checkout page still typed `params` as a plain object and read
`params.planName` synchronously. Next 16 removed the sync compat shim, so
the value was undefined, axios dropped the key, and the API ran
findOne({ name: undefined }). Mongoose 9 returns null for that where
older versions stripped the key, so the guard threw a message about a
misconfigured plan for a request that never named one. Plan data and
Polar product ids were correct throughout.

- unwrap params with React's use() and drop the `as string` cast that
  suppressed the type error
- split the conflated guard into PLAN_NAME_REQUIRED, PLAN_NOT_FOUND and
  the existing unpurchasable case, each logged server side
- stop retrying a rejected checkout twice: a 400 is not transient
- mark the Polar product id indexes sparse, they have been failing to
  build with E11000 on every boot because most plans leave them unset

Checkout now defaults to yearly. A URL that names an interval still
redirects straight through, so the marketing funnel is unchanged; the
in-app CTAs that name none get an interval chooser with yearly
preselected instead of a silent monthly default.

Also redesigns the checkout states, which were the only place in the
dashboard hardcoding gray-100/white and so rendering a light slab in dark
mode, and the plan picker, which painted the accent on every tier.

Tests: a checkout e2e spec asserting the request body carries planName,
which is what would have caught this, plus API cases pinning the three
guard failures apart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
textbee Ready Ready Preview, Comment Jul 20, 2026 7:11pm

Request Review

@vernu
vernu merged commit c36cfc1 into main Jul 20, 2026
9 checks passed
@vernu
vernu deleted the fix/checkout-plan-params-next16 branch July 20, 2026 19:14
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