Как обрабатывать ситуацию, когда пользователь получил оповещение, но платеж не зарегистрировался в системе?
Что проверяют
Понимаете ли вы согласованность между своей базой и внешним платёжным провайдером: идемпотентность, сверку, отправку уведомлений только после фиксации данных. Следом обычно спрашивают, что будет, если провайдер пришлёт один и тот же вебхук дважды.
Как отвечать
Сначала разведите два случая. Пользователь получил уведомление от банка, а наш вебхук не дошёл или упал. Или наша система сама отправила пуш до коммита транзакции, и та откатилась. Лечатся они по-разному.
- платёж существует до оплаты: запись в статусе
PENDINGс ключом идемпотентности создаётся до перехода на форму провайдера. Тогда «потерянного» платежа не бывает, бывает зависшийPENDING, который видно запросом; - источник истины у провайдера: статус подтверждается запросом к API шлюза по его идентификатору платежа, а не текстом уведомления;
- надёжный вебхук: уникальный индекс по
provider_payment_id, обработчик идемпотентен и отвечает 200 только после коммита. Провайдеры повторяют доставку при ошибке, повтор не должен начислить деньги второй раз; - сверка: задача по расписанию (
@Scheduledс ShedLock или Quartz в кластере) опрашивает шлюз по всемPENDINGстарше нескольких минут, плюс ежедневная сверка с реестром провайдера; - уведомления после коммита: transactional outbox, где событие пишется в таблицу в той же транзакции, а отдельный процесс или Debezium публикует его в Kafka. Для простого случая хватит
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT); - что видит пользователь: статус «платёж обрабатывается», а не «ошибка». Поддержка находит платёж по номеру, деньги без заказа уходят в автоматический возврат или ручной разбор.
Где спотыкаются
Отправляют уведомление внутри @Transactional метода до коммита. Транзакция откатывается, а пользователь уже прочитал «оплачено».
Делают неидемпотентный обработчик вебхука. Повторная доставка превращает один платёж в двойное начисление.
Предлагают попросить пользователя оплатить заново. Если первый платёж на самом деле прошёл, это двойное списание и жалоба, а источник проблемы остаётся на месте.