Разделы документации
Документация/Рецепты
Оплата в Telegram-боте
Готовый флоу для бота: платёж, кнопка, вебхук, выдача товара
Типовой сценарий интеграции TabPay - продажи в Telegram-боте: цифровые товары, подписки, доступы. Поле telegramId избавляет от необходимости вести собственную таблицу соответствий «заказ - пользователь»: идентификатор покупателя возвращается в уведомлении об оплате.
Понадобится API-ключ активного магазина (раздел Аутентификация) и сохранённый Webhook URL в настройках магазина.
Шаг 1. Кнопка «Купить» создаёт платёж
Бот создаёт платёж и отправляет кнопку со ссылкой на payUrl; на платёжной странице покупатель выбирает СБП или карту.
import aiohttp
from aiogram import Bot, Dispatcher, F
from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup
API_KEY = "tp_ваш_ключ" # API-ключ магазина из кабинета
bot = Bot("ТОКЕН_БОТА")
dp = Dispatcher()
@dp.callback_query(F.data == "buy")
async def buy(callback: CallbackQuery):
# Создаём платёж и передаём telegramId покупателя -
# он вернётся в вебхуке, и бот сразу поймёт, кому выдавать товар
async with aiohttp.ClientSession() as session:
response = await session.post(
"https://tabpay.org/api/v1/payments",
headers={"X-Api-Key": API_KEY},
json={
"amountKopecks": 19900,
"orderId": f"tg-{callback.id}",
"description": "Подписка на месяц",
"telegramId": callback.from_user.id,
},
)
payment = await response.json()
keyboard = InlineKeyboardMarkup(inline_keyboard=[[
InlineKeyboardButton(text="Оплатить 199,00 ₽", url=payment["payUrl"])
]])
await callback.message.answer("Счёт готов - оплатите по кнопке:", reply_markup=keyboard)
await callback.answer()Полное описание полей - в разделе Создание платежа. orderId должен быть уникальным в рамках магазина - в примерах используется идентификатор колбэка. Если бот продаёт несколько товаров, передайте контекст в metadata (например, id товара и тариф) - объект вернётся в вебхуке без изменений, и бот сразу знает, что выдавать.
Шаг 2. Вебхук выдаёт товар
После оплаты TabPay отправляет POST-запрос на Webhook URL магазина; тело уведомления содержит telegramId покупателя:
{
"id": "6b9d2c88-4f1a-4c0e-9b7d-2f8e5a1c3d90",
"orderId": "tg-4382920412471814230",
"status": "SUCCESS",
"amountKopecks": 19900,
"telegramId": "987654321",
"metadata": null,
"test": false
}Обработчик проверяет подпись X-Signature-V2 (HMAC-SHA256 от строки «метка.тело» по сырым байтам секретом магазина; свежесть X-Timestamp - окно 5 минут; подробнее - в разделе Проверка подписи) и выдаёт товар:
import hashlib
import hmac
import json
import time
from aiohttp import web
WEBHOOK_SECRET = "секрет_подписи_из_кабинета"
async def tabpay_webhook(request: web.Request):
# Подпись v2 считается от строки "метка.тело" по СЫРЫМ байтам - до разбора JSON
raw = await request.read()
timestamp = request.headers.get("X-Timestamp", "")
received = request.headers.get("X-Signature-V2", "")
signed = timestamp.encode() + b"." + raw
expected = hmac.new(WEBHOOK_SECRET.encode(), signed, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, received):
return web.Response(status=400)
# Устаревшая метка - повтор перехваченного уведомления
if abs(time.time() - int(timestamp)) > 300:
return web.Response(status=401)
event = json.loads(raw)
# Тестовое уведомление из кабинета: подтверждаем, но товар не выдаём
if event.get("test"):
return web.Response(status=200)
if event["status"] == "SUCCESS" and event["telegramId"]:
# Идемпотентность: пометьте event["id"] обработанным,
# повторный вебхук с тем же id - просто отвечайте 200
await bot.send_message(
int(event["telegramId"]),
"Оплата получена - выдаём товар.",
)
return web.Response(status=200)
app = web.Application()
app.router.add_post("/tabpay/webhook", tabpay_webhook)
web.run_app(app, port=8080)Проверка интеграции без платежа
В настройках магазина на вкладке Webhook доступна кнопка «Отправить тестовый вебхук»: она отправляет на ваш URL уведомление, собранное как боевое, с корректной подписью, но с test: true (в боевых уведомлениях поле равно false). Обработчик должен ответить 200 и не выдавать товар - как в примерах выше. Так проверяются подпись и доставка уведомлений без реального платежа.
На что обратить внимание
- Выдавайте товар только при
status: "SUCCESS"- другие статусы (справочник) означают, что деньги не получены. - Вебхук может прийти повторно (без ответа 2xx доставка повторяется, всего до 7 попыток с нарастающим интервалом) - помечайте платёж по
idобработанным и не выдавайте товар дважды. - telegramId возвращается строкой - при необходимости приведите его к числу перед отправкой сообщения.
- Если уведомление не было получено (например, сервер бота был недоступен), статус платежа можно сверить запросом Получение платежа.
- Чтобы после оплаты покупатель вернулся в чат с ботом, передайте при создании платежа successUrl со ссылкой вида https://t.me/ваш_бот - платёжная страница переправит покупателя туда после успешной оплаты.
Если интеграцию выполняет ИИ-ассистент, передайте ему материалы со страницы ИИ и инструменты - готовый промпт и llms.txt включают этот рецепт.