feat(membership): consolidate plan name + lifetime card shape + merge with billing #97
No reviewers
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
psa-systems/bunyip!97
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/bunyip-upgrade-01-membership-plan"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Three audit findings (1, 2, 9) all live in the same surface and ride one PR.
Finding 1 (plan name inconsistency): a lifetime member's Membership card
called the plan "Lifetime" in the badge AND "Standard" in the Plan line, the public Pricing card called the same product "Starter", and the Settings "Account Type" cell rendered the literal "subscriber". Four names for one thing across three pages.
fn tier_name(&SubscriptionTier)is nowpuband the canonical helper for the plan-name string. The Settings cell at the Account Information card and the marketing-facing pricing card both call it. Renaming a tier ("Standard" -> "Starter", whatever the product team picks) is now a one-line change intier_namethat updates every consumer.checkout_successalso dropped its inlinematch user.subscription_tier { ... }block and now callstier_name(&user.subscription_tier), so the post-checkout confirmation lines up too.BUSINESS's first feature bullet went from "Everything in Starter" to "Everything in the personal plan" so the marketing copy stays accurate after a tier rename.Finding 2 (cancel buttons for lifetime members): the Membership card body now branches on
lifetimeBEFORE thehascheck. Lifetime members see only "Plan: <tier_name>" and "Access: Lifetime - no billing" - no price field, no next-billing field, no "Cancel Membership" or "Cancel Now" buttons. Thehasboolean also explicitly excludeslifetimeso a lifetime user with a stray Active row from the legacy billing flow can never accidentally see cancel UI (defense in depth).Finding 9 (Membership/Billing redundancy): the
/billinghandler is now a 308 permanent redirect to/membership. The Membership page absorbs the invoices table from the retired Billing page; it always renders so a one-off charge or refund still surfaces (empty-state copy covers the common lifetime-member case). The page title, H1, and tab title rename to "Membership & Billing"; the sidebar nav drops the standalone "Billing" entry and renames the remaining entry to "Membership & Billing".axum::response::IntoResponsewas added to the dashboard.rs import set soRedirect::permanent("/membership").into_response()resolves.See
docs/bunyip-upgrade/01-membership-plan-data.mdfor the full spec and verification.Three audit findings (1, 2, 9) all live in the same surface and ride one PR. Finding 1 (plan name inconsistency): a lifetime member's Membership card called the plan "Lifetime" in the badge AND "Standard" in the Plan line, the public Pricing card called the same product "Starter", and the Settings "Account Type" cell rendered the literal "subscriber". Four names for one thing across three pages. `fn tier_name(&SubscriptionTier)` is now `pub` and the canonical helper for the plan-name string. The Settings cell at the Account Information card and the marketing-facing pricing card both call it. Renaming a tier ("Standard" -> "Starter", whatever the product team picks) is now a one-line change in `tier_name` that updates every consumer. `checkout_success` also dropped its inline `match user.subscription_tier { ... }` block and now calls `tier_name(&user.subscription_tier)`, so the post-checkout confirmation lines up too. `BUSINESS`'s first feature bullet went from "Everything in Starter" to "Everything in the personal plan" so the marketing copy stays accurate after a tier rename. Finding 2 (cancel buttons for lifetime members): the Membership card body now branches on `lifetime` BEFORE the `has` check. Lifetime members see only "Plan: <tier_name>" and "Access: Lifetime - no billing" - no price field, no next-billing field, no "Cancel Membership" or "Cancel Now" buttons. The `has` boolean also explicitly excludes `lifetime` so a lifetime user with a stray Active row from the legacy billing flow can never accidentally see cancel UI (defense in depth). Finding 9 (Membership/Billing redundancy): the `/billing` handler is now a 308 permanent redirect to `/membership`. The Membership page absorbs the invoices table from the retired Billing page; it always renders so a one-off charge or refund still surfaces (empty-state copy covers the common lifetime-member case). The page title, H1, and tab title rename to "Membership & Billing"; the sidebar nav drops the standalone "Billing" entry and renames the remaining entry to "Membership & Billing". `axum::response::IntoResponse` was added to the dashboard.rs import set so `Redirect::permanent("/membership").into_response()` resolves. See `docs/bunyip-upgrade/01-membership-plan-data.md` for the full spec and verification.