Enhance Freekassa payment handling and improve Docker Compose configuration

This commit is contained in:
evansvl
2026-01-11 05:45:09 +03:00
parent edb335ee52
commit b5234f3265
5 changed files with 143 additions and 52 deletions

View File

@@ -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__)

View File

@@ -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

View File

@@ -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",

View File

@@ -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 # Используем существующую сеть панели

View File

@@ -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