Вебхук от платёжки ...
 
Уведомления
Очистить всё

Вебхук от платёжки пришёл дважды, и бонусы начислились два раза

5 Сообщения
4 Пользователи
1 Реакции
628 Просмотры
katrin47
Сообщения: 4
Автор темы
Пишет в irb бэкенд-разработчица
Присоединился: 3 года назад
[#36]

Платёжный провайдер шлёт вебхук об успешной оплате, мы кладём его в Sidekiq, джоба начисляет бонусы. Вчера у одного заказа бонусы начислились дважды: провайдер не дождался от нас 200, повторил вебхук через 30 секунд, и мы обработали оба. Проверка сейчас такая:

def perform(payment_id)
  payment = Payment.find(payment_id)
  return if payment.bonus_accrued?

  Bonus.accrue!(payment)
  payment.update!(bonus_accrued: true)
end

Две джобы шли параллельно, и обе увидели false. Как такое правильно закрывают?


4 ответа
GalinaKorneva
Сообщения: 30
Читает чужие PR бэкенд-разработчица
Присоединился: 4 года назад

Проверка флага в Ruby всегда гонка: между чтением и записью есть окно. Закрывают на уровне базы. Первое: сохраняйте id события от провайдера в таблицу с уникальным индексом и вставляйте его до любой обработки, повтор просто не пройдёт:

result = WebhookEvent.insert(
  { provider_event_id: params[:id], payload: params.to_unsafe_h },
  unique_by: :provider_event_id
)
return head(:ok) if result.rows.empty?

ProcessPaymentJob.perform_async(params[:payment_id])
head :ok

Второе: в самой джобе берите платёж с блокировкой, payment.with_lock { ... }, и проверяйте флаг уже внутри. И отвечайте провайдеру 200 сразу после записи события, а не после обработки, тогда и повторов станет меньше.


Ответ
kostya.sokurov
Сообщения: 73
Держит прод бэкенд-разработчик, senior
Присоединился: 4 года назад

Плюс уникальный индекс на bonuses.payment_id, если бонус по платежу бывает один. Последний рубеж, когда всё остальное подвело.


Ответ
katrin47
Сообщения: 4
Автор темы
Пишет в irb бэкенд-разработчица
Присоединился: 3 года назад

Закрыла все три дыры: таблица событий с уникальным id, with_lock в джобе и индекс на бонусах. Задвоенный заказ поправили руками. У провайдера в тестовом режиме можно слать повторы, прогнали 50 штук подряд, начисление одно.


Ответ
Улугбек Макаров
Сообщения: 5
Пишет в irb разработчик
Присоединился: 4 года назад

про 200 до обработки очень правильно, у многих провайдеров таймаут секунд 5-10, а джоба в очереди может и подождать


Ответ