93 lines
2.7 KiB
Python
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"
|