Skip to content

plans: annual pricing variants (hobby_yearly / pro_yearly / team_yearly) (P2)#6

Merged
mastermanas805 merged 1 commit into
masterfrom
pricing/p2-annual-plans
May 12, 2026
Merged

plans: annual pricing variants (hobby_yearly / pro_yearly / team_yearly) (P2)#6
mastermanas805 merged 1 commit into
masterfrom
pricing/p2-annual-plans

Conversation

@mastermanas805
Copy link
Copy Markdown
Member

Summary

  • Adds three yearly plan variants to the embedded default registry: hobby_yearly ($90/yr ≈ $7.50/mo, ~17% off), pro_yearly ($490/yr ≈ $40.83/mo, ~17% off), team_yearly ($1990/yr ≈ $165.83/mo, ~17% off). Each mirrors its monthly counterpart's limits + features byte-for-byte; only price_monthly_cents (annual amount in cents) and billing_period: yearly differ.
  • New helpers: Plan.BillingPeriod, Registry.BillingPeriod(tier), CanonicalTier(tier) — strips the _yearly suffix so the webhook can map yearly plan_ids back to the bare tier and teams.plan_tier stays cycle-agnostic.
  • Required for the api PR (InstaNode-dev/api#pricing/p2-annual-server-fresh) which depends on commonplans.CanonicalTier + BillingPeriod.

Test plan

  • go test ./plans/... green (4 new tests + existing 21 pass)
  • Mirror-invariant test: every *_yearly plan has the same Limits + Features as its base — drift would silently change subscriber headroom
  • Honest-discount test: yearly_price < monthly_price * 12 for all three tiers
  • BillingPeriod returns "monthly" for base tiers (empty in YAML), "yearly" for variants

🤖 Generated with Claude Code

Adds hobby_yearly ($90/yr), pro_yearly ($490/yr), team_yearly ($1990/yr)
to the embedded default registry — each mirrors its monthly counterpart's
limits + features exactly, only `price_monthly_cents` (annual amount in
cents) and `billing_period: yearly` differ.

New helpers:
  - Plan.BillingPeriod field
  - Registry.BillingPeriod(tier) — "monthly" | "yearly"
  - CanonicalTier(tier) — strips "_yearly" suffix so the webhook can map
    yearly plan_ids back to the bare tier and teams.plan_tier stays
    cycle-agnostic.

Tests pin the mirror invariant (limits + features identical to base tier)
and that yearly_price < monthly_price * 12 so the "save $X/yr" badge is
honest.
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