import hashlib import logging import math from aiogram import Bot from fastapi import Depends, Form, HTTPException from fastapi.routing import APIRouter from sqlalchemy.ext.asyncio import AsyncSession from api.core.deps import get_bot, get_db, get_deps from api.core.notifications import successful_payment_notification from config import settings from db.models.bills import DepositStatus from db.models.transactions import BalanceTxType from misc.pally import BillStatus from schemas.di import APIDependenciesDTO router = APIRouter(prefix="/checkout/pally") logger = logging.getLogger(__name__) @router.post("/callback") async def pally_callback( id: str = Form(...), bill_id: str = Form(...), status: str = Form(...), req_amount: str = Form(...), currency: str = Form(...), order_id: str | None = Form(None), signature: str = Form(...), session: AsyncSession = Depends(get_db), bot: Bot = Depends(get_bot), deps: APIDependenciesDTO = Depends(get_deps), ): oid = order_id or "" raw_string = f"{id}{status}{req_amount}{currency}{oid}{settings.pally_token}" md5_hash = hashlib.md5(raw_string.encode("utf-8")).hexdigest() expected_signature = hashlib.sha1(md5_hash.encode("utf-8")).hexdigest() if signature != expected_signature: logger.critical("Invalid signature for bill %s", bill_id) raise HTTPException(403, detail="Invalid signature.") if status != BillStatus.SUCCESS or not req_amount.isdigit(): logger.info("Bill %s skipped: status=%s, req_amount=%s", bill_id, status, req_amount) return "OK" logger.info("Received successfully paid bill %s", bill_id) if not oid.isdigit(): logger.critical("Invalid order_id format for bill %s", bill_id) return "OK" bill = await deps.bills_repository.get_bill_by_id(session, int(oid)) if not bill: logger.critical("Bill %s is not found in db", bill_id) return "OK" if bill.status != DepositStatus.PENDING: logger.warning("bill=%s is already paid according to the db", bill.id) return "OK" try: amount = int(req_amount) if bill.amount != amount: logger.warning("requested amount for bill=%s is not equal to db entry", bill.id) return "OK" await deps.user_repository.increase_user_balance( session, bill.creator_id, amount=amount, tx_type=BalanceTxType.DEPOSIT, description="personal balance deposit via PALLY", ) await successful_payment_notification(bot, bill.creator_id, amount) user = await deps.user_repository.get_user_by_id(session, bill.creator_id) referal = user.referal_id if user else None if referal is not None: referal_amount = math.floor(amount * (settings.referal_bonus / 100)) await deps.user_repository.increase_user_balance( session, referal, referal_amount, tx_type=BalanceTxType.REFERRAL_BONUS, description=f"referal reward for referal_id={bill.creator_id}", ) await successful_payment_notification(bot, referal, referal_amount) except Exception: logger.exception( "Exception caught while processing balances for bill %s (Creator: %s)", bill_id, bill.creator_id, ) await deps.bills_repository.update_bill_status( session, int(bill.id), status=DepositStatus.SUCCESS ) return "OK"