Получить событие отслеживания
Скачать OpenAPIКогда происходит событие отслеживания, на которое вы подписаны, Cargotime отправляет POST-запрос на ваш callback_url.
После получения события как можно скорее верните 200 OK. Если обработка занимает время, сначала поставьте событие в очередь, отправьте ответ, а затем обработайте его асинхронно. Cargotime ждёт ответ 10 секунд.
Доставка считается успешной при любом статусе 2xx. Cargotime повторяет доставку после статусов 408, 409, 425, 429 и 5xx, а также при таймаутах и ошибках DNS или сети. После статусов 400, 401, 403, 404 и 422 повторной попытки не будет. При статусе 429 Cargotime учитывает заголовок Retry-After, если он передан. Всего может быть не более пяти попыток.
Чтобы проверить источник запроса, используйте секрет вебхука и заголовки x-cargotime-timestamp и x-cargotime-signature. Значение подписи — HMAC SHA-256 от строки timestamp + '.' + rawBody с префиксом sha256=. В rawBody должно попасть исходное тело запроса без изменений. Проверять подпись необязательно, но мы рекомендуем это делать.
Node.js / Express
import crypto from 'node:crypto';import express from 'express';const app = express();const secret = process.env.CARGOTIME_WEBHOOK_SECRET;if (!secret) { throw new Error('CARGOTIME_WEBHOOK_SECRET is not set');}function queueWebhook(payload: unknown): void { // Здесь можно поставить событие в очередь, // сохранить его в базе данных или передать внутреннему сервису. console.log('Cargotime прислал вебхук:', payload);}app.post('/webhooks/cargotime', express.raw({ type: 'application/json' }), (req, res) => { const timestamp = req.header('x-cargotime-timestamp'); const signature = req.header('x-cargotime-signature'); if (!timestamp || !signature) { return res.sendStatus(401); } const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(timestamp).update('.').update(req.body).digest('hex'); const signatureBuffer = Buffer.from(signature); const expectedBuffer = Buffer.from(expected); if (signatureBuffer.length !== expectedBuffer.length || !crypto.timingSafeEqual(signatureBuffer, expectedBuffer)) { return res.sendStatus(401); } const payload = JSON.parse(req.body.toString('utf8')); queueWebhook(payload); return res.sendStatus(200);});app.listen(3000, () => { console.log('Сервер вебхуков запущен на порту 3000');});Python / FastAPI
import hmacimport hashlibimport osfrom fastapi import FastAPI, Request, HTTPExceptionapp = FastAPI()secret = os.environ["CARGOTIME_WEBHOOK_SECRET"].encode()def enqueue_webhook(payload: dict) -> None: # Здесь можно поставить событие в очередь, # сохранить его в базе данных или передать внутреннему сервису. print("Cargotime прислал вебхук:", payload)@app.post("/webhooks/cargotime")async def cargotime_webhook(request: Request): body = await request.body() timestamp = request.headers.get("x-cargotime-timestamp") signature = request.headers.get("x-cargotime-signature") if not timestamp or not signature: raise HTTPException(status_code=401) digest = hmac.new( secret, timestamp.encode() + b"." + body, hashlib.sha256, ).hexdigest() expected = f"sha256={digest}" if not hmac.compare_digest(signature, expected): raise HTTPException(status_code=401) payload = await request.json() enqueue_webhook(payload) return {"ok": True}Параметры заголовков
HMAC SHA-256-подпись строки timestamp + '.' + rawBody с префиксом sha256=. Для rawBody используйте исходное тело запроса без изменений.
Метка времени Unix, использованная для подписи
Уникальный идентификатор события, совпадающий с payload.id
uuidУникальный идентификатор попытки доставки
uuidТело запроса
application/json
Типы TypeScript
Используйте тип request body в TypeScript.
Тело ответа
Примеры запросов
/trackingWebhookDelivery