fix(billing): unify quota counters, fix Stripe webhooks, tier/plan sync
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 3m16s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 3m16s
This commit is contained in:
@@ -93,34 +93,45 @@ class UserRepository:
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
def increment_usage(
|
||||
self, user_id: str, docs: int = 0, pages: int = 0, api_calls: int = 0
|
||||
) -> Optional[User]:
|
||||
"""Increment usage counters"""
|
||||
def reset_usage_if_needed(self, user_id: str) -> Optional[User]:
|
||||
"""Reset monthly counters if usage_reset_date is in a previous month."""
|
||||
user = self.get_by_id(user_id)
|
||||
if not user:
|
||||
return None
|
||||
|
||||
# Check if usage needs to be reset (monthly)
|
||||
if user.usage_reset_date:
|
||||
now = datetime.now(timezone.utc)
|
||||
if (
|
||||
now.month != user.usage_reset_date.month
|
||||
or now.year != user.usage_reset_date.year
|
||||
):
|
||||
user.docs_translated_this_month = 0
|
||||
user.pages_translated_this_month = 0
|
||||
user.api_calls_this_month = 0
|
||||
user.usage_reset_date = now
|
||||
|
||||
user.docs_translated_this_month += docs
|
||||
user.pages_translated_this_month += pages
|
||||
user.api_calls_this_month += api_calls
|
||||
|
||||
self.db.commit()
|
||||
self.db.refresh(user)
|
||||
now = datetime.now(timezone.utc)
|
||||
reset_date = user.usage_reset_date
|
||||
if reset_date is None or reset_date.month != now.month or reset_date.year != now.year:
|
||||
user.docs_translated_this_month = 0
|
||||
user.pages_translated_this_month = 0
|
||||
user.api_calls_this_month = 0
|
||||
user.usage_reset_date = now
|
||||
user.updated_at = now
|
||||
self.db.commit()
|
||||
self.db.refresh(user)
|
||||
return user
|
||||
|
||||
def increment_usage(
|
||||
self, user_id: str, docs: int = 0, pages: int = 0, api_calls: int = 0
|
||||
) -> Optional[User]:
|
||||
"""Increment usage counters atomically via SQL UPDATE."""
|
||||
from sqlalchemy import update
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
self.db.execute(
|
||||
update(User)
|
||||
.where(User.id == user_id)
|
||||
.values(
|
||||
docs_translated_this_month=User.docs_translated_this_month + docs,
|
||||
pages_translated_this_month=User.pages_translated_this_month + pages,
|
||||
api_calls_this_month=User.api_calls_this_month + api_calls,
|
||||
updated_at=now,
|
||||
)
|
||||
.execution_options(synchronize_session=False)
|
||||
)
|
||||
self.db.commit()
|
||||
return self.get_by_id(user_id)
|
||||
|
||||
def add_credits(self, user_id: str, credits: int) -> Optional[User]:
|
||||
"""Add extra credits to user"""
|
||||
user = self.get_by_id(user_id)
|
||||
@@ -133,14 +144,21 @@ class UserRepository:
|
||||
return user
|
||||
|
||||
def use_credits(self, user_id: str, credits: int) -> bool:
|
||||
"""Use credits from user balance"""
|
||||
user = self.get_by_id(user_id)
|
||||
if not user or user.extra_credits < credits:
|
||||
return False
|
||||
"""Use credits from user balance atomically (prevents overdraft)."""
|
||||
from sqlalchemy import update
|
||||
|
||||
user.extra_credits -= credits
|
||||
now = datetime.now(timezone.utc)
|
||||
result = self.db.execute(
|
||||
update(User)
|
||||
.where(User.id == user_id, User.extra_credits >= credits)
|
||||
.values(
|
||||
extra_credits=User.extra_credits - credits,
|
||||
updated_at=now,
|
||||
)
|
||||
.execution_options(synchronize_session=False)
|
||||
)
|
||||
self.db.commit()
|
||||
return True
|
||||
return result.rowcount > 0
|
||||
|
||||
def get_all_users(
|
||||
self, skip: int = 0, limit: int = 100, plan: Optional[PlanType] = None
|
||||
|
||||
Reference in New Issue
Block a user