СБП и способы оплаты

Каскадирование СБП и банковских карт: логика маршрутов

Резервирование должно менять доступный маршрут или предложенный метод, но не создавать два оплачиваемых заказа и не скрывать от пользователя реальную причину отказа.

Зачем держать больше одного способа оплаты

Каскад — это правило «если первый способ не сработал, предложи второй, не создавая новый заказ». Никакой магии: вы просто не отпускаете покупателя после первого отказа. Отдача заметная — часть людей, которые упёрлись в отказ банка, платят со второй попытки другим методом.

Причин для отказа много, и почти все они не про вас. У покупателя не подключён СБП в его банке, лимит переводов исчерпан, приложение банка не установлено на этом устройстве, платёж по карте не прошёл 3-D Secure. В каждом из этих случаев человек готов заплатить — ему просто нужен другой путь.

Второй мотив — устойчивость. Если у одного метода технические проблемы, проект с двумя методами теряет часть конверсии, а проект с одним методом останавливает продажи целиком. Для магазина цифровых товаров с выручкой в выходные это разница между «просели» и «день выпал».

Один заказ, несколько попыток: как это выглядит в базе

Главная ошибка каскада — путать заказ и попытку оплаты. Это две разные сущности, и их нужно развести в модели данных до того, как вы напишете первую строчку кода.

ЗаказПопытка оплаты
Сколько на покупкуровно одинсколько угодно
Ключorder_idpayment_id
Что храниткто, что и на сколько купилметод, статус, время жизни ссылки
Когда закрываетсякогда любая попытка дала paidпо paid, canceled или expired

В RollyPay идемпотентность создания платежа завязана на order_id. Это значит: если вы хотите вторую полноценную попытку с другим методом, у неё должен быть свой идентификатор — например ORD-1042-try2, где ORD-1042 остаётся в вашей таблице заказов как родитель. Тогда обе попытки видны в отчётах, а заказ по-прежнему один.

Практика, которая экономит нервы: заведите таблицу payment_attempts с полями order_id, payment_id, method, status, created_at. Выдачу товара привязывайте к заказу, а не к попытке — тогда даже две одновременно оплаченные попытки не превратятся в две отгрузки.

Что показать покупателю после отказа

Человек, у которого не прошла оплата, находится в худшей точке всей воронки. Ему нужно понять две вещи за три секунды: деньги не списались и что нажать дальше.

  • Скажите про деньги первым делом. «Оплата не прошла, деньги не списались» — эта фраза снимает главный страх.
  • Дайте одну кнопку. «Оплатить картой» под текстом об отказе СБП. Не список из четырёх вариантов.
  • Сохраните сумму и товар на экране. Возвращать человека в корзину заново — почти гарантированная потеря.
  • Не пересказывайте код ошибки банка. «Ошибка 05» не помогает никому.

Отдельно про причину отказа. Прятать её полностью — плохо: покупатель решит, что сломался ваш сайт. Но и подробности вроде «превышен лимит операций» вы часто не знаете точно. Рабочая середина: «Банк отклонил платёж. Так бывает из-за лимитов или настроек приложения — попробуйте другой способ». Честно и без выдумок.

Кнопка «попробовать снова тем же способом» тоже нужна: часть отказов разовые, и повтор через минуту проходит.

Чего нельзя делать: платёж на каждый клик

Самая частая поломка — создавать новый платёж по каждому нажатию кнопки. Покупатель нервничает, кликает пять раз, у вас в базе пять активных ссылок на одну покупку. Дальше начинается интересное.

  1. Двойная оплата. Человек открыл две вкладки и оплатил обе. Вы получаете два payment.paid и должны один из них вернуть.
  2. Мусор в отчётах. Конверсия рушится: знаменатель раздут отменёнными попытками, а вы гадаете, почему всё плохо.
  3. Битые уведомления. Колбэки приходят по всем пяти платежам, и обработчик выдаёт товар столько раз, сколько увидел paid.

Лечится тремя правилами. Первое: кнопка блокируется на время запроса. Второе: если у заказа уже есть попытка в статусе created или processing, отдавайте её pay_url, а не создавайте новую. Третье: обработчик колбэка проверяет, не выдан ли товар по этому заказу, — и если выдан, просто отвечает 2xx и ничего не делает.

Помните и про время жизни ссылки: в ответе на создание платежа есть expires_at. Пока он не наступил, старую ссылку можно и нужно переиспользовать. Новую попытку заводите, когда прилетело payment.canceled или payment.expired, — тогда каскад работает по факту, а не по клику мышью.

Что мерить, чтобы понять, работает ли каскад

Одной цифры «конверсия» здесь мало — она смешивает разные вещи. Считайте три.

МетрикаКак считатьЗачем
Конверсия попыткиоплаченные попытки / все попыткипоказывает качество конкретного метода
Конверсия заказаоплаченные заказы / все заказычестный ответ, сколько денег вы собрали
Доля вторых попытокзаказы с двумя и более попытками / все заказыговорит, стоит ли каскад усилий
Спасённые заказыоплата со второй попытки другим методомпрямая выручка от каскада

Пример из практики: SaaS с подпиской на 1 490 ₽ видел конверсию заказа 71%. После добавления второго метода на экран отказа доля вторых попыток составила около 12% заказов, из них оплатили примерно половину — конверсия заказа выросла до 77%, при этом конверсия попытки формально упала. Смотреть надо на заказы.

Когда каскад не нужен

Если у вас 20 продаж в месяц, разница между 71% и 77% — это один заказ. Потратить неделю на маршрутизацию ради него не стоит: соберите базовую оплату по платёжной ссылке и займитесь трафиком.

Каскад также бесполезен, когда отказы вызваны не методом, а модерацией или настройками кассы: пока категория проекта и доступные методы не подтверждены, второй способ будет отказывать так же, как первый. И он лишний, если у вас честно доступен один метод — тогда лучше вложиться в то, чтобы форма оплаты не теряла людей на телефоне. Когда объёмы дорастут, включить приём платежей на сайте с двумя методами можно за вечер — модель данных к тому времени уже будет правильной.

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

Чем попытка оплаты отличается от заказа?

Заказ один на покупку и хранит, кто и что купил; попыток может быть несколько, и каждая живёт своим payment_id, методом и статусом. Товар выдаётся по заказу, а не по попытке — иначе две оплаченные попытки дадут две отгрузки.

Можно ли создать вторую попытку с тем же order_id?

Нет: создание платежа идемпотентно по order_id, повтор вернёт вам прежний платёж. Для новой попытки используйте производный идентификатор вида ORD-1042-try2, а связь с исходным заказом храните у себя в таблице попыток.

Что писать покупателю, если банк отклонил платёж?

Сначала сообщите, что деньги не списались, — это главный страх человека в этот момент. Затем дайте одну кнопку со вторым способом и сохраните на экране товар и сумму. Коды ошибок банка показывать бесполезно.

Когда заводить новую попытку, а когда переиспользовать старую ссылку?

Пока у платежа не истёк expires_at и он в статусе created или processing, отдавайте покупателю ту же pay_url. Новую попытку создавайте после события payment.canceled или payment.expired.

Как понять, что каскад окупился?

Смотрите на конверсию заказа, а не попытки: при добавлении второго метода конверсия попытки обычно падает, потому что в знаменатель попадают неудачные первые заходы. Отдельно считайте долю заказов с двумя и более попытками и сколько из них дошли до оплаты.

Источники и документация