Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions plans/plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,13 +472,13 @@ plans:
custom_domains: false
sla: false
# hobby_yearly mirrors hobby exactly — same limits + features. Only the
# billing period and price differ ($90/yr ≈ $7.50/mo, ~17% off vs $9 x 12).
# billing period and price differ ($97.20/yr ≈ $8.10/mo, 10% off vs $9 x 12).
# The webhook upgrades teams to the "hobby" tier regardless of which
# cycle the user paid on; this variant exists only so the checkout
# handler can pick the right Razorpay plan_id at subscribe time.
hobby_yearly:
display_name: "Hobby (yearly)"
price_monthly_cents: 9000
price_monthly_cents: 9720
billing_period: "yearly"
trial_days: 14
limits:
Expand Down Expand Up @@ -525,10 +525,10 @@ plans:
alerts: true
custom_domains: true
sla: false
# pro_yearly mirrors pro exactly. $490/yr ≈ $40.83/mo (~17% off $49 x 12).
# pro_yearly mirrors pro exactly. $529.20/yr ≈ $44.10/mo (10% off $49 x 12).
pro_yearly:
display_name: "Pro (yearly)"
price_monthly_cents: 49000
price_monthly_cents: 52920
billing_period: "yearly"
trial_days: 0
limits:
Expand Down Expand Up @@ -575,10 +575,10 @@ plans:
alerts: true
custom_domains: true
sla: true
# team_yearly mirrors team exactly. $1990/yr ≈ $165.83/mo (~17% off $199 x 12).
# team_yearly mirrors team exactly. $2149.20/yr ≈ $179.10/mo (10% off $199 x 12).
team_yearly:
display_name: "Team (yearly)"
price_monthly_cents: 199000
price_monthly_cents: 214920
billing_period: "yearly"
trial_days: 0
limits:
Expand Down
19 changes: 19 additions & 0 deletions plans/plans_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,25 @@ func TestYearlyPrices_DiscountedVsMonthlyTimesTwelve(t *testing.T) {
}
}

// TestYearlyDiscountIsExactly10Percent locks the yearly-discount contract:
// (yearly / 12) / monthly must equal 0.9 within a small tolerance for each
// of hobby/pro/team. Future price changes that accidentally drift the
// discount (e.g. forgetting to re-derive the yearly cents from the new
// monthly) will fail this test.
func TestYearlyDiscountIsExactly10Percent(t *testing.T) {
r := plans.Default()
const tolerance = 0.01
for _, base := range []string{"hobby", "pro", "team"} {
monthly := float64(r.Get(base).PriceMonthly)
yearly := float64(r.Get(base + "_yearly").PriceMonthly)
require.Greater(t, monthly, 0.0, "%s monthly price must be > 0", base)
ratio := (yearly / 12.0) / monthly
assert.InDelta(t, 0.9, ratio, tolerance,
"%s_yearly effective monthly / %s monthly must be 0.9 (10%% off); got %.4f (yearly=%d, monthly=%d)",
base, base, ratio, int(yearly), int(monthly))
}
}

func TestValidatePromotion_ValidCode_ReturnsPromotion(t *testing.T) {
yaml := `
plans:
Expand Down