Replace hardcoded subscription fields (orders_limit, products_limit,
team_members_limit) across 5 frontend pages with dynamic feature
provider APIs. Add admin convenience endpoint for store subscription
lookup. Remove legacy stubs (StoreSubscription, FeatureCode, Feature,
TIER_LIMITS, FeatureInfo, FeatureUpgradeInfo) and schema aliases.
Pages updated:
- Admin subscriptions: dynamic feature overrides editor
- Admin tiers: correct feature catalog/limits API URLs
- Store billing: usage metrics from /store/billing/usage
- Merchant subscription detail: tier.feature_limits rendering
- Admin store detail: new GET /admin/subscriptions/store/{id} endpoint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
144 lines
3.7 KiB
Python
144 lines
3.7 KiB
Python
# app/modules/billing/schemas/subscription.py
|
|
"""
|
|
Pydantic schemas for merchant-level subscription operations.
|
|
|
|
Supports subscription management, tier information, and feature summaries.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
|
|
# ============================================================================
|
|
# Tier Information Schemas
|
|
# ============================================================================
|
|
|
|
|
|
class TierFeatureLimitResponse(BaseModel):
|
|
"""Feature limit entry for a tier."""
|
|
|
|
feature_code: str
|
|
limit_value: int | None = Field(None, description="None = unlimited")
|
|
|
|
|
|
class TierInfo(BaseModel):
|
|
"""Full tier information with feature limits."""
|
|
|
|
code: str
|
|
name: str
|
|
description: str | None = None
|
|
price_monthly_cents: int
|
|
price_annual_cents: int | None
|
|
feature_codes: list[str] = Field(default_factory=list)
|
|
feature_limits: list[TierFeatureLimitResponse] = Field(default_factory=list)
|
|
|
|
|
|
# ============================================================================
|
|
# Subscription Schemas
|
|
# ============================================================================
|
|
|
|
|
|
class MerchantSubscriptionCreate(BaseModel):
|
|
"""Schema for creating a merchant subscription."""
|
|
|
|
tier_code: str = Field(default="essential")
|
|
is_annual: bool = False
|
|
trial_days: int = Field(default=14, ge=0, le=30)
|
|
|
|
|
|
class MerchantSubscriptionUpdate(BaseModel):
|
|
"""Schema for updating a merchant subscription."""
|
|
|
|
tier_code: str | None = None
|
|
status: str | None = Field(None, pattern="^(trial|active|past_due|cancelled|expired)$")
|
|
is_annual: bool | None = None
|
|
|
|
|
|
class MerchantSubscriptionResponse(BaseModel):
|
|
"""Schema for merchant subscription response."""
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: int
|
|
merchant_id: int
|
|
platform_id: int
|
|
tier_id: int | None
|
|
|
|
status: str
|
|
is_annual: bool
|
|
period_start: datetime
|
|
period_end: datetime
|
|
trial_ends_at: datetime | None
|
|
|
|
# Stripe info (optional, may be hidden from client)
|
|
stripe_customer_id: str | None = None
|
|
|
|
# Cancellation
|
|
cancelled_at: datetime | None = None
|
|
|
|
# Computed properties
|
|
is_active: bool
|
|
is_trial: bool
|
|
trial_days_remaining: int | None
|
|
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
# ============================================================================
|
|
# Feature Summary Schemas
|
|
# ============================================================================
|
|
|
|
|
|
class FeatureSummaryResponse(BaseModel):
|
|
"""Feature summary for merchant portal display."""
|
|
|
|
code: str
|
|
name_key: str
|
|
description_key: str
|
|
category: str
|
|
feature_type: str
|
|
scope: str
|
|
enabled: bool
|
|
limit: int | None = None
|
|
current: int | None = None
|
|
remaining: int | None = None
|
|
percent_used: float | None = None
|
|
is_override: bool = False
|
|
unit_key: str | None = None
|
|
ui_icon: str | None = None
|
|
|
|
|
|
class MerchantSubscriptionStatusResponse(BaseModel):
|
|
"""Full subscription status with tier info and feature summary."""
|
|
|
|
subscription: MerchantSubscriptionResponse
|
|
tier: TierInfo | None = None
|
|
features: list[FeatureSummaryResponse] = Field(default_factory=list)
|
|
|
|
|
|
# ============================================================================
|
|
# Limit Check Schemas
|
|
# ============================================================================
|
|
|
|
|
|
class LimitCheckResult(BaseModel):
|
|
"""Result of a limit check."""
|
|
|
|
allowed: bool
|
|
limit: int | None
|
|
current: int
|
|
remaining: int | None
|
|
message: str | None = None
|
|
|
|
|
|
class FeatureCheckResponse(BaseModel):
|
|
"""Response for feature check."""
|
|
|
|
feature_code: str
|
|
enabled: bool
|
|
message: str | None = None
|
|
|
|
|