Перейти к содержимому
Последнее обновление

3DS OTP

Когда карта используется для покупки, эквайер присылает одноразовый код подтверждения (3DS). SIM-карта не нужна: коды приходят письмом на почту карты, мы получаем их сразу при поступлении и отдаём через API и вебхук.

Endpoint

GET /cards/:id/otp/latest

Нужно право cards.sensitive.

Пример

curl 'https://b2b.lumowallet.io/cards/8c7f1823-.../otp/latest' \
  -H 'X-API-Key: YOUR_API_KEY'

Ответ

{
  "id": "a1b2c3d4-...",
  "cardId": "8c7f1823-...",
  "code": "582130",
  "maskedCard": "1234",
  "subject": "3DS Verification",
  "receivedAt": "2026-04-23T12:00:10.000Z",
  "expiresAt": "2026-04-23T12:05:10.000Z",
  "consumedAt": null
}
  • cardId — id карты в нашем API, тот же, что вы передали в пути.
  • maskedCard — окончание номера карты из письма эквайера.
  • expiresAt — receivedAt + 5 минут.

Эндпоинт отдаёт самый свежий код, срок которого ещё не истёк. Если такого кода нет, ответ — 200 с пустым телом, а не JSON null: проверяйте длину тела перед разбором.

Как получать коды в real-time

Вариант 1 — webhook (рекомендуется)

Событие card.otp_received приходит на ваш webhookUrl сразу после того, как письмо с кодом дошло до нас:

{
  "event": "card.otp_received",
  "cardId": "8c7f1823-...",
  "code": "582130",
  "maskedCard": "1234",
  "receivedAt": "2026-04-23T12:00:10.000Z",
  "expiresAt": "2026-04-23T12:05:10.000Z",
  "createdAt": "2026-04-23T12:00:11.000Z"
}

Вебхук приходит не на каждое письмо:

  • если письмо дошло позже чем через 15 минут после отправки, код сохраняется, но вебхук не отправляется — такой код уже недействителен;
  • тот же код по той же карте в течение 8 минут повторно не отправляется.

Подписываться на событие отдельно не нужно: все события компании приходят на один webhookUrl.

Вариант 2 — поллинг

Опрашивайте эндпоинт каждые 3–5 секунд после того, как пользователь начал 3DS-проверку, пока не придёт код с подходящим receivedAt. Код действует 5 минут (expiresAt).

async function getLatestOtp(cardId) {
  const res = await fetch(
    `https://b2b.lumowallet.io/cards/${cardId}/otp/latest`,
    { headers: { 'X-API-Key': process.env.LUMO_API_KEY } },
  );
  const text = await res.text();
  return text ? JSON.parse(text) : null; // пустое тело — кода нет
}

Тестовый режим

cardsTestMode = true всегда возвращает фиксированный код 843903 со сроком 2 минуты от текущего момента — удобно для автотестов.