/
systemsstrategyy
/
Treker
Обзор
Документация
Войти
/
systemsstrategyy
/
Treker
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
api/modules/billing/errors.py
67 строк
3 KB
SystemsStrategy
Синхронизация с актуальной линией разработки (август 2026)
06 авг 2026, 12:52
06 авг 2026, 12:52
19af6aa
Код
Авторство
О чём код?
"""Доменные исключения billing-модуля. LimitReached — превышен лимит ресурса на текущем плане. HTTP 402 Payment Required семантически правильный для billing (не 429 Too Many Requests — то rate-limit). Структурированный detail_payload (`{resource, current, max}`) позволяет фронту показать конкретное сообщение: «Вы достигли лимита (1 / 1) на плане Free, обновитесь до Pro». InvalidPromoCode (422) и FreeViaPromo (409) — для checkout flow с promo_code. Намеренно НЕ раскрываем причину «expired vs unknown vs exhausted» — единый код `invalid_promo_code` мешает злоумышленнику перебирать существующие коды. """ from __future__ import annotations from typing import Any, ClassVar from api.core.errors import BusinessRuleViolation, DomainException, ValidationError class LimitReached(DomainException): """Превышен лимит ресурса на текущем плане → 402 Payment Required.""" code: ClassVar[str] = "limit_reached" status: ClassVar[int] = 402 title: ClassVar[str] = "Limit reached" def __init__(self, *, detail_payload: dict[str, Any]) -> None: super().__init__(detail="Превышен лимит подписки") self.detail_payload = detail_payload class InvalidPromoCode(ValidationError): # noqa: N818 — convention в billing (LimitReached, FreeViaPromo) """Промокод не существует / истёк / исчерпан / неприменим к плану. Единый class-level `code='invalid_promo_code'` для всех 6 причин провала validate (см. PromoCodeService.validate). Не leak'аем конкретную причину — защита от brute-force угадывания валидных кодов. """ code: ClassVar[str] = "invalid_promo_code" title: ClassVar[str] = "Invalid promo code" class PaymentUnavailable(DomainException): """Платёжный провайдер (ЮKassa) не настроен → приём платежей временно недоступен. 503 Service Unavailable. Fail-safe: если биллинг включён, но боевые креды (shop_id/secret_key) не заданы — отдаём понятную ошибку вместо создания живого SDK-клиента с пустыми ключами (который падает/ таймаутит при checkout, см. аудит #16/#18).""" code: ClassVar[str] = "payment_unavailable" status: ClassVar[int] = 503 title: ClassVar[str] = "Payment unavailable" class FreeViaPromo(BusinessRuleViolation): """Промокод покрывает 100% стоимости плана — checkout flow не подходит. PaymentAttempt.amount_kopecks > 0 CHECK не позволит сохранить нулевую сумму. В MVP таких юзеров отбиваем — admin отдельно активирует им подписку через прямой UPDATE (когда такой flow появится). """ code: ClassVar[str] = "free_via_promo" title: ClassVar[str] = "Promo covers full price"