Files
2026-04-14 07:19:14 +00:00

85 lines
2.8 KiB
Python

import hashlib
from aiohttp import web
from config import KASSAI_SECRET_KEY, KASSAI_SHOP_ID, KASSAI_WEBHOOK_RESPONSE
from core.webhook_abuse import (
get_webhook_client_ip,
is_webhook_ip_blocked,
record_webhook_signature_failure,
)
from logger import logger
from services.payments.pipeline import ParsedPayment, process_success_payment
_PROVIDER = "kassai"
def verify_kassai_signature(data: dict, signature: str) -> bool:
"""Проверяет MD5-подпись webhook от KassaAI."""
try:
sign_string = (
f"{KASSAI_SHOP_ID}:{data.get('AMOUNT', '')}:{KASSAI_SECRET_KEY}:{data.get('MERCHANT_ORDER_ID', '')}"
)
expected_signature = hashlib.md5(sign_string.encode("utf-8")).hexdigest()
result = signature.upper() == expected_signature.upper()
if not result:
logger.error(f"KassaAI signature mismatch. Expected: {expected_signature}, Got: {signature}")
else:
logger.info("KassaAI webhook: подпись успешно проверена")
return result
except Exception as e:
logger.error(f"Ошибка проверки подписи KassaAI: {e}")
return False
def _parse_kassai(data) -> ParsedPayment | None:
amount_raw = data.get("AMOUNT")
order_id = data.get("MERCHANT_ORDER_ID")
if not amount_raw or not order_id:
return None
try:
tg_id = int(str(order_id).split("_")[1])
amount = float(amount_raw)
except (IndexError, ValueError) as e:
logger.error(f"KassaAI webhook: не удалось извлечь tg_id/amount из order_id={order_id}: {e}")
return None
return ParsedPayment(
payment_id=str(order_id),
tg_id=tg_id,
amount=amount,
currency="RUB",
)
async def kassai_webhook(request: web.Request):
try:
ip = get_webhook_client_ip(request)
if await is_webhook_ip_blocked(ip):
return web.Response(status=429)
data = await request.post()
logger.info(f"KassaAI webhook received: {dict(data)}")
signature = data.get("SIGN", "")
if not signature:
await record_webhook_signature_failure(ip)
return web.Response(status=400)
if not verify_kassai_signature(data, signature):
await record_webhook_signature_failure(ip)
return web.Response(status=400)
parsed = _parse_kassai(data)
if parsed is None:
return web.Response(status=400)
result = await process_success_payment(_PROVIDER, parsed)
if not result.ok:
return web.Response(status=500)
return web.Response(text=KASSAI_WEBHOOK_RESPONSE)
except Exception as e:
logger.error(f"Ошибка обработки KassaAI webhook: {e}")
return web.Response(status=500)