Перейти к содержимому

Подключение Open API v1 ​

Open API позволяет собственной системе компании, инструментам автоматизации и аналитическим программам читать часть бизнес-данных Jenny Fabric. Первый этап закладывает безопасную основу и предоставляет пилотный доступ только для чтения к базовым данным образцов. Он подходит для синхронизации во внешнюю систему номера и названия образца, состава, ширины, плотности, спецификации, категории и публичной обложки.

Объём первого этапа

На первом этапе поддерживается только чтение базовых данных образцов: GET /openapi/v1/samples. Нельзя создавать, изменять и удалять образцы; записывать обратно остатки, заказы, финансы, изображения, контакты и другие данные; изменять основную базу Jenny. Данные, которые должны попасть в систему Jenny, по-прежнему вводятся в Jenny Fabric по обычному бизнес-процессу.

Если вход Open API ещё не виден в программе, функция пока не открыта для вашей компании. Фактическое состояние подключения, домен API и доступные лимиты определяются текущим входом в Jenny Fabric или уведомлением службы поддержки.

1. Подготовка ​

Перед подключением проверьте:

ПараметрОписание
Право подключенияOpen API управляет владелец компании в Jenny Fabric. Видимость входа для сотрудников зависит от прав компании.
API KeyИдентификатор вызывающей системы. Компания может создать отдельные ключи для разных задач, например «синхронизация с таблицей корпоративного WeChat» и «внутренняя BI-синхронизация».
API SecretСекрет для создания подписи запроса. Он показывается только один раз при создании или ротации; после закрытия окна система больше не показывает его открытым текстом.
ScopeНа первом этапе доступен samples:read.basic — базовые данные образцов только для чтения.
Белый список IPМожно разрешить вызовы только с фиксированного внешнего IP или подсети CIDR. Для рабочего подключения рекомендуется заполнить.
Ограничение частотыМинутный лимит по API Key. По умолчанию 120 запросов в минуту; фактический лимит определяется настройкой при создании ключа.

2. Важные границы ​

Open API v1 на первом этапе работает только на чтение и ничего не записывает:

  • Нет внешних интерфейсов записи POST / PUT / PATCH / DELETE.
  • Внешняя система не может создавать, изменять или удалять образцы.
  • Внешняя система не может записывать данные в основную базу Jenny.
  • Массовая загрузка изображений, файлов и весовых листов не поддерживается.
  • Не возвращаются себестоимость, закупочная цена, предложение поставщика, внутренние или приватные изображения, контакты и финансовая информация.
  • Open API не заменяет вход сотрудника; на первом этапе выдаётся только ограниченный набор полей согласно scope.

3. Адрес интерфейса ​

Интерфейс первого этапа:

text
GET /openapi/v1/samples

Полный URL состоит из домена подключения и пути:

text
https://<api-domain>/openapi/v1/samples

<api-domain> замените адресом, выданным при подключении. Не используйте примерный заполнитель в рабочем запросе.

4. Заголовки запроса ​

Каждый запрос должен содержать:

ЗаголовокОбязателенОписание
X-Jenny-KeyДаAPI Key, например jnk_....
X-Jenny-TimestampДаТекущая временная метка. Рекомендуется использовать миллисекунды; секунды также обрабатываются как секунды. Сервер принимает только запросы в пределах 5-минутного окна.
X-Jenny-NonceДаСлучайная строка, рекомендуется UUID. Для одного API Key один nonce нельзя повторно использовать в течение 10 минут.
X-Jenny-SignatureДаШестнадцатеричная HMAC-SHA256-подпись, рассчитанная с API Secret.

5. Правила подписи ​

Подпись подтверждает, что запрос не изменён и вызывающая сторона владеет API Secret.

Строка подписи состоит из 4 строк, соединённых переводом строки \n:

text
METHOD
/openapi/v1/samples
canonical_query
sha256(body)

Пояснения:

  • METHOD пишется заглавными буквами, например GET.
  • Вторая строка — путь запроса без домена.
  • canonical_query — строка параметров запроса после сортировки.
  • sha256(body) — шестнадцатеричный SHA-256-хеш тела запроса.
  • На первом этапе используется GET, поэтому тело пустое. SHA-256 пустой строки: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.
  • X-Jenny-Timestamp и X-Jenny-Nonce проверяются отдельно для защиты от повтора; не добавляйте их в строку подписи.

Правила canonical_query:

  1. Разберите параметры query URL.
  2. Отсортируйте по имени параметра.
  3. Одинаковые имена отсортируйте по значению.
  4. Примените URL encode к имени и значению.
  5. Соедините пары через & в стандартную строку query.

Например, исходные параметры:

text
page_size=50&page=1&keyword=cotton

После сортировки canonical_query:

text
keyword=cotton&page=1&page_size=50

Расчёт подписи:

text
hex(hmac_sha256(API_SECRET, canonical_string))

6. Пример подписи на Node.js ​

Пример показывает только создание подписи. В рабочем подключении храните API Secret в переменной окружения backend или системе управления секретами. Не помещайте его во фронтенд, браузерный сценарий, открытый репозиторий или снимок экрана.

js
import crypto from 'node:crypto'

const apiKey = process.env.JENNY_API_KEY
const apiSecret = process.env.JENNY_API_SECRET
const baseUrl = 'https://<api-domain>'
const path = '/openapi/v1/samples'

const params = new URLSearchParams({
  page: '1',
  page_size: '50',
})

function canonicalQuery(searchParams) {
  const pairs = Array.from(searchParams.entries())
  pairs.sort((a, b) => {
    if (a[0] === b[0]) return a[1].localeCompare(b[1])
    return a[0].localeCompare(b[0])
  })
  return new URLSearchParams(pairs).toString()
}

const method = 'GET'
const body = ''
const bodyHash = crypto.createHash('sha256').update(body).digest('hex')
const query = canonicalQuery(params)
const canonical = [method, path, query, bodyHash].join('\n')
const signature = crypto.createHmac('sha256', apiSecret).update(canonical).digest('hex')

const res = await fetch(`${baseUrl}${path}?${query}`, {
  method,
  headers: {
    'X-Jenny-Key': apiKey,
    'X-Jenny-Timestamp': String(Date.now()),
    'X-Jenny-Nonce': crypto.randomUUID(),
    'X-Jenny-Signature': signature,
  },
})

const data = await res.json()
console.log(data)

7. Интерфейс samples только для чтения ​

Запрос ​

text
GET /openapi/v1/samples

Необходимый scope:

text
samples:read.basic

Параметры query:

ПараметрОбязателенПо умолчаниюОписание
pageНет1Номер страницы, начиная с 1.
page_sizeНет50Количество строк на странице. Стандартный максимум 100; большее значение ограничивается максимумом.
updated_sinceНетНетВременная метка в миллисекундах; возвращаются только образцы, обновлённые не раньше неё.
keywordНетНетБазовый поиск по номеру, названию, составу и категории.

Результат сортируется по времени обновления в обратном порядке и подходит для инкрементальной синхронизации. updated_since передаётся в миллисекундах, например 1783075200000.

Возвращаемые поля ​

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

ПолеОписание
external_idВнешний идентификатор образца. Сохраните его во внешней системе и не полагайтесь на внутренний ID.
codeНомер образца.
nameНазвание образца.
constituentsСостав.
widthШирина.
weightПлотность.
specificationСпецификация.
typeКатегория.
public_coverURL публичной обложки. Возвращается только публичное изображение, без внутренней и приватной галереи.
created_atВремя создания в миллисекундах.
updated_atВремя обновления в миллисекундах.

Интерфейс не возвращает:

  • Себестоимость, закупочную цену, предложение поставщика, прибыль и результаты калькулятора себестоимости.
  • Поставщика готового товара, поставщика суровой ткани, фабрику, контакты и способы связи.
  • Внутренние и приватные изображения.
  • Финансы, платежи, сверки, счета-фактуры и личные данные клиентов.
  • Детализацию остатков, заказов, журналы действий и другие данные вне первого этапа.

Пример ответа ​

json
{
  "code": 0,
  "data": {
    "list": [
      {
        "external_id": "smp_xxxxx",
        "code": "A-1001",
        "name": "Хлопковый стрейч-сатин",
        "constituents": ["Хлопок 97%", "Эластан 3%"],
        "width": ["150CM"],
        "weight": ["220GSM"],
        "specification": ["В наличии"],
        "type": ["Хлопок-стрейч"],
        "public_cover": "https://<image-url>",
        "created_at": 1783075200000,
        "updated_at": 1783078800000
      }
    ],
    "total": 1,
    "page": 1,
    "page_size": 50,
    "has_more": false
  }
}

8. Защита от повторов: timestamp и nonce ​

Каждый запрос содержит X-Jenny-Timestamp и X-Jenny-Nonce.

Правила timestamp:

  • Рекомендуется метка в миллисекундах, например 1783075200000.
  • Сервер принимает только время в пределах 5 минут до или после текущего.
  • Если расхождение слишком велико, синхронизируйте часы вызывающей системы.

Правила nonce:

  • Создавайте новую случайную строку для каждого запроса, рекомендуется UUID.
  • Для одного API Key один nonce можно использовать только один раз в течение 10 минут.
  • При повторной попытке также создайте новый nonce и новую подпись.

При повторном nonce сервер отклоняет запрос, не позволяя повторно отправить уже подписанный вызов.

9. Права scope ​

На первом этапе есть один scope:

text
samples:read.basic

Он означает «базовые данные образцов только для чтения». Чтобы вызвать GET /openapi/v1/samples, отметьте этот scope при создании API Key.

Если позже появятся другие интерфейсы, каждый получит отдельный scope. Не используйте один ключ во всех системах ради удобства; создавайте отдельный ключ для каждого назначения и выдавайте только нужный scope.

10. Ограничение частоты ​

Open API применяет минутный лимит по API Key:

  • По умолчанию 120 запросов в минуту.
  • Фактический минутный лимит задаётся при создании ключа.
  • При превышении возвращается 429.

Для инкрементальной синхронизации используйте updated_since, не выполняйте часто полную загрузку. Получив 429, программа должна сделать паузу и повторить попытку позже.

11. Белый список IP ​

При создании API Key можно указать белый список:

  • Один публичный IP на строку, например 203.0.113.10.
  • Допускается подсеть CIDR, например 203.0.113.0/24.
  • Пустой список не ограничивает исходный IP.

Для рабочего подключения рекомендуется фиксированный внешний IP или подсеть. Если вызывающая сторона использует динамический IP, эластичный выход облачной функции или несколько NAT-выходов, заранее уточните все возможные адреса; иначе появится ошибка «Текущий IP отсутствует в белом списке».

12. Хранение и ротация Secret ​

Secret показывается только один раз:

  • Один раз при создании API Key.
  • Один раз при ротации Secret.
  • После закрытия окна система больше не показывает Secret открытым текстом.
  • Если Secret потерян, можно только выполнить ротацию и создать новый.

Рекомендации по хранению:

  • Используйте переменную окружения backend, систему управления секретами или защищённую конфигурацию CI/CD.
  • Не помещайте Secret во фронтенд, пакет мобильного приложения, браузерный сценарий или открытый репозиторий.
  • Не храните его длительное время открытым текстом в снимках экрана, групповых чатах или письмах.
  • Создавайте отдельные ключи для разных задач, чтобы отключать и ротировать их независимо.
  • При подозрении на утечку сразу отключите ключ или ротируйте Secret.

13. Частые ошибки ​

HTTP-статусЧастый codeЗначение
401missing_key / invalid_keyAPI Key отсутствует, недействителен или отключён.
401timestamp_invalidВременная метка отсутствует, имеет неверный формат или выходит за 5-минутное окно.
401missing_nonce / nonce_replayedNonce отсутствует или уже использован.
401signature_invalidНеверная подпись; обычно различаются сортировка query, body hash, путь или Secret.
403ip_not_allowedПубличный IP вызывающей стороны отсутствует в белом списке.
403scope_deniedAPI Key не имеет scope, нужного этому интерфейсу.
413body_too_largeТело превышает ограничение. Запрос samples первого этапа обычно не имеет тела.
429rate_limitedПревышен минутный лимит.

При диагностике подписи сначала выведите в журнал вызывающей стороны четыре значения: метод, путь, отсортированный query и body hash. Никогда не записывайте API Secret в журнал.

14. Рекомендуемый порядок подключения ​

  1. В тестовом сценарии запросите только page=1&page_size=10 и проверьте подпись, timestamp и nonce.
  2. Затем постранично прочитайте все образцы и сохраните external_id и updated_at каждой записи.
  3. Для последующих синхронизаций используйте инкрементальный запрос updated_since.
  4. Во внешней системе сохраняйте только необходимые для показа поля; не записывайте Secret, строку подписи и полные заголовки запроса в бизнес-журнал.
  5. Если полей недостаточно, сначала уточните у Jenny, относится ли требование к будущему открытому объёму. На первом этапе не обходите Open API через другие непубличные входы.

Связанные статьи ​

Jenny Software — цифровые решения для текстильной отрасли