feat: self-serve signup backend (Phase 1) #161

Merged
chihlasm merged 27 commits from feat/self-serve-signup-spec into main 2026-05-06 23:46:35 +00:00
2 changed files with 55 additions and 2 deletions
Showing only changes of commit e3f5ed4985 - Show all commits

View File

@@ -32,8 +32,20 @@ class Subscription(Base):
@property
def is_active(self) -> bool:
return self.status in ("active", "trialing")
return self.status in ("active", "trialing", "complimentary")
@property
def is_paid(self) -> bool:
return self.plan in ("pro", "team")
# Excludes complimentary and trialing so MRR/paid-customer metrics aren't inflated.
return self.plan in ("pro", "team") and self.status not in ("complimentary", "trialing")
@property
def has_pro_entitlement(self) -> bool:
"""True if the account can access Pro features right now."""
if self.plan in ("pro", "team"):
if self.status in ("active", "complimentary"):
return True
if self.status == "trialing" and self.current_period_end is not None:
from datetime import datetime, timezone
return self.current_period_end > datetime.now(timezone.utc)
return False

View File

@@ -0,0 +1,41 @@
from datetime import datetime, timezone, timedelta
from app.models.subscription import Subscription
def make_sub(**kwargs):
sub = Subscription()
sub.plan = kwargs.get("plan", "free")
sub.status = kwargs.get("status", "active")
sub.current_period_end = kwargs.get("current_period_end")
return sub
def test_complimentary_is_active_but_not_paid():
sub = make_sub(plan="pro", status="complimentary")
assert sub.is_active is True
assert sub.is_paid is False
assert sub.has_pro_entitlement is True
def test_paid_pro_active():
sub = make_sub(plan="pro", status="active")
assert sub.is_paid is True
assert sub.has_pro_entitlement is True
def test_trial_unexpired_has_entitlement():
sub = make_sub(plan="pro", status="trialing", current_period_end=datetime.now(timezone.utc) + timedelta(days=5))
assert sub.is_active is True
assert sub.is_paid is False
assert sub.has_pro_entitlement is True
def test_trial_expired_no_entitlement():
sub = make_sub(plan="pro", status="trialing", current_period_end=datetime.now(timezone.utc) - timedelta(hours=1))
assert sub.has_pro_entitlement is False
def test_canceled_no_entitlement():
sub = make_sub(plan="pro", status="canceled")
assert sub.is_active is False
assert sub.has_pro_entitlement is False