Тестовый режим нужен ради одного: прогнать все ветки оплаты до того, как деньги станут настоящими. Не только «купил — получил доступ», а ещё пять неприятных веток, на которых обычно и ломается интеграция: покупатель передумал, ссылка истекла, колбэк пришёл с чужой подписью, событие пришло дважды, ваш сервер в этот момент лежал.
Если прогнать только успешный сценарий, первая же отменённая оплата в проде превратится в переписку с покупателем и ручную правку заказа. Ниже — что именно проверять, как развести тестовые и боевые ключи и как не испортить аналитику фиктивными продажами.
Что тестовый режим проверяет, а что нет
Тестовый режим проверяет ваш код: как вы создаёте платёж, как принимаете колбэк, как меняете статус заказа и что показываете покупателю. Этого достаточно, чтобы поймать 90% ошибок интеграции.
Чего он не проверяет: реальную маршрутизацию до банка, поведение конкретного банковского приложения, скорость зачисления и решение модерации по вашей категории проекта. Доступные методы и условия подтверждает команда при подключении — это отдельный шаг, и никакой sandbox его не заменит.
Практическое правило: после тестов сделайте одну живую оплату на минимальную сумму со своей карты или телефона. Это последняя проверка, которую нельзя подделать.
Разделите ключи и окружения до первой строки кода
Самая частая авария первого дня — тестовый стенд с боевым ключом. Разведите их так, чтобы перепутать было физически трудно.
- Две кассы, а не одна. У каждой свой
api_key, свойsigning_secretи свойcallback_url. Секреты выдаются один раз при создании кассы — сохраните их в менеджере секретов сразу. - Ключи только из переменных окружения.
ROLLYPAY_API_KEYиROLLYPAY_SIGNING_SECRETв.env, которого нет в git. Боевой ключ начинается сrpk_live_— по префиксу в логе видно, куда вы только что сходили. - Разные базы. Тестовые заказы не должны попадать в ту же таблицу
orders, из которой вы считаете выручку. Либо отдельная база, либо колонкаis_test boolean not null default falseи фильтр во всех отчётах. - Проверка на старте приложения. Если
APP_ENV=production, а ключ не боевой — падайте с понятной ошибкой, не запускайтесь «как-нибудь».
В колбэке приходит поле test: true для тестовых платежей, false для боевых. Это ваш последний рубеж — обработчик обязан на него смотреть и никогда не выдавать реальный доступ по событию с test: true.
Шесть сценариев до первой реальной продажи
| Сценарий | Как воспроизвести | Что должно получиться |
|---|---|---|
| Успех | Создать платёж и оплатить его | Колбэк payment.paid, заказ переходит в paid, доступ выдан один раз |
| Отмена | Закрыть форму оплаты, дождаться payment.canceled | Заказ закрыт как неоплаченный, покупателю предложен повтор |
| Истечение | Создать платёж и не платить до expires_at | Колбэк payment.expired, заказ не висит в «ожидании» вечно |
| Неверная подпись | Отправить себе POST с испорченным X-Signature | 403, тело не разбирается, заказ не меняется, запись в логе |
| Повтор события | Отправить один и тот же валидный колбэк дважды | 200 на оба, доступ выдан один раз, письмо отправлено один раз |
| Недоступный колбэк | Погасить обработчик на 10 минут во время оплаты | После подъёма событие доезжает повтором, заказ закрывается сам |
Отдельно проверьте повтор X-Nonce: одно и то же значение во втором запросе даёт 401 "nonce already used". Нонс живёт 10 минут, генерируйте новый UUID на каждый вызов. И проверьте создание платежа с тем же order_id — идемпотентность считается именно по нему, дубля быть не должно.
Как поймать колбэк на ноутбуке
Колбэк идёт на публичный HTTPS-адрес, а ваш обработчик пока живёт на localhost:3000. Поднимите туннель (ngrok, cloudflared, localtunnel — любой), получите адрес вида https://…/webhooks/rollypay и пропишите его в callback_url тестовой кассы.
Дальше — две вещи, которые ломают половину первых попыток. Первая: подпись считается по сырому телу запроса, поэтому в Express нужен express.raw() на этом маршруте, а не express.json() — после разбора JSON байты уже не те. Вторая: адрес туннеля меняется при перезапуске, и вы обновляете callback_url каждый раз. Подробности проверки — в статье про проверку подписи webhook.
Чтобы тесты не попали в отчёты
Бот-магазин пресетов на 40 заказов в месяц за неделю тестов сгенерировал 60 «продаж». Через месяц владелец смотрел на график и не мог понять, почему июнь оказался вдвое лучше июля.
Лечится тремя правилами. Тестовые заказы помечайте флагом при создании и фильтруйте во всех отчётах, а не только на главном дашборде. Событие покупки в аналитику отправляйте только при test == false. Письма, чеки и уведомления в рабочий чат на тестовых платежах не шлите — иначе бухгалтер увидит продажу, которой не было.
Чек-лист перед включением боевого режима
- Все шесть сценариев выше прошли и результат зафиксирован, а не «вроде работало».
- В проде подставлены боевые
api_keyиsigning_secret, тестовые из окружения убраны. callback_urlуказывает на настоящий домен, отвечает 2xx быстрее 10 секунд и доступен снаружи.success_redirect_urlиfail_redirect_urlведут на существующие страницы, а не на 404.- Обработчик проверяет подпись до разбора JSON и сверяет
order_id, сумму и валюту с заказом. - Есть журнал входящих событий, по которому можно поднять историю конкретного платежа.
- Тестовые заказы вычищены из базы или помечены флагом и исключены из отчётов.
- Сделана одна живая оплата на минимальную сумму и один возврат по ней.
Дальше держите тестовую кассу живой: любое изменение в обработчике колбэков сначала прогоняется на ней. Как вести себя, когда что-то всё-таки сломалось у покупателя, разобрано в статье что делать, если платёж не прошёл, а общая схема подключения — в материале про API приёма платежей. Если вы только выбираете, как встроить оплату в сайт, посмотрите решение для приёма платежей на сайте.