Приём платежей в существующем веб-приложении: микросервис на Python с CloudPayments
У заказчика было работающее веб-приложение на Python, которому требовался приём оплаты, а вместе с ним полный цикл сделки: удержание суммы, списание, возврат и отражение в учёте. Мы не стали встраивать платежи в его код: написали отдельный микросервис, который подключается к приложению по gRPC, принимает уведомления эквайрера по HTTP и обменивается данными с 1С.
- Период
- 2025
Имя заказчика не раскрывается. Отрасль, масштаб и цифры остаются те же.
Что изменилось
- 0 строк
- Платёжного кода в приложении заказчикавесь платёжный контур вынесен в отдельный сервис; приложение вызывает его по контракту и не содержит ни ключей эквайрера, ни логики обработки платежей
- gRPC + HTTP
- Два интерфейса к одному сервисуgRPC используется приложением заказчика для внутренних вызовов по строгому контракту, HTTP служит для уведомлений эквайрера и подключения систем, которые gRPC не поддерживают
- Платежи → 1С
- Учёт получает данные обменом, а не выгрузкой рукамисервис передаёт в 1С сведения о проведённых платежах и возвратах; до внедрения соответствие поступлений и заказов устанавливалось сверкой вручную
У каждой величины указано, с чем сравнивали и за какой период.
Что было не так
Приложение работало и приносило деньги, но деньги эти собирались мимо него. Приём оплаты нужно было встроить в продукт, который уже написан, уже используется и который нельзя останавливать ради доработки.
Первый соблазн в такой ситуации это вписать платежи прямо в код приложения. Он плох сразу по нескольким причинам, и все они выясняются позже, чем принимается решение. Платёжная логика это самая изменчивая часть системы: меняются условия эквайрера, добавляются способы оплаты, появляются требования к чекам. Каждое такое изменение означало бы правку и перевыкладку всего приложения целиком.
Вторая причина серьёзнее. Платежи тянут за собой секреты: ключи доступа к эквайреру. Внутри общего кода они оказываются доступны всему приложению и всем, кто с этим кодом работает. Для контура, через который проходят деньги, это плохая отправная точка.
И третье: сделка не заканчивается списанием. Нужны удержание суммы до подтверждения, возвраты, обработка неуспешных попыток, и всё это должно отражаться в учёте. Заказчик вёл учёт в 1С, и сверка поступлений с заказами была ручной работой, которая растёт линейно вместе с числом платежей.
Приложение уже написано, уже используется и его нельзя остановить ради доработки. Вопрос был не «как написать платежи», а «как их добавить, ничего не сломав».
Что мы построили
Отдельный сервис на Python, упакованный в Docker и работающий рядом с приложением заказчика. Он занимается только платежами: принимает запрос на оплату, ведёт сделку через все её состояния, общается с эквайрером и отдаёт результат обратно.
Граница проведена намеренно жёстко. Приложение не знает, как устроен приём платежей, и не хранит ключей эквайрера, оно просто просит «принять оплату по такому-то заказу» и получает ответ. Всё, что за этим стоит, живёт в сервисе и может меняться независимо: подключить новый способ оплаты или поменять условия эквайринга теперь можно, не трогая продукт.
Приём оплаты реализован через CloudPayments, с полным циклом сделки. Сумма может удерживаться до подтверждения и списываться, когда обязательство исполнено, вместо схемы «взяли деньги сразу, а дальше разберёмся». Возвраты и неуспешные попытки это такие же состояния сделки, а не исключения, которые обрабатывает человек.
Docker здесь не дань моде: платёжный сервис обновляется чаще остального и перезапускается отдельно. Контейнер это то, что делает «выкатить новую версию платежей» действием, которое не касается приложения.
Приложение не знает, как устроен приём платежей, и не хранит ключей эквайрера. Оно просит «принять оплату по заказу» и получает ответ.
Почему два интерфейса, а не один
Сервис отвечает по двум протоколам одновременно, и это не избыточность, а ответ на то, что у него два разных собеседника.
Внутрь смотрит gRPC. Приложение заказчика вызывает сервис по строгому контракту: набор операций и структура данных описаны явно, и несовпадение обнаруживается при сборке, а не в момент оплаты. Для вызова, которым обмениваются две наши системы в одном контуре, это правильный выбор: быстрый бинарный протокол с проверяемым контрактом.
Наружу смотрит HTTP. По нему приходят уведомления от эквайрера о судьбе платежа, а платёжная система не будет подстраиваться под наш протокол, она умеет только вебхуки. По нему же подключается всё остальное: инструменты поддержки, отладка, любая система, у которой gRPC нет.
Отдельная ценность этого решения достаётся заказчику, а не нам. Сервис не посадил его приложение на единственный способ общения: если завтра платежи понадобятся другой части продукта или стороннему подрядчику, подключиться можно тем протоколом, который у них есть. Подрядчик, навязывающий свой велосипед как условие работы, это распространённый страх, и здесь он снят устройством решения.
У сервиса два собеседника: приложение в том же контуре и платёжная система снаружи. Заставлять их говорить на одном протоколе неудобно обоим.
Что происходит, когда что-то пошло не так
В платежах ошибка стоит дороже, чем где-либо ещё: здесь можно не просто потерять данные, а списать деньги дважды или не отдать оплаченное. Поэтому состояния сделки описаны явно, и переход между ними это операция, которую нельзя выполнить наполовину.
Уведомления эквайрера обрабатываются с защитой от повтора. Платёжная система повторяет доставку, если не получила подтверждения, и сервис, который этого не учитывает, рано или поздно проведёт один платёж как два. Повторное уведомление о той же операции распознаётся и не создаёт второй сделки.
Неуспешные и брошенные платежи не остаются в подвешенном состоянии: у каждого есть определённый статус, видимый и приложению, и поддержке. Это то, что отличает работающий платёжный контур от демонстрации: в демонстрации все платежи успешны.
Связка с 1С
1С осталась учётной системой заказчика, и сервис её не заменяет. Между ними налажен обмен: сведения о проведённых платежах и возвратах попадают в учёт как данные, а не как повод сесть и свести две таблицы.
Это убирает работу, которая росла вместе с бизнесом. Пока платежей десяток в месяц, сверка поступлений с заказами занимает полчаса; на сотнях она становится постоянной задачей человека, причём задачей, где ошибка означает расхождение в деньгах.
Обмен идёт фоновыми задачами: учётная система может быть недоступна или занята, и платёж в этот момент не должен ни теряться, ни блокировать пользователя. Задача, не выполнившаяся с первого раза, повторяется.
Что это дало
Приложение получило приём платежей, не будучи переписанным. Это главный результат: работающий продукт с пользователями остался тем же продуктом, к которому добавилась новая способность.
Платёжная часть стала обновляемой отдельно. Изменение условий эквайринга, новый способ оплаты, доработка логики возвратов: всё это правки в одном сервисе, которые не требуют выкладывать приложение целиком и не рискуют им.
Ключи доступа к эквайреру живут в платёжном контуре, а не в общем коде. Это не делает систему неуязвимой, но сокращает круг того, что нужно защищать, до одного сервиса.
И ручная сверка платежей с учётом ушла из ежедневной работы: данные доезжают до 1С обменом, а не переносом из выгрузки в таблицу.
На чём построено
Технологии проекта взяты из общего реестра компании: у каждой описана роль и класс задач, где мы её применяли.
Серверная часть
- PythonВторой серверный язык: данные и ИИ
- FastAPIВеб-слой для Python-сервисов
Данные
- PostgreSQLОсновная база данных
- RedisКеш, сессии и очереди
Интеграции
- 1СОбмен с учётной системой компании
- ЮKassa, CloudPayments, РобокассаПриём платежей на сайте
- REST, gRPC, GraphQL, SOAPСтандартные протоколы обмена
- Вебхуки, обмен файламиОбмен там, где готового API нет
Инфраструктура
- DockerУпаковка сервисов в контейнеры
- NginxБалансировка, маршруты, HTTPS
- LinuxСерверная операционная система
Что применяли в проекте
Каждая услуга это отдельный раздел с составом работ и ориентиром по стоимости.
У вас похожая задача?
Первый разговор ни к чему не обязывает: разберём вашу ситуацию и скажем, нужен ли вам такой проект вообще.
Другие проекты
- ИИ-обработка входящих заявок: ответы клиентам в Telegram, почте и на сайтеСистема собирает обращения из трёх каналов, отвечает на них сама по материалам заказчика и передаёт человеку то, что должен решать человек
- Единый канал заявок: Telegram-бот, форма сайта и переписка с клиентом в Битрикс24Заявки из бота и с сайта попадают в воронку Битрикс24 сами, а менеджер отвечает клиенту в Telegram прямо из карточки сделки
- Автосалон: выгрузка автомобилей на Авито и Авто.ру и сбор заявок в Битрикс24Объявления публикуются и обновляются из учётных данных салона, а обращения с обеих площадок попадают в воронку как сделки