Залили на Доктурист внутрисистемные уведомления. В первую очередь работают внутри агентств, если кто-то добавил, поменял, удалил бронь, платеж, дедлайн, или статус поменял, остальным агентам (у которых есть доступ) приходит уведомление. Но и для соло-агентов — если они применяют интеграции с банком или сервисом подписания документов — тоже пригодятся.
Уведомления приходят на иконку колокольчика, сопровождаются анимацией, полупрозрачной всплывашкой на 5 секунд, и нежным ненавязчивым «пыком» из динамика. Всплывашку и «пык» при желании можно отключить.
Генеративные нейросети и код-агенты конечно позволяют ворочать значительно большими смысловыми блоками при разработке ПО. Первый предложенный вариант реализации был еще более массивным shotgun surgery, чем прошлая фича — десятки изменений по 3–4 одинаковых строчки, утечки деталей реализации в контроллеры. 60+ измененных файлов (включая тесты). Предложил клоду более элегантное решение, практически без повторений, DI и event-driven, всё как в лучших домах Рио плюс де-Жанейро. Получилось решение более красивое, чистое как слеза Роберта Мартина — но уже на 100+ измененных файлов.
Работает хорошо, надо бы теперь пуши к этому прикрутить. И выделить время, чтобы расписать в виде небольших кейсов.
Когда-то давно описывал довольно простой способ виртуализации проектов, над которыми работаю, чтобы было удобно переключаться и они друг другу не мешали. Прошло уже 7 лет и способ конечно эволюционировал. Когда-то использовать отдельные контейнеры, запускаемые по `docker run` было быстрее и удобнее и поэтому я докерфайлы и docker-compose-файлы не писал. Но прогресс конечно добрался до меня в этом вопросе и победил. Теперь я для локальной разработки тоже использую docker compose.
Инструментами со всякими пресетами типа lando или ddev я, тем не менее, стараюсь не пользоваться. В основном потому что когда с ними сталкивался, они нормально работали — до тех пор пока не надо было что-то в них поменять или сделать нестандартно. После чего начинался вынос мозга, иногда такой, что проще было самому пересобрать сразу.
Кроме того, если собирать «чистые» окружения, есть шанс лучше понять, как это всё работает, а если пользоваться lando, forge, ddev и прочими — наоборот.
Добрые люди из команды Qui-Quo — плагина без которого обходится мало какой уважающий себя турагент — добавили наш Доктурист в список интеграций.
Теперь пользователям Доктуриста не нужно копировать в настройки интеграции загадочные адреса редиректов, а достаточно просто поставить галочку и интеграция заработает. Продолжаем развивать сервис!
Турагенты могут вести десятки броней одновременно. Каждый день — новые оплаты от туристов, каждый день нужно вручную открыть банк, найти платёж, отметить в CRM оплату по туру (иначе как отслеживать что ничего не забыл?) Звучит как мелочь — но это копится ежедневно, сбивает с ритма и иногда порождает ошибки. И в целом это довольно нудно — переписывать руками из одного места в другое. А что нудно — нам делать не очень хочется.
Поэтому мы добавили в Доктурист возможность импорта платежей из банков и начали с интеграции API банка Точка. Здесь рассмотрим кейс о том, как подключили Точку к Доктуристу: сделали автоматические уведомления о платежах, умную привязку к бронированиям и создание платёжных ссылок прямо со страницы брони. И о нескольких нетривиальных вещах, с которыми пришлось разобраться по дороге. Технических и не очень.
Дано
Турагенты постоянно работают с деньгами, основных операций при этом две: принять (пред)оплату от туриста, отправить оплату оператору. Доктурист уже умеет вести историю платежей и показывать их в симпатичном таймлайне:
Задача: нужно сделать что-то более автоматизированное, чем старательно переписывать входящие платежи из банк-клиента. То есть получать входящие платежи в Доктуристе.
Кроме того, агенты часто принимают оплату по ссылкам: создают в банковском приложении ссылку на оплату определенной суммы картой и/или СБП и отправляют туристу. Это такой облегченный эквайринг, когда можно не делать полноценную форму оплаты. И банк не против (если соблюсти определенные правила). Задача 2: почему бы не дать пользователям возможность создавать ссылки на оплату прямо из интерфейса Доктуриста. Чтобы они могли копировать их и отправлять клиентам, а банковское приложение открывать было не нужно.
Вот что конкретно мы научили Доктурист делать:
— получать уведомления от банка о каждом входящем платеже;
— автоматически привязывать платеж к бронированию (если это возможно);
— давать пользователю привязывать платеж к бронированию вручную (если автоматически не вышло);
— создавать ссылки на оплату картой и/или СБП.
С чем столкнулись в процессе
Для входящих платежей Точка отправляет уведомления на зарегистрированные вебхуки, причем вебхуки регистрировать можно программно. Пользователю нужно лишь добавить токен для интеграции и проставить несколько разрешений в банк-клиенте. Уведомления по вебхукам приходят практически мгновенно.
Вебхуки как подписанный JWT
Первое, с чем пришлось разобраться — формат входящих уведомлений от банка. В отличие от сервисов, которые присылают простой JSON с подписью в заголовке, Точка отправляет тело вебхука целиком как подписанный JWT. Это значит, что нельзя просто прочитать JSON — нужно сначала верифицировать подпись публичным ключом банка.
Контроллер читает сырое тело запроса и декодирует JWT через библиотеку firebase/php-jwt:
При любой ошибке декодирования — логируем и тем не менее отвечаем http 200. Это важно: Точка интерпретирует любой не-200 как сбой и будет повторно доставлять уведомление. А так — бизнес-логика не выполняется, но и банк не уходит в цикл отправки.
Тестирование этой механики потребовало отдельных настроек инфраструктуры: RSA-пара генерируется прямо в тестовом классе, публичный ключ подставляется в конфиг, и тогда мы подписываем тестовый пейлоад так же, как это делает Точка и расшифровываем как живое приложение.
Умная авто-линковка
Когда платёж приходит от банка, система пытается найти бронь автоматически — по тексту назначения платежа (например, «Оплата по бронированию ДЕМ-001»). Здесь возникает классическая проблема строкового поиска: если в агентстве есть брони ДЕМ-10 и ДЕМ-100, то поиск ДЕМ-10 найдёт обе. Но мы не хотим ошибиться при связи броней с платежами, поэтому автоматически связываем только если найден ровно 1 результат.
Если есть хотя бы еще одна бронь, чей номер начинается с найденного — линковка не происходит, платёж остаётся непривязанным. Пользователь увидит его в ленте и привяжет вручную. Для удобства привязки, поиск отфильтровывает старые, выполненные, оплаченные и отмененные брони.
Очень умная авто-линковка
Чтобы пользователи не забывали включать в назначение платежа номер брони, сделали, чтобы дефолтный текст при генерации платежной ссылки уже включал номер. Но боевое использование показало, что почти все платежи проходят мимо и ни с чем не линкуются.
После некоторых исследований оказалось, что хотя платежи в банк-клиенте показываются с нормальным назначением типа Оплата тура ФИО, номер брони, как мы задумали, в вебхук почему-то вместо этого прилетает Оплата по QR-коду ID QRABCD12345678 …. Пришлось реализовать дополнительный метод из API, который при получении платежа с таким описанием дергает Точку еще раз, чтобы получить инфу о данном QR-коде. В этой информации назначение платежа уже правильно отражается и по нему можно линковать платеж к брони.
Валютная конвертация при привязке
Точка естественно присылает суммы в рублях, поскольку расчёты в России могут быть только рублевые. Но брони в Доктуристе могут быть заведены и в USD, и в EUR, так как туроператоры могут работать в этих единицах, а платежи принимать по курсу. Когда пользователь вручную привязывает рублёвый платёж к заявке в EUR, система должна рассчитать сумму в EUR, чтобы записать её в правильной валюте. При этом у каждого туроператора свой курс конвертации.
Для этих случаев сделали два сценария:
Валютная заявка + override → пользователь вводит вручную, какое количество USD/EUR получается из платежа (например, «150 EUR»), мы используем это значение в сохраняемом платеже по брони и вместе с ним сохраняем расчетный курс валюты.
Валютная заявка, без override → берём последний курс ЦБ (он обновляется каждый день). В таком случае мы отмечаем связанный платеж как требующий внимания, поскольку авторасчет по курсу ЦБ это примерный расчет, а курс у операторов всегда выше.
Дефолтные значения при создании платежных ссылок
В процессе использования интеграции оказалось, что во многих случаях пользователи хотят создавать платежные ссылки с одним и тем же пресетом, например «Карта выключена, СБП включен». Ошибка в выборе средства оплаты может привести к денежным потерям (комиссия банка на эквайринге и СБП очень разная), что конечно недопустимо.
Пришлось реализовать дефолтную настройку для пользователей, указывающую, какие способы оплаты по умолчанию включены и какие выключены, чтобы снизить вероятность ошибки.
Песочница
В самом начале разработки из документации на API Точки выяснилось, что у них нет полноценной песочницы, позволяющей проводить «игрушечные» платежи, есть только фиксированный набор тестовых запросов, которые, если сделаны верно, возвращают фиксированные ответы. Тестовые запросы конечно небесполезны, но без песочницы невозможно, например, провести полный тестовый цикл: генерация платежной ссылки → её оплата → получение платежа → разбор и линковка к брони. Только по частям.
Собственно, создание ссылок пришлось по большей части тестировать наживую, поскольку оно работает только на аккаунтах, для которых уже активирован эквайринг, а для остальных аккаунтов кидает ошибку 403 "Forbidden by consent".
Но зато в банке есть линия поддержки по интеграции API, которая действительно отвечает по имейлу и даже дает подробную информацию, которой в документации не нашлось.
Результаты
100+ тестов на разные случаи жизни. Мы сделали верификацию JWT-подписи, дедупликацию, авто-линковку с граничными случаями, валютную конвертацию, права доступа. Интеграция API работает с Laravel Horizon, то есть все входящие и исходящие процессы (уведомления о платежах, создание платежной ссылки, регистрация пользователя в системе, регистрация вебхуков) запускаются асинхронно в очередях. А для уведомления ожидающих пользователей используются вебсокеты через Reverb.
Реализация позволила создать и протестировать логику для импорта платежей и создания платежных ссылок, которая не обязана привязываться к одному банку. Теперь необходимо добавить уровень абстракции между платежами/ссылками и API банка, и можно подключать другие банки к системе уже с меньшими трудозатратами. Следующий кандидат на интеграцию — Альфа.
Интеграция уже работает в продакшене. Агенты создают платёжные ссылки прямо со страницы брони и отправляют их туристам — без переключения в банковское приложение. Входящие платежи появляются в системе автоматически и в большинстве случаев сами привязываются к нужному бронированию. Руками нужно разбирать только то, что система не смогла распознать однозначно.
На днях в новостях сообщили, что разработчики Symfony закрыли 19 уязвимостей, найденных моделью Claude Mythos Preview. Что особенно интересно:
A few days later, and following Symfony's security disclosure process, we received a ZIP file with all their findings. In total, Claude Mythos reported 19 security vulnerabilities in Symfony and Twig codebases. The Symfony Core Team reviewed every report manually, and all 19 findings turned out to be real vulnerabilities, with no false positives.
ни одного ложноположительного результата.
Раньше я уже сталкивался с AI-отчетами по безопасности разных web-проектов. Обычно коллеги, или даже заказчики присылали для изучения и доработки. Тогда эти отчеты состояли из ложноположительных срабатываний чуть менее чем полностью. Но даже так затраченное на изучение отчета время того стоило: за 20 «ненастоящими» уязвимостями находилась одна-две-три актуальных. Что само по себе круто, ведь теперь можно их самому не искать, достаточно отсеять из отчета всякую фигню.
А тут — почти два десятка подтвержденных дырок и ни одной ошибки. И хотя Claude Mythos Preview еще не доступен каждому простолюдину — идет закрытое тестирование — это, видимо, успешный успех.
Что тут можно сказать. В наши дни нужно внимательно, ещё внимательнее относиться к регулярным обновлениям ваших программ, пакетов, фреймворков. Claude Mythos Preview может быть доступен и не всем, но исследовать код программы на уязвимости с помощью Claude Code уже сейчас может любой. И на входе не спрашивают, с какими намерениями.
А находить уязвимости умеет и он: XSS, утечки API-токенов, idorы всякие, господипрости. Так что был бы инструмент для поиска, а дырочка найдется. Как говорил нам классик, — There is a crack in everything. That's how the light gets in.
Как понять, что у какого-то проекта клёвая инфраструктура?
Ты прочитал письмо в рассылке, что у проекта через несколько дней выйдет обновление безопасности. Добавил в календарь, чтоб не забыть обновить всё и всех. Потом получил письмо СРОЧНО ВАЖНО от одного хостера. Потом от другого и третьего. Всё про это обновление безопасности.
Потом прочитал напоминание в тематическом телеграм-канале. Потом ожили коллеги в джире, каждый тоже написал о будущем обновлении. Вслед за коллегами ожили клиенты: они тоже получили уведомления от своих хостеров и теперь переживают. В общем ВСЕ уже знают, что у Друпала завтра вечером выйдет патч безопасности. Все знают, все готовятся.
Так что и я глаголю вам: да, завтра с 17 до 21 UTC должен выйти патч безопасности Drupal 10 и 11, вот страничка об этом. Обновление обозначено как высоко критичное, набрало 20 из 25 баллов по шкале трэша, угара и содомиии рисков безопасности. В воздухе запахло озоном, на теле заныли шрамы, полученные в ходе Друпалгеддона I и II.
Обновляйтесь, друзья, как можно скорее — это нужно. А если вы хотите сначала посмотреть, чо там будет, ответственно заявляю: всегда лучше обновиться, и посмотреть, чо там будет с теми, кто не стал, чем наоборот ;)
И да — вот такая клёвая инфраструктура сейчас у Drupal. Отдельная рассылка по обновлениям безопасности. Команда, которая этими дырками занимается. Проверки дырочек не только в ядре, но и в контрибных модулях. Патчи выкладываются в определенный день с предварительным уведомлением.
Когда готовил Доктурист к открытию, решил что должна быть все-таки у проекта какая-никакая новостная лента. До этого я сделал самый примитивный вариант: вывод ленты заголовков и текстовых сообщений с датами.
Решил, что этого конечно мало и какой-никакой джентльменский набор функций для постинга надо иметь. Хотя бы:
— редактор с форматированием и загрузкой картинок
— рубрики и/или теги
— редактируемые адреса страниц
— минимальную админку, чтобы из панели Orchid туда заходить
Конечно, писать я все это не хотел. Точнее я может и хотел бы, но каждый день старался сокращать число задач до запуска, а не увеличивать. Стал смотреть готовое.
Поиск и чат жопотэ присоветовали пакет Canvas для Laravel, его и взял. Редактор Quill внутри, небольшая админка на Vue.js с тёмной темой, есть немного лишних приблуд типа счётчика просмотров, есть базовое SEO, топики и теги. В общем, достаточно, чтобы не делать самому.
Прикрутил SSO, чтобы отдельно в эту админку не логиниться. Немного смутило, что в последнее время пакет не поддерживается. Но для ограниченного применения — ленты новостей и страниц документации — хватит. Стал пользоваться.
В процессе, правда, оказалось что админка почему-то не дружит с транслитом и не генерирует слаги с русскоязычных заголовков. Да вдобавок не даёт писать слаги для тегов и топиков самому: поле слага зачем-то блокируется во фронте, хотя бэк не против принять ручной ввод.
Взял Клода под мышку и пошёл это недоразумение исправлять. Клод говорит — смотри, эта фигня только во фронте, сейчас пару контролов попатчим, через композер патчи подключим, пересоберем и вуаля.
Стали делать — чото не работает, патченые контролы не собираются. И тут Клод такой: Je m'en fous, не патчатся исходники — я тебе попатчу билд! И написал скрипт на питоне, который транслит и ручные слаги просто втыкает в минифицированный джаваскипт. И прописал в пост-инсталл скрипты композера.
И я ведь как раз это недавно упоминал в статье про вайбкодинг. Машине всё равно, минимизирован код или нет, она его прочитает. Прочитает, воткнёт питоньим скриптом ещё нечитаемого кода, который через месяц никто не поймёт уже что делает, и будет такова. А для нас это неподдерживаемый код, которым ситуативно, в краткосрочной перспективе можно попользоваться (и я попользуюсь конечно). Но в долгосрочной надо форкать этот Canvas. Тем более редактор Quill в него так крепко встроен, что если я захочу кнопочек добавить в панель и плагинов всяких — всё равно придётся.
А в остальном, пакет пригодный, если не хочется ставить целую CMS, или руками весь процесс публикаций создавать в админ-панели. Накатал в Канвасе уже несколько страниц документации для проекта, и продолжу пользоваться.
P.S. Ещё недавно на другом проекте поработал с BookStack — прикольным wiki-решением для документации. Но оно для моих целей чересчур. И к тому же не прокатывает как ещё одна зависимость: её надо на отдельный сабдомен втыкать. И композером не обновляется — авторы предлагают обновления через git pull ставить.
В последнее время все чаще и больше в разработке использую генеративные нейросети в агентском режиме, для простоты будем называть это как все, вайб-кодингом. Ну а чего? Сейчас каждый утюг и его бабушка вайб-кодят и рассказывают о том, как это стало здорово. И в плане доступа к разработке тех, кто раньше разрабатывать не умел, это, кстати, действительно здорово.
Но кто и раньше умел разрабатывать сам, всё-таки смотрит на предмет немножко под другим углом. Поэтому решил немного растечься мыслями, поворчать, позанудствовать и поразмышлять не о том, как настроить Codex, MCP-сервер, или натравить Claude Code на KWork. А об эффектах, о неизбежных изменениях, которые внесли, вносят и будут вносить в нашу повседневную работу — в разработку программного обеспечения живыми человеческими людьми — нейросети.
Сначала коротко определим наше место в пространстве и времени относительно самого феномена нейросетей (здесь и далее — в разработке ПО). Обсуждать, нужны ли они, помогают ли они, следует ли их использовать — уже поздно. Этот вопрос больше не стоит. Если вдруг еще не начали, надо начать. Кот смотрит на часы и говорит — Пора!
Ну что ж, прошло несколько лет, ковидов, и других катаклизмов — и я открыл-таки регистрацию в нашей туристической crmке, которая называется Доктурист. Ура!
За последние месяцы была добавлена куча функций, без которых, как мне кажется, нельзя было запускать сервис. В моем списке таких функций еще очень много, но это ведь как ремонт — хочется сделать ещё вот это и вон то, а уж потом… В общем, в какой-то момент добавление фич можно было только приостановить для запуска. Последнее обновление, которое я выкатил сегодня ночью, готовилось 2 месяца и распухло до размеров небольшой планеты. В нём, в частности, была наконец открыта регистрация — так что теперь любой турагент может зайти и зарегистрироваться.
Теперь можно вернуться в нормальный режим постепенной подготовки и накатывания обновлений. По чуть-чуть, но непрерывно, добавлять, улучшать, допиливать и даже переделывать. Как оно и должно было быть у нормального программного обеспечения. Continuous integration, continuous delivery. Continuous joy.
В ближайшее время на очереди — открытие раздела статистики и подключение сервиса оплаты картами (сейчас пока можно только запросить счёт для оплаты по реквизитам).
По технической части (у нас же здесь технический бложек) Доктурист — это Laravel, Vue.js, Redis / Horizon / Supervisor для очередей, Reverb для вебсокетов, Pulse для быстрого ревью чо-как в системе, Canvas для новостей и статей, и ~1000 тестов по методике TDD для лучшей поддерживаемости. Vue.js используется версии 2. Прежде чем пытаться апгрейдить фронт на 3 (серьезная работа) — нужно было довести до ума что есть. В конце концов, я только в прошлом году апнул Laravel с 5 до 12.
В общем, если вы турагент, добро пожаловать! На сайте https://docturist.ru есть также ссылки на телеграм-канал и чатик. А если нет — можете просто за меня немножко порадоваться. Не каждый день удается отбросить сомнения и все-таки запустить проект, который начал делать еще в прошлом десятилетии. Есть повод порадоваться.
Отправить письмо
На нашем сайте используются куки-файлы, потому что без них всё плохо работает. А с ними — хорошо. Нажмите на кнопку «Согласен», если согласны с условиями обработки куки и данных на сайте. Если не согласны, можете отключить куки в настройках браузера.