"""Heleket payment integration.""" import base64 import hashlib import json import logging from typing import Optional import aiohttp from config.settings import settings logger = logging.getLogger(__name__) def _make_sign(body_bytes: bytes, api_key: str) -> str: """Генерирует подпись запроса: md5(base64(body) + api_key).""" b64 = base64.b64encode(body_bytes).decode("utf-8") return hashlib.md5((b64 + api_key).encode("utf-8")).hexdigest() class HelketPayment: """Heleket payment gateway. Принимает сумму в рублях напрямую.""" BASE_URL = "https://api.heleket.com/v1" def __init__(self): self.api_key = settings.HELEKET_API_KEY def _post_headers(self, body_bytes: bytes) -> dict: """Заголовки для POST-запроса с подписью.""" return { "merchant": self.api_key, "sign": _make_sign(body_bytes, self.api_key), "Content-Type": "application/json", } def _get_headers(self) -> dict: """Заголовки для GET-запроса (тело пустое).""" return { "merchant": self.api_key, "sign": _make_sign(b"", self.api_key), } async def create_invoice(self, amount_rub: int, order_id: str) -> Optional[dict]: """ Создаёт счёт на сумму в рублях. Возвращает dict: payment_id, address, url. None при ошибке. """ if not self.api_key: logger.error("HELEKET_API_KEY не задан") return None payload = { "amount": str(amount_rub), "currency": "RUB", "order_id": order_id, "url_callback": settings.HELEKET_WEBHOOK_URL, } body = json.dumps(payload, separators=(",", ":")).encode("utf-8") try: async with aiohttp.ClientSession() as session: async with session.post( f"{self.BASE_URL}/payment", data=body, headers=self._post_headers(body), timeout=aiohttp.ClientTimeout(total=15), ) as resp: data = await resp.json() if data.get("state") == 0: r = data.get("result", {}) return { "payment_id": r.get("uuid"), "address": r.get("address"), "url": r.get("url"), "mock": False, } logger.error(f"Heleket create error: {data}") return None except Exception as e: logger.error(f"Heleket request error: {e}") return None async def check_status(self, payment_id: str) -> str: """Возвращает: 'paid' | 'pending' | 'expired' | 'error'""" try: async with aiohttp.ClientSession() as session: async with session.get( f"{self.BASE_URL}/payment/info/{payment_id}", headers=self._get_headers(), timeout=aiohttp.ClientTimeout(total=15), ) as resp: data = await resp.json() if data.get("state") == 0: status = data.get("result", {}).get("status", "") if status in ("paid", "paid_over"): return "paid" if status in ("cancel", "expired", "fail", "wrong_amount"): return "expired" return "pending" return "error" except Exception as e: logger.error(f"Heleket check error: {e}") return "error"