Как не дать пользователю заплатить дважды — и остаться в законе
Наткнулся на разбор реализации платежей в AI-продукте с ЮKassa. Не просто «как заплатить», а как не дать пользователю заплатить дважды, не дать скидке сработать, если она уже не действует, и не дать сбоям из-за ручного ввода. Это про то, как система думает о безопасности и законе — и как это касается любого сайта с формой заявки.
О чём речь и почему это важно для сайта
Речь о том, как работает оплата в системе, где пользователь вводит купон, смотрит итог, нажимает кнопку — и всё должно быть честно. Не просто «заплатил», а «заплатил один раз, по той цене, которую видел». Это не про технику, а про доверие. Если у вас есть форма заявки — вы оператор персональных данных. И если в системе нет контроля над тем, что попадает в базу, — это уже риск по 152-ФЗ.
Я считаю, что это важно не потому, что «всё должно быть безопасно», а потому, что если система не проверяет, что пользователь видит, — он может заплатить дважды, а потом не понять, за что. Или скидка сработает, хотя уже не действует. Это не баг — это нарушение. А нарушение — это не просто штраф, это потеря доверия.
Как это устроено — что тут вообще происходит
Система не доверяет клиенту. Ни вводу, ни кликам. Купон проверяется на сервере — не в браузере, не в React. Даже если пользователь меняет цену вручную, это не повлияет на итог. Сумма считается на бэкенде, с учётом скидок, с округлением по правилам. И только после этого формируется платеж.
Кнопка оплаты блокируется, чтобы не дать двойному клику. Но это не главное. Главное — Idempotence-Key. Каждый запрос получает уникальный ключ. Даже если пользователь перезагрузит страницу или откроет два таба — система узнает, что это один и тот же платеж. Это не про удобство, а про то, чтобы не создавать дубли в базе.
Как понять, что это касается вашего сайта
Если у вас есть форма заявки — вы уже оператор персональных данных. И если в этой форме есть поле для оплаты, купона, тарифа — вы уже в зоне риска. Даже если вы используете сторонний сервис, как ЮKassa, — контроль должен быть на вашей стороне.
Я проверяю это так: смотрю, где в системе происходит расчёт. Если цена считается в браузере, или если купон не проверяется на сервере — это тревожный сигнал. Если после ввода купона и нажатия на кнопку оплаты система не перепроверяет его — это уже не безопасность, это нарушение. Я смотрю, есть ли валидация на бэкенде, есть ли уникальные ключи, есть ли контроль над повторными запросами.
Что с этим делать — что я делаю в таких случаях
Первое — перенести расчёт цены на сервер. Ни в коем случае не доверять React. Если пользователь может изменить цену в DOM — это уязвимость. Даже если вы используете кэш, это не оправдание.
Второе — перепроверять купоны при каждом платеже. Не только при валидации, но и при создании платежа. Даже если он был проверен пять минут назад — он мог стать неактивным. И третий пункт — использовать уникальные ключи для каждого запроса. Это не про удобство, а про то, чтобы не создавать дубли. Это можно сделать даже без сложных систем — просто генерировать UUID на бэкенде при каждом запросе.
Что здесь даёт поддержка
На постоянной поддержке мы закрываем такие риски заранее. Не ждём, когда пользователь заплатит дважды, а проверяем, как устроена система оплаты — от формы до базы. У нас нет кейсов, но есть проверенная логика: всё, что связано с оплатой, перепроверяется на сервере, купоны не хранятся в клиенте, дубли не создаются. Главный итог — не «мы починим», а «вы перестанете думать о сайте».
Работаем с 2002 года, более 100 клиентов по всему миру. У нас есть сертификаты PHP и MySQL у всех разработчиков, мониторинг доступности 24/7. Приглашаем на бесплатный аудит сайта — проверим, как устроена ваша форма заявки и оплата.