NAV Navbar
cURL

Документация разработчика приложений для Магазина приложений МойСклад

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

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

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

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

Разработчик управляет своими приложениями через аккаунт разработчика. Аккаунт разработчика — привязанный к конкретному разработчику аккаунт МоегоСклада. На таком аккаунте в витрине приложений отображаются приложения разработчика в статусах Черновик (Draft) и Готово, отправлено на модерацию (Ready). Это позволяет выполнять проверку и отладку приложений до их публикации для всех пользователей МоегоСклада. К одному разработчику может быть привязано несколько аккаунтов.

Конечный пользователь сервиса МойСклад также работает с МоимСкладом в рамках своего аккаунта. Обычно аккаунт соответствует одной компании или бизнесу. В аккаунте может быть несколько пользователей. Один из этих пользователей имеет права администратора аккаунта.

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

Как создать и опубликовать приложение

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

Каждый тип приложений подключается к МоемуСкладу с помощью своего API: серверные приложения используют JSON API 1.2, приложения телефонии — Phone API 1.0, приложения лояльности — Loyalty API.

Процесс создания приложений телефонии и лояльности описан в Сценарии работы с Phone API 1.0 и Сценарии работы с LoyaltyAPI. Пошаговый процесс создания серверных приложений рассмотрим ниже.

  1. Определите, к какому типу относится приложение.

  2. Создайте бизнес-логику приложения на сервере.

  3. Создайте черновик приложения в личном кабинете разработчика, используя один из примеров дескриптора. После создания черновика вы получите appId, appUid, secret key, необходимые для работы с Vendor API.

  4. Реализуйте эндпоинты согласно спецификации Vendor API 1.0, чтобы обрабатывать события приложения. При установке и возобновлении приложения МойСклад передает токен для доступа к JSON API 1.2.

  5. Если пользователь должен будет настраивать приложение при его установке на аккаунт, то в ответе на запрос установки приложения верните SettingsRequired и реализуйте iframe-часть приложения. По возможности используйте UI Kit МоегоСклада. При необходимости предусмотрите ролевую модель для ограничения доступа к функциональности приложения.

  6. Если в приложении нужны дополнительные поля для сущностей и/или документов, создайте их через JSON API 1.2 в рамках активации приложения. Если при этом процесс создания занимает более одной минуты, используйте асинхронную активацию с возвратом статуса Activating.

  7. Если приложение нужно встроить в интерфейс МоегоСклада, создайте виджет приложения. Для этого нужно реализовать iframe-часть виджета и обновить дескриптор, добавив тег widgets. Подробнее читайте в разделе Виджеты.

  8. Разместите приложение в магазине приложений.

Порядок размещения приложения в магазине приложений

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

  1. Создать Черновик приложения в личном кабинете разработчика.
  2. После создания Черновика на странице редактирования приложения будет доступен Секретный ключ (Secret Key).
  3. Создать аккаунт разработчика, привязав аккаунт МоегоСклада к аккаунту в личном кабинете разработчика.
  4. Протестировать и отладить приложение на аккаунте разработчика.
  5. Отправить приложение на модерацию через личный кабинет разработчика.

Далее сотрудник МоегоСклада проверяет приложение. Если приложение проходит проверку, оно публикуется на витрине приложений в течение 2 рабочих дней. Если приложение не проходит проверку, оно возращается разработчику для устранения замечаний.

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

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

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

Более подробная информация и исходный код приложения доступны по ссылке.

Типы приложений для магазина приложений

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

В магазин приложений МоегоСклада можно добавить:

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

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

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

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

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

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

Также серверное приложение может иметь iframe-часть — страницу, которую можно использовать как пользовательский интерфейс, в том числе для настроек приложения со стороны пользователя МоегоСклада или администратора аккаунта. Iframe-часть приложения загружается по указанному разработчиком URL на отдельной вкладке внутри раздела Приложения МоегоСклада. Поддерживается возможность расширенного iframe. Подробнее смотрите Дескриптор.

Только для серверных приложений доступны виджеты.

Только серверные приложения могут быть платными.

В общем случае жесткие требования к внешнему виду и визуальному дизайну iframe-части приложений отсутствуют. Приветствуется визуальное соответствие дизайну МоегоСклада. Для этого разработан UI Kit:

useful image

UI Kit доступен по ссылке.

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

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

Телефония

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

Приложения телефонии не могут получать доступ по токену к JSON API, не имеют возможности активации и деактивации по Vendor API. В перспективе эти возможности могут появиться.

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

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

Приложения лояльности

API для интеграции МоегоСклада с системами лояльности и внешними сервисами управления скидками: https://dev.moysklad.ru/doc/api/loyalty/1.0

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

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

Условия размещения приложений

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

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

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

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

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

useful image

Стоимость приложения

По умолчанию все публикуемые приложения платные и оплачиваются клиентом через биллинг МоегоСклада. Клиент может оплатить приложение любым удобным ему способом: картой, по счету от физического лица или юридического лица, через СБП, в рублях, казахстанских тенге или узбекских сумах. Если пользователь не продлил или не оплатил подписку, доступ к приложению приостанавливается, а при поступлении оплаты – возобновляется. Подробнее см. Процесс деактивации приложения на аккаунте.

Приложение не может быть бесплатным, если для его полноценной работы требуется оплата вне магазина приложений. Есть только два исключения: смс-рассылки и телефония, которые используют свой транзакционный биллинг.

Если вы хотите разместить бесплатное приложение, которое будет выполнять все свои функции без дополнительной оплаты, вам нужно заранее связаться с нами по почте apps@moysklad.ru и согласовать такую публикацию.

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

Требования к разработчикам

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

Расчет вознаграждения и комиссия

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

Комиссия МоегоСклада – 25% от стоимости подписки, которую мы получили от пользователя. Если клиент купил подписку на 1 месяц на приложение стоимостью 1000 рублей/месяц, то разработчик получит за него 750 рублей за этот период.

Приложение можно купить на 1 месяц и на 12 месяцев. При покупке сразу на 12 месяцев для клиента действует скидка 15%, вознаграждение разработчику при этом рассчитывается от итоговой суммы, которую заплатил клиент: (Цена приложения х 12 месяцев – 15%) – 25%. Скидки при годовой оплате стимулируют пользователей к переходу на платный тариф и снижают % отказов от продления подписки.

Приложения также могут покупать наши партнеры, которые внедряют и настраивают МойСклад клиентам, им доступна скидка 30% на все приложения. При покупке приложения через партнера вознаграждение рассчитывается от итоговой суммы, которую получил МойСклад: (Цена приложения – 30%) – 25%.

Рекомендуемые цены

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

Основные категории и цены, на которые вы можете ориентироваться при формировании своих тарифных планов:

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

Изменение стоимости приложения

Доступны следующие варианты:

Если приложение не опубликовано, перейдите в ЛКВ в карточку приложения, чтобы указать или изменить стоимость подписки. Чтобы изменить стоимость опубликованного приложения, напишите на apps@moyskad.ru, для добавления нового тарифа к существующему – создайте новую версию, укажите в ней нужные данные и отправьте на модерацию.

Пробный период платных приложений

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

Условия пробного периода:

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

Продление пробного периода

По запросу пользователя вы можете повторно продлить пользователю пробный период на еще один срок от 1 до 14 дней. Для этого в ЛКВ нужно выбрать приложение и нажать кнопку «Дополнительный триал», указать название аккаунта пользователя и срок пробного периода.

Порядок получения выплат

В начале каждого месяца в личном кабинете разработчика формируется отчет за прошедший месяц. В отчете указаны все проданные подписки для всех приложений, их количество и итоговая сумма к выплате.

Чтобы получить выплату:

  1. Заполните реквизиты.
  2. Проверьте и согласуйте отчет в кабинете разработчика. Для этого перейдите в раздел Отчеты → По выплатам, выберите период и нажмите на кнопку Согласовать в соответствующей строке. useful image
  3. Выплата поступит на указанные реквизиты разработчика в течение пяти рабочих дней с даты согласования отчета.

По вопросам, связанным с выплатами, обращайтесь: apps@moysklad.ru.

Руководство разработчика

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

Токен для доступа к 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

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

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

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

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

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

Подробнее о том, как работать с API МоегоСклада, смотрите в видео.

Главный iframe

Главный iframe приложения — это основное окно для настройки и работы с серверным приложением, которое открывается при нажатии на кнопку Перейти в приложение на витрине либо при переходе по прямой ссылке вида https://online.moysklad.ru/app/#embed-apps?id={appId}.

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

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

  1. По возможности используйте UI Kit МоегоСклада для визуального оформления содержимого.
  2. Если в iframe будет какая-либо пользовательская функциональность кроме настроек, предусмотрите доступ к этой функциональности для пользователей с разными правами доступа, не только для администратора.
  3. Если содержимое iframe не умещается в минимальную допустимую высоту окна (768px), то необходимо добавить js скрипт для автоматического масштабирования высоты окна в зависимости от контента на свою страницу. Пример:
<!doctype html>
<html>

<head>
  ...
</head>

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

</html>

Виджеты

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

Можно создать виджеты двух типов:

Виджеты отображаются в двух режимах:

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

Порядок отображения соответствует расположению родительских приложений на витрине. В редакторах, поддерживающих функцию Drag-and-drop, пользователь может сам поменять порядок отображения виджетов.

Размещение виджетов

Виджеты можно добавить на страницы МоегоСклада, которые есть в списке.

Виджеты на страницах создания, например в точке встраивания document.customerorder.create, имеют ограниченную функциональность по сравнению с виджетами на страницах редактирования, например document.customerorder.edit.

Это выражается в меньшем количестве поддерживаемых протоколов и в реализации самих протоколов. Например, в сообщении Change часть полей, которые заполняются после первого сохранения документа (id, created, meta и другие) будет заполнено значением null.

Поддержка сервисных протоколов виджетами на страницах создания пока не реализована.

Каждая точка встраивания имеет свой список поддерживаемых дополнительных протоколов.

Чтобы встроить виджет на страницу МоегоСклада, которой нет в списке, свяжитесь с нами в Telegram или по электронной почте.

Жизненный цикл виджета

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

Если приложение предполагает настройку и статус SettingsRequired, виджет отображается после настройки приложения.

При первом открытии страницы с виджетом в рамках одной вкладки браузера cистема МоегоСклада загружает виджет по HTTP GET-запросом по URL, указанному в теге sourceUrl. При этом система генерирует и передает GET-параметром contextKey ключ, по которому виджет получает текущий контекст пользователя через Vendor API.

Прочие данные передаются c помощью postMessage при первом и последующих открытиях виджета. Так, при каждом открытии виджета система МоегоСклада отображает ранее загруженный iframe виджета без повторной загрузки с сервера разработчика, и через postMessage передает в iframe этого виджета сообщение Open.

Без дополнительных настроек виджет отображает содержимое сразу, как только получает сообщение Open. Даже если еще не успел обновить отображаемую информацию.

Примеры JS-кода доступны в демо-приложении на GitHub. Примеры демонстрируют:

Если виджет поддерживает протокол open-feedback, система не отображает содержимое виджета сразу, а ждет ответного сообщения от виджета о готовности. До этого момента внутри виджета отображается заглушка. Когда виджет готов, он отправляет сообщение OpenFeedback. После этого система полностью открывает виджет пользователю.

Если виджет поддерживает протокол change-handler, при редактировании документа пользователем на странице с виджетом он оповещается об изменениях, получая сообщение Change, содержащее несохраненное состояние документа.

Если виджет поддерживает протокол update-provider, при редактировании документа пользователем на странице с виджетом он может изменять несохраненное состояние документа, отправляя сообщение UpdateRequest со списком полей, которые необходимо изменить.

При сохранении страницы с виджетом, если виджет, который находится на экране редактирования сущности, поддерживает протокол save-handler, он оповещается о факте сохранения объекта пользователем, получая сообщение Save.

Виджет, поддерживающий протокол dirty-state, может сообщить хост-окну, что в виджете есть несохраненные изменения. Для этого виджет отправляет хост-окну сообщение SetDirty. Виджет может отправить хост-окну сообщение ClearDirty, после чего диалог подтвержения закрытия окна не будет появляться, при условии, что отсутствуют несохраненные изменения в самом UI МоегоСклада или в других виджетах.

Внутренний dirty-флаг для виджета в хост-окне сбрасывается при открытии. То есть при отправке сообщения Open хост-окно считает, что в виджете нет несохраненных изменений.

Поддержку виджетом протоколов open-feedback, save-handler, dirty-state, change-handler необходимо указать в дескрипторе приложения.

Как работают виджеты

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

Виджеты доступны для серверных приложений с дескриптором версии v2.

Пример дескриптора приложения с виджетом в карточке контрагента расположен в правой части экрана.

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

   <ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
         https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
       <iframe>...</iframe>
       <vendorApi>...</vendorApi>
       <access>...</access>
       <widgets>
           <entity.counterparty.edit>
               <sourceUrl>https://b2b.moysklad.ru/widget/counter-party</sourceUrl>
               <height>
                   <fixed>28px</fixed>
               </height>
               <supports>
                   <open-feedback/>
                   <save-handler/>
               </supports>
                <uses>
                    <good-folder-selector/>
                </uses>
           </entity.counterparty.edit>
       </widgets>
   </ServerApplication>

Подробнее структура дескриптора для приложения с виджетом описана в разделе Дескриптор приложений.

После того, как пользователь установил и настроил приложение с дескриптором из примера, виджет отображается в карточке контрагента.

useful image

Загрузка виджета на странице

Виджет на странице загружается в iframe по URL, указанному в теге <sourceUrl>...</sourceUrl> виджета в дескрипторе приложения.

Параметры содержимого виджетов: - ширина 400px — для виджетов всех приложений, - высота — статически указывается разработчиком в дескрипторе приложения. В примере высота — 61px.

useful image

Как и основной iframe-части приложения, виджету GET-параметром передается contextKey, по которому через Vendor API можно получить информацию о текущем пользователе.

Пока происходит загрузка виджета, отображается ненавязчивый лоадер.

Кэширование виджетов

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

Если по техническим причинам кэширование не произошло, хост-окно может: - создать несколько iframe-экземпляров для одной точки расширения в рамках одной вкладки браузера, при этом чем эти экземпляры могут существовать одновременно; - не кэшировать iframe виджета после загрузки.

Открытие виджета

Когда пользователь открывает страницу с виджетом, хост-окно отображает iframe виджета, только что загруженный или ранее закэшированный, и передает в этот iframe сообщение Open через postMessage.

Пример сообщения Open cмотрите в правой части экрана.

Сообщение Open для виджета на экране создания в Заказе покупателя

    {
      "name": "Open",
      "messageId": 12345,
      "extensionPoint": "document.customerorder.create",
      "objectId": null,
      "displayMode": "expanded"
    }

Сообщение Open для виджета на экране редактирования в Заказе покупателя

    {
      "name": "Open",
      "messageId": 12345,
      "extensionPoint": "document.customerorder.edit",
      "objectId": "8e9512f3-111b-11ea-0a80-02a2000a3c9c",
      "displayMode": "expanded"
    }

Здесь:

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

Примечание: в сообщении Open передается идентификатор текущей открытой сущности в карточке, который отображается в URL браузера в параметре id. Несмотря на то, что для сущностей Товар, Услуга, Комплект и Модификация этот идентификатор отличается от используемого в remap API, запрос по нему по-прежнему будет работать. При этом сервер будет использовать редирект . Пример запроса для Товара https://online.moysklad.ru/app/#good/edit?id=9e73d736-a0de-11e9-9109-f8fc00095c7f приведен в правой части экрана. Для упрощения часть вывода пропущена.

Ответ на запрос получения Товара

curl -X GET --location "https://api.moysklad.ru/api/remap/1.2/entity/product/9e73d736-a0de-11e9-9109-f8fc00095c7f"     -H "Content-Type: application/json"     -H "Authorization: Bearer ..." -v 

> GET /api/remap/1.2/entity/product/9e73d736-a0de-11e9-9109-f8fc00095c7f HTTP/1.1
> Host: online.moysklad.ru
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Type: application/json
> Authorization: Bearer ...
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Server: nginx/1.18.0
< Date: Fri, 28 Jan 2022 11:13:00 GMT
< Content-Length: 0
< Connection: keep-alive
< Cache-Control: max-age=0, no-cache
< X-Lognex-Reset: 0
< X-Lognex-Retry-After: 0
< Location: https://api.moysklad.ru/api/remap/1.2/entity/product/9e73e41d-a0de-11e9-9109-f8fc00095c81
< X-Lognex-Retry-TimeInterval: 3000
< X-RateLimit-Remaining: 44
< X-RateLimit-Limit: 45
< Strict-Transport-Security: max-age=15552000
< 
* Connection #1 to host online.moysklad.ru left intact
* Issue another request to this URL: 'https://api.moysklad.ru/api/remap/1.2/entity/product/9e73e41d-a0de-11e9-9109-f8fc00095c81'
* Found bundle for host online.moysklad.ru: 0x55cb04fa3970 [serially]
* Can not multiplex, even if we wanted to!
* Re-using existing connection! (#1) with host online.moysklad.ru
* Connected to online.moysklad.ru (88.212.252.4) port 443 (#1)
> GET /api/remap/1.2/entity/product/9e73e41d-a0de-11e9-9109-f8fc00095c81 HTTP/1.1
> Host: online.moysklad.ru
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Type: application/json
> Authorization: Bearer ...
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.18.0
< Date: Fri, 28 Jan 2022 11:13:00 GMT
< Content-Type: application/json;charset=utf-8
< Content-Length: 6535
< Connection: keep-alive
< Vary: Accept-Encoding
< Cache-Control: no-cache
< X-Lognex-Reset: 0
< X-Lognex-Retry-After: 0
< X-Lognex-Retry-TimeInterval: 3000
< X-RateLimit-Remaining: 43
< X-RateLimit-Limit: 45
< Strict-Transport-Security: max-age=15552000
< 
{
  "meta" : {
    "href" : "https://api.moysklad.ru/api/remap/1.2/entity/product/9e73e41d-a0de-11e9-9109-f8fc00095c81",
    "metadataHref" : "https://api.moysklad.ru/api/remap/1.2/entity/product/metadata",
    "type" : "product",
    "mediaType" : "application/json",
    "uuidHref" : "https://online.moysklad.ru/app/#good/edit?id=9e73d736-a0de-11e9-9109-f8fc00095c7f"
  },
  "id" : "9e73e41d-a0de-11e9-9109-f8fc00095c81",
  ...
}

Протокол обратной связи при открытии виджета

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

Если виджет при открытии делает обращение к серверу, может быть видна небольшая задержка. В это время будет отображается прежнее состояние и содержание виджета, например, данные для прошлого контрагента.

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

useful image

Тег дополнительных протоколов supports с протоколом open-feedback

    <supports>
        <open-feedback/>
    </supports>

Для переключения хост-окна на использование протокола обратной связи при открытии виджета в дескрипторе для виджета надо явно указать поддержку дополнительного протокола open-feedback. Пример тега дополнительных протоколов supports с указанным в нем протоколом open-feedback смотрите в правой части экрана.

Виджет передает сообщение OpenFeedback хост-окну в качестве сигнала готовности содержимого виджета для отображения пользователю. Пример сообщения OpenFeedback смотрите в правой части экрана.

Cообщение OpenFeedback

{
  "name": "OpenFeedback",
  "correlationId": 12345
}

Здесь correlationId содержит значение messageId ранее полученного сообщения Open.

Хост-окно, получив сообщение OpenFeedback, отображает содержимое виджета пользователю и убирает лоадер.

Сохранение пользователем редактируемого объекта

Хост-окно может оповещать виджет о факте сохранения редактируемого объекта. Для этого в дескрипторе для виджета нужно объявить поддержку опционального протокола save-handler.

Тег дополнительных протоколов supports с протоколом save-handler

      <supports>
          <save-handler/>
      </supports>

Хост-окно отправляет виджету сообщение Save через postMessage при сохранении редактируемого объекта после сохранения объекта в базе данных. То есть на момент получения виджетом сообщения Save сохраненное состояние объекта уже доступно через JSON API.

Сохранение редактируемого объекта инициируется пользователем:

Пример сообщения Save cмотрите в правой части экрана.

Сообщение Save

     {
       "name": "Save",
       "messageId": 32109,
       "extensionPoint": "entity.counterparty.edit",
       "objectId": "8e9512f3-111b-11ea-0a80-02a2000a3c9c"
     }

Здесь:

Признак несохраненного состояния виджета

Тег дополнительных протоколов supports с протоколом dirty-state

  <supports>
      <dirty-state/>
  </supports>

Хост-окно поддерживает подтверждение закрытия окна пользователем, если он изменил данные в форме виджета, но не сохранил их. Для этого в дескрипторе для виджета нужно объявить поддержку опционального протокола dirty-state.

Сообщение SetDirty

    {
      "name": "SetDirty",
      "messageId": 12,
      "openMessageId": 7
    }

После того, как пользователь внес изменения в виджет, он отправляет хост-окну сообщение SetDirty. Пример сообщения SetDirty смотрите в правой части экрана.

Здесь openMessageId содержит значение messageId ранее полученного сообщения Open.

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

useful image

Сообщение ClearDirty

    {
      "name": "ClearDirty",
      "messageId": 13
    }

Если виджет после отправки SetDirty отправляет хост-окну сообщение ClearDirty, система не учитывает данный виджет при отображении диалога подтверждения сохранения изменений. То есть, если отсутствуют прочие несохраненные изменения самого объекта или в других виджетах, система не запрашивает диалог подтверждения сохранения изменений при закрытии редактируемого объекта.

Получение состояния редактируемого объекта

Хост-окно может оповещать виджет об изменениях несохраненного состояния редактируемого объекта. Для этого в дескрипторе для виджета нужно объявить поддержку опционального протокола change-handler.

Тег дополнительных протоколов supports с протоколом change-handler

      <supports>
          <change-handler/>
      </supports>

Хост-окно отправляет виджету сообщение Change через postMessage, содержащее несохраненное состояние документа при редактировании документа пользователем.

Отправка сообщения Change инициируется при следующих действиях пользователя:

Отправка сообщения Change не происходит в следующих случаях:

Узнать, для каких точек поддерживается протокол change-handler, можно тут.

Пример сообщения Change cмотрите в правой части экрана.

Здесь changeHints представляет собой массив с подсказками о том, что именно было изменено в редактируемом объекте:

Поле objectState — измененное состояние объекта, которое представляет собой JavaScript-объект, соответствующий по структуре ответу JSON API 1.2 на получение того же объекта (документа) с позициями.

Несмотря на то, что структура objectState в целом соответствует JSON API 1.2, имеются расхождения:

Актуальные сведения о поддержке конкретных полей документов в протоколе change-handler смотрите в документации JSON API 1.2:

Сообщение Change

{
  "name": "Change",
  "extensionPoint": "document.customerorder.edit",
  "messageId": 7,
  "changeHints": [
    "positions",
    "_fields"
  ],
  "objectState": {
    "meta": {
      "href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/c4c6e6ea-b3f5-11eb-0a80-35ed000000b8",
      "metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata",
      "type": "customerorder",
      "mediaType": "application/json",
      "uuidHref": "https://online.moysklad.ru/app/#customerorder/edit?id=c4c6e6ea-b3f5-11eb-0a80-35ed000000b8"
    },
    "id": "c4c6e6ea-b3f5-11eb-0a80-35ed000000b8",
    "accountId": "5fc956ad-b3f2-11eb-0a80-1b8a00000000",
    "created": "2021-05-13 17:16:11.465",
    "payedSum": 0,
    "shippedSum": 0,
    "invoicedSum": 0,
    "name": "00001",
    "applicable": true,
    "moment": "2021-05-13 17:15:00.000",
    "store": {
      "meta": {
        "href": "https://api.moysklad.ru/api/remap/1.2/entity/store/605491e4-b3f2-11eb-0a80-35ed00000074",
        "metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/store/metadata",
        "type": "store",
        "mediaType": "application/json",
        "uuidHref": "https://online.moysklad.ru/app/#warehouse/edit?id=605491e4-b3f2-11eb-0a80-35ed00000074"
      }
    },
    "rate": {
      "currency": {
        "meta": {
          "href": "https://api.moysklad.ru/api/remap/1.2/entity/currency/6055a619-b3f2-11eb-0a80-35ed00000079",
          "metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/currency/metadata",
          "type": "currency",
          "mediaType": "application/json",
          "uuidHref": "https://online.moysklad.ru/app/#currency/edit?id=6055a619-b3f2-11eb-0a80-35ed00000079"
        }
      }
    },
    "organization": {
      "meta": {
        "href": "https://api.moysklad.ru/api/remap/1.2/entity/organization/6051401c-b3f2-11eb-0a80-35ed00000072",
        "metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/organization/metadata",
        "type": "organization",
        "mediaType": "application/json",
        "uuidHref": "https://online.moysklad.ru/app/#mycompany/edit?id=6051401c-b3f2-11eb-0a80-35ed00000072"
      }
    },
    "agent": {
      "meta": {
        "href": "https://api.moysklad.ru/api/remap/1.2/entity/counterparty/60550738-b3f2-11eb-0a80-35ed00000077",
        "metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/counterparty/metadata",
        "type": "counterparty",
        "mediaType": "application/json",
        "uuidHref": "https://online.moysklad.ru/app/#company/edit?id=60550738-b3f2-11eb-0a80-35ed00000077"
      }
    },
    "state": {
      "meta": {
        "href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/states/60850d6a-b3f2-11eb-0a80-35ed00000097",
        "type": "state",
        "metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata",
        "mediaType": "application/json"
      }
    },
    "externalCode": "JAGi0Yg0i0OYvylp7SzDi3",
    "vatEnabled": true,
    "vatIncluded": true,
    "vatSum": 0,
    "sum": 21000,
    "updated": "2021-05-13 17:16:11.434",
    "reservedSum": 20000,
    "attributes": [
      {
        "meta": {
          "href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/attributes/14fb3ad9-b3f6-11eb-0a80-35ed000000cb",
          "type": "attributemetadata",
          "mediaType": "application/json"
        },
        "id": "14fb3ad9-b3f6-11eb-0a80-35ed000000cb",
        "name": "Строка",
        "type": "string",
        "value": "123АААББвQ"
      },
      {
        "meta": {
          "href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/attributes/14fbcb79-b3f6-11eb-0a80-35ed000000cc",
          "type": "attributemetadata",
          "mediaType": "application/json"
        },
        "id": "14fbcb79-b3f6-11eb-0a80-35ed000000cc",
        "name": "Ссылка",
        "type": "link",
        "value": null
      },
      {
        "meta": {
          "href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/attributes/14fbd363-b3f6-11eb-0a80-35ed000000cd",
          "type": "attributemetadata",
          "mediaType": "application/json"
        },
        "id": "14fbd363-b3f6-11eb-0a80-35ed000000cd",
        "name": "Компания",
        "type": "counterparty",
        "value": {
          "meta": {
            "href": "https://api.moysklad.ru/api/remap/1.2/entity/counterparty/6054e7f9-b3f2-11eb-0a80-35ed00000075",
            "metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/counterparty/metadata",
            "type": "counterparty",
            "mediaType": "application/json",
            "uuidHref": "https://online.moysklad.ru/app/#company/edit?id=6054e7f9-b3f2-11eb-0a80-35ed00000075"
          },
          "name": "ООО \"Поставщик\""
        }
      }
    ],
    "positions": {
      "meta": {
        "href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/c4c6e6ea-b3f5-11eb-0a80-35ed000000b8/positions",
        "type": "customerorderposition",
        "mediaType": "application/json",
        "size": 2,
        "limit": 1000,
        "offset": 0
      },
      "rows": [
        {
          "meta": {
            "href": null,
            "type": "customerorderposition",
            "mediaType": "application/json"
          },
          "id": null,
          "accountId": "5fc956ad-b3f2-11eb-0a80-1b8a00000000",
          "price": 10000,
          "quantity": 2,
          "reserve": 2,
          "shipped": 0,
          "assortment": {
            "meta": {
              "href": "https://api.moysklad.ru/api/remap/1.2/entity/product/788a1cc7-b3f6-11eb-0a80-35ed000000e2",
              "metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/product/metadata",
              "type": "product",
              "mediaType": "application/json",
              "uuidHref": "https://online.moysklad.ru/app/#good/edit?id=78896bd4-b3f6-11eb-0a80-35ed000000e0"
            }
          },
          "vat": 0,
          "discount": 0
        },
        {
          "meta": {
            "href": null,
            "type": "customerorderposition",
            "mediaType": "application/json"
          },
          "id": null,
          "accountId": "5fc956ad-b3f2-11eb-0a80-1b8a00000000",
          "price": 1000,
          "quantity": 1,
          "shipped": 0,
          "assortment": {
            "meta": {
              "href": "https://api.moysklad.ru/api/remap/1.2/entity/service/9d0c9a63-b3f6-11eb-0a80-35ed000000eb",
              "metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/service/metadata",
              "type": "service",
              "mediaType": "application/json",
              "uuidHref": "https://online.moysklad.ru/app/#good/edit?id=9d0c74c3-b3f6-11eb-0a80-35ed000000e9"
            }
          },
          "vat": 0,
          "discount": 0
        }
      ]
    }
  }
}

Валидация состояния редактируемого объекта

Виджет может проверять состояние редактируемого объекта и запрещать хост-окну сохранять объект, если он невалиден. Для этого в дескрипторе для виджета нужно объявить поддержку опционального протокола validation-feedback, который является параметром тега change-handler.

Тег дополнительных протоколов supports с протоколом change-handler

      <supports>
        <change-handler>
          <validation-feedback/>
        </change-handler>
      </supports>

Протокол работает в паре с change-handler, то есть виджет, поддерживающий протокол validation-feedback, должен отправить сообщение ValidationFeedback о валидности документа в ответ на сообщение Change.

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

useful image

Если виджет по какой-то причине не отправит ValidationFeedback или отправит некорректное сообщение, пользователь не сможет сохранить документ.

Подробнее о том, для каких точек поддерживается протокол validation-feedback, смотрите здесь.

Пример сообщений ValidationFeedback cмотрите в правой части экрана.

Здесь:

Сообщение ValidationFeedback — документ валиден (может быть сохранен)

{
  "name": "ValidationFeedback",
  "messageId": 11,
  "correlationId": 10,
  "valid": true
}

Сообщение ValidationFeedback — документ невалиден (не должен быть сохранен)

{
  "name": "ValidationFeedback",
  "messageId": 12,
  "correlationId": 11,
  "valid": false,
  "message": "Пример ошибки от разработчика"
}

Изменение состояния редактируемого объекта

Тег дополнительных протоколов supports с протоколом update-provider

      <supports>
          <update-provider/>
      </supports>

Виджет может изменять поля текущего редактируемого объекта посредством передачи сообщения UpdateRequest хост-окну. Для этого в дескрипторе для виджета нужно объявить поддержку опционального протокола update-provider.

Изменения в этом протоколе, в отличие от JSON API, происходят без сохранения состояния объекта в базе данных МоегоСклада, так же, как если бы они были сделаны самим пользователем. Виджет должен отправлять сообщения UpdateRequest преимущественно в качестве реакции на действия пользователя, чтобы предупредить его об изменениях в редактируемом документе.

Сообщение UpdateRequest

{
  "name": "UpdateRequest",
  "messageId": 10,
  "updateState": {
    "name": "1",
    "deliveryPlannedMoment": "2021-08-21T12:15:50.333Z",
    "applicable": true,
    "description": null
  }
}

Сценарий работы:

  1. Виджет отправляет хост-окну сообщение UpdateRequest, содержащее набор полей документа и/или позиции документа, которые нужно изменить.
  2. Хост-окно валидирует содержимое сообщения и отправляет обратно ответ UpdateRequest.
  3. Если сообщение UpdateRequest невалидно, хост-окно отправляет в ответ сообщение InvalidMessageError, содержащее описание ошибочных полей.

Примеры сообщений UpdateRequest смотрите в правой части экрана.

Здесь:

Пример сообщения UpdateResponse смотрите в правой части экрана.

Сообщение UpdateResponse

{
  "name": "UpdateResponse",
  "correlationId": 10
}

Здесь:

Сообщение UpdateRequest для изменения дополнительных полей

{
  "name":"UpdateRequest",
  "messageId":10,
  "updateState":{
    "description": "красивое",
    "attributes":[
      {
        "meta":{
          "href":"https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/attributes/f6d39a2b-146f-11ec-0a80-072a002cf678",
          "type":"attributemetadata"
        },
        "name":"Доставлено в срок",
        "type":"boolean",
        "value":true
      },
      {
        "id":"f6d39d12-146f-11ec-0a80-072a002cf679",
        "name":"Срок доставки, дней",
        "type":"long",
        "value":10
      },
      {
        "id":"f6d39d12-146f-11ec-0a80-072a002cf678",
        "value":45.78
      }
    ]
  }
}

Сообщение UpdateRequest для добавления позиций

{
  "name":"UpdateRequest",
  "messageId":10,
  "updateState":{
    "vatIncluded": true,
    "positions": [
      {
        "quantity": 10,
        "price": 100,
        "discount": 0,
        "vat": 0,
        "assortment": {
          "meta": {
            "href": "https://api.moysklad.ru/api/remap/1.2/entity/product/8b382799-f7d2-11e5-8a84-bae5000003a5",
            "type": "product"
          }
        },
        "reserve": 10
      },
      {
        "quantity": 1,
        "price": 200,
        "assortment": {
          "meta": {
            "href": "https://api.moysklad.ru/api/remap/1.2/entity/service/be903062-f504-11e5-8a84-bae50000019a",
            "type": "service"
          }
        },
        "pack": null
      },
      {
        "quantity": 30,
        "price": 300,
        "discount": 0,
        "vat": 18,
        "assortment": {
          "meta": {
            "href": "https://api.moysklad.ru/api/remap/1.2/entity/bundle/c02e3a5c-007e-11e6-9464-e4de00000006",
            "type": "bundle"
          }
        },
        "pack": {
          "id": "1bf22e62-8b47-11e8-56c0-000800000006"
        },
        "reserve": 30
      }
    ]
  }
}

Сообщение UpdateRequest для изменения существующих позиций

{
  "name":"UpdateRequest",
  "messageId":10,
  "updateState":{
    "vatIncluded": true,
    "positions": [
      {
        "id": "be903062-f504-11e5-8a84-bae50000019a",
        "price": 100,
        "discount": -10
      },
      {
        "id": "be903062-f504-11e5-8a84-bae500000123",
        "quantity": 30,
        "price": 300,
        "discount": 0,
        "vat": 18,
        "assortment": {
          "meta": {
            "href": "https://api.moysklad.ru/api/remap/1.2/entity/bundle/c02e3a5c-007e-11e6-9464-e4de00000006",
            "type": "bundle"
          }
        },
        "reserve": 30
      }
    ]
  }
}

Сообщение UpdateRequest для добавления одной новой и сохранения трех существующих позиций

{
  "name":"UpdateRequest",
  "messageId":10,
  "updateState":{
    "vatIncluded": true,
    "positions": [
      {
        "id": "be903062-f504-11e5-8a84-bae50000019a"
      },
      {
        "id": "0fb51a51-e01d-48da-9035-4b21f5e69055"
      },
      {
        "id": "ef34072d-5fd3-4ac4-b4b9-87458ca61da2"
      },
      {
        "quantity": 1,
        "price": 300,
        "assortment": {
          "meta": {
            "href": "https://api.moysklad.ru/api/remap/1.2/entity/service/c02e3a5c-007e-11e6-9464-e4de00000006",
            "type": "service"
          }
        }
      }
    ]
  }
}

Работа с полями из updateState:

Работа с дополнительными полями (attributes):

Работа с позициями (positions):

Подробнее о том, для каких точек поддерживается протокол update-provider, смотрите в статье.

Сервисы хост-окна

В виджетах доступны сервисы хост-окна:

Селектор группы товаров

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

<ServerApplication  xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"             
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"             
                    xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2      
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.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://api.moysklad.ru/api/remap/1.2</resource>
        <scope>admin</scope>
    </access>
    <widgets>        
        <entity.counterparty.edit>            
            <sourceUrl>https://example.com/widget.php</sourceUrl>            
            <height>                
                <fixed>150px</fixed>            
            </height>
            <uses>
                <good-folder-selector/>
            </uses>                  
        </entity.counterparty.edit>    
    </widgets>
</ServerApplication>

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

<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2      
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
  <iframe>
    <sourceUrl>https://example.com/iframe.html</sourceUrl>
    <expand>true</expand>
    <uses>
      <good-folder-selector/>
    </uses>
  </iframe>
  <vendorApi>
    <endpointBase>https://example.com/dummy-app</endpointBase>
  </vendorApi>
  <access>
    <resource>https://api.moysklad.ru/api/remap/1.2</resource>
    <scope>admin</scope>
  </access>
  <popups>
    <popup>
      <name>coolPopup</name>
      <sourceUrl>https://vendorurl.coolpopup.ru</sourceUrl>
      <uses>
        <good-folder-selector/>
      </uses>
    </popup>
  </popups>
</ServerApplication>

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

<uses>
    <good-folder-selector/>
</uses>

Рассмотрим пример с виджетом. Когда виджет отправляет хост-окну сообщение SelectGoodFolderRequest через Window.postMessage, хост-окно запрашивает у пользователя выбор группы товаров, используя встроенный в МойСклад селектор:

useful image

Cообщение SelectGoodFolderRequest

{
  "name": "SelectGoodFolderRequest",
  "messageId": 12345
}

Здесь: - messageId — целочисленный идентификатор сообщения, уникальный в рамках текущего взаимодействия виджет — хост-окно. Назначается виджетом.

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

Cообщение SelectGoodFolderResponse (Пользователь выбрал группу товаров, имеющую идентификатор 8e9512f3-111b-11ea-0a80-02a2000a3c9c)

{
  "name": "SelectGoodFolderResponse",
  "correlationId": 12345,
  "selected": true,
  "goodFolderId": "8e9512f3-111b-11ea-0a80-02a2000a3c9c"
}

Здесь:

Cообщение SelectGoodFolderResponse (Пользователь отменил выбор)

{
  "name": "SelectGoodFolderResponse",
  "correlationId": 12345,
  "selected": false
}

Стандартные диалоги

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

<ServerApplication  xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"             
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"             
                    xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2      
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.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://api.moysklad.ru/api/remap/1.2</resource>
        <scope>admin</scope>
    </access>
    <widgets>        
        <entity.counterparty.edit>            
            <sourceUrl>https://example.com/widget.php</sourceUrl>            
            <height>                
                <fixed>150px</fixed>            
            </height>
            <uses>
                <standard-dialogs/>
            </uses>                  
        </entity.counterparty.edit>    
    </widgets>
</ServerApplication>

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

Чтобы виджет начал поддерживать протокол, в дескрипторе необходимо добавить блок:

<uses>
    <standard-dialogs/>
</uses>

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

useful image

Cообщение ShowDialogRequest

{
  "name": "ShowDialogRequest",
  "messageId": 12345,
  "dialogText": "Учетная запись будет удалена. Вы хотите продолжить?",
  "buttons": [
    {"name": "Yes", "caption": "Да, удалить"},
    {"name": "No", "caption": "Нет"}
  ]  3
}

Параметры сообщения ShowDialogRequest:

После того, как пользователь нажимает кнопку в диалоге или принудительно закрывает хост-окно (нажимает на «крестик»), результат действий пользователя отображается в сообщении ShowDialogResponse.

Cообщение ShowDialogResponse (Пользователь нажимает кнопку Нет)

{
  "name": "ShowDialogResponse",
  "correlationId": 12345,
  "buttonName": "No",
  "dialogResolution": "normal"
}

Параметры ответа ShowDialogResponse:

Cообщение ShowDialogResponse (Пользователь закрыл диалог, нажав на «крестик»)

{
  "name": "ShowDialogResponse",
  "correlationId": 12345,
  "dialogResolution": "closedByUser"
}

Примечание: В версии Google Chrome 92.0 и выше использование браузерных диалоговых окон через вызовы Window.alert(), Window.confirm() из iframe запрещено. Поэтому рекомендуется использовать сервис стандартных диалогов МоегоСклада.

Протокол навигации

Дескриптор с виджетом, использующим протокол навигации

<ServerApplication  xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"             
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"             
                    xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2      
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.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://api.moysklad.ru/api/remap/1.2</resource>
        <scope>admin</scope>
    </access>
    <widgets>        
        <entity.counterparty.edit>            
            <sourceUrl>https://example.com/widget.php</sourceUrl>            
            <height>                
                <fixed>150px</fixed>            
            </height>
            <uses>
                <navigation-service/>
            </uses>                  
        </entity.counterparty.edit>    
    </widgets>
</ServerApplication>

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

<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2      
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
  <iframe>
    <sourceUrl>https://example.com/iframe.html</sourceUrl>
    <expand>true</expand>
    <uses>
      <navigation-service/>
    </uses>
  </iframe>
  <vendorApi>
    <endpointBase>https://example.com/dummy-app</endpointBase>
  </vendorApi>
  <access>
    <resource>https://api.moysklad.ru/api/remap/1.2</resource>
    <scope>admin</scope>
  </access>
  <popups>
    <popup>
      <name>coolPopup</name>
      <sourceUrl>https://vendorurl.coolpopup.ru</sourceUrl>
      <uses>
        <navigation-service/>
      </uses>
    </popup>
  </popups>
</ServerApplication>

Позволяет виджетам, iframe-части и модальным окнам приложений осуществлять переход на другую страницу МоегоСклада и открывать МойСклад в новой вкладке. Чтобы виджет, iframe или модальное окно начали поддерживать протокол навигации в дескрипторе необходимо добавить в блоки widgets, iframe или popup блок: <uses> <navigation-service/> </uses> Примеры смотрите в правой части экрана.

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

Cообщение NavigateRequest

{
  "name": "NavigateRequest",
  "messageId": 12345,
  "path": "#good/edit?id=e8a46787-0ff4-11ec-0a80-1eb200000740",
  "target": "blank"
}

Параметры сообщения NavigateRequest:

Если валидация сообщения пройдет успешно, перед переходом пользователя будет отправлен NavigateResponse обратно в виджет.

Cообщение NavigateResponse

{
  "name": "NavigateResponse",
  "correlationId": 12345 
}

Параметры ответа NavigateResponse:

При навигации из модального окна в текущей вкладке (target имеет значение self) произойдет переход, и модальное окно будет отображаться поверх страницы. Если необходимо, чтобы после перехода окно закрывалось, используйте сообщение ClosePopup. Подробнее смотрите в разделе Кастомные модальные окна.

Кастомные модальные окна

Кастомные модальные окна позволяют расширить функциональность iframe-части и виджетов с фиксированной высотой и шириной.

Характеристики кастомного модального окна:

Рассмотрим работу кастомных модальных окон на примере виджетов. В случае iframe-части все работает аналогично.

Чтобы виджет использовал кастомные модальные окна, добавьте в дескриптор приложения блок:

<popups>
    ...
</popups>

Пример дескриптора с блоком popups смотрите в правой части экрана.

Дескриптор с виджетом и iframe-частью, использующие кастомные модальные окна

<ServerApplication  xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"             
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"             
                    xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2      
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.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://api.moysklad.ru/api/remap/1.2</resource>
        <scope>admin</scope>
    </access>
    <widgets>        
        <entity.counterparty.edit>            
            <sourceUrl>https://example.com/widget.php</sourceUrl>            
            <height>                
                <fixed>150px</fixed>            
            </height>
            <uses>
                <good-folder-selector/>
            </uses>                  
        </entity.counterparty.edit>    
    </widgets>
    <popups>
        <popup>
            <name>somePopup</name>
            <sourceUrl>https://example.com/popup.php</sourceUrl>
        </popup>
        <popup>
            <name>somePopup2</name>
            <sourceUrl>https://example.com/popup-2.php</sourceUrl>
        </popup>
    </popups>
</ServerApplication>

Сообщение ShowPopupRequest

{
  "name": "ShowPopupRequest",
  "messageId": 12,
  "popupName": "somePopup",
  "popupParameters": "hello"
}

Сообщение OpenPopup

{
  "name": "OpenPopup",
  "messageId": 36,
  "popupName": "somePopup",
  "popupParameters": "hello"
}

Сообщение ClosePopup

{
  "name": "ClosePopup",
  "messageId": 17,
  "popupResponse": "world"
}

Сообщение ShowPopupResponse

{
  "name": "ShowPopupResponse",
  "correlationId": 12,
  "popupName": "somePopup",
  "popupResolution": "normal",
  "popupResponse": "world"
}

Виджет может отобразить одно из кастомных модальных окон, отправив сообщение ShowPopupRequest с именем выбранного окна хост-окну. Пример такого сообщения смотрите в правой части экрана.

Здесь:

МойСклад проверяет сообщение ShowPopupRequest. Если сообщение валидно, отображается модальное окно: загружается страница окна по адресу sourceUrl в iframe, в GET-параметре передается contextKey. Процесс аналогичен загрузке виджета. Значение sourceUrl загружается из соответствующего элемента списка модальных окон <popups> в дескрипторе. Поиск производится по popupName, переданному в сообщении.

После загрузки модального окна хост-окно отправляет ему сообщение OpenPopup. Набор полей тот же, что и в ShowPopupRequest. При этом messageId в данном сообщении свой, а не тот, что был передан в сообщении ShowPopupRequest.

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

Здесь:

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

Здесь:

Страницы кастомных модальных окон кэшируются аналогично кэшированию виджетов. При повторном открытии окна по сообщению ShowPopupRequest переиспользуется ранее загруженный iframe.

Ниже приводятся примеры работы с кастомными модальными окнами.

Пример работы без возврата параметров из модального окна

Пример взаимодействия без передачи дополнительных параметров

// виджет -> хост-окно
{
  "name": "ShowPopupRequest",
  "messageId": 12,
  "popupName": "somePopup"
}

// хост-окно -> модальное окно
{
  "name": "OpenPopup",
  "messageId": 35,
  "popupName": "somePopup"
}

// хост-окно -> виджет
{
  "name": "ShowPopupResponse",
  "correlationId": 12,
  "popupName": "somePopup",
  "popupResolution": "closedByUser"
}

Пример взаимодействия с передачей параметров в виде строки

// виджет -> хост-окно
{
  "name": "ShowPopupRequest",
  "messageId": 17,
  "popupName": "somePopup",
  "popupParameters": "hello"
}

// хост-окно -> модальное окно
{
  "name": "OpenPopup",
  "messageId": 36,
  "popupName": "somePopup",
  "popupParameters": "hello"
}

// хост-окно -> виджет
{
  "name": "ShowPopupResponse",
  "correlationId": 17,
  "popupName": "somePopup",
  "popupResolution": "closedByUser"
}
  1. Виджет отправляет хост-окну сообщение ShowPopupRequest. В сообщении указывается имя модального окна и опциональные параметры.
  2. Хост-окно отображает модальное окно, загружая страницу окна по адресу sourceUrl в iframe с передачей contextKey в GET-параметре.
  3. Хост-окно отправляет в iframe модального окна сообщение OpenPopup, передавая в нем опциональные параметры от виджета.
  4. Пользователь взаимодействует с веб-содержимым модального окна, после чего закрывает его через системную кнопку («крестик»), находящуюся в верхнем правом углу окна.
  5. Система скрывает модальное окно и отправляет виджету сообщение ShowPopupResponse с указанием того, что окно было закрыто пользователем через системную кнопку ("popupResolution": "closedByUser").

Пример модального окна с наличием только системной кнопки закрытия:

useful image

Закрытие модального окна с использованием сообщения ClosePopup

...
// модальное окно -> хост-окно
{
  "name": "ClosePopup",
  "messageId": 37,
}

// хост-окно -> виджет
{
  "name": "ShowPopupResponse",
  "correlationId": 14,
  "popupName": "somePopup",
  "popupResolution": "normal"
}

Разработчик может отобразить на странице и собственную кнопку закрытия окна. При нажатии на нее будет отправляться сообщение ClosePopup, а виджет получит сообщение ShowPopupResponse с "popupResolution": "normal".

useful image

Пример работы с возвратом параметров из модального окна

Пример ответа с передачей данных о нажатой кнопке

// виджет -> хост-окно
{
  "name": "ShowPopupRequest",
  "messageId": 29,
  "popupName": "somePopup"
}

// хост-окно -> модальное окно
{
  "name": "OpenPopup",
  "messageId": 36,
  "popupName": "somePopup"
}

// пользователь нажимает на кнопку «Сохранить»

// модальное окно -> хост-окно
{
  "name": "ClosePopup",
  "messageId": 44,
  "popupResponse": "save"
}

// хост-окно -> виджет
{
  "name": "ShowPopupResponse",
  "correlationId": 29,
  "popupName": "somePopup",
  "popupResolution": "normal",
  "popupResponse": "save"
}

Если модальному окну требуется вернуть информацию обратно в виджет, окно должно передать ее в поле popupResponse сообщения ClosePopup.

  1. Виджет отправляет хост-окну сообщение ShowPopupRequest, указывая в нем имя модального окна и опциональные параметры.
  2. Хост-окно отображает модальное окно, загружая его страницу по адресу sourceUrl в iframe с передачей contextKey в GET-параметре.
  3. Хост-окно отправляет в iframe модального окна сообщение OpenPopup с опциональными параметрами.
  4. Пользователь взаимодействует с веб-содержимым модального окна, после чего нажимает кнопку закрытия или сохранения, находящуюся внутри страницы модального окна.
  5. Модальное окно отправляет хост-окну сообщение ClosePopup. В нем передаются параметры, которые зависят от действий пользователя, например тип нажатой кнопки.
  6. Система скрывает модальное окно и отправляет виджету сообщение ShowPopupResponse с указанием параметров, переданных модальным окном.

Пользователь может закрыть модальное окно принудительно. При этом параметры в виджет не будут переданы.

Пример модального окна с кнопками «Сохранить» и «Отмена»:

useful image

Отображение содержимого, которое не вмещается в окно целиком

Пример плавающей верстки содержимого

<!doctype html>
<html lang="ru">
<head>
    <meta charset="utf-8">

    <title>Popup example</title>
    <style>
        body {
            overflow: hidden;
        }
        .main-container {
            display: flex;
            flex-direction: column;
            height: 100vh;
        }
        .content-container {
            overflow: auto;
            flex-grow: 1;
        }
        .buttons-container {
            padding-top: 15px;
            min-height: 55px;
        }
    </style>
    <link rel="stylesheet" href="css/uikit.css">
</head>

<body>
<div class="main-container">
    <div class="content-container">
        <!--Разместите содержимое здесь -->
    </div>
    <div class="buttons-container">
        <button class="button button--success">Сохранить</button>
        <button class="button">Отмена</button>
    </div>
</div>
</body>
</html>

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

Пример модального окна с полосами прокрутки:

useful image

Пример такой верстки с использованием UI Kit представлен справа.

Способы передачи параметров

Пример взаимодействия с передачей параметров в виде строки

// виджет -> хост-окно
{
  "name": "ShowPopupRequest",
  "messageId": 12,
  "popupName": "somePopup",
  "popupParameters": "hello"
}

// хост-окно -> модальное окно
{
  "name": "OpenPopup",
  "messageId": 35,
  "popupName": "somePopup",
  "popupParameters": "hello"
}

Пример взаимодействия с передачей параметров в виде объекта

// виджет -> хост-окно
{
  "name": "ShowPopupRequest",
  "messageId": 12,
  "popupName": "somePopup",
  "popupParameters": {
    "aaa": 1,
    "bbb": "qwerty"
  }
}

// хост-окно -> модальное окно
{
  "name": "OpenPopup",
  "messageId": 35,
  "popupName": "somePopup",
  "popupParameters": {
    "aaa": 1,
    "bbb": "qwerty"
  }
}

Пример взаимодействия с передачей параметров в виде массива

// виджет -> хост-окно
{
  "name": "ShowPopupRequest",
  "messageId": 12,
  "popupName": "somePopup",
  "popupParameters": [123, "foobar"]
}

// хост-окно -> модальное окно
{
  "name": "OpenPopup",
  "messageId": 35,
  "popupName": "somePopup",
  "popupParameters": [123, "foobar"]
}

Существует несколько способов передачи параметров между виджетами и модальными окнами:

Справа приведены примеры передачи параметров из виджета в модальное окно через сообщение ShowPopupRequest.

Аналогичные способы передачи можно использовать для возврата ответа в сообщении ClosePopup.

Ошибки

При получении сообщения от виджета хост-окно производит валидацию сообщения. Если проверка не пройдена, хост-окно возвращает в ответ сообщение InvalidMessageError со списком ошибок.

Пример сообщения InvalidMessageError

{
  "name": "InvalidMessageError",
  "correlationId": null,
  "invalidMessage": {
    "name": "SelectGoodFolderRequest"
  },
  "errors": [
    {
      "code": 1001,
      "error": "Отсутствует обязательный параметр messageId"
    }
  ]
}

Пример такого сообщения смотрите в правой части экрана.

Здесь:

Перечень возможных ошибок представлен в таблице.

Код ошибки Сообщение Описание Пример
1000 Недопустимое состояние виджета %state% для сообщения %message.name%, допустимые состояния: %message.expectedStates% Не допускается отправка данного сообщения из текущего состояния виджета Недопустимое состояние виджета Opened для сообщения OpenFeedback, допустимые состояния: Opening
1001 Отсутствует обязательный параметр %parameter.name% В сообщении отсутствует обязательный параметр Отсутствует обязательный параметр messageId
1002 Некорректное значение параметра %parameter.name%: %пояснение% Параметр сообщения имеет некорректное значение Некорректное значение параметра popupName: popup with name = 'somePopup1' not found
1003 Параллельный запрос %message.name% Не допускается отправка виджетом повторного запроса до момента получения ответа на предыдущий такой же запрос. Например: виджет уже отправил SelectGoodFolderRequest и пользователь еще не завершил выбор Параллельный запрос SelectGoodFolderRequest
1004 Виджет не поддерживает протокол для обработки сообщения %message.name% Не допускается обработка сообщения в виджете, который не поддерживает протокол данного сообщения Виджет не поддерживает протокол для обработки сообщения ShowDialogRequest

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

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

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

История версий XSD-схемы дескриптора

Версия Описание Разрешенное содержимое дескриптора Поддерживаемые типы приложений
1.0.0 Серверные и простые iFrame-приложения vendorApi, access, iframe iFrame, Серверные
1.1.0 Расширение iFrame (тег expand) vendorApi, access, iframe(c expand) iFrame, Серверные
v2 Виджеты в документах и сущностях. Кастомные модальные окна. Гибкие права приложений. Дополнительные и сервисные протоколы. vendorApi, access(с permissions), iframe(c expand), widgets, popups Серверные

Основные отличия дескриптора v2 от дескрипторов версий 1.x.x:

Дескрипторы версий 1.x.x некоторое время будут продолжать поддерживаться.

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


<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2    
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.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://api.moysklad.ru/api/remap/1.2</resource>
    <scope>custom</scope>
    <permissions>
      <viewDashboard/>
      <customerOrder>
        <view/>
        <create/>
        <update/>
      </customerOrder>
    </permissions>
  </access>
  <widgets>
    <entity.counterparty.edit>
      <sourceUrl>https://example.com/widget.php</sourceUrl>
      <height>
        <fixed>150px</fixed>
      </height>
      <uses>
        <good-folder-selector/>
        <standard-dialogs/>
        <navigation-service/>
      </uses>
    </entity.counterparty.edit>
  </widgets>
  <popups>
    <popup>
      <name>somePopup</name>
      <sourceUrl>https://example.com/popup.php</sourceUrl>
    </popup>
    <popup>
      <name>somePopup2</name>
      <sourceUrl>https://example.com/popup-2.php</sourceUrl>
    </popup>
  </popups>
</ServerApplication>

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

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

Блок iframe

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

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

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

Если значение установлено (expand=true), то на странице, указанной в sourceUrl, должен работать скрипт, оповещающий страницу МоегоСклада об изменении высоты его контента. Для этого необходимо реализовать отправку сообщения EventMessage при любом изменении высоты контента. При этом:

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

Тег uses — опциональный. Предназначен для сервисных протоколов, используемых iframe. На данный момент в нем можно указать следующие протоколы:

Блок 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

Требуется для серверных приложений, которые хотят получить доступ по JSON API к ресурсам аккаунта. В случае отсутствия этого блока в дескрипторе приложения при установке на аккаунт приложению не выдаются никакие доступы к ресурсам. Наличие блока access требует наличия блока vendorApi для передачи токена к ресурсам аккаунта при активации приложения по Vendor API.

В теге access/resource указывается ресурс, к которому приложению нужен доступ. На текущий момент для ресурса возможно только одно значение: https://api.moysklad.ru/api/remap/1.2

В теге access/scope указывается требуемый уровень доступа. Для него на текущий момент доступно два значения: admin и custom.

В теге access/permissions указываются требуемые пермиссии. Данный тег обязателен для уровня доступа со значением custom.

Пример заполнения блока access с указанием прав Администратора:


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

Пример заполнения блока access с явным перечислением пермиссий:


<access>
  <resource>https://api.moysklad.ru/api/remap/1.2</resource>
  <scope>custom</scope>
  <permissions>
    <viewDashboard/>
    <viewAudit/>
    <viewProductCostAndProfit/>
    <customerOrder>
      <view/>
      <create/>
      <update/>
      <delete/>
      <approve/>
      <print/>
    </customerOrder>
    <company>
      <view/>
      <create/>
    </company>
  </permissions>
</access>

Перечисленные в теге permissions права доступа могут включать в себя:

Есть три типа значений для пермиссий сущностей:

Подробнее о пермиссиях в МоемСкладе смотрите в документации JSON API.

Примечания:

Блок widgets

Сейчас доступны следующие точки расширения:

Блок widgets с точками расширения в контрагенте и заказе покупателя


<widgets>
  <entity.counterparty.edit>
    <sourceUrl>https://example.com/widget-counterparty.php</sourceUrl>
    <height>
      <fixed>200px</fixed>
    </height>
    <supports>
      <open-feedback/>
    </supports>
  </entity.counterparty.edit>

  <document.customerorder.create>
    <sourceUrl>https://example.com/widget-customerorder-create.php</sourceUrl>
    <height>
      <fixed>50px</fixed>
    </height>
  </document.customerorder.create>

  <document.customerorder.edit>
    <sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
    <height>
      <fixed>50px</fixed>
    </height>
    <uses>
      <good-folder-selector/>
      <standard-dialogs/>
      <navigation-service/>
    </uses>
  </document.customerorder.edit>
</widgets>

Сначала необходимо определить в блоке widgets точку расширения — указать страницу, где будет расположен виджет.

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

Тем не менее в итоге на одной странице может оказаться несколько виджетов (от разных приложений).

Список тегов для точек расширения:

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

Тег height — обязательный. В теге height/fixed задается фиксированная высота виджета в пикселях, в формате 150px.

Пример заполненного блока widgets можно увидеть справа.

Блок дополнительных протоколов (supports)

Блок supports — опциональный. Предназначен для дополнительных протоколов, поддерживаемых виджетом. На данный момент в нем можно указать протоколы:

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

Точка встраивания open-feedback save-handler dirty-state change-handler validation-feedback update-provider
entity.counterparty.edit
entity.product.edit
entity.variant.edit
entity.service.edit
entity.bundle.edit
entity.productfolder.edit
document.customerorder.create
document.customerorder.edit
document.demand.create
document.demand.edit
document.invoiceout.create
document.invoiceout.edit
document.invoicein.create
document.invoicein.edit
document.processingorder.edit
document.purchaseorder.edit
document.supply.create
document.supply.edit
document.paymentin.edit
document.paymentout.edit
document.cashin.edit
document.cashout.edit
document.move.create
document.move.edit
document.loss.create
document.loss.edit
document.enter.create
document.enter.edit
document.internalorder.edit
document.inventory.edit
document.purchasereturn.edit
document.salesreturn.create
document.salesreturn.edit
document.retaildemand.create
document.retaildemand.edit
document.retailsalesreturn.edit
document.retaildrawercashin.edit
document.retaildrawercashout.edit

Подробнее о дополнительных протоколах читайте в разделе Как работают виджеты.

Блок сервисных протоколов (uses)

Блок uses — опциональный. Предназначен для сервисных протоколов, используемых виджетом. На данный момент в нем можно указать следующие протоколы:

Блок popups

Блок popups с двумя модальными окнами, одно из которых использует протокол good-folder-selector


<popups>
  <popup>
    <name>somePopup1</name>
    <sourceUrl>https://example.com/popup-1.php</sourceUrl>
  </popup>
  <popup>
    <name>somePopup2</name>
    <sourceUrl>https://example.com/popup-2.php</sourceUrl>
    <uses>
      <good-folder-selector/>
    </uses>
  </popup>
</popups>

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

Тег uses — опциональный. Предназначен для сервисных протоколов, используемых модальным окном. Он указывается в блоке popup. На данный момент в этом теге можно указать следующие протоколы:

Подробнее о работе с кастомными модальными окнами читайте в разделе Кастомные модальные окна.

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

Для серверных приложений (актуальная версия схемы дескриптора v2)

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


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

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


<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2    
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.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://api.moysklad.ru/api/remap/1.2</resource>
    <scope>admin</scope>
  </access>
</ServerApplication>

Дескриптор для серверных приложений с виджетом в карточке контрагента


<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2    
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
  <iframe>
    <sourceUrl>https://example.com/iframe.html</sourceUrl>
  </iframe>
  <vendorApi>
    <endpointBase>https://example.com/dummy-app</endpointBase>
  </vendorApi>
  <access>
    <resource>https://api.moysklad.ru/api/remap/1.2</resource>
    <scope>admin</scope>
  </access>
  <widgets>
    <entity.counterparty.edit>
      <sourceUrl>https://example.com/widget.php</sourceUrl>
      <height>
        <fixed>150px</fixed>
      </height>
    </entity.counterparty.edit>
  </widgets>
</ServerApplication>

Дескриптор для серверных приложений с виджетом в карточке контрагента, Заказе покупателя и Отгрузке и протоколами openfeedback, save-handler, change-handler


<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2    
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
  <iframe>
    <sourceUrl>https://example.com/iframe.html</sourceUrl>
  </iframe>
  <vendorApi>
    <endpointBase>https://example.com/dummy-app</endpointBase>
  </vendorApi>
  <access>
    <resource>https://api.moysklad.ru/api/remap/1.2</resource>
    <scope>admin</scope>
  </access>
  <widgets>
    <entity.counterparty.edit>
      <sourceUrl>https://example.com/widget.php</sourceUrl>
      <height>
        <fixed>150px</fixed>
      </height>
      <supports>
        <open-feedback/>
      </supports>
    </entity.counterparty.edit>

    <document.customerorder.edit>
      <sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
      <height>
        <fixed>50px</fixed>
      </height>
      <supports>
        <open-feedback/>
        <save-handler/>
        <change-handler/>
      </supports>
    </document.customerorder.edit>

    <document.demand.edit>
      <sourceUrl>https://example.com/widget-demand.php</sourceUrl>
      <height>
        <fixed>50px</fixed>
      </height>
      <supports>
        <open-feedback/>
        <change-handler/>
      </supports>
    </document.demand.edit>
  </widgets>
</ServerApplication>

Дескриптор для серверных приложений с виджетом в Заказе покупателя и протоколом change-handler c validation-feedback


<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2    
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
  <iframe>
    <sourceUrl>https://example.com/iframe.html</sourceUrl>
  </iframe>
  <vendorApi>
    <endpointBase>https://example.com/dummy-app</endpointBase>
  </vendorApi>
  <access>
    <resource>https://api.moysklad.ru/api/remap/1.2</resource>
    <scope>admin</scope>
  </access>
  <widgets>
    <document.customerorder.edit>
      <sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
      <height>
        <fixed>50px</fixed>
      </height>
      <supports>
        <change-handler>
          <validation-feedback/>
        </change-handler>
      </supports>
    </document.customerorder.edit>
  </widgets>
</ServerApplication>

Дескриптор для серверных приложений с виджетом в карточке контрагента, Заказе покупателя и Отгрузке и протоколами good-folder-selector и dirty-state


<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2    
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
  <iframe>
    <sourceUrl>https://example.com/iframe.html</sourceUrl>
  </iframe>
  <vendorApi>
    <endpointBase>https://example.com/dummy-app</endpointBase>
  </vendorApi>
  <access>
    <resource>https://api.moysklad.ru/api/remap/1.2</resource>
    <scope>admin</scope>
  </access>
  <widgets>
    <entity.counterparty.edit>
      <sourceUrl>https://example.com/widget.php</sourceUrl>
      <height>
        <fixed>150px</fixed>
      </height>
      <supports>
        <dirty-state/>
      </supports>
      <uses>
        <good-folder-selector/>
      </uses>
    </entity.counterparty.edit>

    <document.customerorder.edit>
      <sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
      <height>
        <fixed>50px</fixed>
      </height>
      <supports>
        <dirty-state/>
      </supports>
      <uses>
        <good-folder-selector/>
      </uses>
    </document.customerorder.edit>

    <document.demand.edit>
      <sourceUrl>https://example.com/widget-demand.php</sourceUrl>
      <height>
        <fixed>50px</fixed>
      </height>
      <supports>
        <dirty-state/>
      </supports>
      <uses>
        <good-folder-selector/>
      </uses>
    </document.demand.edit>
  </widgets>
</ServerApplication>

Дескриптор для серверных приложений с виджетом в Заказе покупателя и Счете покупателю


<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2    
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
  <iframe>
    <sourceUrl>https://example.com/iframe.html</sourceUrl>
  </iframe>
  <vendorApi>
    <endpointBase>https://example.com/dummy-app</endpointBase>
  </vendorApi>
  <access>
    <resource>https://api.moysklad.ru/api/remap/1.2</resource>
    <scope>admin</scope>
  </access>
  <widgets>
    <document.customerorder.edit>
      <sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
      <height>
        <fixed>150px</fixed>
      </height>
    </document.customerorder.edit>
    <document.invoiceout.edit>
      <sourceUrl>https://example.com/widget-invoiceout.php</sourceUrl>
      <height>
        <fixed>110px</fixed>
      </height>
    </document.invoiceout.edit>
  </widgets>
</ServerApplication>

Дескриптор для серверных приложений с виджетом в Заказе покупателя и двумя кастомными модальными окнама, одно из которых поддерживает протокол good-folder-selector


<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2    
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
  <iframe>
    <sourceUrl>https://example.com/iframe.html</sourceUrl>
  </iframe>
  <vendorApi>
    <endpointBase>https://example.com/dummy-app</endpointBase>
  </vendorApi>
  <access>
    <resource>https://api.moysklad.ru/api/remap/1.2</resource>
    <scope>admin</scope>
  </access>
  <widgets>
    <document.customerorder.edit>
      <sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
      <height>
        <fixed>150px</fixed>
      </height>
    </document.customerorder.edit>
  </widgets>
  <popups>
    <popup>
      <name>viewPopup</name>
      <sourceUrl>https://example.com/view-popup.php</sourceUrl>
    </popup>
    <popup>
      <name>editPopup</name>
      <sourceUrl>https://example.com/edit-popup.php</sourceUrl>
      <uses>
        <good-folder-selector/>
      </uses>
    </popup>
  </popups>
</ServerApplication>

Дескриптор для серверных приложений с явным указанием прав доступа


<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2    
                    https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
  <iframe>
    <sourceUrl>https://example.com/iframe.html</sourceUrl>
  </iframe>
  <vendorApi>
    <endpointBase>https://example.com/dummy-app</endpointBase>
  </vendorApi>
  <access>
    <resource>https://api.moysklad.ru/api/remap/1.2</resource>
    <scope>custom</scope>
    <permissions>
      <viewDashboard/>
      <viewAudit/>
      <purchaseOrder>
        <view/>
        <create/>
        <update/>
        <delete/>
        <print/>
        <approve/>
      </purchaseOrder>
      <good>
        <view/>
        <create/>
        <print/>
      </good>
    </permissions>
  </access>
</ServerApplication>

Для серверных приложений (устаревшие версии схемы дескриптора 1.x.x)

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


<application xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v1"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v1 
      https://apps-api.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://apps-api.moysklad.ru/xml/ns/appstore/app/v1"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v1 
      https://apps-api.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>

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

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

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

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

Vendor API 1.0

Системы разработчиков взаимодействуют с магазином приложений МоегоСклада через Vendor API.

Функции Vendor API:

Далее приводятся спецификации REST-эндпоинтов на стороне МоегоСклада и разработчика приложений.

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

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

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

Если приложение предполагает взаимодействие по Vendor API — оповещение по активации и деактивации, получение контекста пользователя для iframe, приложению необходим Секретный ключ.

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

Секретный ключ генерируется на стороне МоегоСклада и выдается разработчику:

  1. Разработчику необходимо завести Черновик приложения в личном кабинете разработчика. После этого открывается доступ к странице редактирования.
  2. На странице редактирования в числе полей, доступных для просмотра (read-only), доступен Секретный ключ.
  3. Пока приложение находится в статусе Черновик, разработчик может перегенерировать Секретный ключ самостоятельно.
  4. После передачи приложения на модерацию Секретный ключ доступен только для просмотра и копирования.
  5. Чтобы перегенерировать 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" да Уникальный идентификатор токена Используется для проверки одноразовости использования токена

В системе МоегоСклада есть следующие ограничения:

  1. JWT-токен не может быть использован повторно. Для этого система проверяет значение поля "jti" на уникальность среди предыдущих запросов.
  2. на максимальное время жизни для 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. То есть при возникновении ошибок возвращается аналогичный объект error и заголовки HTTP-ответа X-Lognex-Auth и X-Lognex-Auth-Message.

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

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

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

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

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

useful image

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

Деактивация приложения — это процесс выключения приложения для конкретного аккаунта в системе разработчика. Выполняется при отключении приложения пользователем или при приостановке платного приложения. При деактивации происходит оповещение сервера разработчика со стороны МоегоСклада.

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

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

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

useful image

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

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

Возобновление приложения — это процесс включения приостановленного платного приложения. Возобновление может быть выполнено:

После возобновления приложению выдается новый токен доступа. Первыми возобновляются приложения, установленные ранее других приложений.

REST-эндпоинты на стороне разработчика приложений

Разработчику необходимо реализовать REST-эндпоинт, если ему требуется:

При этом REST--эндпоинт должен иметь доступ по HTTPS, поддерживать HTTP-методы PUT, GET, DELETE и иметь вид:

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

где:

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

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

HTTP-метод: PUT

Content-Type: application/json

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

В теле ответа ожидаем получить следующую JSON-структуру (обратите внимание, в ответе обязательно требуется HTTP-заголовок 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://api.moysklad.ru/api/remap/1.2",
      "scope": ["admin"],
      "access_token": "6ab89be1ae6ff147755625ee8da948e42612233b"
    }
  ],
  "subscription": {
    "tariffId": "23ca69d4-2657-40c4-8ba1-6ce24ddeac2e", 
    "trial": true,
    "tariffName": "Basic",
    "expiryMoment": "2024-01-19T18:50:12+03:00",
    "notForResale": false
  }
}

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://api.moysklad.ru/api/remap/1.2",
      "scope": ["admin"],
      "access_token": "6ab89be1ae6ff147755625ee8da948e42612233b"
    }
  ],
  "subscription": {
    "tariffId": "23ca69d4-2657-40c4-8ba1-6ce24ddeac2e",
    "trial": false,
    "tariffName": "Basic",
    "expiryMoment": "2024-01-19T18:50:12+03:00",
    "notForResale": false
  }
}

Response:

Response 200

Content-Type: application/json

Body:

{
  "status": "Activated"
}


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

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": "account-test",
  "access": [
    {
      "resource": "https://api.moysklad.ru/api/remap/1.2",
      "scope": ["custom"],
      "permissions": {
        "supply": {
          "view": "ALL",
          "update": "ALL"
        },
        "viewDashboard": true,
        "viewAudit": true
      },
      "access_token": "test-token"
    }
  ],
  "cause": "Install"
}

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": "TariffChanged",
  "subscription": {
    "tariffId": "23ca69d4-2657-40c4-8ba1-6ce24ddeac2e",
    "trial": false,
    "tariffName": "Basic",
    "expiryMoment": "2024-01-19T18:50:12+03:00",
    "notForResale": false
  }
}

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

{
  "appUid": "example-app.example-vendor",
  "accountName": "account-test",
  "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

{
  "appUid": "example-app.example-vendor",
  "accountName": "account-test",
  "cause": "Suspend"
}

Response:

Response 200

Content-Type: application/json

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

HTTP-метод: GET

Content-Type: application/json

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

Тело ответа:

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-эндпоинты на стороне МоегоСклада позволяют разработчику ограниченно управлять состоянием установки приложения для конкретного аккаунта и получать информацию о пользователе, который работает с приложением в UI МоегоСклада.

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

https://apps-api.moysklad.ru/api/vendor/1.0

При запросах к API обязательно нужно указать в HTTP-заголовке запроса Accept-Encoding формат сжатия содержимого — gzip. Если указан другой формат, возвращается ошибка 415 Unsupported Media Type (Неподдерживаемый тип данных).

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

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

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

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

Здесь:

HTTP-метод: GET

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

Тело ответа:

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

Название Тип Описание Обязательное при ответе
status String Текущий статус приложения на аккаунте Да
cause String Причина перехода в текущий статус Нет
subscription Object Параметры текущей подписки Нет
Атрибуты сущности subscription
Название Тип Описание Обязательное при ответе
tariffId UUID Идентификатор тарифа, на котором приобретена подписка Да
trial Boolean Признак пробной подписки Да
tariffName String Название тарифа Нет
expiryMoment String Дата окончания подписки в формате RFC 3339 Нет
notForResale Boolean Признак того, что аккаунт, установивший приложение, является аккаунтом партнера МоегоСклада Да

Если приложение не подключено на данном аккаунте или указанный аккаунт отсутствует в МоемСкладе, возвращается ошибка 404 Not Found (код 2004).

Таблица возможных статусов приложения на аккаунте
status cause Значение
Activating Install Приложение в процессе установки на аккаунт
ActivationFailed Install Произошла ошибка при установке приложения на аккаунт
SettingsRequired Приложение успешно установлено на акканут, для полноценной работы требуется настройка пользователя
Activated Приложение активно на аккаунте (успешно установлено на аккаунт и, если нужно, настроено пользователем)
Deactivating Uninstall Приложение в процессе удаления с аккаунта
DeactivationFailed Uninstall Произошла ошибка при удалении приложения с аккаунта
Deactivating Suspend Приложение в процессе приостановки на аккаунте
DeactivationFailed Suspend Произошла ошибка во время приостановки приложения на аккаунте
Suspended Приложение приостановлено на аккаунте
Activating Resume Приложение в процессе возобновления работы на аккаунте
ActivationFailed Resume Произошла ошибка во время возобновления работы приложения на аккаунте

HTTP status codes:

Пример запроса на получение статуса приложения на аккаунте

curl "https://apps-api.moysklad.ru/api/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f/status"     
-H "Accept: application/json"     
-H "Authorization: Bearer ..."  

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

{
  "status": "Activated",
  "cause": "Install",
  "subscription": {
    "tariffId": "23ca69d4-2657-40c4-8ba1-6ce24ddeac2e",
    "trial": false,
    "tariffName": "Basic",
    "expiryMoment": "2024-01-19T18:50:12+03:00",
    "notForResale": false
  }
}

Изменение статуса приложения на аккаунте

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

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

Здесь:

HTTP-метод: PUT

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

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

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

HTTP status codes:

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

curl -X PUT "https://apps-api.moysklad.ru/api/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f/status"     
-H "Content-Type: application/json"
-H "Accept: application/json"    
-H "Authorization: Bearer ..."  
-d '{
      "status": "Activating"
    }'

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

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

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

Пример того, что будет загружаться в iframe, при условии, что в дескрипторе приложения sourceUrl имеет значение https://yoursite.ru/moysklad:

https://yoursite.ru/moysklad?contextKey=1c14e98cd272239c03bf3d9697f167699743292c.

contextKey — это временный ключ, который может быть использован в течение 5 минут с момента загрузки iframe для получения контекста пользователя через данный эндпоинт. Если был использован contextKey, после окончания времени его жизни эндпоинт вернет ошибку 404 Not Found.

Resource: MARKETPLACE-ENDPOINT/context/{contextKey}

Здесь:

HTTP-метод: POST

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

Тело ответа:

В случае успешного ответа возвращается такое же по структуре содержимое как в эндпоинте получения Контекста сотрудника JSON API:

https://api.moysklad.ru/api/remap/1.2/context/employee.

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

HTTP status codes:

Пример запроса на получение контекста пользователя

curl -X POST "https://apps-api.moysklad.ru/api/vendor/1.0/context/6S3BQLsaSRNdEnhPCoW9lplY2LozRUOq6S3BQLsaSRNdEnh"     
-H "Accept: application/json"     
-H "Authorization: Bearer ..."  

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

{
  "meta": {
    "href": "https://api.moysklad.ru/api/remap/1.2/entity/employee/b0a02321-13e3-11e9-912f-f3d4002516e3?expand=cashier.retailStore",
    "metadataHref": "https://api.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://api.moysklad.ru/api/remap/1.2/entity/employee/b0a02321-13e3-11e9-912f-f3d4002516e3",
      "metadataHref": "https://api.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://api.moysklad.ru/api/remap/1.2/entity/group/b0b3c289-13e3-11e9-9109-f8fc0001f189",
      "metadataHref": "https://api.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://api.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 . В тело ответа включается JSON-объект с описанием ошибки. При необходимости в ответе проставляются соответствующие HTTP-заголовки.

Механизм Retry

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

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

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

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

Личный кабинет разработчика — это сервис, который упрощает работу над приложениями для магазина приложений.

В личном кабинете можно:

Получение доступа к личному кабинету разработчика

Для получения доступа в личный кабинет разработчика заполните анкету. Доступ и дальнейшие инструкции будут высланы на почту, указанную в анкете, в течение нескольких дней.

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

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

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

  1. Приложение создается в личном кабинете разработчика со статусом Draft. Разработчик проверяет и отлаживает работу приложения на своем аккаунте. Когда приложение готово, разработчик передает его на публикацию. Статус приложения меняется на Ready.
  2. Сотрудник МоегоСклада проверяет, затем публикует приложение на витрине. Статус приложения меняется на Published. Приложение становится доступно пользователю МоегоСклада для установки.
  3. Пользователь МоегоСклада устанавливает (подключает) приложение на свой аккаунт, настраивает и использует. Если приложение не нужно, пользователь может удалить (отключить) приложение на аккаунте.
  4. Разработчик может внести изменения в опубликованное приложение, создав для нее новую версию и внеся в нее изменения.
  5. Чтобы убрать приложение с витрины приложений, его нужно перевести в статус Hidden. Установленные до этого момента экземпляры приложения на аккаунтах пользователей продолжают работать и отображаться на UI МоегоСклада, пока пользователи их не удалят. Для аккаунтов, удаливших приложение, и аккаунтов, на которых оно не было установлено, приложение скрывается. Установка приложения становится невозможной. Так можно скрыть только бесплатные приложения.

Создание черновика приложения

  1. В личном кабинете разработчика нажмите на кнопку Создать приложение. Откроется форма создания черновика приложения.
  2. Укажите Псевдоним приложения. Псевдоним — уникальный идентификатор. Невозможно создать два приложения с одинаковыми псевдонимами.
  3. Создайте приложение подходящего типа: Серверное приложение, Телефония, Приложение лояльности. Тип приложения влияет на его платность и необходимость заполнения Дескриптора:

Редактирование черновика приложения

Если после создания приложения его настройки нужно изменить, выберите приложение в списке приложений и нажмите в меню Редактировать.

На вкладке Общая информация о приложении доступны те же поля, что и в форме создания черновика приложения. Исключение составляет только Псевдоним приложения - его изменять после создания нельзя.

На вкладке Учетные данные приложения отображаются параметры для идентификации приложения:

Для всех полей доступна кнопка Копировать, которая вставляет содержимое поля в буфер обмена.

По умолчанию Серверные приложения создаются с одним бесплатным тарифом. Для изменения стоимости приложения предназначен раздел Создание и редактирование тарифов на вкладке Тарифы.

Вкладка доступна только для Серверных приложений.

Для добавления тарифа нажмите кнопку Добавить новый тариф и заполните его поля:

Подробнее о выборе стоимости приложений в разделе Стоимость приложения.

Отправка на модерацию

Перед публикацией на витрине МоегоСклада новое приложение должно пройти модерацию.

Чтобы отправить приложение на модерацию, выберите нужное приложение в статусе Черновик из списка и нажмите пункт меню Отправить на модерацию.

Обязательно заполните поле Электронная почта для уведомлений в разделе Реквизиты / Информация о разработчике. Это позволяет получать оповещения и следить за процессом прохождения модерации.

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

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

Для просмотра информации о приложении выберите его в списке приложений и нажмите в меню пункт Посмотреть приложение.

В форме просмотра отображаются те же поля, что и в форме редактирования черновика приложения, но все они находятся в режиме только для чтения («read-only»).

Внесение изменений в опубликованное приложение

Функция доступна только для Серверных приложений.

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

Отличия от обычных черновиков следующие:

После проверки внесенных изменений версия, так же как и черновик, отправляется на модерацию. После успешной публикации версия скрывается, а измененные поля переносятся в опубликованное приложение (в том числе увеличивается номер версии). При этом следующие поля остаются неизменными (не переносятся из черновика):

Ручная приостановка приложения

В личном кабинете разработчик может тестировать и отлаживать приложение при приостановке и возобновлении на аккаунте.

Чтобы протестировать приложение при приостановке:

  1. Привяжите аккаунт в МоемСкладе к аккаунту разработчика в личном кабинете разработчика (создайте аккаунт разработчика).
  2. Заведите платное приложение в личном кабинете разработчика.
  3. Установите приложение на аккаунте разработчика.
  4. Выберите приложение в списке и нажмите на меню Приостановить на аккаунте. Работа приложения будет приостановлена.
  5. Чтобы отладить приложение при возобновлении работы, нажмите на кнопку Активировать в карточке приложения на витрине МоегоСклада.

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

Продление пробного периода платного приложения

В личном кабинете разработчика можно продлить пробный период приложения. Продление на выбранном аккаунте пользователя доступно один раз и только для платных опубликованных приложений. Для этого:

  1. В Списке приложений выберите платное опубликованное приложение.
  2. Нажмите на пункт меню Дополнительный триал.
  3. В окне Предоставление пробного периода заполните поля Аккаунт и Количество дней.
  4. Для приложения с несколькими тарифами, выберите тариф, на который нужно предоставить пробный период.
  5. Если поля заполнены корректно, откроется окно Подтверждение пробного периода.
  6. Проверьте указанный пробный период и нажмите на кнопку Да, предоставить.

Редактирование информации о разработчике

Чтобы отредактировать информацию о разработчике приложения, в меню личного кабинета перейдите на вкладку Реквизиты / Информация о разработчике.

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

Также можно внести или отредактировать реквизиты для выплат денежных средств за платные приложения. Для этого в меню перейдите на вкладку Реквизиты / Реквизиты компании.

Привязка аккаунта к разработчику

Отладить приложение для магазина приложений можно на аккаунте разработчика. Чтобы сделать аккаунт в МоемСкладе аккаунтом разработчика, свяжите аккаунт с нужным разработчиком. Для этого в личном кабинете разработчика, в верхнем меню нажмите на кнопку Привязать.

Важно: Для привязки необходим доступ к электронной почте сотрудника, указанного в аккаунте МоегоСклада на странице Подписка. По умолчанию это администратор, зарегистрировавший аккаунт. Однако далее вместо администратора можно указать другого сотрудника.

Чтобы изменить уже существующую привязку, нажмите на имя текущей привязки. Предыдущая привязка будет удалена, установленные на отвязанном аккаунте приложения в статусах Черновик (Draft) и Готово, отправлено на модерацию (Ready) будут деинсталлированы.

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

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

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

Отчеты в личном кабинете разработчика

Сводный отчет по установкам приложений

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

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

В отчет включаются:

Отчет по выручке платных приложений

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

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

В отчет включаются:

Отчет об использовании приложений

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

Для просмотра и согласования отчета в разделе Отчеты верхнего меню выберите вкладку Выплаты.

Отчет формируется раз в месяц, доступен для скачивания в формате PDF. Процесс работы с отчетом:

  1. Чтобы отчет сформировался, заполните реквизиты.
  2. Отчет поступает в статусе Сформировано. На почту приходит оповещение.
  3. Просмотрите и согласуйте отчет (кнопка Согласовать). Статус отчета меняется на Согласовано.
  4. Вознаграждение разработчика выплачивается согласно банковским реквизитам. В отчете появляется отметка Выплачено.

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

Отчет по причинам удаления приложений

При удалении приложения на витрине МоегоСклада пользователю предлагается оставить отзыв о приложении:

useful image

Если пользователь указывает одну и более причин удаления и нажимает на кнопку Отправить отзыв, разработчик получает этот отзыв.
На email разработчика, указанный в личном кабинете в разделе:
Реквизиты -> Информация о разработчике -> Контакты разработчика -> Электронная почта для уведомлений
приходит сообщение с причинами удаления.

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

Чтоба настроить отправку писем, отметьте флагом "Отправлять уведомления о причинах удаления приложений на email". Если функция отключена, отзывы продолжают собираться в данный отчет.

Ограничение: В отчете выводятся только 1000 последних отзывов за выбранный период.

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

Чтобы серверное приложение получило доступ к JSON API, в дескрипторе приложения нужно указать желаемый уровень доступа к JSON API. Можно запросить следующие уровни доступа:

Права приложения на аккаунте фиксируются на момент установки приложения. При последующем обновлении дескриптора приложения права приложения на аккаунте сохраняются.

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

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

Подробнее о ролях пользователей смотрите в документации JSON API.

Подробнее о правах доступа для приложений смотрите в разделе Информация о правах доступа.

Прямая ссылка на приложение

Разработчик может получить прямую ссылку на приложение.

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

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

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

Если приложение было удалено или недоступно по прямой ссылке, при переходе по ссылке открывается витрина и появляется сообщение о том, что приложение не найдено.

Если приложение снято с публикации, но уже установлено некоторыми пользователями, оно будет отображаться для этих пользователей по ссылке как на витрине. Для остальных пользователей приложение будет недоступно.

Список последних изменений

02-04-2024

Добавлено

07-03-2024

Добавлено

19-01-2024

Добавлено

27-09-2023

Добавлено

19-09-2023

Изменено

06-09-2023

Добавлено

30-08-2023

Изменено

23-11-2022

Изменено

18-11-2022

Изменено

09-11-2022

Изменено

12-10-2022

Изменено

06-09-2022

Изменено

23-08-2022

Изменено

01-08-2022

Добавлено

26-07-2022

Добавлено

22-07-2022

Изменено

07-07-2022

Изменено

04-07-2022

Изменено

20-05-2022

Добавлено

20-04-2022

Изменено

04-03-2022

Изменено

27-01-2022

Изменено

21-01-2022

Изменено

21-12-2021

Изменено

16-12-2021

Изменено

10-12-2021

Изменено

29-11-2021

Изменено

24-11-2021

Изменено

19-11-2021

Изменено

17-11-2021

Изменено

11-11-2021

Изменено

01-11-2021

Изменено

21-10-2021

Изменено

07-10-2021

Изменено

16-09-2021

Изменено

30-08-2021

Изменено

10-08-2021

Изменено

13-05-2021

Изменено

21-01-2021

Изменено

13-01-2021

Изменено

04-12-2020

Изменено

30-11-2020

Изменено

13-11-2020

Изменено

10-11-2020

Изменено

09-11-2020

Изменено

22-10-2020

Добавлено

08-10-2020

Изменено

22-09-2020

Изменено

18-08-2020

Добавлено