К содержимому
TURANYOLРазработчикам

Партнёрский API

Проводите грузы, деньги и заказы через Садарак

Версионированный REST API (/v1), источник истины — OpenAPI: читайте назначенные вам заказы, сообщайте о событиях доставки и получайте подписанные вебхуки. Оплата при доставке — полноценный способ, а все суммы целые в гяпиках.

Что даёт партнёрский API

  • Чтение заказов

    Получайте назначенные вам заказы с позициями, временным окном, зоной, суммой наложенного платежа и полной хронологией статусов. Имя, телефон и адрес клиента приходят только со скоупом orders:pii.

  • События логистики

    Сообщайте нам PICKED_UP, EN_ROUTE, DELIVERED и FAILED. На хабе вы забираете заказ в статусе PACKED; каждое событие проходит через машину состояний заказа, поэтому невозможный переход отклоняется, а не портит заказ.

  • Вебхуки

    order.placed, order.status_changed, order.delivered, order.cancelled, batch.completed и order.assigned_to_partner — каждый подписан HMAC-SHA256 и повторяется с задержками до 12 часов.

  • Песочница

    Второй API со своей заполненной базой и своими ключами. Ключ песочницы никогда не работает с боевыми данными, а боевой — в песочнице.

Базовые URL

Боевая среда
https://api.turanyol.com
live
Песочница
http://localhost:3050
sandbox

Ключ привязан к одной среде. Ключ песочницы, отправленный в боевой API, отклоняется с ERR_API_KEY_ENVIRONMENT.

Быстрый старт

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

1. Аутентификация запроса

Передавайте ключ в заголовке X-Api-Key. Никакого OAuth и bearer-токена — ключ и есть учётные данные, поэтому храните его на сервере.

bash
curl -sS "https://api.turanyol.com/v1/partner/orders?status=EN_ROUTE&page=1" \
  -H "X-Api-Key: $ESADARAK_API_KEY" \
  -H "Accept: application/json"

2. Сообщить о событии доставки

Нужен скоуп logistics:write и заказ, назначенный вам. Деньги — целые гяпики: 4750 это 47,50 ₼.

bash
curl -sS -X POST "https://api.turanyol.com/v1/partner/orders/ES-1042/events" \
  -H "X-Api-Key: $ESADARAK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"DELIVERED","codCollectedQepik":4750}'

3. Проверка подписи вебхука

HMAC-SHA256 над меткой времени, точкой и сырым телом запроса. Сравнивайте за постоянное время и отклоняйте всё старше пяти минут.

javascript
import crypto from "node:crypto";

const SECRET = process.env.ESADARAK_WEBHOOK_SECRET;
const TOLERANCE_SEC = 300;

/** rawBody MUST be the exact bytes we sent (express: express.raw({ type: "application/json" })). */
export function verify(rawBody, signatureHeader) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((part) => part.trim().split("=", 2)),
  );
  const t = Number(parts.t);
  if (!Number.isInteger(t)) return false;
  if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_SEC) return false; // replay window

  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(`${t}.${rawBody}`)
    .digest("hex");

  const received = Buffer.from(parts.v1 ?? "", "hex");
  const digest = Buffer.from(expected, "hex");
  return received.length === digest.length && crypto.timingSafeEqual(received, digest);
}

Соглашения

  • Все суммы — целые в гяпиках (младшая единица AZN), имя поля заканчивается на Qepik. Никогда не float.
  • Ошибки — JSON со стабильным кодом (ERR_*) и никогда не локализованы: сопоставьте код со своими строками.
  • Названия товаров и категорий — объект с полями az, ru и en. Выбирайте локаль на своей стороне.
  • Телефоны в E.164 (+994XXXXXXXXX), метки времени ISO-8601 UTC, идентификаторы — UUID, коды — ES-1042 / B-2081.
  • Списочные эндпоинты возвращают массив items вместе с page, pageSize и total и ограничивают pageSize значением 50.

Дальше

Создайте первый ключ

Ключ показывается один раз при создании. При ротации старый ключ живёт ещё 24 часа, чтобы выкатить без простоя.

Направьте вебхук на свой эндпоинт

До пяти эндпоинтов на ключ, у каждого свой секрет и полный журнал доставок с повтором.

Прочитайте каталог ошибок

Все коды ERR_*, которые может вернуть API, сгруппированные по областям.

error envelope
{
  "statusCode": 403,
  "code": "ERR_API_KEY_SCOPE",
  "message": "key is missing scope logistics:write",
  "details": { "required": "logistics:write" }
}