Skip to content

SCHY-390 default initializeWithPlan to trial when available#1282

Merged
ryanechternacht merged 6 commits into
mainfrom
ryan/sch-6507-trial-checkout-creates-subscriptions-with-no-trial-period
May 26, 2026
Merged

SCHY-390 default initializeWithPlan to trial when available#1282
ryanechternacht merged 6 commits into
mainfrom
ryan/sch-6507-trial-checkout-creates-subscriptions-with-no-trial-period

Conversation

@ryanechternacht
Copy link
Copy Markdown
Member

@ryanechternacht ryanechternacht commented May 26, 2026

https://linear.app/schematic/issue/SCHY-390/trial-checkout-creates-subscriptions-with-no-trial-period

Summary

  • initializeWithPlan skips the plan stage where the "Start X day trial" button normally lives, so customers using the documented bypass flow landed on a paid subscription even when the plan offered a trial and the company was eligible.
  • Adds a startTrialIfAvailable flag on BypassConfig (defaults to true) and pipes it through CheckoutState. The auto-init effect in CheckoutDialog now opts the company into trial mode when the flag is true and the selected plan is trialable. Callers can preserve the charge-immediately behavior by passing startTrialIfAvailable: false.
  • Breaking change: any caller of initializeWithPlan whose pre-selected plan is trialable for the current company will now land in trial mode by default. Pass startTrialIfAvailable: false to keep the old behavior.

Refs SCH-6507.

Test plan

  • yarn test --run passes (301 passed, 4 skipped, no type errors)
  • New reducer tests cover the default-true behavior for both BypassConfig and the legacy string form, plus the explicit false opt-out
  • Manual smoke: initializeWithPlan('plan_trialable') lands in trial mode in checkout
  • Manual smoke: initializeWithPlan({ planId: 'plan_trialable', startTrialIfAvailable: false }) charges immediately
  • Manual smoke: initializeWithPlan('plan_non_trialable') charges immediately (no regression)

🤖 Generated with Claude Code

initializeWithPlan bypasses the plan stage where the "Start X day trial"
button normally lives, so customers using the documented bypass flow were
landing on a paid subscription even when the plan offered a trial and the
company was eligible.

Adds a `startTrialIfAvailable` flag on `BypassConfig` (defaults to true)
and pipes it through `CheckoutState`. The auto-init effect in
`CheckoutDialog` now opts the company into trial mode when the flag is
true and the selected plan is trialable. Callers can preserve the
charge-immediately behavior by passing `startTrialIfAvailable: false`.

Refs SCH-6507.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ryanechternacht ryanechternacht requested review from a team as code owners May 26, 2026 15:08
@ryanechternacht ryanechternacht changed the title feat(components): default initializeWithPlan to trial when available SCH-6507 default initializeWithPlan to trial when available May 26, 2026
@ryanechternacht ryanechternacht changed the title SCH-6507 default initializeWithPlan to trial when available SCHY-390 default initializeWithPlan to trial when available May 26, 2026
ryanechternacht and others added 3 commits May 26, 2026 11:31
isTrialable and companyCanTrial on the plan are already non-optional
booleans, and the chain is part of an && expression, so the !! casts and
?? false were noise. Use the && result directly as shouldTrial.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The legacy-string, pre-selection, and optional-planId tests are checking
other concerns; switch them to toMatchObject (matching the style of the
rest of the file) so adding new fields to checkoutState doesn't force
edits here. The dedicated `startTrialIfAvailable configuration` block
still covers the default and the explicit-false opt-out.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

🚀 Components Preview Deployed!

📍 Preview URL: https://schematic-next-example-5k2e1x2kj-schematichq.vercel.app
🔄 Components Version: Built from commit 59bd6a3
📦 Demo App: schematichq/schematic-next-example

@github-actions
Copy link
Copy Markdown

🚀 Components Preview Deployed!

📍 Preview URL: https://schematic-next-example-h7hcbi4tl-schematichq.vercel.app
🔄 Components Version: Built from commit b965e62
📦 Demo App: schematichq/schematic-next-example

Copy link
Copy Markdown
Contributor

@dontlaugh dontlaugh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks solid

Comment on lines +1248 to +1251
const shouldTrial =
checkoutState?.startTrialIfAvailable &&
selectedPlan.isTrialable &&
selectedPlan.companyCanTrial;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am hesitant to put a sub-field of checkoutState in the dependencies array here.

startTrialIfAvailable is a new parameter of initializeWithPlan, and maybe we can rely on it never changing once a checkout is initiated. The initializeWithPlan function is meant to be called once.

That said, if this doesn't hurt, it's no big deal. But fewer deps is better.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ya, i debated that a bit. not including it feels like a potential subtle bug, but I agree it'd be vv weird if that value ever changed.

want me to remove and leave an inline comment? it might trigger lint issues as well (we can suppress)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove and suppress the lint. If we can get away with it, let's avoid another dep.

The value is set once by initializeWithPlan via SET_PLANID_BYPASS and
stays stable for the dialog's lifetime, and the hasInitializedPlan ref
already guards the effect to run exactly once. Listing the flag as a
dep just churns the diff without changing behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

🚀 Components Preview Deployed!

📍 Preview URL: https://schematic-next-example-qnei5qv2k-schematichq.vercel.app
🔄 Components Version: Built from commit 2bc74d4
📦 Demo App: schematichq/schematic-next-example

@ryanechternacht ryanechternacht merged commit df6a3a9 into main May 26, 2026
6 checks passed
@ryanechternacht ryanechternacht deleted the ryan/sch-6507-trial-checkout-creates-subscriptions-with-no-trial-period branch May 26, 2026 16:44
@dontlaugh
Copy link
Copy Markdown
Contributor

@ryanechternacht @tenub This bugfix from Ryan introduces a behavior change that is not backwards compatible. This will be a minor version bump, I think.

bpapillon pushed a commit to SchematicHQ/schematic-fern-config that referenced this pull request May 26, 2026
)

When the pre-selected plan offers a trial and the company is eligible,
initializeWithPlan now starts checkout in trial mode by default to match
the in-dialog "Start trial" button. Documents the new
`startTrialIfAvailable` option (defaults to true, set to false to charge
immediately) on the bypass config.

Companion to SchematicHQ/schematic-js#1282. Refs SCH-6507.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants