mirror of
https://github.com/stack-auth/stack.git
synced 2026-07-20 21:29:36 +08:00
Canceling a subscription without a Stripe backing (test mode, free plans) writes status=canceled with endedAt set to the end of the current period, so the customer keeps their entitlements until then. But every read path classified subscriptions with isActiveSubscription (active/trialing only), so during the paid-through window the owned product fell through to the one-time-purchase branch: the account settings payments tab showed "One-time purchase" with no end date and no way to tell the plan was winding down, and plan usage reported the free plan while the paid plan's quotas were still in effect. Introduce isSubscriptionInEffect (status-agnostic, endedAt-based — the same semantics Bulldozer's grant timefold and ensure-free-plan already use) and use it at the read sites: product list classification, plan usage resolution, and the switch route's sub-vs-OTP occupancy check. Write sites keep isActiveSubscription so wound-down subs can't be re-canceled or switched from. The product list now also computes is_cancelable instead of hardcoding it, double-canceling returns a dedicated error instead of claiming the product is an OTP, and the Stripe cancel path uses cancel_at_period_end instead of canceling immediately, matching the promise made by the confirmation dialog. The account settings panels render "Ends on <date>" for winding-down subscriptions. |
||
|---|---|---|
| .. | ||
| public | ||
| scripts | ||
| src | ||
| .env | ||
| .env.development | ||
| .eslintrc.cjs | ||
| .gitignore | ||
| .npmrc | ||
| components.json | ||
| DESIGN-GUIDE.md | ||
| instrumentation-client.ts | ||
| LICENSE | ||
| next.config.mjs | ||
| package.json | ||
| postcss.config.js | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
| vitest.config.ts | ||