# Webhooks Получайте real-time уведомления о событиях через HTTP callbacks. ## Настройка Укажите `webhookUrl` в профиле компании: ```bash curl -X PATCH 'https://b2b.lumowallet.io/auth/company/profile' \ -H 'X-API-Key: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "webhookUrl": "https://your-server.com/webhooks/lumo" }' ``` ## Формат запроса Webhooks отправляются как `POST` запросы: ```http POST /webhooks/lumo HTTP/1.1 Host: your-server.com Content-Type: application/json X-Lumo-Signature: 5d41402abc4b2a76b9719d911017c592 X-Lumo-Delivery-Id: 550e8400-e29b-41d4-a716-446655440000 {...payload...} ``` ### Заголовки | Заголовок | Описание | | --- | --- | | `X-Lumo-Signature` | HMAC-SHA256 подпись тела запроса | | `X-Lumo-Delivery-Id` | Уникальный ID доставки | | `Content-Type` | `application/json` | ## Верификация подписи Всегда проверяйте подпись для защиты от подмены: ```javascript const crypto = require('crypto'); function verifySignature(body, signature, secret) { const expected = crypto .createHmac('sha256', secret) .update(JSON.stringify(body)) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); } // В обработчике webhook app.post('/webhooks/lumo', (req, res) => { const signature = req.headers['x-lumo-signature']; if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); } // Обработка события handleEvent(req.body); res.status(200).send('OK'); }); ``` ```python import hmac import hashlib def verify_signature(body: str, signature: str, secret: str) -> bool: expected = hmac.new( secret.encode(), body.encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected) ``` ## События ### Order Events #### `order.status_changed` Изменение статуса ордера. ```json { "event": "order.status_changed", "orderId": "550e8400-e29b-41d4-a716-446655440000", "walletOrderId": "660e8400-e29b-41d4-a716-446655440001", "status": "success", "amountUsdt": 52.63, "amountRub": 5000.00, "completedAt": "2026-03-10T12:00:45Z", "failureReason": null, "createdAt": "2026-03-10T12:00:00Z" } ``` ### Deposit Events #### `deposit.created` Новый депозит обнаружен в сети. ```json { "event": "deposit.created", "depositId": "770e8400-e29b-41d4-a716-446655440002", "txHash": "abc123...", "walletAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE", "fromAddress": "TXyz...", "amount": 100.00, "tokenSymbol": "USDT", "status": "new", "createdAt": "2026-03-10T12:00:00Z", "updatedAt": "2026-03-10T12:00:00Z" } ``` #### `deposit.completed` Депозит успешно зачислен. ```json { "event": "deposit.completed", "depositId": "770e8400-e29b-41d4-a716-446655440002", "txHash": "abc123...", "walletAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE", "amount": 100.00, "status": "completed", "sweepTxHash": "def456...", "createdAt": "2026-03-10T12:00:00Z", "updatedAt": "2026-03-10T12:00:30Z" } ``` #### `deposit.aml_failed` AML-проверка не пройдена. ```json { "event": "deposit.aml_failed", "depositId": "770e8400-e29b-41d4-a716-446655440002", "txHash": "abc123...", "amount": 100.00, "status": "aml_failed", "amlCheckId": "check-123", "amlResult": "high_risk", "error": "Sanctioned address detected", "createdAt": "2026-03-10T12:00:00Z" } ``` ### User Deposit Events Для User Wallets — те же события, но с `externalId`: ```json { "event": "deposit.completed", "depositId": "880e8400-e29b-41d4-a716-446655440003", "externalId": "user-12345", "txHash": "ghi789...", "walletAddress": "TUserWallet...", "amount": 50.00, "status": "completed", "createdAt": "2026-03-10T12:05:00Z" } ``` ## Retry логика При ошибке доставки (не-2xx ответ) — автоматические ретраи: | Попытка | Задержка | | --- | --- | | 1 | Немедленно | | 2 | 1 секунда | | 3 | 2 секунды | | 4 | 4 секунды | Максимум **4 попытки**. После этого webhook помечается как failed. ## Рекомендации ### Быстрый ответ Отвечайте `200 OK` как можно быстрее. Обрабатывайте событие асинхронно: ```javascript app.post('/webhooks/lumo', async (req, res) => { // Сразу отвечаем res.status(200).send('OK'); // Обрабатываем в фоне setImmediate(() => { processWebhook(req.body); }); }); ``` ### Идемпотентность Используйте `X-Lumo-Delivery-Id` для дедупликации: ```javascript const processedDeliveries = new Set(); function handleWebhook(deliveryId, payload) { if (processedDeliveries.has(deliveryId)) { return; // Уже обработано } processedDeliveries.add(deliveryId); // Обработка... } ``` ### Логирование Логируйте все входящие webhooks для отладки: ```javascript console.log({ deliveryId: req.headers['x-lumo-delivery-id'], event: req.body.event, timestamp: new Date().toISOString() }); ``` ## Тестирование Используйте Mock-сервер для тестирования интеграции без реальных транзакций: ``` https://lumo.redocly.app/_mock/apis ```