Skip to content

feat(backend,web): record trial state server-side; wire trial CTA - #2740

Merged
tyler-dane merged 1 commit into
mainfrom
claude/keyboard-education-billing-trial
Aug 10, 2026
Merged

feat(backend,web): record trial state server-side; wire trial CTA#2740
tyler-dane merged 1 commit into
mainfrom
claude/keyboard-education-billing-trial

Conversation

@tyler-dane

Copy link
Copy Markdown
Contributor

Summary

Stacked on #2736 (uses PostOnboardingFlow/TrialCTA.tsx from that branch).

  • Adds durable, server-side trial/subscription state on the user record via a new billing module: POST /api/billing/trial/start (rate-limited, 5/min) and GET /api/billing/status. startTrial is idempotent — a retried or duplicate CTA click can never push the trial window out further.
  • New field is optional on Schema_User (billing?: Schema_UserBilling) with no backfill needed — matches the existing incremental-field pattern on that schema (see lastSeenAt).
  • Placeholder terms only (14-day trial, no payment collection) per keyboard-education/03-monetization-trial-checkout.md, isolated in one BILLING_DEFAULTS constants module so the founder's pricing sweep is a small diff, not a re-implementation.
  • The web TrialCTA now calls this for real (for authenticated users) instead of a no-op stub, with a loading state and error toast on failure.

Explicitly NOT done in this pass

So this isn't mistaken for a working checkout:

  • No Stripe integration, no payment-method collection, no webhooks.
  • No read-only access enforcement wired into the actual event-mutation routes — GET /api/billing/status returns isReadOnly, but nothing currently checks it before allowing a write. That's real scope for a follow-up, not a security claim this PR is making.
  • No repo previously had any rate-limiting middleware at all (express-rate-limit wasn't a dependency anywhere) — added it fresh for this route per the CodeQL js/missing-rate-limiting gate on new authenticated routes.

Test plan

  • bun run test:backend:fast — 243/243 passing (includes new deriveBillingStatus unit tests: none/trialing/expired/exact-boundary)
  • bun test src/components/PostOnboardingFlow/ — 4/4 passing
  • bun run type-check — clean across core/backend/web
  • biome check on all touched files — clean
  • Live browser/route verification — not possible in this worktree: the backend fails to boot without SYNC_SERVICE_URL/SYNC_INTERNAL_AUTH_TOKEN env vars not present here (pre-existing environment gap, unrelated to this change). Routes were verified by reading the existing UserRoutes pattern this mirrors exactly (CommonRoutesConfig, verifySession(), controller → service → mongoService.user), not by hitting them live.

🤖 Generated with Claude Code

@tyler-dane
tyler-dane force-pushed the claude/keyboard-education-billing-trial branch 3 times, most recently from 3523246 to e6d085e Compare August 10, 2026 19:55
Base automatically changed from claude/keyboard-education-onboarding-9cc41b to main August 10, 2026 19:57
Adds durable, server-side trial/subscription state on the user
record (optional field, no backfill needed) via a new billing
module: POST /api/billing/trial/start (rate-limited) and GET
/api/billing/status, idempotent so retries never push the trial
window out further. The web trial CTA now calls this for real
instead of a no-op stub.

Placeholder terms only (14-day trial, single default plan) per
keyboard-education/03-monetization-trial-checkout.md -- isolated in
one constants module so the founder's pricing sweep is a small diff.

Explicitly NOT done in this pass, so it isn't mistaken for a working
checkout: no Stripe integration, no payment-method collection, no
webhooks, and no read-only access enforcement wired into the actual
event-mutation routes (the status API returns isReadOnly, but nothing
currently checks it before allowing a write). See the brief for what
remains.
@tyler-dane
tyler-dane force-pushed the claude/keyboard-education-billing-trial branch from e6d085e to 66d92c1 Compare August 10, 2026 19:58
@tyler-dane
tyler-dane merged commit d925c27 into main Aug 10, 2026
20 checks passed
@tyler-dane
tyler-dane deleted the claude/keyboard-education-billing-trial branch August 10, 2026 20:02
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