Files
malenia-backend/routes/payments/pally.py

93 lines
2.7 KiB
Python

# ruff: noqa: N803
import logging
from fastapi import Depends, Form, HTTPException
from fastapi.routing import APIRouter
from sqlalchemy.ext.asyncio import AsyncSession
from core.deps import get_db
from external.pally import BillStatus
from services.payments import process_subscription_purchase, validate_pally_signature
router = APIRouter(prefix="/payments/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(...),
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),
):
logger.info(
"Pally webhook received - InvId: %s, OutSum: %s, Commission: %s, TrsId: %s, Status: %s, "
"CurrencyIn: %s, custom: %s, BalanceAmount: %s, SignatureValue: %s",
InvId,
OutSum,
Commission,
TrsId,
Status,
CurrencyIn,
custom,
BalanceAmount,
SignatureValue,
)
if Status != BillStatus.SUCCESS:
logger.warning(
"Non-success payment received - Status: %s, ErrorCode: %s, ErrorMessage: %s, TrsId: %s",
Status,
ErrorCode,
ErrorMessage,
TrsId,
)
if not validate_pally_signature(OutSum, InvId, SignatureValue):
logger.critical(
"SECURITY ALERT: Invalid signature for TrsId %s",
TrsId,
)
raise HTTPException(403, detail="Invalid signature.")
if Status != BillStatus.SUCCESS:
logger.info("Bill %s skipped: status=%s", TrsId, Status)
return "OK"
invoice_id_str = InvId
if not invoice_id_str or not invoice_id_str.isdigit():
logger.critical(
"Invalid or non-numeric bill ID in InvId field for TrsId %s: '%s'",
TrsId,
invoice_id_str,
)
return "OK"
amount = int(float(BalanceAmount)) if BalanceAmount is not None else int(float(OutSum))
await process_subscription_purchase(
session,
invoice_id=int(invoice_id_str),
trs_id=TrsId,
amount=amount,
)
return "OK"