Files
malenia/api/routes/pally.py

158 lines
5.7 KiB
Python

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="/pally")
logger = logging.getLogger(__name__)
@router.post("/result")
async def pally_callback(
InvId: str = Form(...),
OutSum: str = Form(...),
Commission: str = Form(...),
TrsId: str = Form(...),
Status: str = Form(...),
CurrencyIn: str = Form(...),
custom: str | None = Form(None),
SignatureValue: str = Form(...),
# Optional fields for additional information
AccountType: str | None = Form(None),
AccountNumber: str | None = Form(None),
BalanceAmount: str | None = Form(None),
BalanceCurrency: str | None = Form(None),
PayerPhone: str | None = Form(None),
PayerEmail: str | None = Form(None),
PayerName: str | None = Form(None),
PayerComment: str | None = Form(None),
ErrorCode: int | None = Form(None),
ErrorMessage: str | None = Form(None),
session: AsyncSession = Depends(get_db),
bot: Bot = Depends(get_bot),
deps: APIDependenciesDTO = Depends(get_deps),
):
oid = custom or ""
logger.info(
"Pally webhook received - InvId: %s, OutSum: %s, Commission: %s, TrsId: %s, Status: %s, "
"CurrencyIn: %s, custom: %s, SignatureValue: %s",
InvId, OutSum, Commission, TrsId, Status, CurrencyIn, custom, SignatureValue
)
# Enhanced logging for failed payments
if Status != BillStatus.SUCCESS:
logger.warning(
"Non-success payment received - Status: %s, ErrorCode: %s, ErrorMessage: %s, TrsId: %s",
Status, ErrorCode, ErrorMessage, TrsId
)
# Validate signature
raw_string = f"{OutSum}:{InvId}:{settings.pally_token}"
expected_signature = hashlib.md5(raw_string.encode("utf-8")).hexdigest().upper()
logger.debug("Signature validation - Expected: %s, Received: %s, Raw: %s",
expected_signature, SignatureValue, raw_string)
if SignatureValue != expected_signature:
logger.critical(
"SECURITY ALERT: Invalid signature for TrsId %s - Expected: %s, Received: %s, Raw: %s",
TrsId, expected_signature, SignatureValue, raw_string
)
raise HTTPException(403, detail="Invalid signature.")
# Only process successful payments
if Status != BillStatus.SUCCESS:
logger.info("Bill %s skipped: status=%s", TrsId, Status)
return "OK"
logger.info("Processing successfully paid bill %s", TrsId)
# Validate custom field (order ID)
if not oid or not oid.isdigit():
logger.critical("Invalid or missing custom field for TrsId %s: '%s'", TrsId, custom)
return "OK"
# Find bill in database
bill = await deps.bills_repository.get_bill_by_id(session, int(oid))
if not bill:
logger.critical("Bill %s not found in database for TrsId %s", oid, TrsId)
return "OK"
# Check if already processed
if bill.status != DepositStatus.PENDING:
logger.warning("Bill %s (TrsId: %s) is already processed with status: %s",
bill.id, TrsId, bill.status)
return "OK"
try:
amount = int(float(OutSum))
# Validate payment amount
if bill.amount != amount:
logger.error(
"Amount mismatch for bill %s (TrsId: %s) - Expected: %s, Received: %s",
bill.id, TrsId, bill.amount, amount
)
return "OK"
logger.info("Processing payment: bill_id=%s, user_id=%s, amount=%s",
bill.id, bill.creator_id, amount)
# Credit user balance
await deps.user_repository.increase_user_balance(
session,
bill.creator_id,
amount=amount,
tx_type=BalanceTxType.DEPOSIT,
description=f"payment via PALLY (TrsId: {TrsId})",
)
await successful_payment_notification(bot, bill.creator_id, amount)
# Process referral bonus
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"referral reward for user {bill.creator_id} (TrsId: {TrsId})",
)
await successful_payment_notification(bot, referal, referal_amount)
logger.info("Referral bonus processed: referrer_id=%s, amount=%s", referal, referal_amount)
logger.info("Payment processing completed successfully for TrsId %s", TrsId)
except Exception as e:
logger.exception(
"CRITICAL ERROR processing payment for TrsId %s, bill_id %s, user_id %s: %s",
TrsId, bill.id, bill.creator_id, str(e)
)
# Don't return early - still mark as success to prevent retries
# The balance operation might have partially succeeded
# Update bill status to success
await deps.bills_repository.update_bill_status(
session, int(bill.id), status=DepositStatus.SUCCESS
)
logger.info("Bill %s marked as SUCCESS for TrsId %s", bill.id, TrsId)
return "OK"