Разделы документации
Документация / Рецепты
Оплата в Telegram-боте
Готовый флоу для бота: платёж, кнопка, вебхук, выдача товара
Типовой сценарий интеграции TabPay - продажи в телеграм-боте: цифровые товары, подписки, доступы. Интеграция состоит из двух шагов: бот создаёт платёж и отправляет покупателю кнопку со ссылкой на оплату, а после оплаты получает вебхук и выдаёт товар. Поле telegramId избавляет от необходимости вести собственную таблицу соответствий «заказ - пользователь»: идентификатор покупателя возвращается в уведомлении об оплате.
Понадобится API-ключ активного магазина (раздел Аутентификация) и сохранённый Webhook URL в настройках магазина.
Шаг 1. Кнопка «Купить» создаёт платёж
По нажатию кнопки бот создаёт платёж одним POST-запросом и отправляет пользователю кнопку со ссылкой на 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 должен быть уникальным в рамках магазина - в примерах используется идентификатор колбэка.
Шаг 2. Вебхук выдаёт товар
После оплаты TabPay отправляет POST-запрос на Webhook URL магазина; тело уведомления содержит telegramId покупателя:
{
"id": "6b9d2c88-4f1a-4c0e-9b7d-2f8e5a1c3d90",
"orderId": "tg-4382920412471814230",
"status": "SUCCESS",
"amountKopecks": 19900,
"telegramId": "987654321"
} Обработчик проверяет подпись X-Signature (HMAC-SHA256 от сырого тела секретом магазина, подробнее - в разделе Проверка подписи) и выдаёт товар:
import hashlib
import hmac
import json
from aiohttp import web
WEBHOOK_SECRET = "секрет_подписи_из_кабинета"
async def tabpay_webhook(request: web.Request):
# Подпись считается от СЫРОГО тела - читаем байты до разбора JSON
raw = await request.read()
expected = hmac.new(WEBHOOK_SECRET.encode(), raw, hashlib.sha256).hexdigest()
received = request.headers.get("X-Signature", "")
if not hmac.compare_digest(expected, received):
return web.Response(status=400)
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. Обработчик должен ответить 200 и не выдавать товар - как в примерах выше. Это позволяет проверить подпись и доставку уведомлений без проведения реального платежа.
На что обратить внимание
- Выдавайте товар только при
status: "SUCCESS"- другие статусы (справочник) означают, что деньги не получены. - Вебхук может прийти повторно (доставка повторяется до ответа 2xx) - помечайте платёж по
idобработанным и не выдавайте товар дважды. - telegramId возвращается строкой - при необходимости приведите его к числу перед отправкой сообщения.
- Если уведомление не было получено (например, сервер бота был недоступен), статус платежа можно сверить запросом Получение платежа.
Если интеграцию выполняет ИИ-ассистент, передайте ему материалы со страницы ИИ и инструменты - готовый промпт и llms.txt включают этот рецепт.