mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-20 11:50:27 +00:00
162 lines
6.5 KiB
Python
162 lines
6.5 KiB
Python
import logging
|
||
import hashlib
|
||
import hmac
|
||
import json
|
||
from typing import Optional, Dict, Any
|
||
|
||
from app.config import settings
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class TributeService:
|
||
|
||
def __init__(self):
|
||
self.api_key = settings.TRIBUTE_API_KEY
|
||
self.donate_link = settings.TRIBUTE_DONATE_LINK
|
||
|
||
async def create_payment_link(
|
||
self,
|
||
user_id: int,
|
||
amount_kopeks: int = 0,
|
||
description: str = "Пополнение баланса"
|
||
) -> Optional[str]:
|
||
|
||
if not settings.TRIBUTE_ENABLED:
|
||
logger.warning("Tribute платежи отключены")
|
||
return None
|
||
|
||
try:
|
||
payment_url = f"{self.donate_link}&user_id={user_id}"
|
||
|
||
logger.info(f"Создана ссылка Tribute для пользователя {user_id}")
|
||
return payment_url
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка создания Tribute ссылки: {e}")
|
||
return None
|
||
|
||
def verify_webhook_signature(self, payload: str, signature: str) -> bool:
|
||
|
||
if not self.api_key:
|
||
logger.warning("API key не настроен, пропускаем проверку")
|
||
return True
|
||
|
||
try:
|
||
expected_signature = hmac.new(
|
||
self.api_key.encode(),
|
||
payload.encode(),
|
||
hashlib.sha256
|
||
).hexdigest()
|
||
|
||
is_valid = hmac.compare_digest(signature, expected_signature)
|
||
|
||
if is_valid:
|
||
logger.info("✅ Подпись Tribute webhook проверена успешно")
|
||
else:
|
||
logger.error("❌ Неверная подпись Tribute webhook")
|
||
|
||
return is_valid
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка проверки подписи webhook: {e}")
|
||
return False
|
||
|
||
async def process_webhook(self, payload_or_data) -> Optional[Dict[str, Any]]:
|
||
|
||
try:
|
||
logger.info(f"🔄 Начинаем обработку Tribute webhook")
|
||
|
||
if isinstance(payload_or_data, str):
|
||
try:
|
||
webhook_data = json.loads(payload_or_data)
|
||
logger.info(f"📊 Распарсенные данные: {webhook_data}")
|
||
except json.JSONDecodeError as e:
|
||
logger.error(f"❌ Ошибка парсинга JSON: {e}")
|
||
return None
|
||
else:
|
||
webhook_data = payload_or_data
|
||
|
||
payment_id = None
|
||
status = None
|
||
amount_kopeks = 0
|
||
telegram_user_id = None
|
||
|
||
payment_id = webhook_data.get("id") or webhook_data.get("payment_id")
|
||
status = webhook_data.get("status")
|
||
amount_kopeks = webhook_data.get("amount", 0)
|
||
telegram_user_id = webhook_data.get("telegram_user_id") or webhook_data.get("user_id")
|
||
|
||
if not payment_id and "payload" in webhook_data:
|
||
data = webhook_data["payload"]
|
||
payment_id = data.get("id") or data.get("payment_id")
|
||
status = data.get("status")
|
||
amount_kopeks = data.get("amount", 0)
|
||
telegram_user_id = data.get("telegram_user_id") or data.get("user_id")
|
||
|
||
if not payment_id and "name" in webhook_data:
|
||
event_name = webhook_data.get("name")
|
||
data = webhook_data.get("payload", {})
|
||
payment_id = str(data.get("donation_request_id"))
|
||
amount_kopeks = data.get("amount", 0)
|
||
telegram_user_id = data.get("telegram_user_id")
|
||
|
||
if event_name == "new_donation":
|
||
status = "paid"
|
||
elif event_name == "cancelled_subscription":
|
||
status = "cancelled"
|
||
else:
|
||
status = "unknown"
|
||
|
||
logger.info(f"📝 Извлеченные данные: payment_id={payment_id}, status={status}, amount_kopeks={amount_kopeks}, user_id={telegram_user_id}")
|
||
|
||
if not telegram_user_id:
|
||
logger.error("❌ Не найден telegram_user_id в webhook данных")
|
||
logger.error(f"🔍 Полные данные для отладки: {json.dumps(webhook_data, ensure_ascii=False, indent=2)}")
|
||
return None
|
||
|
||
try:
|
||
telegram_user_id = int(telegram_user_id)
|
||
except (ValueError, TypeError):
|
||
logger.error(f"❌ Некорректный telegram_user_id: {telegram_user_id}")
|
||
return None
|
||
|
||
result = {
|
||
"event_type": "payment",
|
||
"payment_id": payment_id or f"tribute_{telegram_user_id}_{amount_kopeks}",
|
||
"user_id": telegram_user_id,
|
||
"amount_kopeks": int(amount_kopeks) if amount_kopeks else 0,
|
||
"status": status or "paid",
|
||
"external_id": f"donation_{payment_id or 'unknown'}",
|
||
"payment_system": "tribute"
|
||
}
|
||
|
||
logger.info(f"✅ Tribute webhook обработан успешно: {result}")
|
||
return result
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ Ошибка обработки Tribute webhook: {e}", exc_info=True)
|
||
logger.error(f"🔍 Webhook data для отладки: {json.dumps(webhook_data, ensure_ascii=False, indent=2)}")
|
||
return None
|
||
|
||
async def get_payment_status(self, payment_id: str) -> Optional[Dict[str, Any]]:
|
||
try:
|
||
logger.info(f"Запрос статуса платежа {payment_id}")
|
||
return {"status": "unknown", "payment_id": payment_id}
|
||
except Exception as e:
|
||
logger.error(f"Ошибка получения статуса платежа: {e}")
|
||
return None
|
||
|
||
async def refund_payment(
|
||
self,
|
||
payment_id: str,
|
||
amount_kopeks: Optional[int] = None,
|
||
reason: str = "Возврат по запросу"
|
||
) -> Optional[Dict[str, Any]]:
|
||
try:
|
||
logger.info(f"Создание возврата для платежа {payment_id}")
|
||
return {"refund_id": f"refund_{payment_id}", "status": "pending"}
|
||
except Exception as e:
|
||
logger.error(f"Ошибка создания возврата: {e}")
|
||
return None
|