Использование методов OTAPI в документации описано на примере Коробки ОТ и мобильного приложения для нее.

Методы OTAPI можно использовать и варьировать в работе любых сайтов на различных CMS.

Общая работа с OTAPI

Все методы можно увидеть на странице с документацией: http://docs.otapi.net/

Для получения ответа от api в формате xml нужно отправить GET/POST запрос на адрес http://otapi.net/service/

Для получения ответа от api в формате json нужно отправить GET/POST запрос на адрес http://otapi.net/service-json/

К адресу нужно добавить имя метода, и параметры в GET/POST виде, в зависимости от запроса и его размера. Некоторые параметры могут не поместиться в GET-запрос, поэтому проще сделать всегда POST.

В ответе от api обязательно приходит узел ErrorCode, если он не равен 'Ok' и не равен 'BatchError' - необходимо обработать ошибку. Ошибки необходимо разделять по значению в узлах ErrorCode и/или SubErrorCode.

Некоторые ErrorCode которые можно обрабатывать глобально на уровне приложения:

В некоторых случаях, необходимо показать пользователю само сообщение об ошибке. Оно хранится в узле ErrorDescription и приходит уже с переводом для языка, который указан при запросе в параметре 'language'.

Старт приложения

При старте приложения, необходимо получить настройки приложения: http://docs.otapi.net/ru/Documentations/Method?name=GetCommonInstanceOptionsInfo с передачей параметра applicationType=MobileApplication/Android или applicationType=MobileApplication/iOS в зависимости от системы.

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

На старте так же нужно запросить список переводов для интерфейса приложения: http://docs.otapi.net/ru/Documentations/Method?name=GetApplicationUITranslations с передачей языка и параметра applicationType=MobileApplication/Android или applicationType=MobileApplication/iOS в зависимости от системы.

Список успешно полученных настроек необходимо кэшировать. Обновлять кэш настроек стоит в фоновом режиме, не замедляя этим запросом работу приложения. Обновлять кэш в фоне стоит или раз в N часов или при каждом старте приложения. Настройки одни на всё приложение и не различаются для разных пользователей.

Мобильное приложение при первом запуске должно запросить у пользователя разрешение на получение push-уведомлений. Если пользователь согласился получать push-уведомления, необходимо отправить push-токен приложения в сервисы  http://docs.otapi.net/ru/Documentations/Method?name=SetUserPushNotificationToken. В будущем, приложение должно следить за актуальностью push-токена и при необходимости обновлять его.

Получение сессии (авторизация пользователя)

Работа с пользователем - основные моменты

Часть методов otapi требует параметр сессии покупателя. Перед тем как работать с логикой для пользователей, необходимо получить сессию. Изначально нужно получить анонимную сессию методом http://docs.dev.otapi.net/ru/Documentations/Method?name=GetAnonymousSession, чтобы например по ней могли уже сохраняться корзина, предпочтения, и прочее. Если при авторизации была передана предыдущая анонимная сессия, все данные автоматом перенесутся.

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

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

Принудительно проверить сессию проще всего методом http://docs.otapi.net/ru/Documentations/Method?name=GetUserStatusInfo. Он наименее затратен по времени.

Регистрация

Регистрация пользователей осуществляется методом http://docs.otapi.net/ru/Documentations/Method?name=RegisterUser
Перед вызовом этого метода нужно выполнить проверку входных данных:

На форме регистрации должно быть дополнительное обязательное поле-флаг "Я принимаю пользовательское соглашение", без этого флага не давать выполнить регистрацию.

После успешной регистрации методом RegisterUser проверить в ответе поле Result->IsEmailVerificationUsed. Если IsEmailVerificationUsed = "true" то показать пользователю сообщение "Для активации учетной записи Вам на почту было выслано письмо со ссылкой на активацию". Если IsEmailVerificationUsed = "false" нужно сразу авторизовать пользователя (установить для него новый SessionId полученный в ответ от сервисов).

Активация после регистрации

Вместе с сообщением про то что было отправлено письмо, нужно вывести поле ввода кода активации. Когда пользователь проверит почту, введет код в приложении, нужно вызвать http://docs.otapi.net/ru/Documentations/Method?name=ConfirmEmail. В ответе ConfirmEmail есть поле Result->SessionId->Value которое содержит сессию авторизованного пользователя. На основе этой сессии нужно сразу авторизовать пользователя в приложении.

Авторизация

Авторизация пользователей осуществляется методом http://docs.otapi.net/ru/Documentations/Method?name=Authenticate
На форме авторизации должно быть поля логин(userLogin), поле пароль(userPassword) и флаг запомнить(rememberMe). Перед вызовом метода Authenticate нужно выполнить проверку входных данных:

Параметр sessionId в методе Authenticate - идентификатор сессии не авторизованного покупателя.

В качестве userLogin может быть передан не только логин, но и email пользователя, возможно стоит как-то подсказать это в интерфейсе.

Авторизация через социальные сети

Авторизация через соцсети (OT API)

Список социальных сетей надо брать в настройках от GetCommonInstanceOptionsInfo в зависимости от текущего языка, так как например админ может оставить ВКонтакт для русского, и убрать для английского. Смотреть в TranslatableOptions->выбор по языку->ExternalAuthentications.

Восстановление пароля

Восстановление пароля осуществляется методом http://docs.otapi.net/ru/Documentations/Method?name=RequestPasswordRecovery
Поле userIdentifier может содержать логин или email пользователя. После вызова RequestPasswordRecovery показать пользователю сообщение "На Ваш email высланы дальнейшие инструкции" и отобразить форму с кодом подтверждения и двумя текстовыми полями для ввода пароля. Пользователь смотрит код в почте, возвращается в приложение, вводит код и новый пароль. Если оба поля с паролем совпадают, далее нужно вызвать http://docs.otapi.net/ru/Documentations/Method?name=ConfirmNewPassword. В ответе ConfirmNewPassword есть поле Result->SessionId->Value которое содержит сессию авторизованного пользователя. На основе этой сессии нужно сразу авторизовать пользователя в приложении.

Главная страница приложения

Получение подборок

Для получения подборок, используем метод http://docs.otapi.net/ru/Documentations/Method?name=BatchSearchRatingLists. При первом запросе передаем параметры: 

<BatchRatingListSearchParameters><UseDefaultParameters>true</UseDefaultParameters></BatchRatingListSearchParameters>

и параметр applicationType=MobileApplication/Android или applicationType=MobileApplication/iOS в зависимости от системы. В полученных данных для каждой подборки имеется порядковый номер для сортировки <Order>0</Order> , нужно отобразить на экране приложения подборки согласно этой сортировке.

Рекомендуется показывать подборки покупателю только из кэша. В кэше подборки необходимо обновлять в фоновом режиме: раз в 60 минут запрашивать метод BatchSearchRatingLists и сохранять результат в кэш. При получении ответа, нужно проверить узел HasTranslateErrors (http://docs.dev.otapi.net/ru/Documentations/Type?name=BatchRatingListsSearchResultAnswer), если узел равен "true", то кэшировать ответ не нужно. Для каждого элемента RatingList в ответе необходимо проверить узел HasError (http://docs.dev.otapi.net/ru/Documentations/Type?name=RatingListSearchResult), если узел равен "true", то этот элемент подборки кэшировать не нужно.

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

Получение баннеров

Сперва откройте плагин "Демонстрация MetaUI":

В выпадающем списке выберите "Настройки баннеров" и нажмите кнопку "Открыть":

Затем заполните необходимые данные (обязательно добавьте веб-сайт):

После этого, выполните следующие шаги:

Полный список баннеров, настроенных для приложения, можно получить методом http://docs.otapi.net/ru/Documentations/Method/GetBanners , передав параметр applicationType=WebSite (applicationType=MobileApplication/Android или applicationType=MobileApplication/iOS в зависимости от системы), а также ключ и язык. Выполните запрос и получите ссылку:

 

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

Тип действияПараметр

Url

Абсолютный адрес ссылки, которую нужно открыть при нажатии на баннер

Category

Идентификатор категории, которую нужно открыть при нажатии на баннер


Короткое меню категорий

TODO:

Социальные сети

TODO:

Предпочтения пользователя

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

Получить текущие выбранные предпочтения можно с помощью метода http://docs.otapi.net/ru/Documentations/Method?name=GetUserPreferences

Изменить предпочтение пользователя можно с помощью метода http://docs.otapi.net/ru/Documentations/Method?name=UpdateUserPreferences

Интерфейс приложения должен предоставить пользователю возможность изменять свои предпочтения.

Список категорий

Для начала отправляем запрос: http://docs.otapi.net/ru/Documentations/Method?name=GetRootCategoryInfoList - получаем список корневых категорий сайта. Этот список можно кэшировать на 24 часа. Далее при попытке развернуть каждую категорию - делаем отдельный запрос http://docs.otapi.net/ru/Documentations/Method?name=GetCategorySubcategoryInfoList. Каждый список тоже можно кэшировать на 24 часа, используя в ключе идентификатор родительской категории.

Категория содержит другие категории (т.е. её можно развернуть), если для неё пришел флаг <IsParent>true</IsParent>.

Если категория скрыта <IsHidden>true</IsHidden> - её нужно не показывать в каталоге.

Флаг <IsVirtual>true</IsVirtual> означает что в категории нет товаров (только другие подкатегории).

В необязательном узле <IconImageUrl> http://open-demo.otcommerce.com/uploaded/category/zh_odezhda.jpg </IconImageUrl> может придти абсолютный адрес изображения для категории (изображение устанавливается администратором в админке).

В силу особенностей каталога, флаг <IsParent>true</IsParent> иногда возвращается ошибочно. Поэтому при открытии такой категории нужно реализовать дополнительную логику: если <IsVirtual>false</IsVirtual> и список подкатегорий пуст - открывать экран поиска товаров по данной категории.

Страница рекомендуемых продавцов

Метод http://docs.otapi.net/ru/Documentations/Method?name=BatchSearchRatingLists , xmlSearchParameters=

<BatchRatingListSearchParameters><RatingLists><RatingList><CategoryId>0</CategoryId><ItemRatingType>Best</ItemRatingType><IsRandomSearch>false</IsRandomSearch><ContentType>Vendor</ContentType><FramePosition>0</FramePosition><FrameSize>20</FrameSize></RatingList></RatingLists></BatchRatingListSearchParameters>

FrameSize - количество продавцов на странице, в ответе придет TotalCount - общее количество рекомендуемых продавцов. На основе TotalCount и FrameSize можно нарисовать пагинацию.

Страница рекомендуемых брендов

Метод http://docs.otapi.net/ru/Documentations/Method?name=BatchSearchRatingLists , xmlSearchParameters=

<BatchRatingListSearchParameters><RatingLists><RatingList><CategoryId>0</CategoryId><ItemRatingType>Best</ItemRatingType><IsRandomSearch>false</IsRandomSearch><ContentType>Brand</ContentType><FramePosition>0</FramePosition><FrameSize>20</FrameSize></RatingList></RatingLists></BatchRatingListSearchParameters>

FrameSize - количество брендов на странице, в ответе придет TotalCount - общее количество рекомендуемых брендов. На основе TotalCount и FrameSize можно нарисовать пагинацию.

Отображение продавцов

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

Свойство
Описание
Где отображать
Комментарии
DisplayNameимя продавцавездеБлагодаря этому позже админ магазина сможет менять имена, а для МП ничего не поменяется в логике.
DisplayPictureUrlкартинка продавцавезде

Только если есть, иначе в рекомендуемых продавцах картинка-заглушка, в остальных местах не отображать.
Благодаря этому позже админ магазина сможет менять картинки, а для МП ничего не поменяется в логике.

Credit/Levelрейтинг продавца от 1 до 20в карточке товара
на странице продавца
Только если есть и не равно 0, иначе отображать не нужно.
Credit/TotalFeedbacksчисло отзывовв карточке товара
на странице продавца
Только если есть и не равно 0, иначе отображать не нужно.
Credit/PositiveFeedbacksчисло положительных отзывовв карточке товара
на странице продавца
Только если есть и не равно 0, иначе отображать не нужно.
Если есть, отображать в виде % от числа общих отзывов.
Scores/DeliveryScoreоценка доставки от 1.0 до 5.0в карточке товара
на странице продавца
Только если есть и не равно 0, иначе отображать не нужно.
Scores/ItemScoreоценка товаров от 1.0 до 5.0в карточке товара
на странице продавца
Только если есть и не равно 0, иначе отображать не нужно.
Scores/ServiceScoreоценка услуг (или сервиса) от 1.0 до 5.0в карточке товара
на странице продавца
Только если есть и не равно 0, иначе отображать не нужно.

Поиск

Основная документация по поиску.

Для поиска товаров используем только один удобный метод поиска: http://docs.otapi.net/ru/Documentations/Method?name=BatchSearchItemsFrame

При первом открытии категории/продавца/бренда или поискового запроса, вызываем метод BatchSearchItemsFrame с параметром blockList: SubCategories,HintCategories,Vendor,Brand,Category,RootPath,SearchProperties,AvailableSearchMethods. В xmlParameters передаем параметры поиска (например ид категории <CategoryId>otc-3</CategoryId> или поисковый запрос <ItemTitle>dress</ItemTitle>). Мы рекомендуем с параметрами поиска передать: <UseOptimalFrameSize>true</UseOptimalFrameSize>, это позволит получить в ответе оптимальное количество товаров на странице для данного запроса.

Отображаем информацию полученную в ответе.

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

В узле Items по значениям из узлов Provider и SearchMethod (например: <Provider>Taobao</Provider> <SearchMethod>Extended</SearchMethod>) в AvailableSearchMethods находим подробную информацию о текущем способе поиска, которым сейчас сделан запрос. По этой информации предоставляем пользователю фильтры доступные на текущей странице поиска. О соответствии полей из информации о способе поиска и параметрами поиска можно найти в основной документации.

Показываем пользователю SubCategories (Подкатегории), HintCategories (Возможно вас заинтересует). В узле Items - Categories приходят категории подходящие под текущий поисковый запрос (Уточните категорию).

Показываем товары из узла Items - Items . Проверяем у товара флаг IsSellAllowed=true и SellDisallowReason="IsFiltered" - то показываем заглушку для товара - товар запрещен к покупке. Если ErrorCode товара не равен "Ok" - вместо картинки товара, показываем ошибку: NotFound - Товар удален, NotAvailable - Товар недоступен, все остальные коды ошибок просто слово "Ошибка".

Хорошей практикой является менять отображение в зависимости от того, какие узлы вернулись в ответе на запрос BatchSearchItemsFrame. Если:

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

Карточка товара

При первом открытии карточки, нужно использовать метод: http://docs.otapi.net/ru/Documentations/Method?name=BatchGetSimplifiedItemFullInfo , нужно передавать следующие blockList: RootPath,Vendor,MostPopularVendorItems16,Description

Отображаем карточку товара:

В карточке товара нужно учитывать настройки от метода GetCommonInstanceOptionsInfo, узел Showcase.ItemShowcase . Каждый узел является флагом (админ может управлять этими флагами) и сообщает какую информацию отображать покупателю, а какую скрыть:

Отображаем цену товара:

используем метод: http://docs.otapi.net/ru/Documentations/Method?name=BatchGetSimplifiedItemConfigurationInfo , нужно передавать следующие blockList: AdditionalPrices (первый раз запрос можно сделать с пустым xmlRequest)

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

 

<Request />
равнозначен
<Request>
    <Current />
</Request>
равнозначен
<Request>
    <Current Quantity="1" />
</Request>
<Request>
    <Current Quantity="5" />
</Request>
Запрос на текущие опции:
<Request>
    <Current ConfigurationId="идКонфигурации" />
</Request>
или
<Request>
    <Current ConfigurationId="идКонфигурации" Quantity="5" />
</Request>
или
<Request>
    <Current>
        <Property Id="идЦвета" ValueId="идКрасного"
    </Current>
</Request>
или
<Request>
    <Current>
        <Property Id="идЦвета" ValueId="идКрасного"
        <Property Id="идРазмера" ValueId="идСреднего"
    </Current>
</Request>
или
<Request>
    <Current Quantity="5">
        <Property Id="идЦвета" ValueId="идКрасного"
        <Property Id="идРазмера" ValueId="идСреднего"
        <Property Id="идМодели" ValueId="идНекойМодели"
    </Current>
</Request>
<Request>
    <Selected ConfigurationId="идКонфигурации" Quantity="1" />
    <Selected ConfigurationId="идКонфигурации" Quantity="2" />
    ...
</Request>
или
<Request>
    <Selected Quantity="1">
        <Property Id="идЦвета" ValueId="идКрасного"
        <Property Id="идРазмера" ValueId="идСреднего"
    </Selected>
    <Selected Quantity="2">
        <Property Id="идЦвета" ValueId="идЗеленого"
        <Property Id="идРазмера" ValueId="идСреднего"
    </Selected>
    ...
</Request>

Запрос со всем вместе может быть любой комбинацией других видов запросов, например:
<Request>
    <Current Quantity="5">
        <Property Id="идЦвета" ValueId="идКрасного"
    </Current>
    <Selected Quantity="2">
        <Property Id="идЦвета" ValueId="идЗеленого"
        <Property Id="идРазмера" ValueId="идСреднего"
    </Selected>
</Request>


Что такое Current/Selected - и как правильно? Current или Selected?
Если нужен только 1 одновременно выбранный конфиг, то про Selected можно вообще не думать.
Если нужно несколько (как на 1688), то в Selected шлем всё где введено количество.

Избранные товары

Получить список товаров: Метод http://docs.otapi.net/ru/Documentations/Method?name=BatchGetUserData , blockList=Note.

Добавить товар в избранное http://docs.otapi.net/ru/Documentations/Method?name=AddItemToNote , fieldParameters=<Fields/>

Удалить товар из избранного http://docs.otapi.net/ru/Documentations/Method?name=RemoveItemFromNote , перед удалением требуется подтверждение удаления.

Переместить в корзину http://docs.otapi.net/ru/Documentations/Method?name=MoveItemFromNoteToBasket

Изменить кол-во товара в избранном http://docs.otapi.net/ru/Documentations/Method?name=EditNoteItemQuantity

Изменить комментарийhttp://docs.otapi.net/ru/Documentations/Method?name=EditNoteItemFields , <Fields><FieldInfo Name="Comment" Value="Текст комментария"/></Fields>

Изменить конфигурацию товара:
1. доступные конфигурации получить методами http://docs.otapi.net/ru/Documentations/Method?name=BatchGetSimplifiedItemFullInfo и http://docs.otapi.net/ru/Documentations/Method?name=BatchGetSimplifiedItemConfigurationInfo - так же, как и в карточке товара.
2. сохранение новой конфигурации осуществляется добавлением новой и удалением старой, т.е. последовательными вызовами http://docs.otapi.net/ru/Documentations/Method?name=AddItemToNote (важно передать в метод все fieldParameters от старой записи, иначе можно потерять часть информации, например комментарий пользователя к товару) и http://docs.otapi.net/ru/Documentations/Method?name=RemoveItemFromNote 

Избранные продавцы

Список продавцов: Метод http://docs.otapi.net/ru/Documentations/Method?name=SearchSimplifiedFavoriteVendors

Добавить продавца в избранное http://docs.otapi.net/ru/Documentations/Method?name=AddVendorToFavorites , fieldParameters=<Fields/>

Удалить продавца из избранного http://docs.otapi.net/ru/Documentations/Method?name=RemoveVendorFromFavorites Требуется подтверждение удаления.

Корзина

Получить список товаров: Метод http://docs.otapi.net/ru/Documentations/Method?name=BatchGetUserData , blockList=Basket.

Список товаров должен быть разделен по провайдерам, т.к. в один заказ можно оформить товары только одного провайдера. Пользователя нужно уведомить о минимальной сумме заказа (если она установлена в конфигурации), получить её можно с помощью метода: http://docs.otapi.net/ru/Documentations/Method?name=GetCommonInstanceOptionsInfo (Result->Order->MinOrderCost)


Удалить товар из корзины http://docs.otapi.net/ru/Documentations/Method?name=RemoveItemFromBasket , перед удалением требуется подтверждение удаления.

Полная очистка корзины http://docs.otapi.net/ru/Documentations/Method?name=ClearBasket , перед очисткой требуется подтверждение.

Перенести товар из корзины в избранное http://docs.otapi.net/ru/Documentations/Method?name=MoveItemFromCartToNote для переноса нескольких отмеченных товаров делается несколько вызовов MoveItemFromCartToNote

Изменение количества товара в корзине http://docs.otapi.net/ru/Documentations/Method?name=EditBasketItemQuantity

Добавление/изменение комментария к товару http://docs.otapi.net/ru/Documentations/Method?name=EditBasketItemFields <Fields><FieldInfo Name="Comment" Value="Текст комментария"/></Fields>

Редактирование веса товара http://docs.otapi.net/ru/Documentations/Method?name=EditBasketItemFields <Fields><FieldInfo Name="Weight" Value="Число нового веса товара"/></Fields>

Изменить конфигурацию товара:
1. доступные конфигурации получить методами http://docs.otapi.net/ru/Documentations/Method?name=BatchGetSimplifiedItemFullInfo и http://docs.otapi.net/ru/Documentations/Method?name=BatchGetSimplifiedItemConfigurationInfo - так же, как и в карточке товара.
2. сохранение новой конфигурации осуществляется добавлением новой и удалением старой, т.е. последовательными вызовами http://docs.otapi.net/ru/Documentations/Method?name=AddItemToBasket (важно передать в метод все fieldParameters от старой записи, иначе можно потерять часть информации, например комментарий пользователя к товару) и  http://docs.otapi.net/ru/Documentations/Method?name=RemoveItemFromBasket 

Группировка товаров в корзине по добавочной стоимости

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

Для каждого провайдера находим узел CollectionSummary, метод OTApi BatchGetUserData , blockList=Basket (или метод OTApi GetBasket) по узлу ProviderType
CollectionSummaries.CollectionSummary.ProviderType
В коллекции провайдера разделяем товары на группы. Проходимся по элементам AdditionalPriceInfo массива AdditionalPriceInfoList.Elements.
- Для каждого AdditionalPriceInfo указаны какие элементы корзины хранятся в данной группе (узел ElementIds), указано название группы DisplayName и цена Price
- Те строки корзины, которые не имеют добавочной стоимости - отображаются без группировки

Пример формирования данных для корзины

public function getBasketAction()
{
    $lang = Session::getActiveLang();
    $user = User::getObject();
    $sid = $user->getSid();
    $userAuthenticated = $user->isAuthenticated();
    $currencySign = InstanceProvider::getObject()->GetInternalCurrency()->GetCode();
    $currencyCode = $user->getCurrencyCode();

    $minOrderTotalCost = 0;
    $providers = array();
    $checked = array();
    $currentProviderExists = false;

    $currentProviderAlias = $this->request->getValue('activeProvider');
    $activeLines = $this->request->getValue('activeBasketLines');
    if ($this->request->valueExists('activeProvider')) {
        $activeLines = $activeLines ? explode(',', $activeLines) : array();
    }
    $instanceProvider = InstanceProvider::getObject();

    try {
        OTAPILib2::simpleRequest('GetBasket', [
            'language' => $lang,
            'sessionId' => $sid,
        ], $basket);
        OTAPILib2::makeRequests();
        $collectionInfo = $basket->GetRawData()->CollectionInfo;

        // проходим по всем товарам в корзине
        if (isset($collectionInfo->Elements->ElementInfo)) {
            foreach ($basket->GetRawData()->CollectionInfo->Elements->ElementInfo as $item) {
                $item = new OtapiElementInfo($item);
                $basketLineId = $item->GetId();
                $itemId = $item->GetItemId();
                $provider = $item->GetProviderType();
                $product = Product::getObject($itemId, $item);

                // создаем массив провайдера
                if (!key_exists($provider, $providers)) {
                    $alias = $instanceProvider->GetAliasByProviderName($lang, $provider);

                    $providerData = array();
                    $providerData['alias'] = $alias;
                    $providerData['itemsQuantity'] = 0;
                    $providerData['info'] = InstanceProvider::getObject()->GetProviderInfo($lang, $provider);

                    // создаем группу товаров с id=0. в ней содержатся несгруппированные товары
                    $providerData['groups'][0] = array(
                        'name' => '',
                        'displayName' => '',
                        'convertedPriceList' => array(
                            'sign' => $currencySign,
                            'code' => $currencyCode,
                            'value' => 0
                        ),
                        'items' => array()
                    );

                    // проверяем активного провайдера
                    if (!$currentProviderAlias || $currentProviderAlias === $alias) {
                        $currentProviderExists = true;
                        $currentProviderAlias = $alias;
                        $providerData['isCurrent'] = true;
                    } else {
                        $providerData['isCurrent'] = false;
                    }

                    $providers[$provider] = $providerData;
                }

                // добавляем товар в не сгруппированные
                $providers[$provider]['groups'][0]['items'][$basketLineId] = $product;
                $providers[$provider]['itemsQuantity']++;
            }
        }

        // если мы не нашли активного провайдера, то выставляем первого, как активного
        if (! $currentProviderExists) {
            $keys = array_keys($providers);
            if (! empty($keys)) {
                $firstProvider = $keys[0];
                $providers[$firstProvider]['isCurrent'] = true;
            }
        }

        // проходим по провайдерам, что бы получить их группы
        if (isset($collectionInfo->CollectionSummaries->CollectionSummary)) {
            foreach ($basket->GetRawData()->CollectionInfo->CollectionSummaries->CollectionSummary as $summary) {
                $summary = new OtapiCollectionSummary($summary);
                $provider = $summary->GetProviderType();

                // проходим по группам провайдеров, что бы сгруппировать по ним товары
                foreach ($summary->GetAdditionalPriceInfoList()->GetElements()->GetAdditionalPriceInfo() as $group) {
                    // готовим данные текущей группы
                    $groupType = (string) $group->GetType();
                    $groupName = (string) $group->GetName();
                    $groupId = md5($groupType . $groupName);
                    $groupDisplayName = (string) $group->GetDisplayName();

                    // берем цену по выбранной пользователем валюте
                    $convertedPriceList = array();
                    $displayedMoneys = $group->GetPrice()->GetConvertedPriceList()->GetDisplayedMoneys();
                    foreach ($displayedMoneys->GetMoney() as $displayMoney) {
                        if ($displayMoney->GetCodeAttribute() === $currencyCode) {
                            $convertedPriceList = array(
                                'sign' => (string) $displayMoney->GetSignAttribute(),
                                'code' => (string) $displayMoney->GetCodeAttribute(),
                                'value' => (float) $displayMoney->GetValue()
                            );

                            break;
                        }
                    }

                    // добавляем текущую группу в группы провайдера
                    $providers[$provider]['groups'][$groupId] = array(
                        'name' => $groupName,
                        'displayName' => $groupDisplayName,
                        'convertedPriceList' => $convertedPriceList,
                        'items' => array(),
                    );

                    // проходим по товарам текущей группы
                    foreach ($group->GetElementIds()->GetRawData()->children() as $basketLineId) {
                        $basketLineId = (string) $basketLineId;

                        // ищем товар в списке не сгруппированных товаров
                        $ungroupedItem = $providers[$provider]['groups'][0]['items'][$basketLineId];
                        // добавляем товар в текущую группу
                        $providers[$provider]['groups'][$groupId]['items'][$basketLineId] = $ungroupedItem;
                        // удаляем товар из списка не сгруппированных товаров
                        unset($providers[$provider]['groups'][0]['items'][$basketLineId]);
                    }
                }

                if (empty($providers[$provider]['groups'][0]['items'])) {
                    // если не сгруппированных товаров не осталось, то удаляем пустой массив из групп
                    unset($providers[$provider]['groups'][0]);
                }
            }
        }

        // получаем минимальную стоимость заказа
        $instanceSettings = InstanceProvider::getObject()->GetCommonInstanceOptionsInfo($lang);
        $minOrderTotalCost = $instanceSettings->GetOrder()->GetConvertedMinOrderCost();
        foreach ($minOrderTotalCost->GetDisplayedMoneys()->GetMoney() as $value) {
            if($value->GetSignAttribute() === User::getObject()->getCurrencySign()) {
                $currencySign = $value->GetSignAttribute();
                $minOrderTotalCost = $value->GetValue();
                break;
            }
        }

    } catch (Exception $e) {
        $this->respondAjaxError($e);
    }

    $this->sendAjaxResponse([
        'content' => $this->renderPartial('controllers/basket/list', [
            'providers' => $providers,
            'userAuthenticated' => $userAuthenticated,
            'minOrderTotalCost' => $minOrderTotalCost,
            'currencySign' => $currencySign
        ])
    ]);
}

 

Профиль

Получение основной информации о пользователе (Имя, email и т.п.): http://docs.otapi.net/ru/Documentations/Method?name=GetUserInfo , редактирование этой информации возможно с помощью метода http://docs.otapi.net/ru/Documentations/Method?name=UpdateUser

Адрес доставки. Адрес доставки создается и редактируется отдельно от основной информации о пользователе. Всего возможно N адресов доставки, где N берется из настройки http://docs.otapi.net/ru/Documentations/Method?name=GetCommonInstanceOptionsInfo UserProfile->MaxProfilesCount . Методы для работы с адресом доставки:

Для пользователя, необходим интерфейс, который позволяет выбрать профиль используемый по умолчанию https://www.screencast.com/t/1fZ9HuauBjf . Выбранный профиль необходимо сохранить в предпочтения пользователя: http://docs.otapi.net/ru/Documentations/Method?name=UpdateUserPreferences , поле ProfileId.

Получение списка контентных страниц для экрана профиля: http://docs.otapi.net/ru/Documentations/Method?name=GetContentMenuItemTree , параметры:

applicationType=MobileApplication/Android или applicationType=MobileApplication/iOS в зависимости от системы
menuList=Profile
includeContent=true

Список стран, в которые возможна доставка, нужно получить с помощью метода: http://docs.otapi.net/ru/Documentations/Method?name=GetDeliveryCountryInfoList

Список городов нужно получать методом: http://docs.otapi.net/ru/Documentations/Method?name=SearchCities  (пока пользователь вводит символы для поиска города, т.е. параметр QueryText пуст, нужно отправить IsMainCity=true для получения списка городов по умолчанию). В момент когда пользователь выбрал город из списка, сохраняем в профиле City и CityCode и автоматически заполняем поле Region.

Важно: CountryCode - обязательное поле для оформления заказа, CityCode - не обязательное поле, если пользователь выбрал город из списка, то система должна передать код в сервисы, если же пользователь ввёл город, которого нет в списке SearchCities, сохраняем только узл City.

Список заказов

http://docs.otapi.net/ru/Documentations/Method?name=SearchOrdersForUser

Активные: <OrderSearchParametersForUser><IsCancelled>false</IsCancelled><IsCompleted>false</IsCompleted></OrderSearchParametersForUser>

Отмененные: <OrderSearchParametersForUser><IsCancelled>true</IsCancelled></OrderSearchParametersForUser>

Закрытые: <OrderSearchParametersForUser><IsCompleted>true</IsCompleted></OrderSearchParametersForUser>

Ожидающие действий со стороны пользователя (оплата/доплата или подтверждение цены): <OrderSearchParametersForUser><IsAwaitingUser>true</IsAwaitingUser></OrderSearchParametersForUser>

Дизайном предусмотрено три группы заказов:

1) Ожидающие оплаты: <OrderSearchParametersForUser><IsAwaitingUser>true</IsAwaitingUser></OrderSearchParametersForUser>
2) Доставки: <OrderSearchParametersForUser><IsAwaitingUser>false</IsAwaitingUser><IsCancelled>false</IsCancelled><IsCompleted>false</IsCompleted></OrderSearchParametersForUser>
3) Покупки (иконка самолет), <OrderSearchParametersForUser><IsCompleted>true</IsCompleted><IsCancelled>true</IsCancelled></OrderSearchParametersForUser>

Список полей для отображения:

ID заказа, который передается в API во все возможные методы: Id
Номер заказа, отображаемый покупателю: DisplayId
Дата заказа: CreatedDateTime
Статус: StatusName
Сумма: TotalAmount
Уже оплачено: TotalAmount минус RemainAmount
К оплате: RemainAmount
Стоимость товаров: GoodsAmount
Позиций товаров: такой информации нет - не показываем это поле

Заказ. Подробная информация

Получить информацию о заказе: http://docs.otapi.net/ru/Documentations/Method?name=GetSalesOrderDetails

Отображаем для пользователя информацию о заказе (узел OrderInfo, подробнее здесь http://docs.otapi.net/ru/Documentations/Type?name=OtapiOrderInfo), особенности:

Отображаем список товаров в заказе (узел SalesLinesList, подробнее о каждой строке здесь http://docs.otapi.net/ru/Documentations/Type?name=SalesLine)

Список посылок - http://docs.otapi.net/ru/Documentations/Method?name=GetSalesOrderShippings

Заказ. Оформление заказа и выбор способа доставки

В один заказ можно оформить товары только одного провайдера.

При оформлении заказа, потребуется форма выбора адреса доставки или, если адрес ранее на создавался, форма создания адреса/ов доставки (якорь на документацию по профилям доставки).

При оформлении заказа, потребуется выбрать способ доставки из предоставленного списка: запрос http://docs.otapi.net/ru/Documentations/Method?name=SearchDeliveryModes , параметры xmlSearchParameters:

Пример параметров: <DeliveryModeSearchParameters><CountryCode>RU</CountryCode><CityCode>7700000000000</CityCode><Weight>75.000</Weight><ProviderType>Taobao</ProviderType></DeliveryModeSearchParameters>.

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

После успешного оформления заказа, нужно сохранить ExternalDeliveryId в предпочтения пользователя (http://docs.otapi.net/ru/Documentations/Method?name=UpdateUserPreferences) , а так же обновить параметры адреса доставки, если интерфейс сразу этого не сделал (http://docs.otapi.net/ru/Documentations/Method?name=UpdateUserProfile).

Из корзины

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

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

Заказ можно оформить "из корзины", для этого нужно вызвать метод http://docs.otapi.net/ru/Documentations/Method?name=CreateOrder , передав в него:

Дозаказ

При оформлении заказа, нужно предложить пользователю возможность сделать дозаказ (если такая возможность у пользователя имеется). Получаем заказы, в которые можно сделать дозаказ, вызов http://docs.otapi.net/ru/Documentations/Method?name=SearchOrdersForUser с параметрами xmlSearchParameters=

<OrderSearchParametersForUser><ProviderType>ИдПровайдера(например: Taobao)</ProviderType><IsAvailableForRecreation>true</IsAvailableForRecreation></OrderSearchParametersForUser>. Если такие заказы есть, предлагаем покупателю выбрать: оформляет он новый заказ или делает дозаказ. Для дозаказа нужно вызвать метод http://docs.otapi.net/ru/Documentations/Method?name=RecreateOrder .

Быстрый заказ

Заказ можно оформить из карточки товара, кнопка "Быстрый заказ", для этого нужно вызвать метод http://docs.otapi.net/ru/Documentations/Method?name=AddOrder , передав в него:

Оплата заказа и пополнение лицевого счета

Если заказ не оплачен, обязательно предлагаем пользователю его оплатить. Если на лицевом счете клиента есть средства - запрос http://docs.otapi.net/ru/Documentations/Method?name=GetAccountInfo , узел AvailableAmount (при отображении обязательно используем CurrencySign) , предлагаем пользователю кнопку, которая оплатит заказ с лицевого счета. На кнопке пишем сумму доступную для оплаты, это должна быть или RemainAmount из информации о заказе http://docs.otapi.net/ru/Documentations/Method?name=GetSalesOrderDetails (при условии что на лицевом счете больше чем RemainAmount) или AvailableAmount (при условии что на лицевом счете меньше чем RemainAmount).

При нажатии на кнопку вызываем метод http://docs.otapi.net/ru/Documentations/Method?name=PaymentPersonalAccount и обновляем страницу заказа.

Заказ можно оплатить и через платежные системы. Запрашиваем список доступных платежных систем: http://docs.otapi.net/ru/Documentations/Method?name=GetPaymentModes . Картинка ПС - AbsoluteImageUrl, название ПС - Name. При клике на платежную систему, отображаем кнопку "Оплатить" и если узел CustomField == "Email" - отображаем поле ввода почты, если узел CustomField == "Phone" - отображаем поле ввода номера телефона.

При клике на кнопку Оплатить, вызываем метод http://docs.otapi.net/ru/Documentations/Method?name=GetPaymentParameters . Передаем параметры Amount (RemainAmount из информации о заказе), CurrencyCode, PaymentSystemId, OrderId, в SuccessUrl и FailUrl адреса возврата, которые приложение сможет перехватить. По полученному ответу формируем http-форму, открываем её в браузере, и отправляем её.:

Если в метод GetPaymentParameters не передать OrderId , то лицевой счет клиента пополнится на сумму Amount.

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

Счет пользователя

Информацию о лицевом счете можно получить с помощью метода - http://docs.otapi.net/ru/Documentations/Method?name=GetAccountInfo

Формирование выписки по счету можно получить с помощью метода - http://docs.otapi.net/ru/Documentations/Method?name=GetStatement