44 lines
1.6 KiB
Python
44 lines
1.6 KiB
Python
import os
|
|
import time
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class LicenseDecision:
|
|
valid: bool
|
|
valid_until: Optional[int]
|
|
status: str # "active"|"grace"|"expired"|"none"
|
|
|
|
|
|
def compute_license_decision(current_period_end: Optional[int], status: Optional[str]) -> LicenseDecision:
|
|
"""
|
|
current_period_end: epoch seconds (UTC)
|
|
status: normalized db status or stripe subscription status (best effort)
|
|
"""
|
|
now = int(time.time())
|
|
normalized = (status or "").lower().strip()
|
|
|
|
if not current_period_end:
|
|
if normalized == "active":
|
|
fallback = now + 24 * 60 * 60
|
|
return LicenseDecision(valid=True, valid_until=fallback, status="active")
|
|
return LicenseDecision(valid=False, valid_until=None, status="none")
|
|
|
|
grace_days = int(os.getenv("AZA_GRACE_DAYS", "0"))
|
|
grace_seconds = grace_days * 24 * 60 * 60
|
|
|
|
if now <= int(current_period_end):
|
|
return LicenseDecision(valid=True, valid_until=int(current_period_end), status="active")
|
|
|
|
if grace_seconds > 0 and now <= int(current_period_end) + grace_seconds:
|
|
return LicenseDecision(valid=True, valid_until=int(current_period_end), status="grace")
|
|
|
|
# Period expired but DB status is still "active": trust the status.
|
|
# Stripe/WC webhook may not have updated current_period_end yet.
|
|
if normalized == "active":
|
|
fallback = now + 24 * 60 * 60
|
|
return LicenseDecision(valid=True, valid_until=fallback, status="active")
|
|
|
|
return LicenseDecision(valid=False, valid_until=int(current_period_end), status="expired")
|