NAV Navbar
cURL

Информация для вендоров

Данный раздел посвящен тому,

Терминология

МойСклад - SaaS-сервис управления торговлей, доступный по адресу https://online.moysklad.ru/

Маркетплейс - это набор программных компонентов в рамках сервиса МойСклад, позволяющий сторонним вендорам размещать свои приложения на витрине приложений, а пользователям МоегоСклада устанавливать (подключать) эти приложения и пользоваться ими.

Витрина приложений - отдельный экран МоегоСклада https://online.moysklad.ru/app/#apps, на котором размещаются опубликованные приложения. Витрина приложений доступна только администраторам аккаунта.

Вендор - сторонний по отношению к МоемуСкладу разработчик приложений для Маркетплейса.

Приложение - разработанный вендором программный продукт, расширяющий и дополняющий функционал МоегоСклада. Приложение размещается на витрине Маркетплейса и доступно для установки на аккаунт администратором аккаунта.

Аккаунт - пользовательский аккаунт МоегоСклада. Обычно аккаунт соответствует одной компании/бизнесу, в аккаунте несколько пользователей.

Аккаунт разработчика - привязанный к конкретному вендору пользовательский аккаунт МоегоСклада. На таком аккаунте на витрине приложений отображаются приложения вендора в статусах Draft и Ready, что позволяет выполнять проверку и отладку приложений до их публикации для всех пользователей МоегоСклада. Технически к одному вендору может быть привязано несколько аккаунтов.

Приложение на аккаунте - приложение, установленное (подключенное) на аккаунт. Альтернативный взаимозаменяемый термин - экземпляр приложения.

Пользователь МоегоСклада - конечный пользователь сервиса МойСклад, работающий с МоимСкладом в рамках определенного аккаунта.

Администратор аккаунта - пользователь МоегоСклада с правами администратора (в рамках определенного аккаунта).

Активация приложения - процесс включения приложения для конкретного аккаунта в системе вендора, выполняемый при подключении приложения пользователем (или при возобновлении платного приложения). В рамках активации происходит оповещение сервера вендора со стороны МоегоСклада.

Деактивация приложения - процесс выключения приложения для конкретного аккаунта в системе вендора, выполняемый при отключении приложения пользователем (или при приостановке платного приложения). В рамках деактивации происходит оповещение сервера вендора со стороны МоегоСклада.

Приостановка приложения - автоматический процесс выключения платного приложения при уменьшении количества опций "Платное приложение" или неоплате подписки пользователем МоегоСклада. После приостановки токен доступа приложения аннулируется. Первыми приостанавливаются последние установленные приложения на аккаунте.

Возобновление приложения - процесс включения приостановленного платного приложения при появлении на аккаунте свободных опций "Платное приложение". Возобновление может быть выполнено как автоматически (после оплаты пользователем МоегоСклада подписки или дополнительных опций "Платное приложение"), так и вручную пользователем МоегоСклада (активация экземпляра приложения по кнопке после освобождения опции “Платное приложение“ путем удаления экземпляра другого платного приложения). После возобновления приложению выдается новый токен доступа. Первыми возобновляются приложения, установленные ранее прочих.

Типы приложений в Маркетплейсе

Тип приложения определяет в первую очередь технический способ интеграции приложения с МоимСкладом.

Поддерживаемые на текущий момент типы приложений:

Сводная таблица поддержки функций для типов приложений

Тип приложения Количество приложений на аккаунте Iframe и доступ к контексту пользователя Активация и деактивация по Vendor API Доступ по токену к JSON API 1.2 Отладка на аккаунтах разработчика Phone API Loyalty API Блок на витрине приложений
Телефония 1 + + Звонки
Iframe-приложения Не ограничено + + Новые
Серверные приложения Не ограничено + + + + Новые
Системы лояльности 1 + Скидки

Серверные приложения

Серверные приложения - это приложения, основной функционал которых основан на обмене данными с МоимСкладом по JSON API. Для этого при активации приложения по Vendor API на сервер вендора передается токен доступа к JSON API 1.2 (доступ по токену поддерживается только в JSON API 1.2).

Для серверных приложений поддерживается активация и деактивация по Vendor API.

Для серверных приложений доступно получение контекста текущего пользователя МоегоСклада (через Vendor API) - то есть можно узнать, какой именно пользователь какого аккаунта открывает приложение в UI МоегоСклада.

Также серверное приложение может иметь свою вкладку среди iframe-приложений и пользовательский интерфейс. Используя данную возможность можно, например, реализовать управление настройками приложения со стороны пользователя МоегоСклада / администратора аккаунта. Поддерживается возможность расширенного iframe-окна (подробнее см. дескриптор->iframe).

В общем случае жесткие требования к внешнему виду / визуальному дизайну iframe-части приложений отсутствуют. Тем не менее, приветствуется визуальное соответствие дизайну МоегоСклада. Для этого мы разработали UI Kit (см. скриншот), который доступен по ссылке https://github.com/moysklad/html-marketplace-1.0-uikit.

useful image

Одновременно на аккаунте может быть подключено несколько серверных приложений.

Доступна отладка на аккаунтах разработчика.

Iframe-приложения

Iframe-приложение представляет собой веб-приложение, которое загружается по указанному вендором URL в iframe на отдельной вкладке/странице внутри UI МоегоСклада.

Iframe-приложения просты в разработке и размещении на площадке МоегоСклада, однако такие приложения тоже должны соответствовать требованиям к приложениям.

Для iframe-приложений поддерживается доступ к контексту пользователя и расширение окна iframe, однако не поддерживается активация и деактивация по Vendor API, а также доступ по токену к JSON API 1.2.

При построении пользовательского интерфейса в iframe-приложениях также желательно использовать UI Kit МоегоСклада.

Одновременно на аккаунте может быть подключено несколько iframe-приложений.

Доступна отладка на аккаунтах разработчика.

Телефония

Эти приложения представляют собой интеграцию с внешними сервисами телефонии по Phone API: https://online.moysklad.ru/api/phone/1.0/doc/index.html

Приложения телефонии на витрине приложений размещаются в блоке “Звонки”.

На текущий момент возможность получения доступа по токену к JSON API у приложений телефонии отсутствует, как и возможность активации и деактивации приложений телефонии по Vendor API (в перспективе такая возможность может появиться).

Одновременно на аккаунте может быть подключено только одно приложение телефонии.

Доступна отладка на аккаунтах разработчика.

Интеграция с системами лояльности

API для интеграции МоегоСклада с системами лояльности и внешними сервисами управления скидками: https://msloyaltyapi.docs.apiary.io

Данный тип приложений размещается на витрине в отдельном блоке “Скидки”.

Одновременно на аккаунте может быть подключено только одно приложение интеграции с системами лояльности.

На текущий момент для данного типа приложений недоступны возможности Маркетплейса (iframe, Vendor API, доступ по токену к JSON API, отладка на аккаунтах разработчика), так как данный тип приложений еще не заведен на механизмы Маркетплейса (но возможно ручное размещение приложений данного типа на витрине приложений).

Платные и бесплатные приложения

На текущий момент в Маркетплейсе поддерживаются как бесплатные, так и платные приложения.

Бесплатные приложения могут быть установлены пользователями на аккаунте вне зависимости от типа подписки (с ограничениями на Бесплатном тарифе) и в любом количестве. Тем не менее, вендор может брать оплату с пользователей приложений самостоятельно. Конкретный механизм самостоятельного взимания оплаты и его реализация остается на усмотрение вендора и не рассматривается в данном документе.

Для платных приложений в системе МойСклад на данный момент доступен только один фиксированный вариант стоимости - 500 рублей в месяц. Эта оплата взимается с пользователей в виде платной опции в рамках их подписки на МойСклад, поэтому здесь так же, как на подписку, для пользователей применяются скидки за оплату на 3, 6 и 12 месяцев.

Также мы написали для пользователей подробную инструкцию по тому, как устанавливать и оплачивать приложения. Вы можете посмотреть ее здесь.

Вендор получает 75% от стоимости своих приложений без учета скидок и комиссий. Например, если приложение стоимостью 500 руб было оплачено на 3 месяца, вендор будет получать 500 * 0,75 = 375 рублей ежемесячно.

В дальнейшем возможно изменение доли вендора в ту или иную сторону.

В дальнейшей перспективе будет реализован более гибкий биллинг приложений Маркетплейса, не привязанный в общем случае к подписке на МойСклад и в том числе позволяющий устанавливать вендорам произвольную стоимость приложений.

Требования к приложениям

Ниже перечислены требования, которым должно соответствовать приложение для успешного прохождения модерации:

Жизненный цикл приложения

На текущий момент для приложения Маркетплейса предусмотрены следующие статусы жизненного цикла:

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

  1. Приложение создается в Маркетплейсе со статусом Draft. Вендор проверяет и отлаживает работу приложения на аккаунте разработчика. По готовности приложения вендор отправляет/передает его на публикацию, статус приложения меняется на Ready.
  2. Сотрудники МоегоСклада проверяют приложение и, в случае отсутствия замечаний, публикуют приложение на витрине, статус приложения меняется на Published. Приложение становится доступно для установки пользователями МоегоСклада.
  3. Пользователи МоегоСклада подключают/устанавливают приложение на свой аккаунт, выполняют его настройку (если таковая опция имеется в приложении), пользуются приложением и, если оно становится ненужным, отключают/удаляют приложение с аккаунта.
  4. Если по каким-то причинам приложение требуется убрать с витрины приложений, то оно переводится в статус Suspended (Hidden). Установленные до этого момента экземпляры приложений на аккаунтах продолжают работать.

Дескриптор приложения

Дескриптор приложения представляет собой XML-структуру, которая описывает технические параметры встраивания/интеграции приложения вендора в МойСклад.

Дескриптор приложения должен соответствовать последней версии XSD схемы. Актуальной версией считается 1.1.0.

Сейчас рассмотрим дескриптор приложения на примерах.

Содержимое дескриптора приложения

<application xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"                                  
      xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v1 
      https://online.moysklad.ru/xml/ns/appstore/app/v1/application-1.1.0.xsd">
    <iframe>
        <sourceUrl>https://example.com/iframe.html</sourceUrl>
        <expand>true</expand>
    </iframe>
    <vendorApi>
        <endpointBase>https://example.com/dummy-app</endpointBase>
    </vendorApi>
    <access>
        <resource>https://online.moysklad.ru/api/remap/1.2</resource>
        <scope>admin</scope>
    </access>
</application>

На текущий момент в дескрипторе приложения есть три блока: iframe, vendorApi, access. Порядок расположения этих блоков относительно друг друга в дескрипторе может быть произвольный.

Блок Назначение Доступно для типов приложений Требует наличия других блоков
iframe Описывает iframe-часть приложения. iframe-приложения, серверные приложения Не требует
vendorApi Описывает взаимодействие по Vendor API. серверные приложения Не требует
access Описывает требуемый доступ приложения к ресурсам пользовательского аккаунта. серверные приложения vendorApi

Блок iframe

В теге iframe/sourceUrl указывается URL, по которому будет загружаться содержимое iframe внутри UI МоегоСклада. В URL допускается использование только протокола HTTPS.

В случае отсутствия блока iframe в дескрипторе считается, что у приложения отсутствует iframe-часть.

В теге iframe/expand указывается boolean значение, которое отвечает, будет ли iframe расширяться в основном приложении МоегоСклада. Под расширением подразумевается автоматическое масштабирование iframe элемента в зависимости от контента. Данный элемент является опциональным со значением false по умолчанию. Чтобы расширение iframe работало верно, на странице, которая указана в sourceUrl, должен работать скрипт, оповещающий страницу МоегоСклада об изменении высоты его контента, то есть:

Для удобства можно добавить следующий js скрипт на свою страницу. Пример:

<!doctype html>
<html>

<head>
...
</head>

<body>
...
<script type="text/javascript" src="https://online.moysklad.ru/js/ns/appstore/app/v1/moysklad-iframe-expand-1.js"></script>
</body>

</html>

Блок vendorApi

В теге vendorApi/endpointBase указывается базовый URL эндпоинта на стороне вендора, к которому будет обращаться МойСклад. В URL допускается использование только протокола HTTPS.

Для получения полного адреса конкретного эндпоинта Vendor API на стороне вендора к базовому URL’у добавляется суффикс /api/moysklad/vendor/1.0 и путь эндпоинта. То есть шаблон формирования полного URL ресурса в общем случае такой:

{endpointBase}/api/moysklad/vendor/1.0/{endpointPath}/…

Для эндпоинта активации/деактивации приложений на аккаунте шаблон следующий (endpointPath = apps):

{endpointBase}/api/moysklad/vendor/1.0/apps/{appId}/{accountId}

Например, если:

то полный URL ресурса на стороне вендора, к которому будет выполнять запросы МойСклад при активации (и деактивации) приложения на аккаунте, будет следующим:

https://example.com/dummy-app/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f

В случае отсутствия блока vendorApi в дескрипторе не выполняется активация и деактивация приложения на серверах вендора.

Блок access

В тегах access/resource (в перспективе их может быть несколько) указываются ресурсы, к которым приложению нужен доступ.

В тегах access/scope (в перспективе их тоже может быть несколько) указываются требуемые разрешения/требуемый уровень доступа. При наличии блока access должны присутствовать как минимум по одному тегу resource и scope.

Наличие блока access требует наличия блока vendorApi для передачи токена(ов) к ресурсам аккаунта при активации приложения по Vendor API.

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

На текущий момент для access/resource возможно только одно значение: https://online.moysklad.ru/api/remap/1.2

Для access/scope на текущий момент доступно тоже только одно значение: admin

Другими словами, на текущий момент блок access может иметь только такой вид:

<access>
    <resource>https://online.moysklad.ru/api/remap/1.2</resource>
    <scope>admin</scope>
</access>

Примеры дескрипторов

Для iframe-приложений

Дескриптор для iframe-приложения:

<application xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"                                  
      xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v1 
      https://online.moysklad.ru/xml/ns/appstore/app/v1/application-1.1.0.xsd">
    <iframe>
        <sourceUrl>https://example.com/iframe.html</sourceUrl>
    </iframe>
</application>

Дескриптор для iframe-приложения с расширением окна (expand):

<application xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"                                  
      xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v1 
      https://online.moysklad.ru/xml/ns/appstore/app/v1/application-1.1.0.xsd">
    <iframe>
        <sourceUrl>https://example.com/iframe.html</sourceUrl>
        <expand>true</expand>
    </iframe>
</application>

Для серверных приложений

Минимальный дескриптор для серверных приложений (без возможности настройки параметров приложения пользователем МоегоСклада, так как у приложения отсутствует iframe-часть)

<application xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"                                  
      xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v1 
      https://online.moysklad.ru/xml/ns/appstore/app/v1/application-1.1.0.xsd">
    <vendorApi>
        <endpointBase>https://example.com/dummy-app</endpointBase>
    </vendorApi>
    <access>
        <resource>https://online.moysklad.ru/api/remap/1.2</resource>
        <scope>admin</scope>
    </access>
</application>

Полный дескриптор для серверных приложений (с iframe-частью)

<application xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"                                  
      xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v1 
      https://online.moysklad.ru/xml/ns/appstore/app/v1/application-1.1.0.xsd">
    <iframe>
        <sourceUrl>https://example.com/iframe.html</sourceUrl>
    </iframe>
    <vendorApi>
        <endpointBase>https://example.com/dummy-app</endpointBase>
    </vendorApi>
    <access>
        <resource>https://online.moysklad.ru/api/remap/1.2</resource>
        <scope>admin</scope>
    </access>
</application>

Для телефонии

Для приложений телефонии дескриптор на текущий момент не требуется (не поддерживается).

Для систем лояльности

Для интеграций с системами лояльности дескриптор на текущий момент не требуется (не поддерживается).

Краткая инструкция по разработке, отладке и публикации приложений

На примере iframe- и серверных приложений.

Разработка iframe-приложений

При разработке iframe-приложения вендору следует определиться по следующим вопросам:

Разработка серверных приложений

При разработке серверного приложения следует определиться по следующим вопросам:

Также заметим, что для серверных приложений является обязательным реализация эндпоинта /api/moysklad/vendor/1.0/apps (см. Vendor API) на стороне вендора, и предполагается, что серверное приложение будет взаимодействовать с МоимСкладом по JSON API 1.2 с авторизацией по токену (передаваемому приложению по Vendor API при активации).

Карточка вендора

Карточка вендора - это Google Sheets таблицы, через которые происходит обмен информацией между вендором и МоимСкладом по приложениям. Каждому вендору будет предоставлен доступ к своей карточке. Там на первой вкладке “Вендор - информация” нужно будет заполнить форму с информацией по вендору в целом. Для каждого своего приложения вендору нужно сделать копию вкладки “Приложение - ШАБЛОН”, переименовать вкладку (вместо “ШАБЛОН” указать название приложения) и заполнить форму с информацией по приложению.

Отладка приложений на аккаунтах разработчика

Отладка неопубликованных приложений выполняется вендором на специальном аккаунте(ах), привязанных к вендору. На таких аккаунтах неопубликованные приложения вендора появляются в специальном отдельном блоке на витрине приложений. И неопубликованные приложения можно подключать и отключать так же, как и обычные опубликованные приложения.

Привязка аккаунтов к вендору осуществляется через сотрудника МоегоСклада.

Общая схема процесса размещения приложения в Маркетплейсе

После готовности приложения на стороне вендора в общем случае вендору нужно выполнить следующие шаги по размещению приложения в Маркетплейсе:

  1. Заполнить карточку приложения.
  2. Оповестить сотрудника МоегоСклада о готовности приложения к тестированию.
  3. Получить от сотрудника МоегоСклада secretKey для приложения, если для приложения предполагается взаимодействие. по Vendor API (оповещение по активации и деактивации, получение контекста пользователя для iframe).
  4. Протестировать и отладить приложение на аккаунте разработчика.
  5. Оповестить сотрудника МоегоСклада о готовности приложения к публикации.
  6. Сотрудник МоегоСклада смотрит на приложение и при отсутствии замечаний публикует его на витрине приложений.

Личный кабинет вендора

Для упрощения и автоматизации работы вендоров над приложениями Маркетплейса в ближайшее время будет реализован Личный кабинет вендора. Это сервис, который позволит вендорам самостоятельно создавать приложения в статусе Draft, отлаживать и тестировать на витрине приложений, вносить необходимые изменения (в том числе самостоятельно получать и изменять SecretKey) и, по окончании разработки, отправлять на модерацию в МойСклад. Таким образом, весь процесс размещения приложения в Маркетплейсе будет перенесен в Личный кабинет вендора.

Требования к иконкам приложений

При разработке иконки, которая будет отображаться на витрине приложений, нужно ориентироваться на следующие требования:

  1. Отчетливая крупная форма отчетливого цвета. Иконка будет на белом фоне и может теряться если в ней будут мелкие детали на белом фоне. К тому же один крупный знак будет просто и понятно восприниматься.
  2. Не делайте иконку без фона, если только она не заполняет выделенное пространство плотно, иначе она будет «исчезать» на фоне других иконок.
  3. Не ставьте скриншоты и фотографии и элементы интерфейса. Фотографии и экраны приложений могут включать много ненужных и плохо различичмых деталей. Детали интерфейса могут быть обманчивыми и путающими человека.
  4. По возможности не используйте слова. Используйте знак вашего логотипа, если у вас есть отдельный. Если используете какую-то букву из вашего логотипа как знак, то можно использовать его. Если вы так не делаете, то можно поставить полное название, учитывая все остальные пункты.
  5. Не ставьте логотип МоегоСклада или его имитации. Указать наш логотип значит сказать, что мы как-то учавствовали в разработке приложения, что будет неправдой.
  6. Размер иконки 90x90. Можно использовать все это пространство и не оставлять внутренних белых рамок.

useful image

Доступ по токену к JSON API 1.2

Базовая информация

Токен для доступа к JSON API 1.2 передается вендору при активации приложения через Vendor API в момент установки или возобновления приложения. Время жизни токена не ограничено.

Токен аннулируется при удалении и приостановке приложения. На момент деактивации приложения через Vendor API токен уже нерабочий (аннулирован).

Токен при доступе к JSON API 1.2 следует передавать как Bearer-токен, а именно в виде заголовка HTTP-запроса:

Authorization: Bearer <access_token>

Пример:

Authorization: Bearer 6ab89be1ae6ff147755625ee8da948e42612233b

Диаграмма последовательности предоставления доступа при подключении приложения

useful image

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

useful image

Vendor API

Vendor API предназначено для взаимодействия Маркетплейса с системами вендоров касательно процессов, происходящих в рамках функционирования Маркетплейса.

На текущий момент Vendor API включает в себя следующие функции:

Процесс активации приложения на аккаунте

В общем виде процесс активации приложения выглядит так:

  1. Пользователь МоегоСклада нажимает кнопку “Установить” приложение.
    • Для платных приложений возможно возобновление в случаях:
      • Пользователь, на аккаунте которого есть приостановленные приложения, оплачивает подписку с опцией "Платное приложение"
      • Пользователь удаляет работающее платное приложение, после чего нажимает кнопку "Активировать" у приостановленного приложения.
  2. Маркетплейс делает HTTP PUT запрос на сервер вендора, в том числе передавая причину активации и токен доступа к JSON API (если в дескрипторе приложения таковой доступ запрашивается). При этом доступ по переданному токену уже работает.
  3. Сервер вендора, в рамках допустимого тайм-аута, отвечает на запрос одним из статусов в теле ответа:
    • Activating - этот статус предназначен для асинхронной активации на стороне вендора (то есть если в рамках допустимого тайм-аута обработки HTTP PUT запроса не получается активировать приложение - например, для активации приложения требуется несколько минут). Далее вендор должен оповестить МойСклад через эндпоинт “Обратного вызова изменения статуса приложения на аккаунте” о завершении активации приложения статусом Activated или о том, что приложению требуется настройка статусом SettingsRequired.
    • SettingsRequired - таким статусом вендор сообщает МоемуСкладу, что приложению требуется настройка пользователем (через iframe-часть приложения). После того, как пользователь настроит приложение, вендор должен оповестить МойСклад через эндпоинт “Обратного вызова изменения статуса приложения на аккаунте” об активации приложения, передав статус Activated.
    • Activated - этот статус означает, что приложение сразу полностью активировано и уже заработало.

Диаграмма деятельности активации приложения

useful image

Процесс деактивации приложения на аккаунте

Процесс деактивации приложения существенно проще процесса активации:

  1. Пользователь МоегоСклада нажимает кнопку “Удалить” в карточке приложения.
    • Для платных приложений возможна приостановка в случаях:
      • У пользователя, на аккаунте которого есть установленные платные приложения, кончается подписка.
      • Пользователь, на аккаунте которого есть установленные платные приложения, оплачивает подписку с количеством опций "Платное приложение" меньше, чем установлено платных приложений.
  2. Маркетплейс аннулирует доступ по токену (если таковой был выдан) и выполняет HTTP DELETE запрос с причиной деактивации к серверу вендора. То есть на момент получения вендором оповещения по деактивации доступ по токену уже не работает.

Диаграмма деятельности деактивации приложения

useful image

Работа с вебхуками

Если ваше приложение может создать вебхук(-и) на аккаунте пользователя, то нужно иметь в виду следующее поведение:

Подробную информацию о работе с вебхуками можно получить по ссылке https://dev.moysklad.ru/doc/api/remap/1.2/dictionaries/#suschnosti-veb-huki

Особенности доступа к некоторым функциям JSON API 1.2

На данный момент для приложений существует ряд ограничений в работе со следующими сущностями JSON API 1.2:

МойСклад Vendor API 1.0

API для взаимодействия Маркетплейса с системами вендоров касательно процессов, происходящих в рамках функционирования Маркетплейса.

В данном документе описаны спецификации REST-эндпоинтов на стороне МоегоСклада и вендора приложений.

Аутентификация взаимодействия по Vendor API

Все запросы от МоегоСклада к серверу вендора и в обратную сторону от сервера вендора к МоемуСкладу должны быть подписаны с использованием JWT (JSON Web Token) описанным ниже образом.

Секретный ключ secretKey

Для построения/вычисления сигнатуры JWT используется секретный ключ secretKey.

Секретный ключ генерируется на стороне МоегоСклада и выдается вендору. На текущий момент это будет выглядеть так:

  1. Вендор передает МоемуСкладу дескриптор приложения.
  2. Сотрудник МоегоСклада загружает в Маркетплейс дескриптор, генерирует secretKey и передает его вендору.
  3. Если по какой-то причине вендор хочет перегенерировать secretKey, то он это делает тоже через взаимодействие с сотрудником МоегоСклада.

Исходящие запросы МойСклад → Вендор

МойСклад подписывает свои запросы к REST-эндпоинтам вендора, передавая JWT-токен в заголовке HTTP-запроса следующим образом:

Authorization: Bearer <token>

Описание полей JWT-payload:

Поле Описание Предполагаемое использование вендором
iat Время генерации токена в секундах от 1970-01-01T00:00:00Z UTC На усмотрение вендора
exp Время жизни/“протухания“ токена в секундах от 1970-01-01T00:00:00Z UTC - позволяет ограничить срок валидности токена (после указанного момента времени токен считается невалидным) При получении JWT после времени, указанного в exp, следует отклонять запрос с ошибкой аутентификации
jti Уникальный идентификатор токена - позволяет отслеживать одноразовость применения токена При получении JWT с jti, который был уже получен ранее, следует отклонять запрос с ошибкой аутентификации
JWT-header всегда такой:
{
  "alg": "HS256",
  "typ": "JWT"
}
JWT-payload включает в себя следующие поля:
{
  "iat": 1516239022,
  "exp": 1516239322,
  "jti": "6S3BQLsaSRNdEnhPCoW9lplY2LozRUOq"
}

Входящие запросы Вендор → МойСклад

Вендор при выполнении REST запросов к МоемуСкладу по Vendor API также должен подписывать их токеном JWT, который также следует передавать в заголовке HTTP-запроса:

Authorization: Bearer <token>

Поле alg в JWT-заголовке всегда должно иметь значение HS256. Соответственно, JWT-сигнатура всегда должна генерироваться по алгоритму HMAC SHA-256 с использованием секретного ключа secretKey.

Описание полей JWT-payload:

Поле Обязательное Описание Обработка на стороне МоегоСклада
sub да appUid приложения Используется для идентификации и авторизации приложения
iat да Время генерации токена в секундах от 1970-01-01T00:00:00Z UTC Используется для ограничения допустимого времени жизни токена
exp нет Время жизни/“протухания“ токена в секундах от 1970-01-01T00:00:00Z UTC (после какого момента токен считается невалидным) Если данное поле присутствует, то оно также используется для проверки валидности токена по времени
jti да Уникальный идентификатор токена Используется для проверки одноразовости использования токена

В системе МоегоСклада есть ограничение на максимальное время жизни для JWT-токена (порядка нескольких минут) - назовем его maxTokenLifetime. При проверке/валидации на стороне МоегоСклада значение exp ограничивается максимальным временем жизни: если (exp - iat) превышает maxTokenLifetime или поле exp отсутствует, то в качестве exp используется iat + maxTokenLifetime, то есть:

effectiveExp = exp ? min(exp, iat + maxTokenLifetime) : iat + maxTokenLifetime

JWT-header может быть как с указанием поля typ:
{
  "alg": "HS256",
  "typ": "JWT"
}
так и без поля typ:
{
  "alg": "HS256"
}
JWT-payload должен содержать следующие поля (все поля являются обязательными за исключением exp - это поле опциональное):
{
  "sub": "dummyapp.example-vendor",
  "iat": 1516239022,
  "exp": 1516239322,
  "jti": "6S3BQLsaSRNdEnhPCoW9lplY2LozRUOq"
}

Ошибки аутентификации и авторизации

Обработка ошибок аутентификации и авторизации на стороне МоегоСклада осуществляется аналогично тому как это сделано в JSON API 1.2:

https://dev.moysklad.ru/doc/api/remap/1.2/

То есть при возникновении ошибок возвращается аналогичный объект error и заголовки HTTP-ответа X-Lognex-Auth и X-Lognex-Auth-Message.

REST-эндпоинты на стороне вендора приложений

Если вендору требуется

то в этом случае вендору со своей стороны требуется реализовать REST-endpoint с доступом по HTTPS и поддерживающий HTTP-методы PUT, GET, DELETE:

https://<VENDOR-SERVER-ENDPOINT>/api/moysklad/vendor/1.0/apps/{appId}/{accountId}

где VENDOR-SERVER-ENDPOINT - тот URL, который указан в дескрипторе приложения.

Здесь

Активация приложения на аккаунте

Запрос должен обрабатываться сервером идемпотентно. Маркетплейс может повторять/дублировать запросы в соответствии со своей внутренней логикой (например, при работе механизма Retry).

HTTP-метод: PUT

Content-Type: application/json

В теле запроса передаем:

В теле ответа ожидаем получить следующую структуру:

Статус Описание Дальнейшие действия вендора Отображаемый пользователю статус приложения на витрине приложений
Activating приложение в процессе активации вендор оповестит Маркетплейс по эндпоинту обратного вызова об изменении статуса приложения (на SettingsRequired или Activated) приложение подключается
SettingsRequired требуется настройка приложения пользователем вендор оповестит Маркетплейс об изменении статуса (на Activated), когда пользователь выполнит настройку приложения (через iframe) приложение требует настройки
Activated приложение на аккаунте полностью активировано (и начало свою работу) действий вендора не требуется приложение подключено

HTTP status codes:

Пример активации бесплатного приложения:

Request:

PUT https://example.com/baseurl/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f

Body

{
  "appUid": "example-app.example-vendor",
  "accountName": "dummyaccount",
  "cause": "Install",
  "access": [
    {
      "resource": "https://online.moysklad.ru/api/remap/1.2",
      "scope": ["admin"],
      "access_token": "6ab89be1ae6ff147755625ee8da948e42612233b"
    }
  ]
}

Response:

Response 200

Content-Type: application/json

Body:

{
  "status": "SettingsRequired"
}


Пример активации платного приложения:

Request:

PUT https://example.com/baseurl/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f

Body

{
  "appUid": "example-app.example-vendor",
  "accountName": "dummyaccount",
  "cause": "Resume",
  "access": [
    {
      "resource": "https://online.moysklad.ru/api/remap/1.2",
      "scope": ["admin"],
      "access_token": "6ab89be1ae6ff147755625ee8da948e42612233b"
    }
  ]
}

Response:

Response 200

Content-Type: application/json

Body:

{
  "status": "Activated"
}

Деактивация приложения на аккаунте

HTTP-метод: DELETE

Тело запроса:

Тело ответа: пустое

HTTP status codes:

Пример деактивации бесплатного приложения:

Request:

DELETE https://example.com/baseurl/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f

Body

{
  "cause": "Uninstall"
}

Response:

Response 200

Content-Type: application/json



Пример деактивации платного приложения:

Request:

DELETE https://example.com/baseurl/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f

Body

{
  "cause": "Suspend"
}

Response:

Response 200

Content-Type: application/json

Проверка статуса активации приложения в системе вендора

HTTP-метод: GET

Тело запроса: пустое

Тело ответа:

HTTP status codes:

Пример

Request:

GET https://example.com/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f


Response:

Response 200

Content-Type: application/json

Response body

{
  "status": "SettingsRequired"
}

REST-эндпоинты на стороне МоегоСклада

Rest-эндпоинты на стороне МоегоСклада позволяют Вендору ограниченно управлять состоянием установки приложения для конкретного аккаунта, а также позволяют узнать информацию о пользователе, который загружает iframe-приложение в UI МоегоСклада.

Базовый URL REST-эндпоинтов со стороны МоегоСклада (далее будем называть его MARKETPLACE-ENDPOINT):

На текущий момент со стороны МоегоСклада есть следующие эндпоинты:

Обратный вызов изменения статуса приложения на аккаунте

С помощью этого эндпоинта вендор может изменить статус устанавливающегося приложения пользователя. При активации приложения со стороны вендора, вендор может ответить одним из статусов Activated, Activating, Settings. Если вендор перевел в статусы Activating и Settings, то МойСклад ожидает, что вендор с помощью обратного вызова оповестит МойСклад о том, что активация на его стороне завершена.

Resource: MARKETPLACE-ENDPOINT/apps/{appId}/{accountId}/status

Здесь

HTTP-метод: PUT

Тело запроса:

МойСклад при обработке данного запроса проверяет возможность перехода приложения на аккаунте в требуемое состояние в соответствии с жизненным циклом приложения на аккаунте. При отсутствии перехода по жизненному циклу - ошибка. Если приложение на аккаунте уже находится в целевом состоянии - то ошибки нет.

Тело ответа: пустое (за исключением ошибок)

HTTP status codes:

Пример

Request:

PUT https://online.moyslad.ru/api/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f

Body

{
  "status": "Activating"
}

Response:

Response 200

Content-Type: application/json

Получение контекста пользователя для приложений с iframe-частью

Через этот эндпоинт можно получить информацию по пользователю, который загружает приложение c iframe-частью в UI МоегоСклада. В URL, по которому загружается iframe-часть приложения, добавляется GET-параметр contextKey. Пример того, что будет загружаться в Iframe, при условии, что в дескрипторе приложения Iframe имеет значение https://yoursite.ru/moysklad: https://yoursite.ru/moysklad?contextKey=1c14e98cd272239c03bf3d9697f167699743292c. contextKey - это одноразовый ключ, который может быть использован не более одного раза для получения контекста пользователя через данный эндпоинт. В случае повторного использования contextKey - эндпоинт вернет ошибку.

Resource: MARKETPLACE-ENDPOINT/context/{contextKey}

Здесь

HTTP-метод: POST

Тело запроса: пустое

Тело ответа:

В случае успешного ответа возвращается такое же по структуре содержимое как в https://online.moysklad.ru/api/remap/1.2/context/employee

В случае ошибок - JSON-объект с ошибкой. Подробнее см. Обработка ошибок МоегоСклада.

HTTP status codes:

Пример

Request:

POST https://online.moysklad.ru/api/vendor/1.0/context/6S3BQLsaSRNdEnhPCoW9lplY2LozRUOq6S3BQLsaSRNdEnh


Response 200 (application/json). Успешный запрос.

{
  "meta": {
    "href": "https://online.moysklad.ru/api/remap/1.2/entity/employee/b0a02321-13e3-11e9-912f-f3d4002516e3?expand=cashier.retailStore",
    "metadataHref": "https://online.moysklad.ru/api/remap/1.2/entity/employee/metadata",
    "type": "employee",
    "mediaType": "application/json",
    "uuidHref": "https://online.moysklad.ru/app/#employee/edit?id=b0a02321-13e3-11e9-912f-f3d4002516e3"
  },
  "id": "b0a02321-13e3-11e9-912f-f3d4002516e3",
  "accountId": "b0b309ee-13e3-11e9-9109-f8fc0001f188",
  "owner": {
    "meta": {
      "href": "https://online.moysklad.ru/api/remap/1.2/entity/employee/b0a02321-13e3-11e9-912f-f3d4002516e3",
      "metadataHref": "https://online.moysklad.ru/api/remap/1.2/entity/employee/metadata",
      "type": "employee",
      "mediaType": "application/json",
      "uuidHref": "https://online.moysklad.ru/app/#employee/edit?id=b0a02321-13e3-11e9-912f-f3d4002516e3"
    }
  },
  "shared": true,
  "group": {
    "meta": {
      "href": "https://online.moysklad.ru/api/remap/1.2/entity/group/b0b3c289-13e3-11e9-9109-f8fc0001f189",
      "metadataHref": "https://online.moysklad.ru/api/remap/1.2/entity/group/metadata",
      "type": "group",
      "mediaType": "application/json"
    }
  },
  "updated": "2019-12-10 18:37:25.786",
  "name": "Кожевников",
  "externalCode": "Exh56G1wiRTPHpYBc-nx12",
  "archived": false,
  "created": "2019-01-09 10:53:45.202",
  "uid": "admin@bkozhevnikov",
  "email": "bkozhevnikov@moysklad.ru",
  "lastName": "Кожевников",
  "fullName": "Кожевников",
  "shortFio": "Кожевников",
  "cashiers": [
    {
      "meta": {
        "href": "https://online.moysklad.ru/api/remap/1.2/entity/retailstore/b0b7cd8d-13e3-11e9-912f-f3d400251724/cashiers/b0b7d387-13e3-11e9-912f-f3d400251725",
        "type": "cashier",
        "mediaType": "application/json"
      }
    }
  ],
  "permissions": {
    "currency": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "uom": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "productfolder": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "product": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "bundle": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "service": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "consignment": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "variant": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "store": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "counterparty": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "organization": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "employee": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "contract": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "project": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "country": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "customentity": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "demand": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "customerorder": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "internalorder": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "invoiceout": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "invoicein": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "paymentin": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "paymentout": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "cashin": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "cashout": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "supply": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "salesreturn": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "purchasereturn": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "retailstore": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "receipttemplate": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "retailshift": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "retaildemand": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "retailsalesreturn": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "retaildrawercashin": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "retaildrawercashout": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "prepayment": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "prepaymentreturn": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "purchaseorder": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "move": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "enter": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "loss": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "facturein": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "factureout": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "commissionreportin": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "commissionreportout": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "pricelist": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "processingplanfolder": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "processingplan": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "processing": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "processingorder": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "assortment": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "inventory": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "print": "ALL"
    },
    "bonustransaction": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "crptorder": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "approve": "ALL",
      "print": "ALL"
    },
    "webhook": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL"
    },
    "task": {
      "view": "ALL",
      "create": "ALL",
      "update": "ALL",
      "delete": "ALL",
      "done": "ALL"
    },
    "dashboard": {
      "view": "ALL"
    },
    "stock": {
      "view": "ALL"
    },
    "customAttributes": {
      "view": "ALL"
    },
    "pnl": {
      "view": "ALL"
    },
    "company_crm": {
      "view": "ALL"
    },
    "tariff_crm": {
      "view": "ALL"
    },
    "audit_dashboard": {
      "view": "ALL"
    },
    "admin": {
      "view": "ALL"
    },
    "dashboardMoney": {
      "view": "ALL"
    }
  }
}

Обработка ошибок на стороне МоегоСклада

Обработка ошибок на стороне МоегоСклада (при взаимодействии Вендор → МойСклад) выполняется аналогично тому, как это сделано в JSON API 1.2 https://dev.moysklad.ru/doc/api/remap/1.2/ - в тело ответа включается JSON объект с описанием ошибки и также проставляются (если нужно) соответствующие HTTP-заголовки в ответе.

Механизм Retry

Механизм Retry повторяет попытки выполнить запрос к системе вендора в соответствии с некоторым конфигурационным профилем, глобальным для Маркетплейса, задаваемым в общем случае двумя параметрами:

Если израсходованы все количества повторений, то Retry завершается с ошибкой. Что система делает далее - зависит от контекста (какую операцию мы пытались выполнить).

Демо-приложение

Для демонстрации взаимодействия приложений с Маркетплейсом МоегоСклада было создано демо-приложение на PHP.

В демо-приложении реализованы следующие функции:

Более подробную информацию и исходный код приложения можно получить по ссылке: https://github.com/moysklad/php-dummyapp-marketplace-1.0