mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-19 11:21:17 +00:00
Enhance Freekassa payment handling and improve Docker Compose configuration
This commit is contained in:
8
app/external/yookassa_webhook.py
vendored
8
app/external/yookassa_webhook.py
vendored
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import json
|
||||
@@ -9,13 +11,15 @@ from ipaddress import (
|
||||
ip_address,
|
||||
ip_network,
|
||||
)
|
||||
from typing import Iterable, Optional, Dict, Any, List, Union, Tuple
|
||||
from typing import Iterable, Optional, Dict, Any, List, Union, Tuple, TYPE_CHECKING
|
||||
from aiohttp import web
|
||||
|
||||
from app.config import settings
|
||||
from app.services.payment_service import PaymentService
|
||||
from app.database.database import get_db
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.services.payment_service import PaymentService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
@@ -135,6 +135,16 @@ def _status_info(
|
||||
return "✅", texts.t("ADMIN_PAYMENT_STATUS_PAID", "✅ Paid")
|
||||
return "⏳", texts.t("ADMIN_PAYMENT_STATUS_PENDING", "⏳ Pending")
|
||||
|
||||
if record.method == PaymentMethod.FREEKASSA:
|
||||
mapping = {
|
||||
"pending": ("⏳", texts.t("ADMIN_PAYMENT_STATUS_PENDING", "⏳ Pending")),
|
||||
"success": ("✅", texts.t("ADMIN_PAYMENT_STATUS_PAID", "✅ Paid")),
|
||||
"paid": ("✅", texts.t("ADMIN_PAYMENT_STATUS_PAID", "✅ Paid")),
|
||||
"canceled": ("❌", texts.t("ADMIN_PAYMENT_STATUS_CANCELED", "❌ Cancelled")),
|
||||
"error": ("❌", texts.t("ADMIN_PAYMENT_STATUS_FAILED", "❌ Failed")),
|
||||
}
|
||||
return mapping.get(status, ("❓", texts.t("ADMIN_PAYMENT_STATUS_UNKNOWN", "❓ Unknown")))
|
||||
|
||||
return "❓", texts.t("ADMIN_PAYMENT_STATUS_UNKNOWN", "❓ Unknown")
|
||||
|
||||
|
||||
@@ -158,6 +168,8 @@ def _is_checkable(record: PendingPayment) -> bool:
|
||||
return status in {"pending", "waiting_for_capture"}
|
||||
if record.method == PaymentMethod.CRYPTOBOT:
|
||||
return status in {"active"}
|
||||
if record.method == PaymentMethod.FREEKASSA:
|
||||
return status in {"pending", ""}
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -505,17 +505,7 @@ class FreekassaPaymentMixin:
|
||||
local_payment_id: int,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Проверяет статус платежа Freekassa по локальному ID.
|
||||
|
||||
Freekassa не предоставляет API для проверки статуса платежа,
|
||||
поэтому возвращаем текущее состояние из БД.
|
||||
|
||||
Args:
|
||||
db: Сессия БД
|
||||
local_payment_id: Внутренний ID платежа
|
||||
|
||||
Returns:
|
||||
Dict с информацией о платеже или None если не найден
|
||||
Проверяет статус платежа Freekassa по локальному ID через API.
|
||||
"""
|
||||
freekassa_crud = import_module("app.database.crud.freekassa")
|
||||
|
||||
@@ -524,8 +514,73 @@ class FreekassaPaymentMixin:
|
||||
logger.warning("Freekassa payment not found: id=%s", local_payment_id)
|
||||
return None
|
||||
|
||||
# Freekassa не имеет API для проверки статуса,
|
||||
# информация приходит только через webhook
|
||||
if payment.is_paid:
|
||||
return {
|
||||
"payment": payment,
|
||||
"status": "success",
|
||||
"is_paid": True,
|
||||
}
|
||||
|
||||
if not settings.FREEKASSA_API_KEY:
|
||||
return {
|
||||
"payment": payment,
|
||||
"status": payment.status or "pending",
|
||||
"is_paid": payment.is_paid,
|
||||
}
|
||||
|
||||
try:
|
||||
# Запрашиваем статус заказа в Freekassa
|
||||
response = await freekassa_service.get_order_status(payment.order_id)
|
||||
|
||||
# Freekassa возвращает список заказов
|
||||
orders = response.get("orders", [])
|
||||
target_order = None
|
||||
|
||||
# Ищем наш заказ в списке
|
||||
for order in orders:
|
||||
# В ответе API поле называется merchant_order_id, а не paymentId
|
||||
# Поддерживаем оба варианта на всякий случай
|
||||
order_key = str(order.get("merchant_order_id") or order.get("paymentId"))
|
||||
if order_key == str(payment.order_id):
|
||||
target_order = order
|
||||
break
|
||||
|
||||
if target_order:
|
||||
# Статус 1 = Оплачен
|
||||
fk_status = int(target_order.get("status", 0))
|
||||
|
||||
if fk_status == 1:
|
||||
logger.info("Freekassa payment %s confirmed via API", payment.order_id)
|
||||
|
||||
callback_payload = {
|
||||
"check_source": "api",
|
||||
"fk_order_data": target_order,
|
||||
}
|
||||
|
||||
# ID заказа на стороне FK (fk_order_id или id)
|
||||
fk_intid = str(target_order.get("fk_order_id") or target_order.get("id"))
|
||||
|
||||
# Обновляем статус
|
||||
payment = await freekassa_crud.update_freekassa_payment_status(
|
||||
db=db,
|
||||
payment=payment,
|
||||
status="success",
|
||||
is_paid=True,
|
||||
freekassa_order_id=fk_intid,
|
||||
payment_system_id=int(target_order.get("curID")) if target_order.get("curID") else None,
|
||||
callback_payload=callback_payload,
|
||||
)
|
||||
|
||||
# Финализируем
|
||||
await self._finalize_freekassa_payment(
|
||||
db,
|
||||
payment,
|
||||
intid=fk_intid,
|
||||
trigger="api_check",
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("Error checking Freekassa payment status: %s", e)
|
||||
|
||||
return {
|
||||
"payment": payment,
|
||||
"status": payment.status or "pending",
|
||||
|
||||
@@ -7,14 +7,18 @@ services:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-remnawave_bot}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-remnawave_user}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secure_password_123}
|
||||
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C"
|
||||
POSTGRES_INITDB_ARGS: '--encoding=UTF8 --locale=C'
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- bot_network
|
||||
- remnawave-network # Подключаем к сети панели
|
||||
- remnawave-network # Подключаем к сети панели
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-remnawave_user} -d ${POSTGRES_DB:-remnawave_bot}"]
|
||||
test:
|
||||
[
|
||||
'CMD-SHELL',
|
||||
'pg_isready -U ${POSTGRES_USER:-remnawave_user} -d ${POSTGRES_DB:-remnawave_bot}',
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
@@ -29,9 +33,9 @@ services:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- bot_network
|
||||
- remnawave-network # Подключаем к сети панели
|
||||
- remnawave-network # Подключаем к сети панели
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
test: ['CMD', 'redis-cli', 'ping']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
@@ -48,16 +52,16 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
DOCKER_ENV: "true"
|
||||
DATABASE_MODE: "auto"
|
||||
POSTGRES_HOST: "postgres"
|
||||
POSTGRES_PORT: "5432"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-remnawave_bot}"
|
||||
POSTGRES_USER: "${POSTGRES_USER:-remnawave_user}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-secure_password_123}"
|
||||
REDIS_URL: "redis://redis:6379/0"
|
||||
TZ: "Europe/Moscow"
|
||||
LOCALES_PATH: "${LOCALES_PATH:-/app/locales}"
|
||||
DOCKER_ENV: 'true'
|
||||
DATABASE_MODE: 'auto'
|
||||
POSTGRES_HOST: 'postgres'
|
||||
POSTGRES_PORT: '5432'
|
||||
POSTGRES_DB: '${POSTGRES_DB:-remnawave_bot}'
|
||||
POSTGRES_USER: '${POSTGRES_USER:-remnawave_user}'
|
||||
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-secure_password_123}'
|
||||
REDIS_URL: 'redis://redis:6379/0'
|
||||
TZ: 'Europe/Moscow'
|
||||
LOCALES_PATH: '${LOCALES_PATH:-/app/locales}'
|
||||
volumes:
|
||||
- ./logs:/app/logs:rw
|
||||
- ./data:/app/data:rw
|
||||
@@ -66,12 +70,16 @@ services:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./vpn_logo.png:/app/vpn_logo.png:ro
|
||||
ports:
|
||||
- "${WEB_API_PORT:-8080}:8080"
|
||||
- '${WEB_API_PORT:-8080}:8080'
|
||||
networks:
|
||||
- bot_network
|
||||
- remnawave-network # Подключаем к сети панели
|
||||
- remnawave-network # Подключаем к сети панели
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "python -c \"import requests, os; requests.get('http://localhost:8080/health', headers={'X-API-Key': os.environ.get('WEB_API_DEFAULT_TOKEN')}, timeout=5) or exit(1)\""]
|
||||
test:
|
||||
[
|
||||
'CMD-SHELL',
|
||||
'python -c "import requests, os; requests.get(''http://localhost:8080/health'', headers={''X-API-Key'': os.environ.get(''WEB_API_DEFAULT_TOKEN'')}, timeout=5) or exit(1)"',
|
||||
]
|
||||
interval: 60s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
@@ -90,7 +98,9 @@ networks:
|
||||
config:
|
||||
- subnet: 172.20.0.0/16
|
||||
gateway: 172.20.0.1
|
||||
|
||||
driver_opts:
|
||||
com.docker.network.driver.mtu: 1350
|
||||
|
||||
remnawave-network:
|
||||
name: remnawave-network
|
||||
external: true # Используем существующую сеть панели
|
||||
external: true # Используем существующую сеть панели
|
||||
|
||||
@@ -7,13 +7,17 @@ services:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-remnawave_bot}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-remnawave_user}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secure_password_123}
|
||||
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C"
|
||||
POSTGRES_INITDB_ARGS: '--encoding=UTF8 --locale=C'
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- bot_network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-remnawave_user} -d ${POSTGRES_DB:-remnawave_bot}"]
|
||||
test:
|
||||
[
|
||||
'CMD-SHELL',
|
||||
'pg_isready -U ${POSTGRES_USER:-remnawave_user} -d ${POSTGRES_DB:-remnawave_bot}',
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
@@ -29,7 +33,7 @@ services:
|
||||
networks:
|
||||
- bot_network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
test: ['CMD', 'redis-cli', 'ping']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
@@ -46,18 +50,18 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
DOCKER_ENV: "true"
|
||||
DATABASE_MODE: "auto"
|
||||
POSTGRES_HOST: "postgres"
|
||||
POSTGRES_PORT: "5432"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-remnawave_bot}"
|
||||
POSTGRES_USER: "${POSTGRES_USER:-remnawave_user}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-secure_password_123}"
|
||||
|
||||
REDIS_URL: "redis://redis:6379/0"
|
||||
|
||||
TZ: "Europe/Moscow"
|
||||
LOCALES_PATH: "${LOCALES_PATH:-/app/locales}"
|
||||
DOCKER_ENV: 'true'
|
||||
DATABASE_MODE: 'auto'
|
||||
POSTGRES_HOST: 'postgres'
|
||||
POSTGRES_PORT: '5432'
|
||||
POSTGRES_DB: '${POSTGRES_DB:-remnawave_bot}'
|
||||
POSTGRES_USER: '${POSTGRES_USER:-remnawave_user}'
|
||||
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-secure_password_123}'
|
||||
|
||||
REDIS_URL: 'redis://redis:6379/0'
|
||||
|
||||
TZ: 'Europe/Moscow'
|
||||
LOCALES_PATH: '${LOCALES_PATH:-/app/locales}'
|
||||
volumes:
|
||||
# Логи
|
||||
- ./logs:/app/logs:rw
|
||||
@@ -72,11 +76,15 @@ services:
|
||||
# Логотип для сообщений
|
||||
- ./vpn_logo.png:/app/vpn_logo.png:ro
|
||||
ports:
|
||||
- "${WEB_API_PORT:-8080}:8080"
|
||||
- '${WEB_API_PORT:-8080}:8080'
|
||||
networks:
|
||||
- bot_network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "python -c \"import requests, os; requests.get('http://localhost:8080/health', headers={'X-API-Key': os.environ.get('WEB_API_DEFAULT_TOKEN')}, timeout=5) or exit(1)\""]
|
||||
test:
|
||||
[
|
||||
'CMD-SHELL',
|
||||
'python -c "import requests, os; requests.get(''http://localhost:8080/health'', headers={''X-API-Key'': os.environ.get(''WEB_API_DEFAULT_TOKEN'')}, timeout=5) or exit(1)"',
|
||||
]
|
||||
interval: 60s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
@@ -95,3 +103,5 @@ networks:
|
||||
config:
|
||||
- subnet: 172.20.0.0/16
|
||||
gateway: 172.20.0.1
|
||||
driver_opts:
|
||||
com.docker.network.driver.mtu: 1350
|
||||
|
||||
Reference in New Issue
Block a user