Отслеживание статусов заказов FoxReload: опрос вместо вебхуков
Важно знать это с самого начала: в FoxReload нет вебхуков. Нет X-FoxReload-Signature, нет HMAC-колбэков, нет событий order.*, нет Settings → Webhooks. Единственный способ узнать результат заказа — это опрос GET /api/orders/{order_id}.
Эта статья объясняет, как строить надёжный poll-loop для получения кодов, и как при необходимости построить собственный слой событий поверх опроса в инфраструктуре читателя.
1. Штатный способ: опрос GET /api/orders/{order_id}
После создания заказа (POST /api/orders) регулярно запрашивайте его статус:
curl "https://public-api.foxreload.com/api/orders/{order_id}" \
-H "X-API-Key: YOUR_API_KEY"
Возможные значения status: active, paid, processing, completed, cancelled, failed.
Терминальные состояния: completed, cancelled, failed — дальше статус не изменится.
Когда status == "completed", в каждой позиции items[].externalData — массив выданных кодов. Если по позиции была ошибка — смотрите items[].error.
async function waitForCompletion(orderId: string, maxAttempts = 30): Promise<Order> {
for (let i = 0; i < maxAttempts; i++) {
const order = await fetchOrder(orderId);
if (['completed', 'cancelled', 'failed'].includes(order.status)) {
return order;
}
// экспоненциальный backoff: 1s, 2s, 4s, … до 30s
await sleep(Math.min(1000 * Math.pow(2, i), 30000));
}
throw new Error('order poll timeout');
}
async function fetchOrder(orderId: string): Promise<Order> {
const resp = await fetch(
`https://public-api.foxreload.com/api/orders/${orderId}`,
{ headers: { 'X-API-Key': process.env.FOXRELOAD_KEY! } }
);
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
return resp.json();
}
2. Список заказов в определённом статусе
Для мониторинга незавершённых заказов удобен GET /api/orders/ с фильтром:
curl "https://public-api.foxreload.com/api/orders/?statuses=active,paid,processing&limit=50" \
-H "X-API-Key: YOUR_API_KEY"
Параметр statuses принимает значения через запятую: active, paid, processing, completed, cancelled, failed. Дополнительно: limit, offset.
Полезная стратегия: запускать фоновый воркер каждые 30–60 секунд, который опрашивает все незавершённые заказы (статусы active,paid,processing) и обновляет локальную БД.
3. Предотвращение дублей при отсутствии idempotency-ключей
В FoxReload нет idempotency-ключей. Это значит, что при повторном вызове POST /api/orders после network timeout может создаться второй заказ. Правильный паттерн:
async function createOrderSafe(items: OrderItem[]): Promise<Order> {
// 1. Сохрани pending-заказ локально ДО вызова API
const localRef = await db.orders.create({ status: 'local_pending', items });
try {
const order = await callCreateOrder(items);
await db.orders.update(localRef.id, { foxreloadOrderId: order.id, status: 'submitted' });
return order;
} catch (err) {
if (isNetworkError(err)) {
// 2. При сетевой ошибке — проверь, не создался ли заказ
const recent = await fetchRecentOrders(); // GET /api/orders/?statuses=active,paid,processing
// Сравни по itemId/quantity/сумме с localRef
const dup = recent.find(o => matchesLocalRef(o, localRef));
if (dup) {
await db.orders.update(localRef.id, { foxreloadOrderId: dup.id, status: 'submitted' });
return dup;
}
}
throw err;
}
}
4. Построение собственного слоя уведомлений поверх опроса
Если ваш бэкенд должен пушить события партнёрам или клиентам (например, вебхуки на вашем уровне), стройте их поверх poll-loop:
// Фоновый воркер
async function pollAndNotify() {
const pendingOrders = await db.orders.findByStatus(['active', 'paid', 'processing']);
for (const local of pendingOrders) {
const remote = await fetchOrder(local.foxreloadOrderId);
if (remote.status !== local.lastKnownStatus) {
// Статус изменился — публикуем событие в вашу очередь
await queue.publish('order.status_changed', {
orderId: local.id,
oldStatus: local.lastKnownStatus,
newStatus: remote.status,
externalData: remote.status === 'completed'
? remote.items.map(i => i.externalData)
: null,
});
await db.orders.update(local.id, { lastKnownStatus: remote.status });
}
}
}
// Запускать каждые 10–30 секунд
setInterval(pollAndNotify, 15_000);
Ваш слой затем отправляет вебхуки вашим партнёрам или уведомляет ваших клиентов — это полностью ваша инфраструктура; FoxReload в этой схеме выступает только как источник данных через REST-опрос.
5. Мониторинг и алерты
Метрики, которые стоит отслеживать в poll-loop:
| Метрика | Рекомендуемый алерт |
|---|---|
Заказы в статусе processing старше 5 минут |
Немедленный алерт |
Заказы в статусе active/paid без движения 10+ минут |
Проверка вручную |
Статус failed по позиции (items[].error) |
Лог + уведомление ops |
| HTTP 429 от FoxReload API | Увеличить интервал опроса, применить backoff |
Коды ответов
| Код | Значение |
|---|---|
| 200 | OK |
| 201 | Заказ создан |
| 401 | Нет или неверный X-API-Key |
| 403 | Ключ выключен или IP вне allowlist |
| 404 | Заказ не найден |
| 422 | Ошибка валидации тела запроса |
| 429 | Rate limit — применяйте backoff |
| 500 | Серверная ошибка — ретрай с backoff |
Нужна детальная документация по созданию заказов и структуре ответа? Получите доступ к FoxReload API.
