663 lines
32 KiB
Python
663 lines
32 KiB
Python
"""User-facing key endpoints (/api/keys/*).
|
|
|
|
Регистрирует эндпоинты на ``user_router`` из ``_common``. Импорт этого модуля
|
|
из ``__init__.py`` запускает регистрацию декораторов.
|
|
"""
|
|
|
|
from .._common import * # noqa: F401,F403 — подтягиваем все имена для endpoints
|
|
from .._common import (
|
|
_key_actions_config,
|
|
_normalize_expiry_ms,
|
|
_resolve_available_location_servers,
|
|
_resolve_billing_user_id,
|
|
_resolve_default_web_payment_provider,
|
|
_resolve_public_base_url,
|
|
router,
|
|
user_router,
|
|
)
|
|
|
|
|
|
@user_router.get("/{client_id}/addons-preview", response_model=AccountKeyAddonsPreviewResponse)
|
|
async def user_key_addons_preview(
|
|
client_id: str,
|
|
request: Request,
|
|
selected_device_limit: int | None = Query(None),
|
|
selected_traffic_gb: int | None = Query(None),
|
|
include_device: bool | None = Query(None),
|
|
include_traffic: bool | None = Query(None),
|
|
coupon_code: str | None = Query(None),
|
|
force_web: bool = Query(False),
|
|
session: AsyncSession = Depends(get_session),
|
|
identity=Depends(verify_identity_token),
|
|
):
|
|
actions = _key_actions_config()
|
|
if not force_web and not actions.addons_enabled:
|
|
raise HTTPException(status_code=403, detail="Доп. опции отключены в настройках")
|
|
billing_user_id = await _resolve_billing_user_id(request, identity, session)
|
|
db_key = (
|
|
await session.execute(select(Key).where(Key.user_id == billing_user_id, Key.client_id == client_id).limit(1))
|
|
).scalar_one_or_none()
|
|
if db_key is None:
|
|
raise HTTPException(status_code=404, detail="Подписка не найдена")
|
|
tariff_id = getattr(db_key, "tariff_id", None)
|
|
if not tariff_id:
|
|
raise HTTPException(status_code=400, detail="Для подписки не назначен тариф")
|
|
tariff = await get_tariff_by_id(session, int(tariff_id))
|
|
if not tariff:
|
|
raise HTTPException(status_code=404, detail="Тариф не найден")
|
|
key_details = await get_key_details(session, str(getattr(db_key, "email", "") or ""))
|
|
if not key_details:
|
|
raise HTTPException(status_code=404, detail="Подписка не найдена")
|
|
(
|
|
_tariff_name,
|
|
_subgroup_title,
|
|
_traffic_limit_gb,
|
|
_device_limit,
|
|
_panel,
|
|
is_tariff_configurable,
|
|
addons_devices_enabled,
|
|
addons_traffic_enabled,
|
|
) = await get_key_tariff_addons_state(session=session, key_record=key_details, db_key=db_key)
|
|
if not is_tariff_configurable:
|
|
raise HTTPException(status_code=400, detail="Тариф не поддерживает доп. опции")
|
|
cfg = normalize_tariff_config(tariff)
|
|
raw_device_options = cfg.get("device_options") or tariff.get("device_options") or []
|
|
raw_traffic_options = cfg.get("traffic_options_gb") or tariff.get("traffic_options_gb") or []
|
|
device_options: list[int] = []
|
|
for value in raw_device_options:
|
|
try:
|
|
device_options.append(int(value))
|
|
except (TypeError, ValueError):
|
|
continue
|
|
traffic_options: list[int] = []
|
|
for value in raw_traffic_options:
|
|
try:
|
|
traffic_options.append(int(value))
|
|
except (TypeError, ValueError):
|
|
continue
|
|
device_options = sorted(set(device_options), key=lambda val: (int(val == 0), val))
|
|
traffic_options = sorted(set(traffic_options), key=lambda val: (int(val == 0), val))
|
|
has_device_option = bool(device_options) and bool(addons_devices_enabled)
|
|
has_traffic_option = bool(traffic_options) and bool(addons_traffic_enabled)
|
|
pack_devices, pack_traffic, pack_mode = get_pack_flags()
|
|
if pack_mode:
|
|
has_device_option = has_device_option and bool(pack_devices)
|
|
has_traffic_option = has_traffic_option and bool(pack_traffic)
|
|
if not has_device_option:
|
|
device_options = []
|
|
if not has_traffic_option:
|
|
traffic_options = []
|
|
if not has_device_option and not has_traffic_option:
|
|
raise HTTPException(status_code=400, detail="Доп. опции для этой подписки недоступны")
|
|
selected_device_limit_db = key_details.get("selected_device_limit")
|
|
selected_traffic_limit_db = key_details.get("selected_traffic_limit")
|
|
current_device_limit_db = key_details.get("current_device_limit")
|
|
current_traffic_limit_db = key_details.get("current_traffic_limit")
|
|
base_devices = tariff.get("device_limit")
|
|
base_devices = int(base_devices) if base_devices is not None else None
|
|
base_traffic_bytes = tariff.get("traffic_limit")
|
|
base_traffic_gb_from_tariff = int(base_traffic_bytes / GB) if base_traffic_bytes else None
|
|
current_device_limit = (
|
|
int(current_device_limit_db)
|
|
if current_device_limit_db is not None
|
|
else (int(selected_device_limit_db) if selected_device_limit_db is not None else base_devices)
|
|
)
|
|
current_traffic_gb = (
|
|
int(current_traffic_limit_db)
|
|
if current_traffic_limit_db is not None
|
|
else (int(selected_traffic_limit_db) if selected_traffic_limit_db is not None else base_traffic_gb_from_tariff)
|
|
)
|
|
if pack_mode and current_device_limit is not None and int(current_device_limit) == 0:
|
|
has_device_option = False
|
|
device_options = []
|
|
if pack_mode and current_traffic_gb is not None and int(current_traffic_gb) == 0:
|
|
has_traffic_option = False
|
|
traffic_options = []
|
|
if not has_device_option and not has_traffic_option:
|
|
raise HTTPException(status_code=400, detail="Доп. опции для этой подписки недоступны")
|
|
if pack_mode:
|
|
include_device_effective = (
|
|
bool(include_device) if include_device is not None else selected_device_limit is not None
|
|
)
|
|
include_traffic_effective = (
|
|
bool(include_traffic) if include_traffic is not None else selected_traffic_gb is not None
|
|
)
|
|
selected_device = selected_device_limit if selected_device_limit is not None else None
|
|
selected_traffic = selected_traffic_gb if selected_traffic_gb is not None else None
|
|
else:
|
|
include_device_effective = has_device_option
|
|
include_traffic_effective = has_traffic_option
|
|
selected_device = selected_device_limit if selected_device_limit is not None else current_device_limit
|
|
selected_traffic = selected_traffic_gb if selected_traffic_gb is not None else current_traffic_gb
|
|
if (
|
|
has_device_option
|
|
and include_device_effective
|
|
and selected_device is not None
|
|
and int(selected_device) not in device_options
|
|
):
|
|
raise HTTPException(status_code=400, detail="Выбранный пакет устройств недоступен")
|
|
if (
|
|
has_traffic_option
|
|
and include_traffic_effective
|
|
and selected_traffic is not None
|
|
and int(selected_traffic) not in traffic_options
|
|
):
|
|
raise HTTPException(status_code=400, detail="Выбранный пакет трафика недоступен")
|
|
current_devices_for_price = int(current_device_limit) if current_device_limit is not None else None
|
|
current_traffic_for_price = int(current_traffic_gb) if current_traffic_gb is not None else None
|
|
base_price_for_current = int(
|
|
calculate_config_price(
|
|
tariff=tariff,
|
|
selected_device_limit=current_devices_for_price,
|
|
selected_traffic_gb=current_traffic_for_price,
|
|
)
|
|
)
|
|
if pack_mode:
|
|
diff_full = int(
|
|
calc_pack_full_price_rub(
|
|
tariff=tariff,
|
|
has_device_option=bool(has_device_option and include_device_effective),
|
|
has_traffic_option=bool(has_traffic_option and include_traffic_effective),
|
|
selected_devices=int(selected_device)
|
|
if has_device_option and include_device_effective and selected_device is not None
|
|
else None,
|
|
selected_traffic_gb=int(selected_traffic)
|
|
if has_traffic_option and include_traffic_effective and selected_traffic is not None
|
|
else None,
|
|
)
|
|
)
|
|
recalc_enabled = bool(
|
|
MODES_CONFIG.get(
|
|
"KEY_ADDONS_RECALC_PRICE",
|
|
TARIFFS_CONFIG.get("KEY_ADDONS_RECALC_PRICE", False),
|
|
)
|
|
)
|
|
if recalc_enabled:
|
|
remaining_seconds, total_seconds = calc_remaining_ratio_seconds(
|
|
key_details.get("expiry_time"),
|
|
tariff,
|
|
)
|
|
extra_price_rub = int((diff_full * remaining_seconds + total_seconds - 1) // total_seconds)
|
|
else:
|
|
extra_price_rub = diff_full
|
|
total_price_rub = int(base_price_for_current + diff_full)
|
|
else:
|
|
total_price_rub = int(
|
|
calculate_config_price(
|
|
tariff=tariff,
|
|
selected_device_limit=int(selected_device)
|
|
if has_device_option and include_device_effective and selected_device is not None
|
|
else None,
|
|
selected_traffic_gb=int(selected_traffic)
|
|
if has_traffic_option and include_traffic_effective and selected_traffic is not None
|
|
else None,
|
|
)
|
|
)
|
|
extra_price_rub = int(max(0, total_price_rub - base_price_for_current))
|
|
final_extra_price_rub, discount_rub, _coupon_id, applied_coupon_code = await resolve_percent_coupon_pricing(
|
|
session=session,
|
|
billing_user_id=int(billing_user_id),
|
|
base_price_rub=int(max(0, extra_price_rub)),
|
|
coupon_code=coupon_code,
|
|
)
|
|
return AccountKeyAddonsPreviewResponse(
|
|
client_id=str(getattr(db_key, "client_id", "") or ""),
|
|
tariff_id=int(tariff_id),
|
|
addons_mode=str(pack_mode or ""),
|
|
has_device_option=bool(has_device_option),
|
|
has_traffic_option=bool(has_traffic_option),
|
|
current_device_limit=int(current_device_limit) if current_device_limit is not None else None,
|
|
current_traffic_gb=int(current_traffic_gb) if current_traffic_gb is not None else None,
|
|
selected_device_limit=int(selected_device)
|
|
if has_device_option and include_device_effective and selected_device is not None
|
|
else None,
|
|
selected_traffic_gb=int(selected_traffic)
|
|
if has_traffic_option and include_traffic_effective and selected_traffic is not None
|
|
else None,
|
|
device_options=[
|
|
AccountKeyAddonOptionResponse(
|
|
value=int(val),
|
|
label=(
|
|
"Безлимит устройств"
|
|
if int(val) <= 0
|
|
else (f"+{int(val)} устройств" if pack_mode else f"{int(val)} устройств")
|
|
),
|
|
)
|
|
for val in device_options
|
|
],
|
|
traffic_options=[
|
|
AccountKeyAddonOptionResponse(
|
|
value=int(val),
|
|
label=("Безлимит трафика" if int(val) <= 0 else (f"+{int(val)} ГБ" if pack_mode else f"{int(val)} ГБ")),
|
|
)
|
|
for val in traffic_options
|
|
],
|
|
total_price_rub=int(total_price_rub),
|
|
extra_price_rub=int(max(0, extra_price_rub)),
|
|
discount_rub=int(discount_rub),
|
|
final_price_rub=int(max(0, final_extra_price_rub)),
|
|
applied_coupon_code=applied_coupon_code,
|
|
balance_rub=float(await get_balance(session, int(billing_user_id))),
|
|
)
|
|
|
|
|
|
@user_router.post("/{client_id}/apply-addons", response_model=AccountKeyApplyAddonsResponse)
|
|
async def user_key_apply_addons(
|
|
client_id: str,
|
|
body: AccountKeyAddonsPreviewRequest,
|
|
request: Request,
|
|
force_web: bool = Query(False),
|
|
session: AsyncSession = Depends(get_session),
|
|
identity=Depends(verify_identity_token),
|
|
):
|
|
actions = _key_actions_config()
|
|
if not force_web and not actions.addons_enabled:
|
|
raise HTTPException(status_code=403, detail="Доп. опции отключены в настройках")
|
|
billing_user_id = await _resolve_billing_user_id(request, identity, session)
|
|
db_key = (
|
|
await session.execute(select(Key).where(Key.user_id == billing_user_id, Key.client_id == client_id).limit(1))
|
|
).scalar_one_or_none()
|
|
if db_key is None:
|
|
raise HTTPException(status_code=404, detail="Подписка не найдена")
|
|
tariff_id = getattr(db_key, "tariff_id", None)
|
|
if not tariff_id:
|
|
raise HTTPException(status_code=400, detail="Для подписки не назначен тариф")
|
|
tariff = await get_tariff_by_id(session, int(tariff_id))
|
|
if not tariff:
|
|
raise HTTPException(status_code=404, detail="Тариф не найден")
|
|
key_details = await get_key_details(session, str(getattr(db_key, "email", "") or ""))
|
|
if not key_details:
|
|
raise HTTPException(status_code=404, detail="Подписка не найдена")
|
|
(
|
|
_tariff_name,
|
|
_subgroup_title,
|
|
_traffic_limit_gb,
|
|
_device_limit,
|
|
_panel,
|
|
is_tariff_configurable,
|
|
addons_devices_enabled,
|
|
addons_traffic_enabled,
|
|
) = await get_key_tariff_addons_state(session=session, key_record=key_details, db_key=db_key)
|
|
if not is_tariff_configurable:
|
|
raise HTTPException(status_code=400, detail="Тариф не поддерживает доп. опции")
|
|
cfg = normalize_tariff_config(tariff)
|
|
raw_device_options = cfg.get("device_options") or tariff.get("device_options") or []
|
|
raw_traffic_options = cfg.get("traffic_options_gb") or tariff.get("traffic_options_gb") or []
|
|
device_options: list[int] = []
|
|
for value in raw_device_options:
|
|
try:
|
|
device_options.append(int(value))
|
|
except (TypeError, ValueError):
|
|
continue
|
|
traffic_options: list[int] = []
|
|
for value in raw_traffic_options:
|
|
try:
|
|
traffic_options.append(int(value))
|
|
except (TypeError, ValueError):
|
|
continue
|
|
device_options = sorted(set(device_options), key=lambda val: (int(val == 0), val))
|
|
traffic_options = sorted(set(traffic_options), key=lambda val: (int(val == 0), val))
|
|
has_device_option = bool(device_options) and bool(addons_devices_enabled)
|
|
has_traffic_option = bool(traffic_options) and bool(addons_traffic_enabled)
|
|
pack_devices, pack_traffic, pack_mode = get_pack_flags()
|
|
if pack_mode:
|
|
has_device_option = has_device_option and bool(pack_devices)
|
|
has_traffic_option = has_traffic_option and bool(pack_traffic)
|
|
if not has_device_option:
|
|
device_options = []
|
|
if not has_traffic_option:
|
|
traffic_options = []
|
|
if not has_device_option and not has_traffic_option:
|
|
raise HTTPException(status_code=400, detail="Доп. опции для этой подписки недоступны")
|
|
selected_device_limit_db = key_details.get("selected_device_limit")
|
|
selected_traffic_limit_db = key_details.get("selected_traffic_limit")
|
|
current_device_limit_db = key_details.get("current_device_limit")
|
|
current_traffic_limit_db = key_details.get("current_traffic_limit")
|
|
base_devices = tariff.get("device_limit")
|
|
base_devices = int(base_devices) if base_devices is not None else None
|
|
base_traffic_bytes = tariff.get("traffic_limit")
|
|
base_traffic_gb_from_tariff = int(base_traffic_bytes / GB) if base_traffic_bytes else None
|
|
current_device_limit = (
|
|
int(current_device_limit_db)
|
|
if current_device_limit_db is not None
|
|
else (int(selected_device_limit_db) if selected_device_limit_db is not None else base_devices)
|
|
)
|
|
current_traffic_gb = (
|
|
int(current_traffic_limit_db)
|
|
if current_traffic_limit_db is not None
|
|
else (int(selected_traffic_limit_db) if selected_traffic_limit_db is not None else base_traffic_gb_from_tariff)
|
|
)
|
|
if pack_mode and current_device_limit is not None and int(current_device_limit) == 0:
|
|
has_device_option = False
|
|
device_options = []
|
|
if pack_mode and current_traffic_gb is not None and int(current_traffic_gb) == 0:
|
|
has_traffic_option = False
|
|
traffic_options = []
|
|
if not has_device_option and not has_traffic_option:
|
|
raise HTTPException(status_code=400, detail="Доп. опции для этой подписки недоступны")
|
|
if pack_mode:
|
|
include_device_effective = (
|
|
bool(body.include_device) if body.include_device is not None else body.selected_device_limit is not None
|
|
)
|
|
include_traffic_effective = (
|
|
bool(body.include_traffic) if body.include_traffic is not None else body.selected_traffic_gb is not None
|
|
)
|
|
selected_device = body.selected_device_limit if body.selected_device_limit is not None else None
|
|
selected_traffic = body.selected_traffic_gb if body.selected_traffic_gb is not None else None
|
|
else:
|
|
include_device_effective = has_device_option
|
|
include_traffic_effective = has_traffic_option
|
|
selected_device = body.selected_device_limit if body.selected_device_limit is not None else current_device_limit
|
|
selected_traffic = body.selected_traffic_gb if body.selected_traffic_gb is not None else current_traffic_gb
|
|
if (
|
|
has_device_option
|
|
and include_device_effective
|
|
and selected_device is not None
|
|
and int(selected_device) not in device_options
|
|
):
|
|
raise HTTPException(status_code=400, detail="Выбранный пакет устройств недоступен")
|
|
if (
|
|
has_traffic_option
|
|
and include_traffic_effective
|
|
and selected_traffic is not None
|
|
and int(selected_traffic) not in traffic_options
|
|
):
|
|
raise HTTPException(status_code=400, detail="Выбранный пакет трафика недоступен")
|
|
current_devices_for_price = int(current_device_limit) if current_device_limit is not None else None
|
|
current_traffic_for_price = int(current_traffic_gb) if current_traffic_gb is not None else None
|
|
base_price_for_current = int(
|
|
calculate_config_price(
|
|
tariff=tariff,
|
|
selected_device_limit=current_devices_for_price,
|
|
selected_traffic_gb=current_traffic_for_price,
|
|
)
|
|
)
|
|
total_price_after_purchase = base_price_for_current
|
|
if pack_mode:
|
|
diff_full = int(
|
|
calc_pack_full_price_rub(
|
|
tariff=tariff,
|
|
has_device_option=bool(has_device_option and include_device_effective),
|
|
has_traffic_option=bool(has_traffic_option and include_traffic_effective),
|
|
selected_devices=int(selected_device)
|
|
if has_device_option and include_device_effective and selected_device is not None
|
|
else None,
|
|
selected_traffic_gb=int(selected_traffic)
|
|
if has_traffic_option and include_traffic_effective and selected_traffic is not None
|
|
else None,
|
|
)
|
|
)
|
|
recalc_enabled = bool(
|
|
MODES_CONFIG.get(
|
|
"KEY_ADDONS_RECALC_PRICE",
|
|
TARIFFS_CONFIG.get("KEY_ADDONS_RECALC_PRICE", False),
|
|
)
|
|
)
|
|
if recalc_enabled:
|
|
remaining_seconds, total_seconds = calc_remaining_ratio_seconds(
|
|
key_details.get("expiry_time"),
|
|
tariff,
|
|
)
|
|
extra_price_rub = int((diff_full * remaining_seconds + total_seconds - 1) // total_seconds)
|
|
else:
|
|
extra_price_rub = diff_full
|
|
total_price_after_purchase = int(base_price_for_current + diff_full)
|
|
else:
|
|
selected_total_price = int(
|
|
calculate_config_price(
|
|
tariff=tariff,
|
|
selected_device_limit=int(selected_device)
|
|
if has_device_option and selected_device is not None
|
|
else None,
|
|
selected_traffic_gb=int(selected_traffic)
|
|
if has_traffic_option and selected_traffic is not None
|
|
else None,
|
|
)
|
|
)
|
|
extra_price_rub = int(max(0, selected_total_price - base_price_for_current))
|
|
total_price_after_purchase = selected_total_price
|
|
allow_downgrade = bool(TARIFFS_CONFIG.get("ALLOW_DOWNGRADE", True))
|
|
device_downgrade = (
|
|
allow_downgrade
|
|
and has_device_option
|
|
and current_device_limit is not None
|
|
and selected_device is not None
|
|
and not is_not_downgrade(current_device_limit, selected_device)
|
|
)
|
|
traffic_downgrade = (
|
|
allow_downgrade
|
|
and has_traffic_option
|
|
and current_traffic_gb is not None
|
|
and selected_traffic is not None
|
|
and not is_not_downgrade(current_traffic_gb, selected_traffic)
|
|
)
|
|
if device_downgrade or traffic_downgrade:
|
|
raise HTTPException(status_code=400, detail="Снижение параметров через сайт пока не поддерживается")
|
|
final_extra_price_rub, discount_rub, coupon_id, applied_coupon_code = await resolve_percent_coupon_pricing(
|
|
session=session,
|
|
billing_user_id=int(billing_user_id),
|
|
base_price_rub=int(max(0, extra_price_rub)),
|
|
coupon_code=body.coupon_code,
|
|
)
|
|
balance = float(await get_balance(session, int(billing_user_id)))
|
|
required_amount = int(max(0, ceil(float(final_extra_price_rub) - balance)))
|
|
if extra_price_rub <= 0:
|
|
return AccountKeyApplyAddonsResponse(
|
|
ok=True,
|
|
message="Доплата не требуется",
|
|
client_id=str(getattr(db_key, "client_id", "") or ""),
|
|
tariff_id=int(tariff_id),
|
|
total_price_rub=int(total_price_after_purchase),
|
|
extra_price_rub=0,
|
|
discount_rub=0,
|
|
final_price_rub=0,
|
|
applied_coupon_code=None,
|
|
charged_rub=0,
|
|
balance_rub=balance,
|
|
)
|
|
expiry_time = int(getattr(db_key, "expiry_time", 0) or 0)
|
|
email = str(getattr(db_key, "email", "") or "")
|
|
server_id = str(getattr(db_key, "server_id", "") or "")
|
|
if not email or not server_id:
|
|
raise HTTPException(status_code=400, detail="Некорректные данные подписки")
|
|
if required_amount > 0:
|
|
provider_id = str(body.provider_id or _resolve_default_web_payment_provider() or "").strip().upper()
|
|
if not provider_id:
|
|
raise HTTPException(status_code=503, detail="Нет доступных провайдеров оплаты")
|
|
base_url = _resolve_public_base_url(request)
|
|
success_url = validate_redirect_url(str(body.success_url or ""), f"{base_url}/payment-success")
|
|
failure_url = validate_redirect_url(str(body.failure_url or ""), f"{base_url}/payment-failure")
|
|
payment_request = PaymentLinkRequest(
|
|
legacy_user_ref=int(billing_user_id),
|
|
amount=required_amount,
|
|
currency="RUB",
|
|
provider_id=provider_id,
|
|
success_url=success_url,
|
|
failure_url=failure_url,
|
|
metadata={
|
|
"payment_flow": "key_addons",
|
|
"tariff_id": int(tariff_id),
|
|
"email": email,
|
|
"selected_device_limit": int(selected_device)
|
|
if has_device_option and include_device_effective and selected_device is not None
|
|
else None,
|
|
"selected_traffic_gb": int(selected_traffic)
|
|
if has_traffic_option and include_traffic_effective and selected_traffic is not None
|
|
else None,
|
|
"current_device_limit": int(current_device_limit) if current_device_limit is not None else None,
|
|
"current_traffic_gb": int(current_traffic_gb) if current_traffic_gb is not None else None,
|
|
"original_price": int(base_price_for_current),
|
|
"base_price_rub": int(max(0, extra_price_rub)),
|
|
"discount_rub": int(discount_rub),
|
|
"applied_coupon_code": applied_coupon_code,
|
|
"coupon_id": int(coupon_id) if coupon_id is not None else None,
|
|
},
|
|
)
|
|
payment_result = await create_payment_link(session, payment_request)
|
|
if not payment_result.success or not payment_result.payment_url or not payment_result.payment_id:
|
|
raise HTTPException(status_code=400, detail=payment_result.error or "Не удалось создать ссылку оплаты")
|
|
await create_temporary_data(
|
|
session,
|
|
int(billing_user_id),
|
|
"waiting_for_addons_payment",
|
|
{
|
|
"tariff_id": int(tariff_id),
|
|
"email": email,
|
|
"required_amount": int(required_amount),
|
|
"selected_device_limit": int(selected_device)
|
|
if has_device_option and include_device_effective and selected_device is not None
|
|
else None,
|
|
"selected_traffic_gb": int(selected_traffic)
|
|
if has_traffic_option and include_traffic_effective and selected_traffic is not None
|
|
else None,
|
|
"current_device_limit": int(current_device_limit) if current_device_limit is not None else None,
|
|
"current_traffic_gb": int(current_traffic_gb) if current_traffic_gb is not None else None,
|
|
"original_price": int(base_price_for_current),
|
|
"base_price_rub": int(max(0, extra_price_rub)),
|
|
"discount_rub": int(discount_rub),
|
|
"applied_coupon_code": applied_coupon_code,
|
|
"coupon_id": int(coupon_id) if coupon_id is not None else None,
|
|
},
|
|
)
|
|
return AccountKeyApplyAddonsResponse(
|
|
ok=True,
|
|
message="Требуется оплата для применения доп. опций",
|
|
client_id=str(getattr(db_key, "client_id", "") or ""),
|
|
tariff_id=int(tariff_id),
|
|
total_price_rub=int(total_price_after_purchase),
|
|
extra_price_rub=int(extra_price_rub),
|
|
discount_rub=int(discount_rub),
|
|
final_price_rub=int(final_extra_price_rub),
|
|
applied_coupon_code=applied_coupon_code,
|
|
charged_rub=0,
|
|
balance_rub=balance,
|
|
payment_required=True,
|
|
required_amount_rub=required_amount,
|
|
payment_id=payment_result.payment_id,
|
|
payment_url=payment_result.payment_url,
|
|
)
|
|
target_subgroup = tariff.get("subgroup_title")
|
|
current_subgroup = None
|
|
current_tariff_id = key_details.get("tariff_id")
|
|
if current_tariff_id:
|
|
current_tariff = await get_tariff_by_id(session, int(current_tariff_id))
|
|
if current_tariff:
|
|
current_subgroup = current_tariff.get("subgroup_title")
|
|
if pack_mode:
|
|
device_limit_effective_current, traffic_limit_bytes_effective_current = await get_effective_limits_for_key(
|
|
session=session,
|
|
tariff_id=int(tariff_id),
|
|
selected_device_limit=int(current_device_limit) if current_device_limit is not None else None,
|
|
selected_traffic_gb=int(current_traffic_gb) if current_traffic_gb is not None else None,
|
|
)
|
|
traffic_limit_gb_effective_current = (
|
|
int(traffic_limit_bytes_effective_current / GB) if traffic_limit_bytes_effective_current else 0
|
|
)
|
|
new_device_limit_effective = device_limit_effective_current
|
|
new_traffic_limit_gb_effective = traffic_limit_gb_effective_current
|
|
if has_device_option and include_device_effective and selected_device is not None:
|
|
pack_devices_val = int(selected_device)
|
|
if pack_devices_val <= 0 or (
|
|
new_device_limit_effective is not None and int(new_device_limit_effective) <= 0
|
|
):
|
|
new_device_limit_effective = 0
|
|
else:
|
|
if new_device_limit_effective is None:
|
|
new_device_limit_effective = pack_devices_val
|
|
else:
|
|
new_device_limit_effective = int(new_device_limit_effective) + pack_devices_val
|
|
if has_traffic_option and include_traffic_effective and selected_traffic is not None:
|
|
pack_traffic_val = int(selected_traffic)
|
|
if pack_traffic_val <= 0 or int(new_traffic_limit_gb_effective) <= 0:
|
|
new_traffic_limit_gb_effective = 0
|
|
else:
|
|
new_traffic_limit_gb_effective = int(new_traffic_limit_gb_effective) + pack_traffic_val
|
|
await renew_key_in_cluster(
|
|
cluster_id=server_id,
|
|
email=email,
|
|
client_id=str(getattr(db_key, "client_id", "") or ""),
|
|
new_expiry_time=expiry_time,
|
|
total_gb=int(new_traffic_limit_gb_effective),
|
|
session=session,
|
|
hwid_device_limit=int(new_device_limit_effective) if new_device_limit_effective is not None else 0,
|
|
reset_traffic=False,
|
|
target_subgroup=target_subgroup,
|
|
old_subgroup=current_subgroup,
|
|
plan=int(tariff_id),
|
|
)
|
|
await save_key_config_with_mode(
|
|
session=session,
|
|
email=email,
|
|
selected_devices=int(new_device_limit_effective) if new_device_limit_effective is not None else None,
|
|
selected_traffic_gb=int(new_traffic_limit_gb_effective)
|
|
if new_traffic_limit_gb_effective is not None
|
|
else None,
|
|
total_price=int(total_price_after_purchase),
|
|
has_device_choice=bool(has_device_option and include_device_effective),
|
|
has_traffic_choice=bool(has_traffic_option and include_traffic_effective),
|
|
config_mode="pack",
|
|
)
|
|
else:
|
|
selected_device_for_effective = (
|
|
int(selected_device)
|
|
if has_device_option and include_device_effective and selected_device is not None
|
|
else None
|
|
)
|
|
selected_traffic_for_effective = (
|
|
int(selected_traffic)
|
|
if has_traffic_option and include_traffic_effective and selected_traffic is not None
|
|
else 0
|
|
)
|
|
device_limit_effective_new, traffic_limit_bytes_effective_new = await get_effective_limits_for_key(
|
|
session=session,
|
|
tariff_id=int(tariff_id),
|
|
selected_device_limit=selected_device_for_effective,
|
|
selected_traffic_gb=selected_traffic_for_effective,
|
|
)
|
|
traffic_limit_gb_effective = (
|
|
int(traffic_limit_bytes_effective_new / GB) if traffic_limit_bytes_effective_new else 0
|
|
)
|
|
await renew_key_in_cluster(
|
|
cluster_id=server_id,
|
|
email=email,
|
|
client_id=str(getattr(db_key, "client_id", "") or ""),
|
|
new_expiry_time=expiry_time,
|
|
total_gb=int(traffic_limit_gb_effective),
|
|
session=session,
|
|
hwid_device_limit=int(device_limit_effective_new) if device_limit_effective_new is not None else 0,
|
|
reset_traffic=False,
|
|
target_subgroup=target_subgroup,
|
|
old_subgroup=current_subgroup,
|
|
plan=int(tariff_id),
|
|
)
|
|
await save_key_config_with_mode(
|
|
session=session,
|
|
email=email,
|
|
selected_devices=int(selected_device)
|
|
if has_device_option and include_device_effective and selected_device is not None
|
|
else None,
|
|
selected_traffic_gb=int(selected_traffic)
|
|
if has_traffic_option and include_traffic_effective and selected_traffic is not None
|
|
else None,
|
|
total_price=int(total_price_after_purchase),
|
|
has_device_choice=bool(has_device_option and include_device_effective),
|
|
has_traffic_choice=bool(has_traffic_option and include_traffic_effective),
|
|
config_mode="addon",
|
|
)
|
|
await update_balance(session, int(billing_user_id), -int(final_extra_price_rub))
|
|
if coupon_id is not None:
|
|
await mark_coupon_used(session, int(coupon_id), int(billing_user_id))
|
|
return AccountKeyApplyAddonsResponse(
|
|
ok=True,
|
|
message="Доп. опции применены",
|
|
client_id=str(getattr(db_key, "client_id", "") or ""),
|
|
tariff_id=int(tariff_id),
|
|
total_price_rub=int(total_price_after_purchase),
|
|
extra_price_rub=int(extra_price_rub),
|
|
discount_rub=int(discount_rub),
|
|
final_price_rub=int(final_extra_price_rub),
|
|
applied_coupon_code=applied_coupon_code,
|
|
charged_rub=int(final_extra_price_rub),
|
|
balance_rub=float(await get_balance(session, int(billing_user_id))),
|
|
)
|