robokassa_beta

This commit is contained in:
Vladless
2024-11-15 15:07:51 +03:00
parent c7d5ff94c0
commit 2961477ecf
4 changed files with 223 additions and 4 deletions
+4 -2
View File
@@ -1,7 +1,7 @@
from aiogram import Bot, Dispatcher, Router from aiogram import Bot, Dispatcher, Router
from aiogram.fsm.storage.memory import MemoryStorage from aiogram.fsm.storage.memory import MemoryStorage
from config import API_TOKEN, CRYPTO_BOT_ENABLE, FREEKASSA_ENABLE, STARS_ENABLE, YOOKASSA_ENABLE from config import API_TOKEN, CRYPTO_BOT_ENABLE, FREEKASSA_ENABLE, ROBOKASSA_ENABLE, STARS_ENABLE, YOOKASSA_ENABLE
from middlewares.database import DatabaseMiddleware from middlewares.database import DatabaseMiddleware
from middlewares.logging import LoggingMiddleware from middlewares.logging import LoggingMiddleware
@@ -13,7 +13,7 @@ router = Router()
from handlers import commands, coupons, notifications, pay, profile, start from handlers import commands, coupons, notifications, pay, profile, start
from handlers.admin import admin_commands, admin_coupons, admin_panel, admin_user_editor from handlers.admin import admin_commands, admin_coupons, admin_panel, admin_user_editor
from handlers.keys import key_management, keys from handlers.keys import key_management, keys
from handlers.payments import cryprobot_pay, freekassa_pay, stars_pay, yookassa_pay from handlers.payments import cryprobot_pay, freekassa_pay, robokassa_pay, stars_pay, yookassa_pay
dp.include_router(admin_commands.router) dp.include_router(admin_commands.router)
dp.include_router(admin_coupons.router) dp.include_router(admin_coupons.router)
@@ -34,6 +34,8 @@ if CRYPTO_BOT_ENABLE:
dp.include_router(cryprobot_pay.router) dp.include_router(cryprobot_pay.router)
if STARS_ENABLE: if STARS_ENABLE:
dp.include_router(stars_pay.router) dp.include_router(stars_pay.router)
if ROBOKASSA_ENABLE:
dp.include_router(robokassa_pay.router)
dp.include_router(notifications.router) dp.include_router(notifications.router)
dp.message.middleware(LoggingMiddleware()) dp.message.middleware(LoggingMiddleware())
+8 -1
View File
@@ -2,7 +2,7 @@ from aiogram import F, Router
from aiogram.types import CallbackQuery, InlineKeyboardButton from aiogram.types import CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from config import CRYPTO_BOT_ENABLE, FREEKASSA_ENABLE, STARS_ENABLE, YOOKASSA_ENABLE from config import CRYPTO_BOT_ENABLE, FREEKASSA_ENABLE, ROBOKASSA_ENABLE, STARS_ENABLE, YOOKASSA_ENABLE
from database import get_trial from database import get_trial
from handlers.start import send_welcome_message from handlers.start import send_welcome_message
@@ -42,6 +42,13 @@ async def handle_pay(callback_query: CallbackQuery):
callback_data="pay_stars", callback_data="pay_stars",
) )
) )
if ROBOKASSA_ENABLE:
builder.row(
InlineKeyboardButton(
text="⭐ RoboKassa",
callback_data="pay_robokassa",
)
)
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🎟️ Активировать купон", callback_data="activate_coupon" text="🎟️ Активировать купон", callback_data="activate_coupon"
-1
View File
@@ -170,7 +170,6 @@ async def process_amount_selection(
await callback_query.answer() await callback_query.answer()
async def send_payment_success_notification(user_id: int, amount: float): async def send_payment_success_notification(user_id: int, amount: float):
try: try:
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
+211
View File
@@ -0,0 +1,211 @@
import uuid
from urllib.parse import urlencode
from aiogram import F, Router, types
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiohttp import web
from loguru import logger
from bot import bot
from config import ROBOKASSA_LOGIN, ROBOKASSA_PASSWORD1, ROBOKASSA_PASSWORD2, ROBOKASSA_TEST_MODE
from database import add_connection, check_connection_exists, get_key_count, update_balance
from handlers.texts import PAYMENT_OPTIONS
router = Router()
class ReplenishBalanceState(StatesGroup):
choosing_amount_robokassa = State()
waiting_for_payment_confirmation_robokassa = State()
entering_custom_amount_robokassa = State()
def calculate_signature(*args) -> str:
"""Создание подписи SHA-256 для Робокассы."""
import hashlib
data = ":".join(map(str, args))
return hashlib.sha256(data.encode("utf-8")).hexdigest()
def generate_payment_link(
merchant_login, password1, cost, inv_id, description, is_test
):
"""Генерация ссылки для оплаты через Робокассу."""
signature = calculate_signature(merchant_login, cost, inv_id, password1)
payment_url = "https://auth.robokassa.ru/Merchant/Index.aspx"
params = {
"MerchantLogin": merchant_login,
"OutSum": cost,
"InvId": inv_id,
"Description": description,
"SignatureValue": signature,
"IsTest": int(is_test),
}
return f"{payment_url}?{urlencode(params)}"
async def send_message_with_deletion(
chat_id, text, reply_markup=None, state=None, message_key="last_message_id"
):
"""Отправка сообщения с удалением предыдущего."""
if state:
try:
state_data = await state.get_data()
previous_message_id = state_data.get(message_key)
if previous_message_id:
await bot.delete_message(
chat_id=chat_id, message_id=previous_message_id
)
sent_message = await bot.send_message(
chat_id=chat_id, text=text, reply_markup=reply_markup
)
await state.update_data({message_key: sent_message.message_id})
except Exception as e:
logger.error(f"Ошибка при удалении/отправке сообщения: {e}")
return None
return sent_message
@router.callback_query(F.data == "pay_robokassa")
async def process_callback_pay_robokassa(
callback_query: types.CallbackQuery, state: FSMContext
):
tg_id = callback_query.from_user.id
builder = InlineKeyboardBuilder()
for i in range(0, len(PAYMENT_OPTIONS), 2):
if i + 1 < len(PAYMENT_OPTIONS):
builder.row(
InlineKeyboardButton(
text=PAYMENT_OPTIONS[i]["text"],
callback_data=f'robokassa_{PAYMENT_OPTIONS[i]["callback_data"]}',
),
InlineKeyboardButton(
text=PAYMENT_OPTIONS[i + 1]["text"],
callback_data=f'robokassa_{PAYMENT_OPTIONS[i + 1]["callback_data"]}',
),
)
else:
builder.row(
InlineKeyboardButton(
text=PAYMENT_OPTIONS[i]["text"],
callback_data=f'robokassa_{PAYMENT_OPTIONS[i]["callback_data"]}',
)
)
builder.row(
InlineKeyboardButton(
text="💰 Ввести свою сумму", callback_data="enter_custom_amount_robokassa"
)
)
builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="back_to_profile"))
key_count = await get_key_count(tg_id)
if key_count == 0:
exists = await check_connection_exists(tg_id)
if not exists:
await add_connection(tg_id, balance=0.0, trial=0)
try:
await bot.delete_message(
chat_id=tg_id, message_id=callback_query.message.message_id
)
except Exception as e:
logger.error(f"Не удалось удалить сообщение: {e}")
await bot.send_message(
chat_id=tg_id,
text="Выберите сумму пополнения:",
reply_markup=builder.as_markup(),
)
await state.set_state(ReplenishBalanceState.choosing_amount_robokassa)
await callback_query.answer()
@router.callback_query(F.data.startswith("robokassa_amount|"))
async def process_amount_selection(
callback_query: types.CallbackQuery, state: FSMContext
):
data = callback_query.data.split("|", 1)
if len(data) != 2:
await send_message_with_deletion(
callback_query.from_user.id,
"Неверные данные для выбора суммы.",
state=state,
)
return
amount_str = data[1]
try:
amount = int(amount_str)
except ValueError:
await send_message_with_deletion(
callback_query.from_user.id, "Некорректная сумма.", state=state
)
return
await state.update_data(amount=amount)
inv_id = uuid.uuid4().hex[:8]
payment_url = generate_payment_link(
ROBOKASSA_LOGIN,
ROBOKASSA_PASSWORD1,
amount,
inv_id,
"Пополнение баланса",
ROBOKASSA_TEST_MODE,
)
confirm_keyboard = InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="Оплатить", url=payment_url)],
[InlineKeyboardButton(text="⬅️ Назад", callback_data="pay")],
]
)
await callback_query.message.edit_text(
text=f"Вы выбрали пополнение на {amount} рублей. Для оплаты перейдите по ссылке ниже:",
reply_markup=confirm_keyboard,
)
await callback_query.answer()
async def robokassa_webhook(request):
"""Обработка уведомлений от Робокассы."""
params = dict(await request.post())
logger.debug(f"Webhook event received: {params}")
received_signature = params.get("SignatureValue")
amount = params.get("OutSum")
inv_id = params.get("InvId")
signature = calculate_signature(amount, inv_id, ROBOKASSA_PASSWORD2)
if signature.lower() == received_signature.lower():
user_id = params.get("Shp_user_id")
await update_balance(int(user_id), float(amount))
await send_payment_success_notification(user_id, float(amount))
return web.Response(text=f"OK{inv_id}")
else:
return web.Response(status=400)
async def send_payment_success_notification(user_id: int, amount: float):
"""Уведомление о успешном пополнении."""
builder = InlineKeyboardBuilder()
builder.row(
InlineKeyboardButton(text="Перейти в профиль", callback_data="view_profile")
)
await bot.send_message(
chat_id=user_id,
text=f"Ваш баланс успешно пополнен на {amount} рублей. Спасибо за оплату!",
reply_markup=builder.as_markup(),
)