feat: make MulenPay display name configurable
This commit is contained in:
@@ -212,6 +212,7 @@ class Settings(BaseSettings):
|
||||
MULENPAY_SHOP_ID: Optional[int] = None
|
||||
MULENPAY_BASE_URL: str = "https://mulenpay.ru/api"
|
||||
MULENPAY_WEBHOOK_PATH: str = "/mulenpay-webhook"
|
||||
MULENPAY_DISPLAY_NAME: str = "Mulen Pay"
|
||||
MULENPAY_DESCRIPTION: str = "Пополнение баланса"
|
||||
MULENPAY_LANGUAGE: str = "ru"
|
||||
MULENPAY_VAT_CODE: int = 0
|
||||
@@ -793,6 +794,15 @@ class Settings(BaseSettings):
|
||||
and self.MULENPAY_SHOP_ID is not None
|
||||
)
|
||||
|
||||
def get_mulenpay_display_name(self) -> str:
|
||||
name = (self.MULENPAY_DISPLAY_NAME or "").strip()
|
||||
if not name:
|
||||
return "Mulen Pay"
|
||||
return name
|
||||
|
||||
def get_mulenpay_display_name_html(self) -> str:
|
||||
return html.escape(self.get_mulenpay_display_name())
|
||||
|
||||
def is_pal24_enabled(self) -> bool:
|
||||
return (
|
||||
self.PAL24_ENABLED
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Optional
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.config import settings
|
||||
from app.database.models import MulenPayPayment
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -40,7 +41,8 @@ async def create_mulenpay_payment(
|
||||
await db.refresh(payment)
|
||||
|
||||
logger.info(
|
||||
"Создан MulenPay платеж #%s (uuid=%s) на сумму %s копеек для пользователя %s",
|
||||
"Создан %s платеж #%s (uuid=%s) на сумму %s копеек для пользователя %s",
|
||||
settings.get_mulenpay_display_name(),
|
||||
payment.mulen_payment_id,
|
||||
uuid,
|
||||
amount_kopeks,
|
||||
|
||||
@@ -3427,18 +3427,19 @@ async def run_universal_migration():
|
||||
else:
|
||||
logger.warning("⚠️ Проблемы с таблицей CryptoBot payments")
|
||||
|
||||
logger.info("=== СОЗДАНИЕ ТАБЛИЦЫ MULEN PAY ===")
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
logger.info("=== СОЗДАНИЕ ТАБЛИЦЫ %s ===", mulenpay_name)
|
||||
mulenpay_created = await create_mulenpay_payments_table()
|
||||
if mulenpay_created:
|
||||
logger.info("✅ Таблица Mulen Pay payments готова")
|
||||
logger.info("✅ Таблица %s payments готова", mulenpay_name)
|
||||
else:
|
||||
logger.warning("⚠️ Проблемы с таблицей Mulen Pay payments")
|
||||
logger.warning("⚠️ Проблемы с таблицей %s payments", mulenpay_name)
|
||||
|
||||
mulenpay_schema_ok = await ensure_mulenpay_payment_schema()
|
||||
if mulenpay_schema_ok:
|
||||
logger.info("✅ Схема Mulen Pay payments актуальна")
|
||||
logger.info("✅ Схема %s payments актуальна", mulenpay_name)
|
||||
else:
|
||||
logger.warning("⚠️ Не удалось обновить схему Mulen Pay payments")
|
||||
logger.warning("⚠️ Не удалось обновить схему %s payments", mulenpay_name)
|
||||
|
||||
logger.info("=== СОЗДАНИЕ ТАБЛИЦЫ PAL24 ===")
|
||||
pal24_created = await create_pal24_payments_table()
|
||||
|
||||
Vendored
+44
-15
@@ -48,7 +48,12 @@ class WebhookServer:
|
||||
logger.info(f"Webhook сервер настроен:")
|
||||
logger.info(f" - Tribute webhook: POST {settings.TRIBUTE_WEBHOOK_PATH}")
|
||||
if settings.is_mulenpay_enabled():
|
||||
logger.info(f" - Mulen Pay webhook: POST {settings.MULENPAY_WEBHOOK_PATH}")
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
logger.info(
|
||||
" - %s webhook: POST %s",
|
||||
mulenpay_name,
|
||||
settings.MULENPAY_WEBHOOK_PATH,
|
||||
)
|
||||
if settings.is_cryptobot_enabled():
|
||||
logger.info(f" - CryptoBot webhook: POST {settings.CRYPTOBOT_WEBHOOK_PATH}")
|
||||
logger.info(f" - Health check: GET /health")
|
||||
@@ -84,8 +89,10 @@ class WebhookServer:
|
||||
settings.TRIBUTE_WEBHOOK_PATH,
|
||||
)
|
||||
if settings.is_mulenpay_enabled():
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
logger.info(
|
||||
"Mulen Pay webhook URL: http://%s:%s%s",
|
||||
"%s webhook URL: http://%s:%s%s",
|
||||
mulenpay_name,
|
||||
settings.TRIBUTE_WEBHOOK_HOST,
|
||||
settings.TRIBUTE_WEBHOOK_PORT,
|
||||
settings.MULENPAY_WEBHOOK_PATH,
|
||||
@@ -128,24 +135,37 @@ class WebhookServer:
|
||||
|
||||
async def _mulenpay_webhook_handler(self, request: web.Request) -> web.Response:
|
||||
try:
|
||||
logger.info(f"Получен Mulen Pay webhook: {request.method} {request.path}")
|
||||
logger.info(f"MulenPay webhook headers: {dict(request.headers)}")
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
logger.info(
|
||||
"%s webhook: %s %s",
|
||||
mulenpay_name,
|
||||
request.method,
|
||||
request.path,
|
||||
)
|
||||
logger.info(
|
||||
"%s webhook headers: %s",
|
||||
mulenpay_name,
|
||||
dict(request.headers),
|
||||
)
|
||||
raw_body = await request.read()
|
||||
|
||||
if not raw_body:
|
||||
logger.warning("Пустой Mulen Pay webhook")
|
||||
logger.warning("Пустой %s webhook", mulenpay_name)
|
||||
return web.json_response({"status": "error", "reason": "empty_body"}, status=400)
|
||||
|
||||
# Временно отключаем проверку подписи для отладки
|
||||
# TODO: Включить обратно после настройки MulenPay
|
||||
if not self._verify_mulenpay_signature(request, raw_body):
|
||||
logger.warning("MulenPay webhook signature verification failed, but processing anyway for debugging")
|
||||
logger.warning(
|
||||
"%s webhook signature verification failed, but processing anyway for debugging",
|
||||
mulenpay_name,
|
||||
)
|
||||
# return web.json_response({"status": "error", "reason": "invalid_signature"}, status=401)
|
||||
|
||||
try:
|
||||
payload = json.loads(raw_body.decode('utf-8'))
|
||||
except json.JSONDecodeError as error:
|
||||
logger.error(f"Ошибка парсинга Mulen Pay webhook: {error}")
|
||||
logger.error(f"Ошибка парсинга {mulenpay_name} webhook: {error}")
|
||||
return web.json_response({"status": "error", "reason": "invalid_json"}, status=400)
|
||||
|
||||
payment_service = PaymentService(self.bot)
|
||||
@@ -160,7 +180,10 @@ class WebhookServer:
|
||||
return web.json_response({"status": "ok"}, status=200)
|
||||
return web.json_response({"status": "error", "reason": "processing_failed"}, status=400)
|
||||
except Exception as error:
|
||||
logger.error(f"Ошибка обработки Mulen Pay webhook: {error}", exc_info=True)
|
||||
logger.error(
|
||||
f"Ошибка обработки {mulenpay_name} webhook: {error}",
|
||||
exc_info=True,
|
||||
)
|
||||
return web.json_response({"status": "error", "reason": "internal_error"}, status=500)
|
||||
finally:
|
||||
try:
|
||||
@@ -169,7 +192,11 @@ class WebhookServer:
|
||||
pass
|
||||
|
||||
except Exception as error:
|
||||
logger.error(f"Критическая ошибка Mulen Pay webhook: {error}", exc_info=True)
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
logger.error(
|
||||
f"Критическая ошибка {mulenpay_name} webhook: {error}",
|
||||
exc_info=True,
|
||||
)
|
||||
return web.json_response({"status": "error", "reason": "internal_error", "message": str(error)}, status=500)
|
||||
|
||||
@staticmethod
|
||||
@@ -183,12 +210,13 @@ class WebhookServer:
|
||||
@staticmethod
|
||||
def _verify_mulenpay_signature(request: web.Request, raw_body: bytes) -> bool:
|
||||
secret_key = settings.MULENPAY_SECRET_KEY
|
||||
display_name = settings.get_mulenpay_display_name()
|
||||
if not secret_key:
|
||||
logger.error("Mulen Pay secret key is not configured")
|
||||
logger.error("%s secret key is not configured", display_name)
|
||||
return False
|
||||
|
||||
# Логируем все заголовки для отладки
|
||||
logger.info("MulenPay webhook headers for signature verification:")
|
||||
logger.info("%s webhook headers for signature verification:", display_name)
|
||||
for header_name, header_value in request.headers.items():
|
||||
if any(keyword in header_name.lower() for keyword in ['signature', 'sign', 'token', 'auth']):
|
||||
logger.info(f" {header_name}: {header_value}")
|
||||
@@ -239,7 +267,7 @@ class WebhookServer:
|
||||
if hmac.compare_digest(normalized_signature_no_padding, expected_urlsafe_base64_signature.rstrip('=')):
|
||||
return True
|
||||
|
||||
logger.error("Неверная подпись Mulen Pay webhook")
|
||||
logger.error("Неверная подпись %s webhook", display_name)
|
||||
return False
|
||||
|
||||
authorization_header = request.headers.get('Authorization')
|
||||
@@ -252,7 +280,7 @@ class WebhookServer:
|
||||
if hmac.compare_digest(token, secret_key):
|
||||
return True
|
||||
|
||||
logger.error("Неверный %s токен Mulen Pay webhook", scheme)
|
||||
logger.error("Неверный %s токен %s webhook", scheme, display_name)
|
||||
return False
|
||||
|
||||
if not value and hmac.compare_digest(token, secret_key):
|
||||
@@ -270,11 +298,12 @@ class WebhookServer:
|
||||
return True
|
||||
|
||||
logger.info(
|
||||
"Mulen Pay webhook headers received: %s",
|
||||
"%s webhook headers received: %s",
|
||||
display_name,
|
||||
{key: value for key, value in request.headers.items() if 'authorization' not in key.lower()}
|
||||
)
|
||||
|
||||
logger.error("Отсутствует подпись Mulen Pay webhook")
|
||||
logger.error("Отсутствует подпись %s webhook", display_name)
|
||||
return False
|
||||
|
||||
async def _tribute_webhook_handler(self, request: web.Request) -> web.Response:
|
||||
|
||||
@@ -1303,7 +1303,10 @@ def _build_settings_keyboard(
|
||||
label = texts.t("PAYMENT_CARD_TRIBUTE", "💳 Банковская карта (Tribute)")
|
||||
test_payment_buttons.append([_test_button(f"{label} · тест", "tribute")])
|
||||
elif category_key == "MULENPAY":
|
||||
label = texts.t("PAYMENT_CARD_MULENPAY", "💳 Банковская карта (Mulen Pay)")
|
||||
label = texts.t(
|
||||
"PAYMENT_CARD_MULENPAY",
|
||||
"💳 Банковская карта ({mulenpay_name})",
|
||||
).format(mulenpay_name=settings.get_mulenpay_display_name())
|
||||
test_payment_buttons.append([_test_button(f"{label} · тест", "mulenpay")])
|
||||
elif category_key == "WATA":
|
||||
label = texts.t("PAYMENT_CARD_WATA", "💳 Банковская карта (WATA)")
|
||||
@@ -1834,8 +1837,13 @@ async def test_payment_provider(
|
||||
return
|
||||
|
||||
if method == "mulenpay":
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
mulenpay_name_html = settings.get_mulenpay_display_name_html()
|
||||
if not settings.is_mulenpay_enabled():
|
||||
await callback.answer("❌ MulenPay отключен", show_alert=True)
|
||||
await callback.answer(
|
||||
f"❌ {mulenpay_name} отключен",
|
||||
show_alert=True,
|
||||
)
|
||||
return
|
||||
|
||||
amount_kopeks = 1 * 100
|
||||
@@ -1843,18 +1851,21 @@ async def test_payment_provider(
|
||||
db=db,
|
||||
user_id=db_user.id,
|
||||
amount_kopeks=amount_kopeks,
|
||||
description="Тестовый платеж MulenPay (админ)",
|
||||
description=f"Тестовый платеж {mulenpay_name} (админ)",
|
||||
language=language,
|
||||
)
|
||||
|
||||
if not payment_result or not payment_result.get("payment_url"):
|
||||
await callback.answer("❌ Не удалось создать платеж MulenPay", show_alert=True)
|
||||
await callback.answer(
|
||||
f"❌ Не удалось создать платеж {mulenpay_name}",
|
||||
show_alert=True,
|
||||
)
|
||||
await _refresh_markup()
|
||||
return
|
||||
|
||||
payment_url = payment_result["payment_url"]
|
||||
message_text = (
|
||||
"🧪 <b>Тестовый платеж MulenPay</b>\n\n"
|
||||
f"🧪 <b>Тестовый платеж {mulenpay_name_html}</b>\n\n"
|
||||
f"💰 Сумма: {texts.format_price(amount_kopeks)}\n"
|
||||
f"🆔 ID: {payment_result['mulen_payment_id']}"
|
||||
)
|
||||
@@ -1875,7 +1886,10 @@ async def test_payment_provider(
|
||||
]
|
||||
)
|
||||
await callback.message.answer(message_text, reply_markup=reply_markup, parse_mode="HTML")
|
||||
await callback.answer("✅ Ссылка на платеж MulenPay отправлена", show_alert=True)
|
||||
await callback.answer(
|
||||
f"✅ Ссылка на платеж {mulenpay_name} отправлена",
|
||||
show_alert=True,
|
||||
)
|
||||
await _refresh_markup()
|
||||
return
|
||||
|
||||
|
||||
@@ -21,19 +21,28 @@ async def start_mulenpay_payment(
|
||||
state: FSMContext,
|
||||
):
|
||||
texts = get_texts(db_user.language)
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
mulenpay_name_html = settings.get_mulenpay_display_name_html()
|
||||
|
||||
if not settings.is_mulenpay_enabled():
|
||||
await callback.answer("❌ Оплата через Mulen Pay временно недоступна", show_alert=True)
|
||||
await callback.answer(
|
||||
f"❌ Оплата через {mulenpay_name} временно недоступна",
|
||||
show_alert=True,
|
||||
)
|
||||
return
|
||||
|
||||
message_text = texts.t(
|
||||
message_template = texts.t(
|
||||
"MULENPAY_TOPUP_PROMPT",
|
||||
(
|
||||
"💳 <b>Оплата через Mulen Pay</b>\n\n"
|
||||
"💳 <b>Оплата через {mulenpay_name_html}</b>\n\n"
|
||||
"Введите сумму для пополнения от 100 до 100 000 ₽.\n"
|
||||
"Оплата происходит через защищенную платформу Mulen Pay."
|
||||
"Оплата происходит через защищенную платформу {mulenpay_name}."
|
||||
),
|
||||
)
|
||||
message_text = message_template.format(
|
||||
mulenpay_name=mulenpay_name,
|
||||
mulenpay_name_html=mulenpay_name_html,
|
||||
)
|
||||
|
||||
keyboard = get_back_keyboard(db_user.language)
|
||||
|
||||
@@ -63,9 +72,11 @@ async def process_mulenpay_payment_amount(
|
||||
state: FSMContext,
|
||||
):
|
||||
texts = get_texts(db_user.language)
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
mulenpay_name_html = settings.get_mulenpay_display_name_html()
|
||||
|
||||
if not settings.is_mulenpay_enabled():
|
||||
await message.answer("❌ Оплата через Mulen Pay временно недоступна")
|
||||
await message.answer(f"❌ Оплата через {mulenpay_name} временно недоступна")
|
||||
return
|
||||
|
||||
if amount_kopeks < settings.MULENPAY_MIN_AMOUNT_KOPEKS:
|
||||
@@ -96,8 +107,8 @@ async def process_mulenpay_payment_amount(
|
||||
await message.answer(
|
||||
texts.t(
|
||||
"MULENPAY_PAYMENT_ERROR",
|
||||
"❌ Ошибка создания платежа Mulen Pay. Попробуйте позже или обратитесь в поддержку.",
|
||||
)
|
||||
"❌ Ошибка создания платежа {mulenpay_name}. Попробуйте позже или обратитесь в поддержку.",
|
||||
).format(mulenpay_name=mulenpay_name)
|
||||
)
|
||||
await state.clear()
|
||||
return
|
||||
@@ -112,8 +123,8 @@ async def process_mulenpay_payment_amount(
|
||||
types.InlineKeyboardButton(
|
||||
text=texts.t(
|
||||
"MULENPAY_PAY_BUTTON",
|
||||
"💳 Оплатить через Mulen Pay",
|
||||
),
|
||||
"💳 Оплатить через {mulenpay_name}",
|
||||
).format(mulenpay_name=mulenpay_name),
|
||||
url=payment_url,
|
||||
)
|
||||
],
|
||||
@@ -132,11 +143,11 @@ async def process_mulenpay_payment_amount(
|
||||
message_template = texts.t(
|
||||
"MULENPAY_PAYMENT_INSTRUCTIONS",
|
||||
(
|
||||
"💳 <b>Оплата через Mulen Pay</b>\n\n"
|
||||
"💳 <b>Оплата через {mulenpay_name_html}</b>\n\n"
|
||||
"💰 Сумма: {amount}\n"
|
||||
"🆔 ID платежа: {payment_id}\n\n"
|
||||
"📱 <b>Инструкция:</b>\n"
|
||||
"1. Нажмите кнопку 'Оплатить через Mulen Pay'\n"
|
||||
"1. Нажмите кнопку 'Оплатить через {mulenpay_name}'\n"
|
||||
"2. Следуйте подсказкам платежной системы\n"
|
||||
"3. Подтвердите перевод\n"
|
||||
"4. Средства зачислятся автоматически\n\n"
|
||||
@@ -148,6 +159,8 @@ async def process_mulenpay_payment_amount(
|
||||
amount=settings.format_price(amount_kopeks),
|
||||
payment_id=payment_id_display,
|
||||
support=settings.get_support_contact_display_html(),
|
||||
mulenpay_name=mulenpay_name,
|
||||
mulenpay_name_html=mulenpay_name_html,
|
||||
)
|
||||
|
||||
await message.answer(
|
||||
@@ -159,19 +172,20 @@ async def process_mulenpay_payment_amount(
|
||||
await state.clear()
|
||||
|
||||
logger.info(
|
||||
"Создан MulenPay платеж для пользователя %s: %s₽, ID: %s",
|
||||
"Создан %s платеж для пользователя %s: %s₽, ID: %s",
|
||||
mulenpay_name,
|
||||
db_user.telegram_id,
|
||||
amount_rubles,
|
||||
payment_id_display,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка создания MulenPay платежа: {e}")
|
||||
logger.error(f"Ошибка создания {mulenpay_name} платежа: {e}")
|
||||
await message.answer(
|
||||
texts.t(
|
||||
"MULENPAY_PAYMENT_ERROR",
|
||||
"❌ Ошибка создания платежа Mulen Pay. Попробуйте позже или обратитесь в поддержку.",
|
||||
)
|
||||
"❌ Ошибка создания платежа {mulenpay_name}. Попробуйте позже или обратитесь в поддержку.",
|
||||
).format(mulenpay_name=mulenpay_name)
|
||||
)
|
||||
await state.clear()
|
||||
|
||||
@@ -204,8 +218,9 @@ async def check_mulenpay_payment_status(
|
||||
|
||||
emoji, status_text = status_labels.get(payment.status, ("❓", "Неизвестно"))
|
||||
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
message_lines = [
|
||||
"💳 Статус платежа Mulen Pay:\n\n",
|
||||
f"💳 Статус платежа {mulenpay_name}:\n\n",
|
||||
f"🆔 ID: {payment.mulen_payment_id or payment.id}\n",
|
||||
f"💰 Сумма: {settings.format_price(payment.amount_kopeks)}\n",
|
||||
f"📊 Статус: {emoji} {status_text}\n",
|
||||
@@ -234,5 +249,7 @@ async def check_mulenpay_payment_status(
|
||||
await callback.answer(message_text, show_alert=True)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка проверки статуса MulenPay: {e}")
|
||||
logger.error(
|
||||
f"Ошибка проверки статуса {settings.get_mulenpay_display_name()}: {e}"
|
||||
)
|
||||
await callback.answer("❌ Ошибка проверки статуса", show_alert=True)
|
||||
@@ -186,8 +186,9 @@ def _get_simple_subscription_payment_keyboard(language: str) -> types.InlineKeyb
|
||||
)])
|
||||
|
||||
if settings.is_mulenpay_enabled():
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
keyboard.append([types.InlineKeyboardButton(
|
||||
text="💳 MulenPay",
|
||||
text=f"💳 {mulenpay_name}",
|
||||
callback_data="simple_subscription_mulenpay"
|
||||
)])
|
||||
|
||||
@@ -773,12 +774,19 @@ async def handle_simple_subscription_payment_method(
|
||||
|
||||
elif payment_method == "mulenpay":
|
||||
# Оплата через MulenPay
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
if not settings.is_mulenpay_enabled():
|
||||
await callback.answer("❌ Оплата через MulenPay временно недоступна", show_alert=True)
|
||||
await callback.answer(
|
||||
f"❌ Оплата через {mulenpay_name} временно недоступна",
|
||||
show_alert=True,
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
# Здесь должна быть реализация оплаты через MulenPay
|
||||
await callback.answer("❌ Оплата через MulenPay пока не реализована", show_alert=True)
|
||||
await callback.answer(
|
||||
f"❌ Оплата через {mulenpay_name} пока не реализована",
|
||||
show_alert=True,
|
||||
)
|
||||
|
||||
elif payment_method == "pal24":
|
||||
# Оплата через PayPalych
|
||||
|
||||
@@ -2412,8 +2412,9 @@ def _get_simple_subscription_payment_keyboard(language: str) -> types.InlineKeyb
|
||||
)])
|
||||
|
||||
if settings.is_mulenpay_enabled():
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
keyboard.append([types.InlineKeyboardButton(
|
||||
text="💳 MulenPay",
|
||||
text=f"💳 {mulenpay_name}",
|
||||
callback_data="simple_subscription_mulenpay"
|
||||
)])
|
||||
|
||||
|
||||
@@ -1101,9 +1101,13 @@ def get_payment_methods_keyboard(amount_kopeks: int, language: str = DEFAULT_LAN
|
||||
])
|
||||
|
||||
if settings.is_mulenpay_enabled():
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
keyboard.append([
|
||||
InlineKeyboardButton(
|
||||
text=texts.t("PAYMENT_CARD_MULENPAY", "💳 Банковская карта (Mulen Pay)"),
|
||||
text=texts.t(
|
||||
"PAYMENT_CARD_MULENPAY",
|
||||
"💳 Банковская карта ({mulenpay_name})",
|
||||
).format(mulenpay_name=mulenpay_name),
|
||||
callback_data=_build_callback("mulenpay")
|
||||
)
|
||||
])
|
||||
|
||||
@@ -922,10 +922,10 @@
|
||||
"MINIAPP_PURCHASE_DISCOUNT_TRAFFIC": "Traffic discount: -{amount} ({percent}%)",
|
||||
"MINIAPP_PURCHASE_STATUS_INSUFFICIENT": "Not enough funds on balance",
|
||||
"MINIAPP_PURCHASE_SUMMARY_DISCOUNT": "You save {amount}",
|
||||
"MULENPAY_PAYMENT_ERROR": "❌ Failed to create Mulen Pay payment. Please try again later or contact support.",
|
||||
"MULENPAY_PAYMENT_INSTRUCTIONS": "💳 <b>Mulen Pay payment</b>\n\n💰 Amount: {amount}\n🆔 Payment ID: {payment_id}\n\n📱 <b>How to pay:</b>\n1. Press ‘Pay with Mulen Pay’\n2. Follow the instructions on the payment page\n3. Confirm the transfer\n4. Funds will be credited automatically\n\n❓ Need help? Contact {support}",
|
||||
"MULENPAY_PAY_BUTTON": "💳 Pay with Mulen Pay",
|
||||
"MULENPAY_TOPUP_PROMPT": "💳 <b>Mulen Pay payment</b>\n\nEnter an amount between 100 and 100,000 ₽.\nThe payment is processed by the secure Mulen Pay platform.",
|
||||
"MULENPAY_PAYMENT_ERROR": "❌ Failed to create a {mulenpay_name} payment. Please try again later or contact support.",
|
||||
"MULENPAY_PAYMENT_INSTRUCTIONS": "💳 <b>{mulenpay_name_html} payment</b>\n\n💰 Amount: {amount}\n🆔 Payment ID: {payment_id}\n\n📱 <b>How to pay:</b>\n1. Press ‘Pay with {mulenpay_name}’\n2. Follow the instructions on the payment page\n3. Confirm the transfer\n4. Funds will be credited automatically\n\n❓ Need help? Contact {support}",
|
||||
"MULENPAY_PAY_BUTTON": "💳 Pay with {mulenpay_name}",
|
||||
"MULENPAY_TOPUP_PROMPT": "💳 <b>{mulenpay_name_html} payment</b>\n\nEnter an amount between 100 and 100,000 ₽.\nThe payment is processed by the secure {mulenpay_name} platform.",
|
||||
"MY_BALANCE_BUTTON": "💰 My balance",
|
||||
"MY_SUBSCRIPTION_BUTTON": "📱 My subscription",
|
||||
"MY_TICKETS_BUTTON": "📋 My tickets",
|
||||
@@ -965,7 +965,7 @@
|
||||
"PAL24_SELECT_PAYMENT_METHOD": "Choose a PayPalych payment method:",
|
||||
"PAL24_TOPUP_PROMPT": "🏦 <b>PayPalych (SBP) payment</b>\n\nEnter an amount between 100 and 1,000,000 ₽.\nThe payment is processed via the PayPalych Faster Payments System.",
|
||||
"PAYMENTS_TEMPORARILY_UNAVAILABLE": "⚠️ Payment methods are temporarily unavailable",
|
||||
"PAYMENT_CARD_MULENPAY": "💳 Bank card (Mulen Pay)",
|
||||
"PAYMENT_CARD_MULENPAY": "💳 Bank card ({mulenpay_name})",
|
||||
"PAYMENT_CARD_PAL24": "🏦 SBP (PayPalych)",
|
||||
"PAYMENT_CARD_TRIBUTE": "💳 Bank card (Tribute)",
|
||||
"PAYMENT_CARD_WATA": "💳 Bank card (WATA)",
|
||||
@@ -979,8 +979,8 @@
|
||||
"PAYMENT_METHODS_UNAVAILABLE_ALERT": "⚠️ Automated payment methods are temporarily unavailable. Contact support to top up your balance.",
|
||||
"PAYMENT_METHOD_CRYPTOBOT_DESCRIPTION": "via CryptoBot",
|
||||
"PAYMENT_METHOD_CRYPTOBOT_NAME": "🪙 <b>Cryptocurrency</b>",
|
||||
"PAYMENT_METHOD_MULENPAY_DESCRIPTION": "via Mulen Pay",
|
||||
"PAYMENT_METHOD_MULENPAY_NAME": "💳 <b>Bank card (Mulen Pay)</b>",
|
||||
"PAYMENT_METHOD_MULENPAY_DESCRIPTION": "via {mulenpay_name}",
|
||||
"PAYMENT_METHOD_MULENPAY_NAME": "💳 <b>Bank card ({mulenpay_name})</b>",
|
||||
"PAYMENT_METHOD_PAL24_DESCRIPTION": "via Faster Payments System",
|
||||
"PAYMENT_METHOD_PAL24_NAME": "🏦 <b>SBP (PayPalych)</b>",
|
||||
"PAYMENT_METHOD_STARS_DESCRIPTION": "fast and convenient",
|
||||
|
||||
@@ -922,10 +922,10 @@
|
||||
"MINIAPP_PURCHASE_DISCOUNT_TRAFFIC": "Скидка на трафик: -{amount} ({percent}%)",
|
||||
"MINIAPP_PURCHASE_STATUS_INSUFFICIENT": "Недостаточно средств на балансе",
|
||||
"MINIAPP_PURCHASE_SUMMARY_DISCOUNT": "Экономия {amount}",
|
||||
"MULENPAY_PAYMENT_ERROR": "❌ Ошибка создания платежа Mulen Pay. Попробуйте позже или обратитесь в поддержку.",
|
||||
"MULENPAY_PAYMENT_INSTRUCTIONS": "💳 <b>Оплата через Mulen Pay</b>\n\n💰 Сумма: {amount}\n🆔 ID платежа: {payment_id}\n\n📱 <b>Инструкция:</b>\n1. Нажмите кнопку ‘Оплатить через Mulen Pay’\n2. Следуйте подсказкам платежной системы\n3. Подтвердите перевод\n4. Средства зачислятся автоматически\n\n❓ Если возникнут проблемы, обратитесь в {support}",
|
||||
"MULENPAY_PAY_BUTTON": "💳 Оплатить через Mulen Pay",
|
||||
"MULENPAY_TOPUP_PROMPT": "💳 <b>Оплата через Mulen Pay</b>\n\nВведите сумму для пополнения от 100 до 100 000 ₽.\nОплата происходит через защищенную платформу Mulen Pay.",
|
||||
"MULENPAY_PAYMENT_ERROR": "❌ Ошибка создания платежа {mulenpay_name}. Попробуйте позже или обратитесь в поддержку.",
|
||||
"MULENPAY_PAYMENT_INSTRUCTIONS": "💳 <b>Оплата через {mulenpay_name_html}</b>\n\n💰 Сумма: {amount}\n🆔 ID платежа: {payment_id}\n\n📱 <b>Инструкция:</b>\n1. Нажмите кнопку ‘Оплатить через {mulenpay_name}’\n2. Следуйте подсказкам платежной системы\n3. Подтвердите перевод\n4. Средства зачислятся автоматически\n\n❓ Если возникнут проблемы, обратитесь в {support}",
|
||||
"MULENPAY_PAY_BUTTON": "💳 Оплатить через {mulenpay_name}",
|
||||
"MULENPAY_TOPUP_PROMPT": "💳 <b>Оплата через {mulenpay_name_html}</b>\n\nВведите сумму для пополнения от 100 до 100 000 ₽.\nОплата происходит через защищенную платформу {mulenpay_name}.",
|
||||
"MY_BALANCE_BUTTON": "💰 Мой баланс",
|
||||
"MY_SUBSCRIPTION_BUTTON": "📱 Моя подписка",
|
||||
"MY_TICKETS_BUTTON": "📋 Мои тикеты",
|
||||
@@ -965,7 +965,7 @@
|
||||
"PAL24_SELECT_PAYMENT_METHOD": "Выберите способ оплаты PayPalych:",
|
||||
"PAL24_TOPUP_PROMPT": "🏦 <b>Оплата через PayPalych (СБП)</b>\n\nВведите сумму для пополнения от 100 до 1 000 000 ₽.\nОплата проходит через систему быстрых платежей PayPalych.",
|
||||
"PAYMENTS_TEMPORARILY_UNAVAILABLE": "⚠️ Способы оплаты временно недоступны",
|
||||
"PAYMENT_CARD_MULENPAY": "💳 Банковская карта (Mulen Pay)",
|
||||
"PAYMENT_CARD_MULENPAY": "💳 Банковская карта ({mulenpay_name})",
|
||||
"PAYMENT_CARD_PAL24": "🏦 СБП (PayPalych)",
|
||||
"PAYMENT_CARD_TRIBUTE": "💳 Банковская карта (Tribute)",
|
||||
"PAYMENT_CARD_WATA": "💳 Банковская карта (WATA)",
|
||||
@@ -979,8 +979,8 @@
|
||||
"PAYMENT_METHODS_UNAVAILABLE_ALERT": "⚠️ В данный момент автоматические способы оплаты временно недоступны. Для пополнения баланса обратитесь в техподдержку.",
|
||||
"PAYMENT_METHOD_CRYPTOBOT_DESCRIPTION": "через CryptoBot",
|
||||
"PAYMENT_METHOD_CRYPTOBOT_NAME": "🪙 <b>Криптовалюта</b>",
|
||||
"PAYMENT_METHOD_MULENPAY_DESCRIPTION": "через Mulen Pay",
|
||||
"PAYMENT_METHOD_MULENPAY_NAME": "💳 <b>Банковская карта (Mulen Pay)</b>",
|
||||
"PAYMENT_METHOD_MULENPAY_DESCRIPTION": "через {mulenpay_name}",
|
||||
"PAYMENT_METHOD_MULENPAY_NAME": "💳 <b>Банковская карта ({mulenpay_name})</b>",
|
||||
"PAYMENT_METHOD_PAL24_DESCRIPTION": "через систему быстрых платежей",
|
||||
"PAYMENT_METHOD_PAL24_NAME": "🏦 <b>СБП (PayPalych)</b>",
|
||||
"PAYMENT_METHOD_STARS_DESCRIPTION": "быстро и удобно",
|
||||
|
||||
@@ -802,11 +802,12 @@ class AdminNotificationService:
|
||||
return self.enabled and bool(self.chat_id)
|
||||
|
||||
def _get_payment_method_display(self, payment_method: Optional[str]) -> str:
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
method_names = {
|
||||
'telegram_stars': '⭐ Telegram Stars',
|
||||
'yookassa': '💳 YooKassa (карта)',
|
||||
'tribute': '💎 Tribute (карта)',
|
||||
'mulenpay': '💳 Mulen Pay (карта)',
|
||||
'mulenpay': f'💳 {mulenpay_name} (карта)',
|
||||
'pal24': '🏦 PayPalych (СБП)',
|
||||
'manual': '🛠️ Вручную (админ)',
|
||||
'balance': '💰 С баланса'
|
||||
|
||||
@@ -28,13 +28,16 @@ class MulenPayPaymentMixin:
|
||||
language: Optional[str] = None,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Создаёт локальный платеж и инициализирует сессию в MulenPay."""
|
||||
display_name = settings.get_mulenpay_display_name()
|
||||
display_name_html = settings.get_mulenpay_display_name_html()
|
||||
if not getattr(self, "mulenpay_service", None):
|
||||
logger.error("MulenPay сервис не инициализирован")
|
||||
logger.error("%s сервис не инициализирован", display_name)
|
||||
return None
|
||||
|
||||
if amount_kopeks < settings.MULENPAY_MIN_AMOUNT_KOPEKS:
|
||||
logger.warning(
|
||||
"Сумма MulenPay меньше минимальной: %s < %s",
|
||||
"Сумма %s меньше минимальной: %s < %s",
|
||||
display_name,
|
||||
amount_kopeks,
|
||||
settings.MULENPAY_MIN_AMOUNT_KOPEKS,
|
||||
)
|
||||
@@ -42,7 +45,8 @@ class MulenPayPaymentMixin:
|
||||
|
||||
if amount_kopeks > settings.MULENPAY_MAX_AMOUNT_KOPEKS:
|
||||
logger.warning(
|
||||
"Сумма MulenPay больше максимальной: %s > %s",
|
||||
"Сумма %s больше максимальной: %s > %s",
|
||||
display_name,
|
||||
amount_kopeks,
|
||||
settings.MULENPAY_MAX_AMOUNT_KOPEKS,
|
||||
)
|
||||
@@ -74,7 +78,7 @@ class MulenPayPaymentMixin:
|
||||
)
|
||||
|
||||
if not response:
|
||||
logger.error("Ошибка создания MulenPay платежа")
|
||||
logger.error("Ошибка создания %s платежа", display_name)
|
||||
return None
|
||||
|
||||
mulen_payment_id = response.get("id")
|
||||
@@ -100,7 +104,8 @@ class MulenPayPaymentMixin:
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"Создан MulenPay платеж %s на %s₽ для пользователя %s",
|
||||
"Создан %s платеж %s на %s₽ для пользователя %s",
|
||||
display_name,
|
||||
mulen_payment_id,
|
||||
amount_rubles,
|
||||
user_id,
|
||||
@@ -116,7 +121,7 @@ class MulenPayPaymentMixin:
|
||||
}
|
||||
|
||||
except Exception as error:
|
||||
logger.error("Ошибка создания MulenPay платежа: %s", error)
|
||||
logger.error("Ошибка создания %s платежа: %s", display_name, error)
|
||||
return None
|
||||
|
||||
async def process_mulenpay_callback(
|
||||
@@ -125,6 +130,7 @@ class MulenPayPaymentMixin:
|
||||
callback_data: Dict[str, Any],
|
||||
) -> bool:
|
||||
"""Обрабатывает callback от MulenPay, обновляет статус и начисляет баланс."""
|
||||
display_name = settings.get_mulenpay_display_name()
|
||||
try:
|
||||
payment_module = import_module("app.services.payment_service")
|
||||
uuid_value = callback_data.get("uuid")
|
||||
@@ -138,14 +144,15 @@ class MulenPayPaymentMixin:
|
||||
mulen_payment_id_int = None
|
||||
amount_value = callback_data.get("amount")
|
||||
logger.debug(
|
||||
"MulenPay callback: uuid=%s, status=%s, amount=%s",
|
||||
"%s callback: uuid=%s, status=%s, amount=%s",
|
||||
display_name,
|
||||
uuid_value,
|
||||
payment_status,
|
||||
amount_value,
|
||||
)
|
||||
|
||||
if not uuid_value and mulen_payment_id_raw is None:
|
||||
logger.error("MulenPay callback без uuid и id")
|
||||
logger.error("%s callback без uuid и id", display_name)
|
||||
return False
|
||||
|
||||
payment = None
|
||||
@@ -159,7 +166,8 @@ class MulenPayPaymentMixin:
|
||||
|
||||
if not payment:
|
||||
logger.error(
|
||||
"MulenPay платеж не найден (uuid=%s, id=%s)",
|
||||
"%s платеж не найден (uuid=%s, id=%s)",
|
||||
display_name,
|
||||
uuid_value,
|
||||
mulen_payment_id_raw,
|
||||
)
|
||||
@@ -167,7 +175,8 @@ class MulenPayPaymentMixin:
|
||||
|
||||
if payment.is_paid:
|
||||
logger.info(
|
||||
"MulenPay платеж %s уже обработан, игнорируем повторный callback",
|
||||
"%s платеж %s уже обработан, игнорируем повторный callback",
|
||||
display_name,
|
||||
payment.uuid,
|
||||
)
|
||||
return True
|
||||
@@ -183,7 +192,8 @@ class MulenPayPaymentMixin:
|
||||
|
||||
if payment.transaction_id:
|
||||
logger.info(
|
||||
"Для MulenPay платежа %s уже создана транзакция",
|
||||
"Для %s платежа %s уже создана транзакция",
|
||||
display_name,
|
||||
payment.uuid,
|
||||
)
|
||||
return True
|
||||
@@ -199,7 +209,7 @@ class MulenPayPaymentMixin:
|
||||
user_id=payment.user_id,
|
||||
type=TransactionType.DEPOSIT,
|
||||
amount_kopeks=payment.amount_kopeks,
|
||||
description=f"Пополнение через MulenPay: {payment_description}",
|
||||
description=f"Пополнение через {display_name}: {payment_description}",
|
||||
payment_method=PaymentMethod.MULENPAY,
|
||||
external_id=payment.uuid,
|
||||
is_completed=True,
|
||||
@@ -214,8 +224,9 @@ class MulenPayPaymentMixin:
|
||||
user = await payment_module.get_user_by_id(db, payment.user_id)
|
||||
if not user:
|
||||
logger.error(
|
||||
"Пользователь %s не найден при обработке MulenPay",
|
||||
"Пользователь %s не найден при обработке %s",
|
||||
payment.user_id,
|
||||
display_name,
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -226,7 +237,7 @@ class MulenPayPaymentMixin:
|
||||
db,
|
||||
user,
|
||||
payment.amount_kopeks,
|
||||
f"Пополнение MulenPay: {payment.amount_kopeks // 100}₽",
|
||||
f"Пополнение {display_name}: {payment.amount_kopeks // 100}₽",
|
||||
)
|
||||
|
||||
if was_first_topup and not user.has_made_first_topup:
|
||||
@@ -261,7 +272,8 @@ class MulenPayPaymentMixin:
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
"Ошибка отправки уведомления о пополнении MulenPay: %s",
|
||||
"Ошибка отправки уведомления о пополнении %s: %s",
|
||||
display_name,
|
||||
error,
|
||||
)
|
||||
|
||||
@@ -273,7 +285,7 @@ class MulenPayPaymentMixin:
|
||||
(
|
||||
"✅ <b>Пополнение успешно!</b>\n\n"
|
||||
f"💰 Сумма: {settings.format_price(payment.amount_kopeks)}\n"
|
||||
"🦊 Способ: Mulen Pay\n"
|
||||
f"🦊 Способ: {display_name_html}\n"
|
||||
f"🆔 Транзакция: {transaction.id}\n\n"
|
||||
"Баланс пополнен автоматически!"
|
||||
),
|
||||
@@ -282,7 +294,8 @@ class MulenPayPaymentMixin:
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
"Ошибка отправки уведомления пользователю MulenPay: %s",
|
||||
"Ошибка отправки уведомления пользователю %s: %s",
|
||||
display_name,
|
||||
error,
|
||||
)
|
||||
|
||||
@@ -329,7 +342,8 @@ class MulenPayPaymentMixin:
|
||||
logger.error(f"Ошибка при работе с сохраненной корзиной для пользователя {user.id}: {e}", exc_info=True)
|
||||
|
||||
logger.info(
|
||||
"✅ Обработан MulenPay платеж %s для пользователя %s",
|
||||
"✅ Обработан %s платеж %s для пользователя %s",
|
||||
display_name,
|
||||
payment.uuid,
|
||||
payment.user_id,
|
||||
)
|
||||
@@ -343,7 +357,7 @@ class MulenPayPaymentMixin:
|
||||
callback_payload=callback_data,
|
||||
mulen_payment_id=mulen_payment_id_int,
|
||||
)
|
||||
logger.info("MulenPay платеж %s отменен", payment.uuid)
|
||||
logger.info("%s платеж %s отменен", display_name, payment.uuid)
|
||||
return True
|
||||
|
||||
await payment_module.update_mulenpay_payment_status(
|
||||
@@ -354,14 +368,20 @@ class MulenPayPaymentMixin:
|
||||
mulen_payment_id=mulen_payment_id_int,
|
||||
)
|
||||
logger.info(
|
||||
"Получен MulenPay callback со статусом %s для платежа %s",
|
||||
"Получен %s callback со статусом %s для платежа %s",
|
||||
display_name,
|
||||
payment_status,
|
||||
payment.uuid,
|
||||
)
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error("Ошибка обработки MulenPay callback: %s", error, exc_info=True)
|
||||
logger.error(
|
||||
"Ошибка обработки %s callback: %s",
|
||||
display_name,
|
||||
error,
|
||||
exc_info=True,
|
||||
)
|
||||
return False
|
||||
|
||||
def _map_mulenpay_status(self, status_code: Optional[int]) -> str:
|
||||
@@ -383,6 +403,7 @@ class MulenPayPaymentMixin:
|
||||
local_payment_id: int,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Возвращает текущее состояние платежа и при необходимости синхронизирует его."""
|
||||
display_name = settings.get_mulenpay_display_name()
|
||||
try:
|
||||
payment_module = import_module("app.services.payment_service")
|
||||
|
||||
@@ -437,6 +458,9 @@ class MulenPayPaymentMixin:
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
"Ошибка получения статуса MulenPay: %s", error, exc_info=True
|
||||
"Ошибка получения статуса %s: %s",
|
||||
display_name,
|
||||
error,
|
||||
exc_info=True,
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -209,12 +209,13 @@ class PaymentService(
|
||||
)
|
||||
self.wata_service = WataService() if settings.is_wata_enabled() else None
|
||||
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
logger.debug(
|
||||
"PaymentService инициализирован (YooKassa=%s, Stars=%s, CryptoBot=%s, "
|
||||
"MulenPay=%s, Pal24=%s, Wata=%s)",
|
||||
"PaymentService инициализирован (YooKassa=%s, Stars=%s, CryptoBot=%s, %s=%s, Pal24=%s, Wata=%s)",
|
||||
bool(self.yookassa_service),
|
||||
bool(self.stars_service),
|
||||
bool(self.cryptobot_service),
|
||||
mulenpay_name,
|
||||
bool(self.mulenpay_service),
|
||||
bool(self.pal24_service),
|
||||
bool(self.wata_service),
|
||||
|
||||
@@ -76,7 +76,7 @@ class BotConfigurationService:
|
||||
"CRYPTOBOT": "🪙 CryptoBot",
|
||||
"YOOKASSA": "🟣 YooKassa",
|
||||
"TRIBUTE": "🎁 Tribute",
|
||||
"MULENPAY": "💰 MulenPay",
|
||||
"MULENPAY": "💰 {mulenpay_name}",
|
||||
"PAL24": "🏦 PAL24 / PayPalych",
|
||||
"WATA": "💠 Wata",
|
||||
"EXTERNAL_ADMIN": "🛡️ Внешняя админка",
|
||||
@@ -124,7 +124,7 @@ class BotConfigurationService:
|
||||
"PAYMENT": "Общие тексты платежей, описания чеков и шаблоны.",
|
||||
"YOOKASSA": "Интеграция с YooKassa: идентификаторы магазина и вебхуки.",
|
||||
"CRYPTOBOT": "CryptoBot и криптоплатежи через Telegram.",
|
||||
"MULENPAY": "Платежи MulenPay и параметры магазина.",
|
||||
"MULENPAY": "Платежи {mulenpay_name} и параметры магазина.",
|
||||
"PAL24": "PAL24 / PayPalych подключения и лимиты.",
|
||||
"TRIBUTE": "Tribute и донат-сервисы.",
|
||||
"TELEGRAM": "Telegram Stars и их стоимость.",
|
||||
@@ -166,6 +166,14 @@ class BotConfigurationService:
|
||||
"MODERATION": "Настройки фильтров отображаемых имен и защиты от фишинга.",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _format_dynamic_copy(category_key: Optional[str], value: str) -> str:
|
||||
if not value:
|
||||
return value
|
||||
if category_key == "MULENPAY":
|
||||
return value.format(mulenpay_name=settings.get_mulenpay_display_name())
|
||||
return value
|
||||
|
||||
CATEGORY_KEY_OVERRIDES: Dict[str, str] = {
|
||||
"DATABASE_URL": "DATABASE",
|
||||
"DATABASE_MODE": "DATABASE",
|
||||
@@ -459,7 +467,8 @@ class BotConfigurationService:
|
||||
|
||||
@classmethod
|
||||
def get_category_description(cls, category_key: str) -> str:
|
||||
return cls.CATEGORY_DESCRIPTIONS.get(category_key, "")
|
||||
description = cls.CATEGORY_DESCRIPTIONS.get(category_key, "")
|
||||
return cls._format_dynamic_copy(category_key, description)
|
||||
|
||||
@classmethod
|
||||
def is_toggle(cls, key: str) -> bool:
|
||||
@@ -598,6 +607,7 @@ class BotConfigurationService:
|
||||
category_key,
|
||||
category_key.capitalize() if category_key else "Прочее",
|
||||
)
|
||||
category_label = cls._format_dynamic_copy(category_key, category_label)
|
||||
|
||||
cls._definitions[key] = SettingDefinition(
|
||||
key=key,
|
||||
|
||||
@@ -672,7 +672,10 @@ class UserService:
|
||||
mulenpay_payments = mulenpay_result.scalars().all()
|
||||
|
||||
if mulenpay_payments:
|
||||
logger.info(f"🔄 Удаляем {len(mulenpay_payments)} MulenPay платежей")
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
logger.info(
|
||||
f"🔄 Удаляем {len(mulenpay_payments)} {mulenpay_name} платежей"
|
||||
)
|
||||
await db.execute(
|
||||
update(MulenPayPayment)
|
||||
.where(MulenPayPayment.user_id == user_id)
|
||||
@@ -684,7 +687,9 @@ class UserService:
|
||||
)
|
||||
await db.flush()
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Ошибка удаления MulenPay платежей: {e}")
|
||||
logger.error(
|
||||
f"❌ Ошибка удаления {settings.get_mulenpay_display_name()} платежей: {e}"
|
||||
)
|
||||
|
||||
try:
|
||||
pal24_result = await db.execute(
|
||||
|
||||
@@ -46,11 +46,12 @@ def get_available_payment_methods() -> List[Dict[str, str]]:
|
||||
})
|
||||
|
||||
if settings.is_mulenpay_enabled():
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
methods.append({
|
||||
"id": "mulenpay",
|
||||
"name": "Банковская карта",
|
||||
"icon": "💳",
|
||||
"description": "через Mulen Pay",
|
||||
"description": f"через {mulenpay_name}",
|
||||
"callback": "topup_mulenpay"
|
||||
})
|
||||
|
||||
@@ -129,6 +130,12 @@ def get_payment_methods_text(language: str) -> str:
|
||||
f"PAYMENT_METHOD_{method_id}_DESCRIPTION",
|
||||
method['description'],
|
||||
)
|
||||
if method_id == "MULENPAY":
|
||||
mulenpay_name = settings.get_mulenpay_display_name()
|
||||
mulenpay_name_html = settings.get_mulenpay_display_name_html()
|
||||
name = name.format(mulenpay_name=mulenpay_name_html)
|
||||
description = description.format(mulenpay_name=mulenpay_name)
|
||||
|
||||
text += f"{name} - {description}\n"
|
||||
|
||||
text += "\n" + texts.t(
|
||||
|
||||
@@ -668,6 +668,7 @@ async def get_payment_methods(
|
||||
methods.append(
|
||||
MiniAppPaymentMethod(
|
||||
id="mulenpay",
|
||||
name=settings.get_mulenpay_display_name(),
|
||||
icon="💳",
|
||||
requires_amount=True,
|
||||
currency="RUB",
|
||||
|
||||
@@ -361,6 +361,7 @@ class MiniAppPaymentMethodsRequest(BaseModel):
|
||||
|
||||
class MiniAppPaymentMethod(BaseModel):
|
||||
id: str
|
||||
name: Optional[str] = None
|
||||
icon: Optional[str] = None
|
||||
requires_amount: bool = False
|
||||
currency: str = "RUB"
|
||||
|
||||
@@ -248,7 +248,7 @@ async def main():
|
||||
if settings.TRIBUTE_ENABLED:
|
||||
enabled_services.append("Tribute")
|
||||
if settings.is_mulenpay_enabled():
|
||||
enabled_services.append("Mulen Pay")
|
||||
enabled_services.append(settings.get_mulenpay_display_name())
|
||||
if settings.is_cryptobot_enabled():
|
||||
enabled_services.append("CryptoBot")
|
||||
|
||||
@@ -257,7 +257,11 @@ async def main():
|
||||
stage.log(f"Активированы: {', '.join(enabled_services)}")
|
||||
stage.success("Webhook сервера запущены")
|
||||
else:
|
||||
stage.skip("Tribute, Mulen Pay и CryptoBot отключены")
|
||||
stage.skip(
|
||||
"Tribute, "
|
||||
f"{settings.get_mulenpay_display_name()}"
|
||||
" и CryptoBot отключены"
|
||||
)
|
||||
|
||||
async with timeline.stage(
|
||||
"YooKassa webhook",
|
||||
@@ -372,7 +376,8 @@ async def main():
|
||||
)
|
||||
if settings.is_mulenpay_enabled():
|
||||
webhook_lines.append(
|
||||
f"Mulen Pay: {settings.WEBHOOK_URL}:{settings.TRIBUTE_WEBHOOK_PORT}{settings.MULENPAY_WEBHOOK_PATH}"
|
||||
f"{settings.get_mulenpay_display_name()}: "
|
||||
f"{settings.WEBHOOK_URL}:{settings.TRIBUTE_WEBHOOK_PORT}{settings.MULENPAY_WEBHOOK_PATH}"
|
||||
)
|
||||
if settings.is_cryptobot_enabled():
|
||||
webhook_lines.append(
|
||||
|
||||
Reference in New Issue
Block a user