Legan Studio
Все статьи
~ 8 мин чтения

Бот для тур-агентства в MAX: подбор тура, бронирование, поддержка

Как сделать бот для тур-агентства в MAX: подбор тура по параметрам, интеграция с GDS и Sletat, оплата, документы, поддержка в дороге и допродажи.

  • MAX
  • туризм
  • вертикали

Тур-агентство в мессенджере — отличная история: клиент задаёт 3–5 параметров, бот отдаёт топ-10 туров за 30 секунд, сравнивает, бронирует, присылает документы, поддерживает в поездке, напоминает о трансфере. Без бота тот же диалог занимает 40 минут в переписке менеджера + час обзвона по поставщикам. В этой статье — как спроектировать бот для тур-агентства в MAX: подбор по фильтрам, интеграция с GDS и агрегаторами (Sletat, TourVisor, Onlinetours API), бронирование, документы, push в поездке, допродажи (страховка, экскурсии, трансфер).

Что должен уметь бот тур-агентства

  1. Подбор тура: страна, даты, гости, бюджет, отель N* и выше.
  2. Сравнение 2–3 туров рядом (фото, цена, отель).
  3. Бронирование с предоплатой / полной оплатой.
  4. Загрузка паспортных данных туристов (с защитой ПДн).
  5. Документы: ваучер, авиабилеты, страховка — PDF присылаются в чат.
  6. Поддержка в поездке: «потерял документы», «как добраться», «отмена рейса».
  7. Допродажи: трансфер, экскурсии, ускоренный паспортный контроль.
  8. После возвращения — отзыв и предложение следующего тура.

Подбор тура

@bot.message_handler(commands=["search"])
async def on_search(msg):
    await set_state(msg.from_user.id, "search:country")
    kb = InlineKeyboardMarkup([
        [InlineKeyboardButton("🇹🇷 Турция", callback_data="ctry:TR"),
         InlineKeyboardButton("🇪🇬 Египет", callback_data="ctry:EG")],
        [InlineKeyboardButton("🇦🇪 ОАЭ", callback_data="ctry:AE"),
         InlineKeyboardButton("🇹🇭 Таиланд", callback_data="ctry:TH")],
        [InlineKeyboardButton("Другая страна", callback_data="ctry:other")],
    ])
    await bot.send_message(msg.chat.id, "Куда летим?", reply_markup=kb)

# дальше FSM: даты → гости → бюджет → отель

Состояние FSM в Redis:

state = {
    "country": "TR",
    "date_from": "2026-06-15",
    "nights": 7,
    "adults": 2,
    "children": [10],
    "budget_max": 250_000,
    "hotel_min_stars": 4,
    "meal": "all_inclusive",
}

Интеграция с поставщиками

Реальные источники в РФ-сегменте 2026 года:

ИсточникЧто даётОсобенности
Sletat.ru APIпоиск туров от 100+ туроператоровде-факто стандарт агентств
TourVisor APIальтернатива Sletatболее свежий API
Onlinetours.ruкаталог туровподходит для лидогенерации
Прямые API туроператоров (TUI, ANEX, Pegas)глубокая интеграция, лучше ценасложнее в подключении

Запрос к Sletat (упрощённо):

async def search_sletat(state: dict) -> list[dict]:
    params = {
        "departureId": 1,                 # Москва
        "countryId": SLETAT_COUNTRY[state["country"]],
        "dateFrom": state["date_from"],
        "dateTo":   add_days(state["date_from"], 3),
        "nightsFrom": state["nights"] - 1,
        "nightsTo":   state["nights"] + 1,
        "adults": state["adults"],
        "kids": ",".join(str(a) for a in state["children"]),
        "priceMax": state["budget_max"],
        "starsMin": state["hotel_min_stars"],
        "meal": state["meal"],
    }
    async with httpx.AsyncClient(timeout=30) as client:
        r = await client.get(
            "https://module.sletat.ru/Main.svc/GetTours",
            params=params,
            headers={"Authorization": f"Bearer {SLETAT_TOKEN}"},
        )
        return r.json()["tours"][:10]

Кеширование на 30 минут — обязательно: одинаковые запросы у разных пользователей дают тот же результат, экономите квоту API.

Карточка тура и сравнение

async def show_tour(chat_id: int, tour: dict):
    text = (
        f"*{tour['hotel']}* {'⭐' * tour['stars']}\n"
        f"📍 {tour['region']}, {tour['country']}\n"
        f"🛬 {tour['date_from']} → 🛫 {tour['date_to']} ({tour['nights']} ночей)\n"
        f"🍽 {tour['meal']}\n"
        f"👥 {tour['adults']} взр + {len(tour['kids'])} детей\n\n"
        f"💰 *{tour['price']:,} ₽*\n\n"
        f"_{tour['description'][:300]}..._"
    )
    kb = InlineKeyboardMarkup([
        [InlineKeyboardButton("📋 Подробнее", callback_data=f"tour:full:{tour['id']}"),
         InlineKeyboardButton("🆚 Сравнить", callback_data=f"tour:compare:{tour['id']}")],
        [InlineKeyboardButton("✅ Забронировать", callback_data=f"tour:book:{tour['id']}")],
    ])
    await bot.send_photo(chat_id, photo=tour["hotel_photo"], caption=text, reply_markup=kb, parse_mode="Markdown")

«Сравнить» — добавляет тур в сравнительный список (макс. 3), отдельная команда /compare рисует таблицу.

Бронирование и сбор паспортных данных

Это самый деликатный момент: ФИО, паспорт, дата рождения, гражданство — критичные ПДн. Шаги:

  1. Запрашиваем согласие на обработку ПДн (см. статью «Согласие и оферта в боте MAX»).
  2. Формы paspport — желательно через mini app с HTTPS, маскированием в чате, не plaintext в сообщении.
  3. На бэкенде шифруем перед сохранением (PostgreSQL pgcrypto или приложением на AES-256).
  4. Логи запросов в LLM/CRM — без ПДн (маскирование).
  5. Право на удаление (/delete_my_data) — обязательно по 152-ФЗ.
@bot.message_handler(commands=["book"])
async def on_book(msg):
    if not await has_consent(msg.from_user.id):
        return await request_consent(msg)
    # Открываем mini app для ввода паспортных данных
    url = f"https://passport.tour-agency.ru/?token={await issue_form_token(msg.from_user.id)}"
    kb = InlineKeyboardMarkup([[InlineKeyboardButton("Заполнить данные туристов", web_app=WebAppInfo(url=url))]])
    await bot.send_message(msg.chat.id, "Заполните данные туристов в защищённой форме:", reply_markup=kb)

Оплата

Тур-агентство обычно работает с предоплатой 50% + остаток за 30 дней до вылета. Платежи — ЮKassa / Тинькофф Эквайринг с поддержкой рассрочки (Долями, Тинькофф Pay).

async def create_tour_payment(booking_id: int) -> str:
    booking = await db.fetch_one("SELECT * FROM bookings WHERE id = $1", booking_id)
    return await yookassa.create_payment({
        "amount": {"value": str(booking.prepayment), "currency": "RUB"},
        "confirmation": {"type": "redirect", "return_url": "https://t.me/tour_agency_bot"},
        "description": f"Бронирование тура #{booking.id}",
        "metadata": {"booking_id": booking.id, "user_id": booking.user_id},
        "capture": True,
    })

Документы как PDF

После подтверждения от туроператора — генерация и отправка PDF:

async def send_tour_documents(user_id: int, booking: Booking):
    pdf_bytes = await render_pdf("ваучер", booking)         # weasyprint / reportlab
    await bot.send_document(user_id, document=pdf_bytes, filename=f"voucher_{booking.id}.pdf")
    await bot.send_document(user_id, document=booking.tickets_pdf, filename="avia_tickets.pdf")
    await bot.send_document(user_id, document=booking.insurance_pdf, filename="страховка.pdf")
    await bot.send_message(user_id, "📁 Документы сохранены. Распечатайте перед поездкой.")

Поддержка в поездке

В день вылета — push «До трансфера 4 часа, паспорт и ваучер при себе». В аэропорту — «У вашего рейса задержка X часов» (через парсинг авиа API). На отдыхе — кнопка «🆘 Помощь» с прямым звонком в поддержку и быстрыми ответами на ТОП-вопросы:

  • «Потерял паспорт» — инструкция + телефон консульства.
  • «Болезнь, нужна страховая» — телефон страховой ассистанс.
  • «Отмена обратного рейса» — что делать.
@bot.callback_query_handler(lambda c: c.data == "support:lost_passport")
async def on_lost_passport(call):
    booking = await get_active_booking(call.from_user.id)
    text = (
        f"📞 Консульство РФ в {booking.country}: {EMBASSY_PHONE[booking.country]}\n"
        f"📞 Наша поддержка 24/7: +7 (495) 000-00-00\n\n"
        "1. Напишите заявление в полицию.\n"
        "2. Получите справку.\n"
        "3. С ней — в консульство за свидетельством на возвращение."
    )
    await bot.send_message(call.message.chat.id, text)

Допродажи (upsell)

Высокомаржинальные продукты:

  • страховка от невыезда (5–7% от стоимости тура);
  • расширенная медицинская страховка;
  • индивидуальный трансфер вместо группового;
  • экскурсии (партнёрка с локальными гидами);
  • ускоренный паспортный контроль (Fast Track);
  • повышение класса перелёта.

После бронирования — серия cross-sell сообщений с TTL 1 неделю до вылета:

async def cross_sell_cron():
    bookings = await db.fetch_all("""
        SELECT * FROM bookings 
        WHERE status='confirmed' 
          AND start_date BETWEEN now() + interval '5 days' AND now() + interval '14 days'
          AND NOT cross_sell_sent
    """)
    for b in bookings:
        await suggest_insurance(b)
        await suggest_excursions(b)
        await mark_cross_sell_sent(b.id)

Возврат и отмена

Сложный процесс. Бот автоматизирует первый шаг:

  • запрос причины (передумали / болезнь / форс-мажор);
  • расчёт штрафа по правилам туроператора;
  • создание заявки в CRM на менеджера;
  • отслеживание статуса возврата.

Финальное решение — за менеджером, бот просто стандартизирует и ускоряет.

Маркетинг и реактивация

После возвращения:

  • отзыв (через /feedback);
  • через 1 месяц — «Вы были в Турции в июне, может ОАЭ зимой?»;
  • сегментация по бюджету / типу отдыха / детям.

CDP с историей путешествий + RFM-сегментация даёт CTR в 3–5 раз выше массовой рассылки.

152-ФЗ для тур-агентства

Особенно строго:

  • ФИО, паспорт, дата рождения — это спецкатегория (биометрия для авиабилетов).
  • Согласие на обработку с указанием цели.
  • Согласие на трансграничную передачу — для туров за рубеж паспортные данные передаются туроператору, а оттуда — авиакомпании, отелю. Всё фиксируется.
  • Право на удаление — после возврата клиент может попросить удалить ПДн, кроме обязательных к хранению (бухгалтерские документы — 5 лет).

Common pitfalls

  1. Кеш поиска с TTL 24 часа — цены меняются за минуты, продаёте «вчерашний» тур.
  2. Паспорт в plain text в чате — катастрофа по 152-ФЗ.
  3. Один менеджер на 1000 клиентов — поддержка валится в пик сезона.
  4. Нет напоминаний о трансфере — клиент опоздал в аэропорт, виноват кто? (Виноваты вы.)
  5. Без mini app для паспортов — клиенты пишут «Иванов 4509 123456» в открытом сообщении.

Итого

Бот для тур-агентства в MAX автоматизирует подбор по 5–8 параметрам, интегрируется с Sletat/TourVisor/Onlinetours, бронирует с предоплатой через ЮKassa, собирает паспортные данные через защищённую mini app с шифрованием, присылает PDF-документы, поддерживает в поездке push'ами и быстрыми сценариями, делает upsell страховки и экскурсий. Ключевые риски — 152-ФЗ (паспорта — спецкатегория ПДн), актуальность цен (кеш ≤ 30 минут), готовность поддержки в высокий сезон. MVP — 6–8 недель и 1–2 млн ₽; полнофункциональная версия с upsell, реактивацией, mini app для паспортов и интеграцией 3+ поставщиков — 12–18 недель и 3–6 млн ₽. Окупаемость — 4–8 месяцев за счёт снижения нагрузки на менеджеров и роста допродаж.

Частые вопросы

К каким API подключаться для подбора туров?

В РФ-сегменте 2026 года основной — Sletat.ru (де-факто стандарт, охватывает 100+ туроператоров), плюс альтернативы TourVisor и Onlinetours. Для глубокой интеграции и лучших комиссий — прямые API туроператоров (TUI, ANEX, Pegas, Coral), но это сложнее в подключении и поддержке. Обычно делают связку: Sletat для основного поиска + 1–2 прямых API крупнейших партнёров для эксклюзивных условий. Кешируйте результаты на 15–30 минут — экономит квоту и ускоряет ответы.

Как собирать паспортные данные в боте безопасно?

Категорически не в plain text в чат — это нарушение 152-ФЗ и риск утечки. Используйте mini app в MAX (или защищённую веб-форму) с авторизацией по одноразовому токену из бота. Поля валидируйте на клиенте и сервере. На бэкенде шифруйте поля паспорта/даты рождения через pgcrypto или приложением AES-256, ключ — в Vault. В чате никогда не цитируйте обратно полные паспортные данные, только last 4 цифры. Логи без PII. После завершения тура — оставляйте только обязательное по бухучёту.

Как часто обновлять цены и доступность?

Цены и места меняются в реальном времени — туроператоры закрывают категории за минуты. Кеш поиска — максимум 15–30 минут. На карточке тура перед бронированием обязательно делайте свежий запрос «availability check» в API поставщика. Если цена изменилась более чем на 5% — показываете «Цена изменилась с X на Y, продолжить?». Это защищает от продажи устаревших цен и от негативных отзывов «вы обещали 95 000, а в итоге 110 000».

Что бот должен делать в поездке клиента?

За 24 часа до вылета — напоминание про сборы, документы, время трансфера. За 4 часа — push «выезжайте через час». В аэропорту — мониторинг рейса через авиа-API (Aviasales, FlightStats), уведомление о задержках. В отеле — приветствие с контактами поддержки и скрипт «помощь по типовым ситуациям». За 24 часа до обратного рейса — напоминание о выезде. После возвращения — благодарность и просьба об отзыве. Это превращает бота из «сдал и забыл» в реальный сервис, что повышает повторные продажи на 30–50%.

Какие допродажи лучше всего работают?

Топ-3 по марже: страховка от невыезда (5–7% стоимости тура, конверсия 15–25% при предложении в правильный момент), индивидуальный трансфер вместо группового (+3–8 тыс. ₽, конверсия 20–30%), экскурсии (партнёрка с гидами, маржа 20–40%). Менее заметны, но прибыльны: расширенная медстраховка для пожилых и детей, повышение класса перелёта, ранний заезд / поздний выезд в отеле, упаковка багажа в аэропорту. Серия cross-sell сообщений с TTL 1 неделя до вылета — обычно 3–5 точек контакта.

Как обработать возврат и отмену тура?

Бот автоматизирует первый шаг: запрашивает причину (передумали / болезнь / форс-мажор), показывает расчёт штрафа по правилам туроператора (доступен через тот же Sletat API), создаёт заявку в CRM (amoCRM/Bitrix24) на менеджера. Финальное решение — за человеком: страховые случаи требуют документов, форс-мажоры — индивидуальной оценки. Бот трекает статус заявки и шлёт пользователю обновления, не заставляя его звонить и спрашивать. Среднее время разрешения сокращается с 5–7 дней до 1–2.

Сколько стоит и сколько занимает разработка?

MVP с подбором туров через 1 поставщика, бронированием, оплатой, выдачей документов — 6–8 недель и 1–2 млн ₽. Полнофункциональная версия с интеграцией 3+ поставщиков, mini app для паспортов, поддержкой в поездке, допродажами, реактивационными цепочками, кабинетом менеджера — 12–18 недель и 3–6 млн ₽. Поддержка инфраструктуры — 15–40 тыс. ₽/мес. Окупаемость для агентства с 100+ заявками в месяц — 4–8 месяцев за счёт снижения нагрузки на менеджеров (один обрабатывает 3× больше заявок) и роста среднего чека через upsell.