Sobes.tech
Middle+
17

Пользователь нажимает кнопку оплаты на 100 рублей, POST-запрос уходит на сервер, но ответ не приходит — таймаут. Как сделать так, чтобы повторная отправка не привела к двойному списанию? Как эта задача решается на фронтенде и в архитектуре?

Companies where asked
Альфа СОФТ

Answer from AI

sobes.tech AI

Чтобы избежать двойного списания при повторной отправке запроса оплаты из-за таймаута, применяются следующие подходы:

На фронтенде:

  • После первого нажатия кнопки оплаты блокировать кнопку, чтобы пользователь не мог отправить запрос повторно.
  • Использовать уникальный идентификатор транзакции (например, UUID), который генерируется на клиенте и отправляется на сервер вместе с запросом. При повторной отправке с тем же ID сервер понимает, что это повтор и не создает новую оплату.
  • Показывать пользователю индикатор загрузки и сообщение о том, что запрос обрабатывается.

В архитектуре и на сервере:

  • Идempotency key — сервер сохраняет состояние обработки запроса с данным уникальным ключом. Если приходит повторный запрос с тем же ключом, сервер возвращает тот же результат, не создавая новую операцию.
  • Транзакционность и атомарность операций в базе данных: списание средств и запись транзакции должны быть одной атомарной операцией.

Пример генерации idempotency key на фронтенде (JavaScript):

function generateIdempotencyKey() {
  return crypto.randomUUID();
}

const idempotencyKey = generateIdempotencyKey();
fetch('/api/pay', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Idempotency-Key': idempotencyKey
  },
  body: JSON.stringify({ amount: 100 })
});

Таким образом, даже если запрос повторится из-за таймаута, сервер не спишет деньги дважды.