Crypto payment via Heleket
This commit is contained in:
@@ -7,13 +7,16 @@ from config import TBLOCKER_WEBHOOK_PATH
|
||||
from .tblocker import tblocker_webhook
|
||||
from .wata_payment import wata_payment_webhook
|
||||
from .kassai_payment import kassai_payment_webhook
|
||||
from .heleket_payment import heleket_payment_webhook
|
||||
|
||||
|
||||
WATA_WEBHOOK_PATH = "/wata/webhook"
|
||||
KASSAI_WEBHOOK_PATH = "/kassai/webhook"
|
||||
HELEKET_WEBHOOK_PATH = "/heleket/webhook"
|
||||
|
||||
|
||||
async def register_web_routes(router: UrlDispatcher) -> None:
|
||||
router.add_post(TBLOCKER_WEBHOOK_PATH, tblocker_webhook)
|
||||
router.add_post(WATA_WEBHOOK_PATH, wata_payment_webhook)
|
||||
router.add_post(KASSAI_WEBHOOK_PATH, kassai_payment_webhook)
|
||||
router.add_post(HELEKET_WEBHOOK_PATH, heleket_payment_webhook)
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import hashlib
|
||||
import base64
|
||||
import json
|
||||
from aiohttp import web
|
||||
from database import add_payment, async_session_maker, update_balance
|
||||
from handlers.payments.utils import send_payment_success_notification
|
||||
from config import HELEKET_API_KEY, HELEKET_CURRENCY_RATE
|
||||
from logger import logger
|
||||
|
||||
processed_payments = set()
|
||||
|
||||
|
||||
async def heleket_payment_webhook(request: web.Request):
|
||||
"""
|
||||
Обработчик вебхука от Heleket для подтверждения оплаты
|
||||
"""
|
||||
try:
|
||||
data = await request.json()
|
||||
|
||||
logger.info(f"Heleket webhook received from {request.remote}")
|
||||
logger.info(f"Heleket webhook data: {data}")
|
||||
|
||||
signature = data.get("sign", "")
|
||||
if not verify_heleket_webhook_signature(data, signature):
|
||||
logger.error("Heleket: Invalid signature")
|
||||
return web.Response(status=400, text="Invalid signature")
|
||||
|
||||
uuid = data.get("uuid")
|
||||
order_id = data.get("order_id")
|
||||
payment_status = data.get("status")
|
||||
amount = data.get("amount")
|
||||
payment_amount = data.get("payment_amount")
|
||||
additional_data = data.get("additional_data", "")
|
||||
|
||||
logger.info(f"Heleket payment: uuid={uuid}, order_id={order_id}, status={payment_status}, amount={amount}, payment_amount={payment_amount}")
|
||||
|
||||
if payment_status != "paid":
|
||||
logger.info(f"Heleket: Payment not completed, status={payment_status}")
|
||||
return web.Response(status=200, text="OK")
|
||||
|
||||
if not amount or not uuid:
|
||||
logger.error(f"Heleket: Missing amount or uuid")
|
||||
return web.Response(status=400, text="Missing required fields")
|
||||
|
||||
if uuid in processed_payments:
|
||||
logger.warning(f"Heleket: Duplicate payment uuid={uuid}")
|
||||
return web.Response(status=200, text="OK")
|
||||
|
||||
if order_id and order_id in processed_payments:
|
||||
logger.warning(f"Heleket: Duplicate payment order_id={order_id}")
|
||||
return web.Response(status=200, text="OK")
|
||||
|
||||
tg_id = None
|
||||
rub_amount = None
|
||||
try:
|
||||
if additional_data and "tg_id:" in additional_data:
|
||||
parts = additional_data.split(",")
|
||||
for part in parts:
|
||||
if part.startswith("tg_id:"):
|
||||
tg_id = int(part.split("tg_id:")[1])
|
||||
elif part.startswith("rub_amount:"):
|
||||
rub_amount = float(part.split("rub_amount:")[1])
|
||||
|
||||
elif order_id and "_" in order_id:
|
||||
tg_id = int(order_id.split("_")[1])
|
||||
usd_amount = float(amount)
|
||||
rub_amount = usd_amount * HELEKET_CURRENCY_RATE
|
||||
else:
|
||||
logger.error(f"Heleket: Cannot extract tg_id from data")
|
||||
return web.Response(status=400, text="Cannot extract user ID")
|
||||
|
||||
except (ValueError, IndexError) as e:
|
||||
logger.error(f"Heleket: Error extracting tg_id or rub_amount: {e}")
|
||||
return web.Response(status=400, text="Invalid user ID or amount format")
|
||||
|
||||
if not rub_amount:
|
||||
logger.error(f"Heleket: Could not determine rub_amount")
|
||||
return web.Response(status=400, text="Cannot determine amount")
|
||||
|
||||
async with async_session_maker() as session:
|
||||
await update_balance(session, tg_id, rub_amount)
|
||||
await send_payment_success_notification(tg_id, rub_amount, session)
|
||||
await add_payment(session, tg_id, rub_amount, "heleket")
|
||||
|
||||
processed_payments.add(uuid)
|
||||
if order_id:
|
||||
processed_payments.add(order_id)
|
||||
|
||||
logger.info(f"✅ Heleket: Payment processed for user {tg_id}, amount {rub_amount} RUB (${amount}), uuid={uuid}")
|
||||
return web.Response(status=200, text="OK")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Heleket webhook error: {e}")
|
||||
return web.Response(status=500, text="Internal server error")
|
||||
|
||||
|
||||
def verify_heleket_webhook_signature(data: dict, signature: str) -> bool:
|
||||
"""
|
||||
Проверка подписи вебхука Heleket
|
||||
"""
|
||||
try:
|
||||
data_without_sign = {k: v for k, v in data.items() if k != "sign"}
|
||||
|
||||
json_data = json.dumps(data_without_sign, separators=(',', ':'))
|
||||
base64_data = base64.b64encode(json_data.encode('utf-8')).decode('utf-8')
|
||||
sign_string = base64_data + HELEKET_API_KEY
|
||||
expected_signature = hashlib.md5(sign_string.encode('utf-8')).hexdigest()
|
||||
|
||||
result = signature.upper() == expected_signature.upper()
|
||||
|
||||
if not result:
|
||||
logger.error(f"Heleket webhook signature mismatch")
|
||||
logger.error(f"Expected: {expected_signature}, Got: {signature}")
|
||||
logger.error(f"Base64 data: {base64_data}")
|
||||
logger.error(f"Sign string: {sign_string}")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Heleket signature verification error: {e}")
|
||||
return False
|
||||
Reference in New Issue
Block a user