128 lines
3.8 KiB
Python
128 lines
3.8 KiB
Python
"""Автозачисление по уникальному коду Digiseller."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from dataclasses import dataclass
|
|
|
|
from aiogram import Bot
|
|
|
|
from config import get_settings
|
|
from services.db import Database
|
|
from services.digiseller import Digiseller, DigisellerError
|
|
from services.mailer import notify_important
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class DigisellerFulfillResult:
|
|
ok: bool
|
|
message: str
|
|
amount: float = 0.0
|
|
balance: float = 0.0
|
|
telegram_id: int | None = None
|
|
already: bool = False
|
|
inv: int | None = None
|
|
|
|
|
|
async def fulfill_digiseller_unique_code(
|
|
*,
|
|
db: Database,
|
|
digiseller: Digiseller,
|
|
unique_code: str,
|
|
telegram_id_hint: int | None = None,
|
|
bot: Bot | None = None,
|
|
notify: bool = True,
|
|
) -> DigisellerFulfillResult:
|
|
"""Проверить код в Digiseller API и зачислить баланс. Идемпотентно."""
|
|
purchase = await digiseller.get_by_unique_code(unique_code)
|
|
digiseller.assert_payable(purchase)
|
|
amount = digiseller.credit_amount(purchase)
|
|
|
|
telegram_id = digiseller.extract_telegram_id(purchase) or telegram_id_hint
|
|
if not telegram_id:
|
|
raise DigisellerError(
|
|
"В платеже нет telegram_id. Открой оплату из бота "
|
|
"(ссылка с твоим ID) или введи код вручную в боте."
|
|
)
|
|
|
|
if db.digiseller_inv_used(purchase.inv) or db.digiseller_code_used(
|
|
purchase.unique_code
|
|
):
|
|
balance = db.get_balance(telegram_id)
|
|
return DigisellerFulfillResult(
|
|
ok=True,
|
|
already=True,
|
|
message="Этот платёж уже был зачислен ранее.",
|
|
amount=amount,
|
|
balance=balance,
|
|
telegram_id=telegram_id,
|
|
inv=purchase.inv,
|
|
)
|
|
|
|
recorded = db.record_digiseller_payment(
|
|
inv=purchase.inv,
|
|
unique_code=purchase.unique_code,
|
|
telegram_id=telegram_id,
|
|
amount_rub=amount,
|
|
product_id=purchase.product_id,
|
|
)
|
|
if not recorded:
|
|
balance = db.get_balance(telegram_id)
|
|
return DigisellerFulfillResult(
|
|
ok=True,
|
|
already=True,
|
|
message="Этот платёж уже был зачислен ранее.",
|
|
amount=amount,
|
|
balance=balance,
|
|
telegram_id=telegram_id,
|
|
inv=purchase.inv,
|
|
)
|
|
|
|
balance = db.add_balance(
|
|
telegram_id,
|
|
amount,
|
|
kind="digiseller_topup",
|
|
meta=f"digi:{purchase.inv}:{purchase.unique_code}",
|
|
)
|
|
|
|
try:
|
|
await digiseller.mark_delivered(purchase.unique_code)
|
|
except DigisellerError as exc:
|
|
logger.warning(
|
|
"Digiseller deliver failed inv=%s: %s", purchase.inv, exc
|
|
)
|
|
|
|
text = (
|
|
f"✅ <b>Баланс пополнен через Digiseller</b>\n\n"
|
|
f"+{amount:.0f} ₽\n"
|
|
f"Счёт Digiseller: <code>{purchase.inv}</code>\n"
|
|
f"Доступно: <b>{balance:.0f} ₽</b>"
|
|
)
|
|
|
|
if notify:
|
|
try:
|
|
settings = get_settings(require_bot_token=False)
|
|
await notify_important(
|
|
db=db,
|
|
settings=settings,
|
|
telegram_id=telegram_id,
|
|
html=text,
|
|
subject="Баланс пополнен — VPN Service",
|
|
bot=bot,
|
|
)
|
|
except Exception as exc: # noqa: BLE001
|
|
logger.warning(
|
|
"Digiseller notify failed tg=%s: %s", telegram_id, exc
|
|
)
|
|
|
|
return DigisellerFulfillResult(
|
|
ok=True,
|
|
message=text,
|
|
amount=amount,
|
|
balance=balance,
|
|
telegram_id=telegram_id,
|
|
inv=purchase.inv,
|
|
)
|