Подключение 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. Адрес интерфейса
Интерфейс первого этапа:
GET /openapi/v1/samplesПолный URL состоит из домена подключения и пути:
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:
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:
- Разберите параметры query URL.
- Отсортируйте по имени параметра.
- Одинаковые имена отсортируйте по значению.
- Примените URL encode к имени и значению.
- Соедините пары через
&в стандартную строку query.
Например, исходные параметры:
page_size=50&page=1&keyword=cottonПосле сортировки canonical_query:
keyword=cotton&page=1&page_size=50Расчёт подписи:
hex(hmac_sha256(API_SECRET, canonical_string))6. Пример подписи на Node.js
Пример показывает только создание подписи. В рабочем подключении храните API Secret в переменной окружения backend или системе управления секретами. Не помещайте его во фронтенд, браузерный сценарий, открытый репозиторий или снимок экрана.
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 только для чтения
Запрос
GET /openapi/v1/samplesНеобходимый scope:
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_cover | URL публичной обложки. Возвращается только публичное изображение, без внутренней и приватной галереи. |
created_at | Время создания в миллисекундах. |
updated_at | Время обновления в миллисекундах. |
Интерфейс не возвращает:
- Себестоимость, закупочную цену, предложение поставщика, прибыль и результаты калькулятора себестоимости.
- Поставщика готового товара, поставщика суровой ткани, фабрику, контакты и способы связи.
- Внутренние и приватные изображения.
- Финансы, платежи, сверки, счета-фактуры и личные данные клиентов.
- Детализацию остатков, заказов, журналы действий и другие данные вне первого этапа.
Пример ответа
{
"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:
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 | Значение |
|---|---|---|
401 | missing_key / invalid_key | API Key отсутствует, недействителен или отключён. |
401 | timestamp_invalid | Временная метка отсутствует, имеет неверный формат или выходит за 5-минутное окно. |
401 | missing_nonce / nonce_replayed | Nonce отсутствует или уже использован. |
401 | signature_invalid | Неверная подпись; обычно различаются сортировка query, body hash, путь или Secret. |
403 | ip_not_allowed | Публичный IP вызывающей стороны отсутствует в белом списке. |
403 | scope_denied | API Key не имеет scope, нужного этому интерфейсу. |
413 | body_too_large | Тело превышает ограничение. Запрос samples первого этапа обычно не имеет тела. |
429 | rate_limited | Превышен минутный лимит. |
При диагностике подписи сначала выведите в журнал вызывающей стороны четыре значения: метод, путь, отсортированный query и body hash. Никогда не записывайте API Secret в журнал.
14. Рекомендуемый порядок подключения
- В тестовом сценарии запросите только
page=1&page_size=10и проверьте подпись, timestamp и nonce. - Затем постранично прочитайте все образцы и сохраните
external_idиupdated_atкаждой записи. - Для последующих синхронизаций используйте инкрементальный запрос
updated_since. - Во внешней системе сохраняйте только необходимые для показа поля; не записывайте Secret, строку подписи и полные заголовки запроса в бизнес-журнал.
- Если полей недостаточно, сначала уточните у Jenny, относится ли требование к будущему открытому объёму. На первом этапе не обходите Open API через другие непубличные входы.
