🚚 Интеграция самовывоза на следующий день (доставка со склада, extendedPickup
)
#
Тип заказа самовывоз на следующий день
подразумевает доставку Партнером товаров в выбранную клиентом аптеку. Когда
заказ
доставлен в аптеку, клиенту посылается уведомление, что его заказ готов к выдаче и он может его забирать. При таком типе
заказа, важно знать предварительную дату поставки или алгоритм, по которому эту дату можно вычислить, а также передавать
ее возможные изменения через статус заказа.
Требования к Партнёру для интеграции extendedPickup
:
- регулярная передача остатков и цен склада(ов) - не реже одного раза в час. Важно! На один регион может быть несколько складов, один склад может быть на несколько регионов
- ежедневное обновление дат доставки остатков в аптеки на ближайшие (2-3) дня
- передача для каждой аптеки признака - с какого склада она получает остатки. Важно! Одна аптека может принимать остатки только с одного склада.
- передача статусов по заказу: принят. собран (возможно частично), отправлен, доставлен, выполнен, отменен
Из чего состоит интеграция #
Интеграция с Ютекой состоит из двух глобальных процессов:
- синхронизация каталога (выгрузки)
- обмен заказами
graph TD A[Интеграция с Ютекой] --> B[Выгрузки] A --> C[Обмен заказами]
Выгрузки #
Это процесс получения Ютекой информации от Патрнёра о:
- складах
- аптеках
- товарах
- остатках
Обмен заказами #
Это процесс, при котором Ютека:
- отправляет новые заказы в инфраструктуру Партнёра
- получает статусы по отправленным в инфраструктуру Партнёра заказам
- отправляет отмены по отправленным в инфраструктуру Партнёра заказам
Примеры реализации интеграции самовывоза на следующий день с нуля #
Выгрузки через протокол FTP #
graph TD Partner[Партнёр] --> Warehouses[Склады] Partner[Партнёр] --> Pharmacies[Аптеки] Partner[Партнёр] --> Products[Товары] Partner[Партнёр] --> Stocks[Остатки] Warehouses[Склады] --> FTP Pharmacies[Аптеки] --> FTP Products[Товары] --> FTP Stocks[Остатки] --> FTP FTP --> Uteka[Ютека] Uteka[Ютека] --> FTP
На данный момент мы поддерживаем следующие форматы файлов: JSON
(приоритетнее), CSV
, XML
.
Файлы должны быть в кодировке UTF-8 или Windows-1251 без BOM.
Для CSV
формата Рекомендуемый разделитель должен быть: ;
, но можно ,
, если технически можете сформировать *
корректный* csv. Формат файлов должен быть одинаковый для всех файлов в выгрузках.
Т.е. не должно быть такого, что аптеки находятся в json
, товары - в xml
, а остатки - в csv
.
Для всех процессов выбирается только 1 формат.
В случае, если у Партнера нет своего FTP сервера, Ютека может предоставить доступ на свой FTP сервер.
Склады FTP #
Частота выгрузки складов должна быть до 1 раза в сутки. Нестандартная частота выгрузок обсуждается в индивидуальном порядке.
Для выгрузки складов в корень FTP выгружается файл region.json
(или csv/xml в зависимости от выбранного формата).
Файл должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание | Комментарий |
---|---|---|---|---|
id |
string | ✔️ | ID склада | Нужен для привязки к аптекам, куда будут доставляться остатки. Может содержать название региона. |
title |
string | ✔️ | Название склада | Краткое описание. Может содержать название региона. |
Пример JSON
[
{
"id": "msc",
"title": "Москва"
},
{
"id": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"title": "Краснодарский край \"Парк А\""
}
]
Пример CSV
id;title
msc;Москва
20247701-bf4b-11ed-812f-00e0ed9e2e92;Краснодарский край "Парк А"
Пример XML
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<id>msc</id>
<title>Москва</title>
</item>
<item>
<id>20247701-bf4b-11ed-812f-00e0ed9e2e92</id>
<title>Краснодарский край "Парк А"</title>
</item>
</items>
Аптеки FTP #
Частота выгрузки складов должна быть от 1 раза в 3 часа до 1 раза в сутки. Нестандартная частота выгрузок обсуждается в индивидуальном порядке.
Для выгрузки аптек в корень FTP выгружается файл pharmacies.json
(или csv/xml в зависимости от выбранного формата).
Файл должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание | Комментарий |
---|---|---|---|---|
id или pharmacyId |
string | ✔️ | ID аптеки | |
title |
string | ✔️ | Название аптеки | |
warehouseId |
string | ✔️ | ID склада из выгрузок складов | |
region |
string | Регион местонахождения аптеки | Не обязателен, если указан в поле address |
|
city |
string | ️ | Город местонахождения аптеки | Не обязателен, если указан в поле address |
address |
string | ✔️ | Адрес местонахождения аптеки полностью (Область, населённый пункт, улица, дом ). Без почтового индекса. | С точностью до дома. Если возможно, без дополнительных комментариев, вида пом.75843б, за магазином канцтоваров . Регион и город не обязательно указывать тут, если они передаются в полях region и city |
phone |
string | ✔️ | Телефон в международном формате (+71234567890 ) |
Если это городской номер телефона, то с кодом города (например для Новосибирска это +7(383)000‒00‒00 ) |
workingHours |
Object или string (см. пример ниже) | ✔️ | Часы работы в формате | |
deliveryDates |
Object (см. пример ниже) | ✔️ | Даты доставки остатков со склада warehouseId |
|
location |
string | ✔️ | GPS координаты Latitude,Longitude |
Например, 59.939032,30.315826 . При подстановке в гугл/яндекс/другие карты должна показываться точка на аптеку. По координатам проверяется адрес |
email |
string | Email аптеки для связи | Мы можем присылать email оповещения в аптеку при поступлении новых или отмене заказов |
Возможно добавление дополнительных полей для более специфичной логики, например, ограничение доставки некоторых остатков в некоторые аптеки (например, по признаку наличия холодильника в аптеке для вакцин) или по другим условиям.
Обсуждается в индивидуальном порядке.
График работы #
График работы ожидается в виде JSON объекта, разбитый по дням недели, либо в строковом типе.
Для JSON-объекта:
- Дни недели пронумерованы от 1 до 7, где 1 - понедельник, 7 - воскресенье.
open
- время открытия аптеки,close
- время закрытия. Относительно этих значений присылаются уведомления пользователям, и считаются регламенты (например, “будет собран до”).- Если аптека не работает в какой-то день (например, в воскресенье), то день недели пропускается, либо значения
open
иclose
заполняются пустыми строками:"open":"","close":""
. - Если аптека круглосуточная, то
open
иclose
присылается со значением 00:00:"open":"00:00","close":"00:00"
- Разработчикам: нужно учитывать, что JSON формат относится только к одному полю из АПИ, а поле (колонка в выгрузке) должна быть строкой в соответствующем формате (JSON, CSV или XML, как договорились). Поэтому нужно не забывать про экранирование данных при подстановке в выгрузку.
Пример JSON объекта workingHours
"workingHours": {
"1": {
"open": "08:00",
"close": "20:00"
},
"2": {
"open": "08:00",
"close": "20:00"
},
"3": {
"open": "08:00",
"close": "20:00"
},
"4": {
"open": "08:00",
"close": "20:00"
},
"5": {
"open": "08:00",
"close": "20:00"
},
"6": {
"open": "10:00",
"close": "17:30"
},
"7": {
"open": "",
"close": ""
}
}
Для строкового типа:
Это может быть тот же самый json-объект, но завёрнутый в строку.
Пример строкового workingHours с завёрнутым JSON-объектом
"workingHours": "{\"1\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"2\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"3\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"4\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"5\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"6\":{\"open\":\"10:00\",\"close\":\"17:30\"},\"7\":{\"open\":\"\",\"close\":\"\"}}"
- Либо это может быть текст, содержащий сокращённые названия дней недели и их время работы.
- Дни недели могут указываться интервалами от
пн
довс
. - Интервалы разделяются:
,
- Если аптека не работает в какой-то день или интервал, то для него нужно указать ключевое слово
выходной
. - Если аптека работает круглосуточно, то для этого дня или интервала нужно указать ключевое слово
круглосуточно
Пример строкового workingHours с текстовыми днями недели
"пн-пт 08:00-20:00, сб 10:00-17:30, вс выходной"
Для круглосуточной аптеки:
"пн-вс круглосуточно"
Примеры файлов целиком:
Пример JSON с часами работы в виде объекта
[
{
"pharmacyId": "228",
"title": "Аптечный пункт №1",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 101",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": {
// пн-пт 08:00-20:00, сб 10:00-17:30, вс выходной
"1": {
"open": "08:00",
"close": "20:00"
},
"2": {
"open": "08:00",
"close": "20:00"
},
"3": {
"open": "08:00",
"close": "20:00"
},
"4": {
"open": "08:00",
"close": "20:00"
},
"5": {
"open": "08:00",
"close": "20:00"
},
"6": {
"open": "10:00",
"close": "17:30"
},
"7": {
"open": "",
"close": ""
}
},
"location": "45.868670,40.144246",
"email": "test@test.com"
},
{
"pharmacyId": "229",
"title": "Аптечный пункт №2",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 106/1",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": {
//Круглосуточная
"1": {
"open": "00:00",
"close": "00:00"
},
"2": {
"open": "00:00",
"close": "00:00"
},
"3": {
"open": "00:00",
"close": "00:00"
},
"4": {
"open": "00:00",
"close": "00:00"
},
"5": {
"open": "00:00",
"close": "00:00"
},
"6": {
"open": "00:00",
"close": "00:00"
},
"7": {
"open": "00:00",
"close": "00:00"
}
},
"location": "45.865996,40.142404",
"email": "test@test.com"
}
]
Пример JSON с часами работы в виде строки
[
{
"pharmacyId": "228",
"title": "Аптечный пункт №1",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 101",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": "{\"1\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"2\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"3\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"4\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"5\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"6\":{\"open\":\"10:00\",\"close\":\"17:30\"},\"7\":{\"open\":\"\",\"close\":\"\"}}",
"location": "45.868670,40.144246",
"email": "test@test.com"
},
{
"pharmacyId": "229",
"title": "Аптечный пункт №2",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 106/1",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": "{\"1\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"2\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"3\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"4\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"5\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"6\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"7\":{\"open\":\"00:00\",\"close\":\"00:00\"}}",
"location": "45.865996,40.142404",
"email": "test@test.com"
}
]
ИЛИ
[
{
"pharmacyId": "228",
"title": "Аптечный пункт №1",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 101",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": "пн-пт 08:00-20:00, сб 10:00-17:30, вс выходной",
"location": "45.868670,40.144246",
"email": "test@test.com"
},
{
"pharmacyId": "229",
"title": "Аптечный пункт №2",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 106/1",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": "круглосуточно",
"location": "45.865996,40.142404",
"email": "test@test.com"
}
]
Пример CSV
pharmacyId;title;address;phone;warehouseId;deliveryDates;workingHours;location;email
228;Аптечный пункт №1;Краснодарский край, г Тихорецк, ул Октябрьская, д 101;8-800-228-22-88;20247701-bf4b-11ed-812f-00e0ed9e2e92;"[{""orderDeadlineDate"": ""2024-04-26T20:30:00"", ""deliveryDate"": ""2023-04-27T00:00:00""}, {""orderDeadlineDate"": ""2023-04-28T20:30:00"", ""deliveryDate"": ""2023-04-29T00:00:00""}]";"{""1"":{""open"":""08:00"",""close"":""20:00""},""2"":{""open"":""08:00"",""close"":""20:00""},""3"":{""open"":""08:00"",""close"":""20:00""},""4"":{""open"":""08:00"",""close"":""20:00""},""5"":{""open"":""08:00"",""close"":""20:00""},""6"":{""open"":""10:00"",""close"":""17:30""},""7"":{""open"":"""",""close"":""""}}";45.868670,40.144246;test@test.com
229;Аптечный пункт №2;Краснодарский край, г Тихорецк, ул Октябрьская, д 106/1;8-800-228-22-88;20247701-bf4b-11ed-812f-00e0ed9e2e92;"[{""orderDeadlineDate"": ""2024-04-26T20:30:00"", ""deliveryDate"": ""2023-04-27T00:00:00""}, {""orderDeadlineDate"": ""2023-04-28T20:30:00"", ""deliveryDate"": ""2023-04-29T00:00:00""}]";"{""1"":{""open"":""00:00"",""close"":""00:00""},""2"":{""open"":""00:00"",""close"":""00:00""},""3"":{""open"":""00:00"",""close"":""00:00""},""4"":{""open"":""00:00"",""close"":""00:00""},""5"":{""open"":""00:00"",""close"":""00:00""},""6"":{""open"":""00:00"",""close"":""00:00""},""7"":{""open"":""00:00"",""close"":""00:00""}}";45.865996,40.142404;test@test.com
Пример XML
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<pharmacyId>228</pharmacyId>
<title>Аптечный пункт №1</title>
<address>Краснодарский край, г Тихорецк, ул Октябрьская, д 101</address>
<phone>8-800-228-22-88</phone>
<warehouseId>20247701-bf4b-11ed-812f-00e0ed9e2e92</warehouseId>
<deliveryDates>
<deliveryDate>
<orderDeadlineDate>2024-04-26T20:30:00</orderDeadlineDate>
<deliveryDate>2023-04-27T00:00:00</deliveryDate>
</deliveryDate>
<deliveryDate>
<orderDeadlineDate>2023-04-28T20:30:00</orderDeadlineDate>
<deliveryDate>2023-04-29T00:00:00</deliveryDate>
</deliveryDate>
</deliveryDates>
<workingHours>
{"1":{"open":"08:00","close":"20:00"},"2":{"open":"08:00","close":"20:00"},"3":{"open":"08:00","close":"20:00"},"4":{"open":"08:00","close":"20:00"},"5":{"open":"08:00","close":"20:00"},"6":{"open":"10:00","close":"17:30"},"7":{"open":"","close":""}}
</workingHours>
<location>45.868670,40.144246</location>
<email>test@test.com</email>
</item>
<item>
<pharmacyId>229</pharmacyId>
<title>Аптечный пункт №2</title>
<address>Краснодарский край, г Тихорецк, ул Октябрьская, д 106/1</address>
<phone>8-800-228-22-88</phone>
<warehouseId>20247701-bf4b-11ed-812f-00e0ed9e2e92</warehouseId>
<deliveryDates>
<deliveryDate>
<orderDeadlineDate>2024-04-26T20:30:00</orderDeadlineDate>
<deliveryDate>2023-04-27T00:00:00</deliveryDate>
</deliveryDate>
<deliveryDate>
<orderDeadlineDate>2023-04-28T20:30:00</orderDeadlineDate>
<deliveryDate>2023-04-29T00:00:00</deliveryDate>
</deliveryDate>
</deliveryDates>
<workingHours>
{"1":{"open":"00:00","close":"00:00"},"2":{"open":"00:00","close":"00:00"},"3":{"open":"00:00","close":"00:00"},"4":{"open":"00:00","close":"00:00"},"5":{"open":"00:00","close":"00:00"},"6":{"open":"00:00","close":"00:00"},"7":{"open":"00:00","close":"00:00"}}
</workingHours>
<location>45.865996,40.142404</location>
<email>test@test.com</email>
</item>
</items>
Товары FTP #
Частота выгрузки товаров должна быть от 1 раза в 3 часа до 1 раза в сутки. Нестандартная частота выгрузок обсуждается в индивидуальном порядке.
Для выгрузки аптек в корень FTP выгружается файл products.json
(или csv/xml в зависимости от выбранного формата).
Файл должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
id или productId |
string | ✔️ | ID товара |
barcode или barcodes |
string | ✔️ | Штрихкод. Если несколько, перечислять через , , например: 4250369505166,2250369505188 |
title |
string | ✔️ | Название |
vendor |
string | ✔️ | Производитель |
country |
string | ✔️ | Страна |
egk |
string | код egk значительно ускорит привязку товаров к нашему каталогу. Если несколько, то перечислять через , , например: 161421,161369 |
|
rls |
string | код rls значительно ускорит привязку товаров к нашему каталогу. Если несколько, то перечислять через , , например: 82531,82592 |
|
katren |
string | код katren значительно ускорит привязку товаров к нашему каталогу. Если несколько, то перечислять через , , например: 55079077,55079091 |
|
protek |
string | код protek значительно ускорит привязку товаров к нашему каталогу. Если несколько, то перечислять через , , например: 1-218661,218661,70218661 |
Пример JSON
[
{
"productId": "228",
"barcode": "4250369505166",
"title": "Лекарственные средства АСПИРИН ЭКСПРЕСС табл. шип. 500мг N12",
"vendor": "БАЙЕР",
"country": "ГЕРМАНИЯ",
"katren": "55079077",
"protek": "1-218661,218661,70218661",
"egk": "100986"
},
{
"productId": "229",
"barcode": "4630000891184",
"title": "Травы, чаи прочие ГАЛЕГИ ТРАВА (Козлятник) 50г N1",
"vendor": "Камелия-ЛТ",
"country": "РФ",
"katren": "400707176"
},
{
"productId": "230",
"barcode": "4640008530442",
"title": "Лекарственные средства ЛОСЕК МАПС табл. п.п.о. 20мг N28",
"vendor": "АСТРА ЗЕНЕКА",
"country": "ШВЕЦИЯ",
"katren": "225564307,59745118",
"egk": "104371",
"protek": "70219514"
}
]
Пример CSV
productId;barcode;title;vendor;country;rls;katren;protek;egk
228;4250369505166;Лекарственные средства АСПИРИН ЭКСПРЕСС табл. шип. 500мг N12;БАЙЕР;ГЕРМАНИЯ;;55079077;1-218661,218661,70218661;100986
229;4630000891184;Травы, чаи прочие ГАЛЕГИ ТРАВА (Козлятник) 50г N1;Камелия-ЛТ;РФ;;400707176;;
230;4640008530442;Лекарственные средства ЛОСЕК МАПС табл. п.п.о. 20мг N28;АСТРА ЗЕНЕКА;ШВЕЦИЯ;;225564307,59745118;70219514;104371
Пример XML
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<productId>228</productId>
<barcode>4250369505166</barcode>
<title>Лекарственные средства АСПИРИН ЭКСПРЕСС табл. шип. 500мг N12</title>
<vendor>БАЙЕР</vendor>
<country>ГЕРМАНИЯ</country>
<katren>55079077</katren>
<protek>1-218661,218661,70218661</protek>
<egk>100986</egk>
</item>
<item>
<productId>229</productId>
<barcode>4630000891184</barcode>
<title>Травы, чаи прочие ГАЛЕГИ ТРАВА (Козлятник) 50г N1</title>
<vendor>Камелия-ЛТ</vendor>
<country>РФ</country>
<katren>400707176</katren>
</item>
<item>
<productId>230</productId>
<barcode>4640008530442</barcode>
<title>Лекарственные средства ЛОСЕК МАПС табл. п.п.о. 20мг N28</title>
<vendor>АСТРА ЗЕНЕКА</vendor>
<country>ШВЕЦИЯ</country>
<katren>225564307,59745118</katren>
<protek>70219514</protek>
<egk>104371</egk>
</item>
</items>
Остатки FTP #
Частота выгрузки остатков должна быть не чаще 1 раза в 15 минут, не реже 1 раза в час. Нестандартная частота выгрузок обсуждается в индивидуальном порядке.
Для выгрузки аптек в директорию /stocks
FTP выгружается файл stocks_<pharmacyId>.json
(или csv/xml в зависимости от
выбранного формата), где суффикс названия соответствует ID аптеки.
Файл должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
productId |
string | ✔️ | ID товара из выгрузок товаров |
warehouseId |
string | ID склада из выгрузок складов. Не обязательный, т.к. мы можем достать его из названия файла | |
price |
float64 или string или int (если стоимость в копейках) | ✔️ | Цена |
quantity |
int или string | ✔️ | Количество |
partNumber или consignment |
string | Номер партии | |
expirationDate |
string | Срок годности в формате: 2006-01-02T15:04:05 или 2006-01-02 |
|
maxQuantity |
int или string | Максимально доступное количесство этой позиции на единицу заказа |
Возможно добавление дополнительных полей для более специфичной логики, например, ограничение доставки некоторых остатков в некоторые аптеки (например, требуется ли холодильник в аптеке для текущего остатка) или по другим условиям.
Пример JSON
[
{
"product_id": "1234",
"price": 51.0,
"quantity": 914,
"partNumber": "20250201228228",
"expirationDate": "2040-01-02T00:00:00",
"maxQuantity": 10
},
{
"product_id": "1235",
"price": 78.0,
"quantity": 1324,
"partNumber": "20250201228114",
"expirationDate": "2045-06-02T00:00:00"
},
{
"product_id": "1236",
"price": 58.0,
"quantity": 730,
"partNumber": "20250201228553",
"expirationDate": "2042-09-01T00:00:00"
}
]
Пример CSV
product_id;price;quantity;partNumber;expirationDate;maxQuantity
1234;51.0;914;20250201228228;2040-01-02T00:00:00;10
1235;78.0;1324;20250201228114;2045-06-02T00:00:00;
1236;58.0;730;20250201228553;2042-09-01T00:00:00;
Пример XML
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<product_id>1234</product_id>
<price>51.0</price>
<quantity>914</quantity>
<partNumber>20250201228228</partNumber>
<expirationDate>2040-01-02T00:00:00</expirationDate>
<maxQuantity>10</maxQuantity>
</item>
<item>
<product_id>1235</product_id>
<price>78.0</price>
<quantity>1324</quantity>
<partNumber>20250201228114</partNumber>
<expirationDate>2045-06-02T00:00:00</expirationDate>
</item>
<item>
<product_id>1236</product_id>
<price>58.0</price>
<quantity>730</quantity>
<partNumber>20250201228553</partNumber>
<expirationDate>2042-09-01T00:00:00</expirationDate>
</item>
</items>
Обмен заказами через протокол FTP (менее предпочтительный ❌) #
graph TD Partner[Партнёр] -->|Статус заказа| FTP Uteka[Ютека] -->|Новый заказ| FTP Uteka -->|Отмена заказа| FTP
Этот способ обмена заказами устарел и поддерживается минимально. Для малейших отклонений от схемы и различной кастомизации понадобятся отдельные доработки и тестирование кастомизированной схемы обмена заказами. Мы не рекомендуем его использовать в обмене заказами.
Вместо этого способа, лучше использовать процесс обмена заказами через HTTP REST интерфейса для обработки заказов.
Это примечание не относится к процессу выгрузок через протокол FTP
Раскрыть
Ютека будет загружать свои запросы в директорию /orders/outgoing
. А аптечная сеть должна выгружать статусы по заказам
в директорию
/orders/incoming
.
Передача запроса считается успешной, когда файл будет удалён с ftp-сервера получателем.
То есть, если файл удалён после передачи его Ютекой в /orders/outgoing
, то считается, что интеграция приняла и
обработала заказ.
И если файл удалён после передачи в нём интеграцией статуса по заказу в /orders/incoming
, то считается, что Ютека
обработала этот статус.
ID заказов - внутренние ID Ютеки.
Создание заказа FTP #
После того, как пользователь создал заказ, Ютека передаёт его в интеграцию через FTP в виде файла.
На данный момент поддерживается обмен файлами в формате .json
, и .xml
. Формат файлов должен быть одинаковый для всех
процессов. Т.е. не должны заказы создаваться в json
, а статусы получать в xml
.
Имя файла соответствует номеру заказа + расширение (.json
, .xml
) в кодировке UTF-8
без BOM.
Например, файл с запросом на создание заказа с ID=1234
будет лежать в папке /orders/outgoing
и называться
1234.json
, полный путь: /orders/outgoing/1234.json
Файл будет состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
orderId |
int | ✔️ | ID заказа Ютеки |
warehouse |
string | ✔️ | ID склада из выгрузок складов, из которого нужно отправить препараты в аптеку |
pharmacy |
string | ✔️ | ID аптеки из [выгрузок аптек#аптеки-ftp), куда нужно отправить препараты из склада |
amount |
float64 | ✔️ | Полная стоимость корзины в рублях (сумма всех item.price ), округлённая до копеек |
name |
string | ✔️ | Имя покупателя |
phone |
string | ✔️ | Телефон покупателя в международном формате без 7 , например, 9181231234 |
items |
[]Object | ✔️ | Корзина |
items.productId |
string | ✔️ | ID товара из выгрузок товаров. Если есть номера партий, в это поле можем подставлять его |
items.quantity |
int | ✔️ | Количество текущего ID товара в корзине |
items.price |
float64 | ✔️ | Цена за единицу текущего ID товара |
Кастомизация полей, их содержания и типов обговаривается с Партнёром в индивидуальном порядке.
Пример JSON
{
"orderId": 1234,
"pharmacy": "228",
"items": [
{
"productId": "1234",
"quantity": 2,
"price": 51
},
{
"productId": "1235",
"quantity": 1,
"price": 78
}
],
"amount": 180,
"name": "Иванов Иван Иванович",
"phone": "9181231234"
}
Пример XML
<order>
<OrderID>1234</OrderID>
<Pharmacy>228</Pharmacy>
<Items>
<Item>
<ProductID>1234</ProductID>
<Quantity>2</Quantity>
<Price>51</Price>
</Item>
<Item>
<ProductID>1235</ProductID>
<Quantity>1</Quantity>
<Price>78</Price>
</Item>
</Items>
<Amount>180</Amount>
<Name>Иванов Иван Иванович</Name>
<Phone>9181231234</Phone>
</order>
Получение статусов по заказам от Парнёра FTP #
Для получения статусов, Партнёр должен выгрузить файлы со статусами по каждому заказу в директорию /orders/incoming
.
Имя файла соответствует номеру заказа + расширение (.json
, .xml
) в кодировке UTF-8
без BOM.
Например, файл со статусом заказа 1234
будет лежать в папке /orders/incoming
и называться
status_1234.xml
, полный путь: orders/incoming/status_1234.xml
Файл должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
orderId |
string | ✔️ | ID заказа Ютеки |
status |
string | ✔️ | Статус заказа |
comment |
string | Причина отмены (например, “нет на складе” или “отказ покупателя”) |
Возможные статусы заказов:
Статус | Описание |
---|---|
approved |
Подтверждён. Интеграция взяла заказ в обработку. Фармацевт в аптеке увидел заказ или заказ дошёл до аптеки и сохранился со стороны аптечной сети. |
ready |
Готов к выдаче. Интеграция доставила корзину в аптеку, собрала заказ и готова выдать покупателю. Покупатель может идти в аптеку |
completed |
Продан. Покупатель пришёл в аптеку и выкупил корзину |
cancelled |
Отменён аптекой. Аптека не смогла на каком-то из этов собрать заказ и отменила его |
Заказ подтверждён JSON
{
"orderId": "1234",
"status": "approved"
}
Заказ подтверждён XML
<Status>
<OrderID>1234</OrderID>
<Status>approved</Status>
</Status>
Заказ отов к выдаче JSON
{
"orderId": "1234",
"status": "ready"
}
Заказ отов к выдаче XML
<Status>
<OrderID>1234</OrderID>
<Status>ready</Status>
</Status>
Заказ продан JSON
{
"orderId": "1234",
"status": "completed"
}
Заказ продан XML
<Status>
<OrderID>1234</OrderID>
<Status>completed</Status>
</Status>
Заказ отменён XML
{
"orderId": "1234",
"status": "cancelled",
"comment": "Нет на складе"
}
Заказ отменён JSON
<Status>
<OrderID>1234</OrderID>
<Status>cancelled</Status>
<Comment>Нет на складе</Comment>
</Status>
Частичная сборка корзины заказа FTP #
К сожалению, в этом способе обмена заказами частичная сборка не предусмотрена. Лучше рассмотреть использование процесса обмена заказами через HTTP REST интерфейса для обработки заказов.
В индивидуальном порядке можем рассмотреть реализацию и текущем способе, но любые отклонения от текущей схемы и различная кастомизация потребует дополнительных ресурсов для реализации и тестирования.
Отмена заказа FTP #
Ютека будет выгружать отмены заказов так же в директорию /orders/outgoing
.
ID заказов - внутренние ID Ютеки.
Имя файла соответствует status_<id>.xml
.
Например, файл с запросом на отмену заказа с номером 1234 будет лежать в папке /orders/outgoing
и называться
status_1234.xml
, полный путь: /orders/outgoing/status_1234.xml
Файл будет состоять из следующих полей (по аналогии с получаемыми от интеграции статусами):
Поле | Тип | Обязательное | Описание |
---|---|---|---|
orderId |
string | ✔️ | ID заказа Ютеки |
status |
string | ✔️ | Статус заказа |
Заказ отменён со стороны Ютеки JSON
{
"orderId": "1234",
"status": "cancelled"
}
Заказ отменён со стороны Ютеки XML
<Status>
<OrderID>1234</OrderID>
<Status>cancelled</Status>
</Status>
Выгрузки через HTTP REST интерфейс #
graph TD Uteka[Ютека] -->|GET /warehouses| Partner[REST API Партнёра] Uteka -->|GET /pharmacies| Partner Uteka -->|GET /products| Partner Uteka -->|GET /stocks| Partner
Этот раздел описывает процесс интеграции Ютеки и Партнера через реализацию на стороне Партнера HTTP REST интерфейса для процесса выгрузкок. Партнёру нужно реализовать на своей стороне HTTP REST интерфейс для получения Ютекой информации о:
Склады REST #
Частота обновления складов в системе Партнёра должна быть до 1 раза в сутки.
Для выгрузки складов со стороны Партнёра должен быть реализован эндпоинт, принимающий GET-запросы, и возвращающий
список складов с content-type: application/json
(или xml
, на усмотрение Партнёра).
Ютека будет отправлять запросы, например:
GET https://partner-host.ru/warehouses
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
partner-host.ru
- хост партнёра,warehouses
- адрес ресурса с информацией по складам,X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Допускается возможность авторизации разными способами, например, Basic
, Bearer
и др.
Допускается наличие у партнёра отдельного авторизационного эндпоинта, который будет возвращать временный токен доступа, а Ютека будет обновлять его по истечению срока его жизни.
Допускается использование комбинированных подходов, дополнительных заголовков и др.
Допускается реализация пагинации, но обсуждается в индивидуальном порядке с Партнёром.
Ответ должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание | Комментарий |
---|---|---|---|---|
id |
string | ✔️ | ID склада | Нужен для привязки к аптекам, куда будут доставляться остатки. Может содержать название региона. |
title |
string | ✔️ | Название склада | Краткое описание. Может содержать название региона. |
Допускается использование названий полей, типов их содержания и не по шаблону (обговаривается с Партнёром в индивидуальном порядке).
Пример ответа JSON
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
[
{
"id": "msc",
"title": "Москва"
},
{
"id": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"title": "Краснодарский край \"Парк А\""
}
]
Пример ответа XML
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/xml; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<id>msc</id>
<title>Москва</title>
</item>
<item>
<id>20247701-bf4b-11ed-812f-00e0ed9e2e92</id>
<title>Краснодарский край "Парк А"</title>
</item>
</items>
Аптеки REST #
Частота обновления аптек в системе Партнёра должна быть от 1 раза в 3 часа до 1 раза в сутки.
Для выгрузки аптек со стороны Партнёра должен быть реализован эндпоинт, принимающий GET-запросы, и возвращающий
список аптек с content-type: application/json
(или xml
, на усмотрение Партнёра).
Ютека будет отправлять запросы, например:
GET https://partner-host.ru/pharmacies
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
partner-host.ru
- хост партнёра,pharmacies
- адрес ресурса с информацией по аптекам,X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Допускается возможность авторизации разными способами, например, Basic
, Bearer
и др.
Допускается наличие у партнёра отдельного авторизационного эндпоинта, который будет возвращать временный токен доступа, а Ютека будет обновлять его по истечению срока его жизни.
Допускается использование комбинированных подходов, дополнительных заголовков и др.
Допускается реализация пагинации, но обсуждается в индивидуальном порядке с Партнёром.
Ответ должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание | Комментарий |
---|---|---|---|---|
id или pharmacyId |
string | ✔️ | ID аптеки | |
title |
string | ✔️ | Название аптеки | |
warehouseId |
string | ✔️ | ID склада из выгрузок складов | |
region |
string | Регион местонахождения аптеки | Не обязателен, если указан в поле address |
|
city |
string | ️ | Город местонахождения аптеки | Не обязателен, если указан в поле address |
address |
string | ✔️ | Адрес местонахождения аптеки полностью (Область, населённый пункт, улица, дом ). Без почтового индекса. | С точностью до дома. Если возможно, без дополнительных комментариев, вида пом.75843б, за магазином канцтоваров . Регион и город не обязательно указывать тут, если они передаются в полях region и city |
phone |
string | ✔️ | Телефон в международном формате (+71234567890 ) |
Если это городской номер телефона, то с кодом города (например для Новосибирска это +7(383)000‒00‒00 ) |
workingHours |
Object или string (см. пример ниже) | ✔️ | Часы работы в формате | |
deliveryDates |
Object (см. пример ниже) | ✔️ | Даты доставки остатков со склада warehouseId |
|
location |
string | ✔️ | GPS координаты Latitude,Longitude |
Например, 59.939032,30.315826 . При подстановке в гугл/яндекс/другие карты должна показываться точка на аптеку. По координатам проверяется адрес |
email |
string | Email аптеки для связи | Мы можем присылать email оповещения в аптеку при поступлении новых или отмене заказов |
Допускается использование названий полей, типов их содержания и не по шаблону (обговаривается с Партнёром в индивидуальном порядке).
Возможно добавление дополнительных полей для более специфичной логики, например, ограничение доставки некоторых остатков в некоторые аптеки (например, по признаку наличия холодильника в аптеке для вакцин) или по другим условиям.
Обсуждается в индивидуальном порядке.
Пример JSON с часами работы в виде объекта
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
[
{
"pharmacyId": "228",
"title": "Аптечный пункт №1",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 101",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": {
// пн-пт 08:00-20:00, сб 10:00-17:30, вс выходной
"1": {
"open": "08:00",
"close": "20:00"
},
"2": {
"open": "08:00",
"close": "20:00"
},
"3": {
"open": "08:00",
"close": "20:00"
},
"4": {
"open": "08:00",
"close": "20:00"
},
"5": {
"open": "08:00",
"close": "20:00"
},
"6": {
"open": "10:00",
"close": "17:30"
},
"7": {
"open": "",
"close": ""
}
},
"location": "45.868670,40.144246",
"email": "test@test.com"
},
{
"pharmacyId": "229",
"title": "Аптечный пункт №2",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 106/1",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": {
//Круглосуточная
"1": {
"open": "00:00",
"close": "00:00"
},
"2": {
"open": "00:00",
"close": "00:00"
},
"3": {
"open": "00:00",
"close": "00:00"
},
"4": {
"open": "00:00",
"close": "00:00"
},
"5": {
"open": "00:00",
"close": "00:00"
},
"6": {
"open": "00:00",
"close": "00:00"
},
"7": {
"open": "00:00",
"close": "00:00"
}
},
"location": "45.865996,40.142404",
"email": "test@test.com"
}
]
Пример JSON с часами работы в виде строки
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
[
{
"pharmacyId": "228",
"title": "Аптечный пункт №1",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 101",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": "{\"1\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"2\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"3\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"4\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"5\":{\"open\":\"08:00\",\"close\":\"20:00\"},\"6\":{\"open\":\"10:00\",\"close\":\"17:30\"},\"7\":{\"open\":\"\",\"close\":\"\"}}",
"location": "45.868670,40.144246",
"email": "test@test.com"
},
{
"pharmacyId": "229",
"title": "Аптечный пункт №2",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 106/1",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": "{\"1\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"2\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"3\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"4\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"5\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"6\":{\"open\":\"00:00\",\"close\":\"00:00\"},\"7\":{\"open\":\"00:00\",\"close\":\"00:00\"}}",
"location": "45.865996,40.142404",
"email": "test@test.com"
}
]
ИЛИ
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
[
{
"pharmacyId": "228",
"title": "Аптечный пункт №1",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 101",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": "пн-пт 08:00-20:00, сб 10:00-17:30, вс выходной",
"location": "45.868670,40.144246",
"email": "test@test.com"
},
{
"pharmacyId": "229",
"title": "Аптечный пункт №2",
"address": "Краснодарский край, г Тихорецк, ул Октябрьская, д 106/1",
"phone": "8-800-228-22-88",
"warehouseId": "20247701-bf4b-11ed-812f-00e0ed9e2e92",
"deliveryDates": [
{
"orderDeadlineDate": "2024-04-26T20:30:00",
"deliveryDate": "2023-04-27T00:00:00"
},
{
"orderDeadlineDate": "2023-04-28T20:30:00",
"deliveryDate": "2023-04-29T00:00:00"
}
],
"workingHours": "круглосуточно",
"location": "45.865996,40.142404",
"email": "test@test.com"
}
]
Пример XML
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/xml; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<pharmacyId>228</pharmacyId>
<title>Аптечный пункт №1</title>
<address>Краснодарский край, г Тихорецк, ул Октябрьская, д 101</address>
<phone>8-800-228-22-88</phone>
<warehouseId>20247701-bf4b-11ed-812f-00e0ed9e2e92</warehouseId>
<deliveryDates>
<deliveryDate>
<orderDeadlineDate>2024-04-26T20:30:00</orderDeadlineDate>
<deliveryDate>2023-04-27T00:00:00</deliveryDate>
</deliveryDate>
<deliveryDate>
<orderDeadlineDate>2023-04-28T20:30:00</orderDeadlineDate>
<deliveryDate>2023-04-29T00:00:00</deliveryDate>
</deliveryDate>
</deliveryDates>
<workingHours>
{"1":{"open":"08:00","close":"20:00"},"2":{"open":"08:00","close":"20:00"},"3":{"open":"08:00","close":"20:00"},"4":{"open":"08:00","close":"20:00"},"5":{"open":"08:00","close":"20:00"},"6":{"open":"10:00","close":"17:30"},"7":{"open":"","close":""}}
</workingHours>
<location>45.868670,40.144246</location>
<email>test@test.com</email>
</item>
<item>
<pharmacyId>229</pharmacyId>
<title>Аптечный пункт №2</title>
<address>Краснодарский край, г Тихорецк, ул Октябрьская, д 106/1</address>
<phone>8-800-228-22-88</phone>
<warehouseId>20247701-bf4b-11ed-812f-00e0ed9e2e92</warehouseId>
<deliveryDates>
<deliveryDate>
<orderDeadlineDate>2024-04-26T20:30:00</orderDeadlineDate>
<deliveryDate>2023-04-27T00:00:00</deliveryDate>
</deliveryDate>
<deliveryDate>
<orderDeadlineDate>2023-04-28T20:30:00</orderDeadlineDate>
<deliveryDate>2023-04-29T00:00:00</deliveryDate>
</deliveryDate>
</deliveryDates>
<workingHours>
{"1":{"open":"00:00","close":"00:00"},"2":{"open":"00:00","close":"00:00"},"3":{"open":"00:00","close":"00:00"},"4":{"open":"00:00","close":"00:00"},"5":{"open":"00:00","close":"00:00"},"6":{"open":"00:00","close":"00:00"},"7":{"open":"00:00","close":"00:00"}}
</workingHours>
<location>45.865996,40.142404</location>
<email>test@test.com</email>
</item>
</items>
Товары REST #
Частота обновления товаров в системе Партнёра должна быть от 1 раза в 3 часа до 1 раза в сутки.
Для выгрузки товаров со стороны Партнёра должен быть реализован эндпоинт, принимающий GET-запросы, и возвращающий
список товаров с content-type: application/json
(или xml
, на усмотрение Партнёра).
Ютека будет отправлять запросы, например:
GET https://partner-host.ru/products
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
partner-host.ru
- хост партнёра,products
- адрес ресурса с информацией по товарам,X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Допускается возможность авторизации разными способами, например, Basic
, Bearer
и др.
Допускается наличие у партнёра отдельного авторизационного эндпоинта, который будет возвращать временный токен доступа, а Ютека будет обновлять его по истечению срока его жизни.
Допускается использование комбинированных подходов, дополнительных заголовков и др.
Допускается реализация пагинации, но обсуждается в индивидуальном порядке с Партнёром.
Ответ должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
id или productId |
string | ✔️ | ID товара |
barcode или barcodes |
string | ✔️ | Штрихкод. Если несколько, перечислять через , , например: 4250369505166,2250369505188 |
title |
string | ✔️ | Название |
vendor |
string | ✔️ | Производитель |
country |
string | ✔️ | Страна |
egk |
string | код egk значительно ускорит привязку товаров к нашему каталогу. Если несколько, то перечислять через , , например: 161421,161369 |
|
rls |
string | код rls значительно ускорит привязку товаров к нашему каталогу. Если несколько, то перечислять через , , например: 82531,82592 |
|
katren |
string | код katren значительно ускорит привязку товаров к нашему каталогу. Если несколько, то перечислять через , , например: 55079077,55079091 |
|
protek |
string | код protek значительно ускорит привязку товаров к нашему каталогу. Если несколько, то перечислять через , , например: 1-218661,218661,70218661 |
Допускается использование названий полей, типов их содержания и не по шаблону (обговаривается с Партнёром в индивидуальном порядке).
Пример JSON
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
[
{
"productId": "228",
"barcode": "4250369505166",
"title": "Лекарственные средства АСПИРИН ЭКСПРЕСС табл. шип. 500мг N12",
"vendor": "БАЙЕР",
"country": "ГЕРМАНИЯ",
"katren": "55079077",
"protek": "1-218661,218661,70218661",
"egk": "100986"
},
{
"productId": "229",
"barcode": "4630000891184",
"title": "Травы, чаи прочие ГАЛЕГИ ТРАВА (Козлятник) 50г N1",
"vendor": "Камелия-ЛТ",
"country": "РФ",
"katren": "400707176"
},
{
"productId": "230",
"barcode": "4640008530442",
"title": "Лекарственные средства ЛОСЕК МАПС табл. п.п.о. 20мг N28",
"vendor": "АСТРА ЗЕНЕКА",
"country": "ШВЕЦИЯ",
"katren": "225564307,59745118",
"egk": "104371",
"protek": "70219514"
}
]
Пример XML
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/xml; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<productId>228</productId>
<barcode>4250369505166</barcode>
<title>Лекарственные средства АСПИРИН ЭКСПРЕСС табл. шип. 500мг N12</title>
<vendor>БАЙЕР</vendor>
<country>ГЕРМАНИЯ</country>
<katren>55079077</katren>
<protek>1-218661,218661,70218661</protek>
<egk>100986</egk>
</item>
<item>
<productId>229</productId>
<barcode>4630000891184</barcode>
<title>Травы, чаи прочие ГАЛЕГИ ТРАВА (Козлятник) 50г N1</title>
<vendor>Камелия-ЛТ</vendor>
<country>РФ</country>
<katren>400707176</katren>
</item>
<item>
<productId>230</productId>
<barcode>4640008530442</barcode>
<title>Лекарственные средства ЛОСЕК МАПС табл. п.п.о. 20мг N28</title>
<vendor>АСТРА ЗЕНЕКА</vendor>
<country>ШВЕЦИЯ</country>
<katren>225564307,59745118</katren>
<protek>70219514</protek>
<egk>104371</egk>
</item>
</items>
Остатки REST #
Частота обновления остатков в системе Партнёра должна быть не реже, чем 1 раза в 15 минут.
Для выгрузки остатков со стороны Партнёра должен быть реализован эндпоинт, принимающий GET-запросы, и возвращающий
список остатков с content-type: application/json
(или xml
, на усмотрение Партнёра).
Ютека будет отправлять запросы, например:
GET https://partner-host.ru/stocks?warehouseId={warehouseId}
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
partner-host.ru
- хост партнёраstocks
- адрес ресурса с информацией по остаткам{warehouseId}
- ID склада, по которому Ютека получит остаткиX-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Ютека будет получать остатки по каждому складу, т.е. кол-во запросов на остатки = кол-ву складов Партнёра.
Ютека будет отправлять запросы на получение остатков одновременно по нескольким складам, но с лимитом на кол-во одновременных запросов (по умолчанию 10 одновременных запросов, увеличение/уменьшение обсуждается индивидуально).
Допускается возможность авторизации разными способами, например, Basic
, Bearer
и др.
Допускается наличие у партнёра отдельного авторизационного эндпоинта, который будет возвращать временный токен доступа, а Ютека будет обновлять его по истечению срока его жизни.
Допускается использование комбинированных подходов, дополнительных заголовков и др.
Допускается реализация пагинации, но обсуждается в индивидуальном порядке с Партнёром.
Ответ должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
productId |
string | ✔️ | ID товара из выгрузок товаров |
warehouseId |
string | ID склада из выгрузок складов. Не обязательный, т.к. мы можем достать его из запроса | |
price |
float64 или string или int (если стоимость в копейках) | ✔️ | Цена |
quantity |
int или string | ✔️ | Количество |
partNumber или consignment |
string | Номер партии | |
expirationDate |
string | Срок годности в формате: 2006-01-02T15:04:05 или 2006-01-02 |
|
maxQuantity |
int или string | Максимально доступное количесство этой позиции на единицу заказа |
Допускается использование названий полей, типов их содержания и не по шаблону (обговаривается с Партнёром в индивидуальном порядке).
Возможно добавление дополнительных полей для более специфичной логики, например, ограничение доставки некоторых остатков в некоторые аптеки (например, требуется ли холодильник в аптеке для текущего остатка) или по другим условиям.
Пример JSON
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
[
{
"product_id": "1234",
"price": 51.0,
"quantity": 914,
"partNumber": "20250201228228",
"expirationDate": "2040-01-02T00:00:00",
"maxQuantity": 10
},
{
"product_id": "1235",
"price": 78.0,
"quantity": 1324,
"partNumber": "20250201228114",
"expirationDate": "2045-06-02T00:00:00"
},
{
"product_id": "1236",
"price": 58.0,
"quantity": 730,
"partNumber": "20250201228553",
"expirationDate": "2042-09-01T00:00:00"
}
]
Пример XML
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/xml; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<product_id>1234</product_id>
<price>51.0</price>
<quantity>914</quantity>
<partNumber>20250201228228</partNumber>
<expirationDate>2040-01-02T00:00:00</expirationDate>
<maxQuantity>10</maxQuantity>
</item>
<item>
<product_id>1235</product_id>
<price>78.0</price>
<quantity>1324</quantity>
<partNumber>20250201228114</partNumber>
<expirationDate>2045-06-02T00:00:00</expirationDate>
</item>
<item>
<product_id>1236</product_id>
<price>58.0</price>
<quantity>730</quantity>
<partNumber>20250201228553</partNumber>
<expirationDate>2042-09-01T00:00:00</expirationDate>
</item>
</items>
Обмен заказами через протокол REST (рекомендуемый ✅) #
graph TD Uteka[Ютека] -->|POST /orders/create| Partner[REST API Партнёра] Uteka -->|GET или POST /orders/status| Partner Uteka -->|GET или DELETE /orders/cancel| Partner
Ютека будет отправлять запросы с новыми заказами в инфраструктуру Партнёра используя HTTP REST протокол. Далее Ютека будет опрашивать API партнёра по каждому незавершённому заказу для получения обновлений их статусов. Также, Ютека может отправлять отмены по этим заказам, когда отмена инициирована, например, пользователем, либо истекло время сборки заказа.
Этот раздел описывает процесс интеграции Ютеки и Партнера через реализацию на стороне Партнера HTTP REST интерфейса для обработки заказов. Партнёру нужно реализовать на своей стороне HTTP REST интерфейс для отправки Ютекой:
- Новых заказов
- Запросов на получение статусов по заказам
- Отмену заказов
Создание заказа REST #
После того, как пользователь создал заказ, Ютека передаёт его в интеграцию используя HTTP REST интерфейс.
Для отправки заказов Ютекой со стороны Партнёра должен быть реализован эндпоинт, принимающий POST-запросы, и
возвращающий
ID заказа в инфраструктуре партнёра с content-type: application/json
(или xml
, на усмотрение Партнёра).
Ютека будет отправлять запросы, которые будут состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
orderId или utekaOrderId |
string | ✔️ | ID заказа Ютеки |
warehouseId |
string | ✔️ | ID склада из выгрузок складов, из которого нужно отправить препараты в аптеку |
pharmacyId |
string | ✔️ | ID аптеки из выгрузок аптек, куда нужно отправить препараты из склада |
amount |
float64 | ✔️ | Полная стоимость корзины в рублях (сумма всех item.price ), округлённая до копеек |
name |
string | ✔️ | Имя покупателя |
phone |
string | ✔️ | Телефон покупателя в международном формате без 7 , например, 9181231234 |
items |
[]Object | ✔️ | Корзина |
items.productId |
string | ✔️ | ID товара из выгрузок товаров |
items.consignment или items.partNumber |
string | ️ | Номер партии товара при наличии из выгрузки остатков |
items.quantity |
int | ✔️ | Количество текущего ID товара в корзине |
items.price |
float64 | ✔️ | Цена за единицу текущего ID товара из выгрузки остатков |
Допускается использование названий полей, типов их содержания и не по шаблону (обговаривается с Партнёром в индивидуальном порядке).
Допускается возможность авторизации разными способами, например, Basic
, Bearer
и др.
Допускается наличие у партнёра отдельного авторизационного эндпоинта, который будет возвращать временный токен доступа, а Ютека будет обновлять его по истечению срока его жизни.
Допускается использование комбинированных подходов, дополнительных заголовков и др.
Пример JSON
POST https://partner-host.ru/orders/create
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
{
"utekaOrderId": "1234",
"pharmacyId": "228",
"warehouseId": "msc",
"items": [
{
"productId": "1234",
"quantity": 2,
"price": 51
},
{
"productId": "1235",
"quantity": 1,
"price": 78
}
],
"amount": 180,
"name": "Иванов Иван Иванович",
"phone": "9181231234"
}
, где:
-
partner-host.ru
- хост партнёра, -
orders/create
- адрес ресурса для создания заказов, -
X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Пример XML
POST https://partner-host.ru/orders/create
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/xml; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<order>
<utekaOrderId>1234</utekaOrderId>
<pharmacy>228</pharmacy>
<items>
<item>
<productId>1234</productId>
<quantity>2</quantity>
<price>51</price>
</item>
<item>
<productId>1235</productId>
<quantity>1</quantity>
<price>78</price>
</item>
</items>
<amount>180</amount>
<name>Иванов Иван Иванович</name>
<phone>9181231234</phone>
</order>
, где:
-
partner-host.ru
- хост партнёра, -
orders/create
- адрес ресурса для создания заказов, -
X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Ответ должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
partnerOrderId |
string | ✔️ | ID заказа в инфраструктуре Партнёра. Можно использовать ID Ютеки (обсуждается перед реализацией) |
utekaOrderId |
string | ️ | ID заказа Ютеки |
status |
string | Статус заказа. Если не указан, считаем, что заказ взят в обработку | |
deliveryDate |
string | Дата доставки корзины в аптеку, если она не совпадает с датой из выгрузки аптек |
Допускается использование названий полей, типов их содержания и не по шаблону (обговаривается с Партнёром в индивидуальном порядке).
Пример JSON
HTTP/2.0 201 Created
Content-Type: application/json
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
{
"partnerOrderId": "0001-4321",
"utekaOrderId": "1234",
"status": "approved"
}
Пример XML
HTTP/2.0 201 Created
Content-Type: application/xml; charset=utf-8
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
<?xml version="1.0" encoding="UTF-8"?>
<orderStatus>
<partnerOrderId>0001-4321</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
<status>approved</status>
</orderStatus>
Получение статусов по заказам от Парнёра REST #
После того, как Ютека передала заказ в инфраструктуру партнёра, Ютека будет отправлять запросы на получение статусов по заказам.
Для отправки статусов по заказам со стороны Партнёра должен быть реализован эндпоинт, принимающий GET или POST-запросы,
и
возвращающий объект заказа в инфраструктуре партнёра с content-type: application/json
(или xml
, на усмотрение
Партнёра).
Ютека будет отправлять запросы, которые будут состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
partnerOrderId |
string | ✔️ | ID заказа в инфраструктуре Партнёра. Можно использовать ID Ютеки (обсуждается перед реализацией) |
utekaOrderId |
string | ️ | ID заказа Ютеки |
Допускается использование названий полей, типов их содержания и не по шаблону (обговаривается с Партнёром в индивидуальном порядке).
Допускается возможность авторизации разными способами, например, Basic
, Bearer
и др.
Допускается наличие у партнёра отдельного авторизационного эндпоинта, который будет возвращать временный токен доступа, а Ютека будет обновлять его по истечению срока его жизни.
Допускается использование комбинированных подходов, дополнительных заголовков и др.
Пример JSON
POST-запрос:
POST https://partner-host.ru/orders/status
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
{
"partnerOrderId": "228",
"utekaOrderId": "1234"
}
Или GET-запрос:
GET https://partner-host.ru/orders/status?partnerOrderId=228
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
-
partner-host.ru
- хост партнёра, -
orders/status
- адрес ресурса для получения статусов по заказам, -
X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Пример XML
POST https://partner-host.ru/orders/status
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/xml; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<orderStatus>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
</orderStatus>
Или GET-запрос:
GET https://partner-host.ru/orders/status?partnerOrderId=228
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
-
partner-host.ru
- хост партнёра, -
orders/status
- адрес ресурса для получения статусов по заказам, -
X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Ответ должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
partnerOrderId |
string | ✔️ | ID заказа в инфраструктуре Партнёра. Можно использовать ID Ютеки (обсуждается перед реализацией) |
utekaOrderId |
string | ️ | ID заказа Ютеки |
status |
string | ✔️ | Статус заказа |
deliveryDate |
string | Дата доставки корзины в аптеку, если она не совпадает с датой из выгрузки аптек | |
items |
[]Object | Актуальная корзина при частичной сборки заказа | |
items.productId |
string | ✔️(если заполнен items ) |
ID товара из выгрузок товаров |
items.consignment или items.partNumber |
string | ️ | Номер партии товара при наличии из выгрузки остатков |
items.quantity |
int | ✔️(если заполнен items ) |
Количество текущего ID товара в корзине |
items.price |
float64 | ✔️(если заполнен items ) |
Цена за единицу текущего ID товара из выгрузки остатков |
Заказ подтверждён JSON
Content-Type: application/json
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
{
"partnerOrderId": "228",
"utekaOrderId": "1234",
"status": "approved"
}
Заказ подтверждён XML
Content-Type: application/xml; charset=utf-8
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
<status>
<orderId>1234</orderId>
<status>approved</status>
<partnerOrderId>approved</partnerOrderId>
</status>
Возможные статусы заказов:
Статус | Описание |
---|---|
approved |
Подтверждён. Интеграция взяла заказ в обработку. Фармацевт в аптеке увидел заказ или заказ дошёл до аптеки и сохранился со стороны аптечной сети. |
ready |
Готов к выдаче. Интеграция доставила корзину в аптеку, собрала заказ и готова выдать покупателю. Покупатель может идти в аптеку |
completed |
Продан. Покупатель пришёл в аптеку и выкупил корзину |
cancelled |
Отменён аптекой. Аптека не смогла на каком-то из этов собрать заказ и отменила его |
Могут быть дополнительные статусы, текст статусов может быть разным, главное, наличие 4х разных статусов, соответствующих по смыслу к описаниям в таблице выше.
Частичная сборка корзины заказа REST #
Это необязательный механизм, при котором после подтверждения заказа его состав может быть изменён.
Например, инфраструктура Партнёра взяла заказ в обработку, но при сборке на складе или уже в аптеке собрать заказ целиком не получилось. В таком случае вместо мгновенной отмены, со стороны Ютеки предусмотрен механизм, при котором Ютека отправляет покупателю уведомление об изменении состава заказа, заказ переходит в состояние ожидание действия от пользователя. Далее может быть 2 ситуации:
- пользователь согласился с изменениями. В этом случае, состав корзины меняется на новый и заказ идёт дальше по флоу обработки и выдаче. Можем отправлять подтверждение в инфраструктуру Партнёра (обговаривается индивидуально), но это необязательное действие.
- пользователь отклонил изменения. В этом случае, со стороны Ютеки будет отправлена отмена по этому заказу в инфраструктуру Партнёра.
graph TD status[Мониторинг статуса на Ютеке] -->|POST /orders/status| Partner[REST API Партнёра] validateCart -->|было изменение| userFlow[Ожидание ответа пользователя] validateCart -->|не было изменений| status Partner -->|HTTP 200 + items| validateCart[Проверка состава на Ютеке] userFlow -->|Не принял изменения POST /orders/cancel| Partner userFlow -->|Принял изменения| status
Для поддержки этого механизма при опросе Ютекой статусов по заказам в ответе должен приходить массив объектов каждого элемента корзины.
Под изменениями в корзине понимается:
- уменьшение количества каких-нибудь позиций в корзине
- увеличение стоимости каких-нибудь позиций в корзине
- если корзина целиком недоступна, отмена заказа будет сразу
При этом, если количество позиций в корзине увеличилось или стали дешевле, изменения в корзине не будут зафиксированы.
Примеры ответов ниже.
Корзина доступна целиком JSON
В этом случае корзина будет совпадать с исходной. Ютека будет дальше продолжать получать статусы по заказу.
Вместе (либо вместо) с productId
дополнительно (но не обязательно) можно использовать номера партий partNumber
для
более точного сравнения корзин. Допускается использование названий полей, типов их содержания и не по шаблону (
обговаривается с Партнёром в индивидуальном порядке).
Content-Type: application/json
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
{
"partnerOrderId": "228",
"utekaOrderId": "1234",
"items": [
{
"productId": "1234",
"quantity": 2,
"price": 51
},
{
"productId": "1235",
"quantity": 1,
"price": 78
}
],
"status": "approved"
}
Корзина доступна целиком XML
В этом случае корзина будет совпадать с исходной. Ютека будет дальше продолжать получать статусы по заказу.
Вместе (либо вместо) с productId
дополнительно (но не обязательно) можно использовать номера партий partNumber
для
более точного сравнения корзин. Допускается использование названий полей, типов их содержания и не по шаблону (
обговаривается с Партнёром в индивидуальном порядке).
Content-Type: application/xml; charset=utf-8
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
<?xml version="1.0" encoding="UTF-8"?>
<status>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
<items>
<item>
<productId>1234</productId>
<quantity>2</quantity>
<price>51</price>
</item>
<item>
<productId>1235</productId>
<quantity>1</quantity>
<price>78</price>
</item>
</items>
<status>approved</status>
</status>
В корзине не хватает одной из позиций JSON
В этом случае корзина будет отличаться от исходной количеством товара 1234
, 2 -> 1
. Ютека отправит пользователю
уведомление и
будет ждать его ответа. В зависимости от ответа пришлёт либо отмену, либо продолжит опрашивать статусы. При продолжении
опроса корзины из ответов будут сравниваться уже с новой корзиной.
Вместе (либо вместо) с productId
дополнительно (но не обязательно) можно использовать номера партий partNumber
для
более точного сравнения корзин. Допускается использование названий полей, типов их содержания и не по шаблону (
обговаривается с Партнёром в индивидуальном порядке).
Content-Type: application/json
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
{
"partnerOrderId": "228",
"utekaOrderId": "1234",
"items": [
{
"productId": "1234",
"quantity": 1,
"price": 51
},
{
"productId": "1235",
"quantity": 1,
"price": 78
}
],
"status": "approved"
}
В корзине не хватает одной из позиций XML
В этом случае корзина будет отличаться от исходной количеством товара 1234
, 2 -> 1
. Ютека отправит пользователю
уведомление и
будет ждать его ответа. В зависимости от ответа пришлёт либо отмену, либо продолжит опрашивать статусы. При продолжении
опроса корзины из ответов будут сравниваться уже с новой корзиной.
Вместе (либо вместо) с productId
дополнительно (но не обязательно) можно использовать номера партий partNumber
для
более точного сравнения корзин. Допускается использование названий полей, типов их содержания и не по шаблону (
обговаривается с Партнёром в индивидуальном порядке).
Content-Type: application/xml; charset=utf-8
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
<?xml version="1.0" encoding="UTF-8"?>
<status>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
<items>
<item>
<productId>1234</productId>
<quantity>1</quantity>
<price>51</price>
</item>
<item>
<productId>1235</productId>
<quantity>1</quantity>
<price>78</price>
</item>
</items>
<status>approved</status>
</status>
В корзине изменилась цена на одну из позиций JSON
В этом случае корзина будет отличаться от исходной ценой товара 1235
, 78 -> 105
. Ютека отправит пользователю
уведомление и
будет ждать его ответа. В зависимости от ответа пришлёт либо отмену, либо продолжит опрашивать статусы. При продолжении
опроса корзины из ответов будут сравниваться уже с новой корзиной.
Вместе (либо вместо) с productId
дополнительно (но не обязательно) можно использовать номера партий partNumber
для
более точного сравнения корзин. Допускается использование названий полей, типов их содержания и не по шаблону (
обговаривается с Партнёром в индивидуальном порядке).
Content-Type: application/json
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
{
"partnerOrderId": "228",
"utekaOrderId": "1234",
"items": [
{
"productId": "1234",
"quantity": 2,
"price": 51
},
{
"productId": "1235",
"quantity": 1,
"price": 105
}
],
"status": "approved"
}
В корзине изменилась цена на одну из позиций XML
В этом случае корзина будет отличаться от исходной ценой товара 1235
, 78 -> 105
. Ютека отправит пользователю
уведомление и
будет ждать его ответа. В зависимости от ответа пришлёт либо отмену, либо продолжит опрашивать статусы. При продолжении
опроса корзины из ответов будут сравниваться уже с новой корзиной.
Вместе (либо вместо) с productId
дополнительно (но не обязательно) можно использовать номера партий partNumber
для
более точного сравнения корзин. Допускается использование названий полей, типов их содержания и не по шаблону (
обговаривается с Партнёром в индивидуальном порядке).
Content-Type: application/xml; charset=utf-8
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
<?xml version="1.0" encoding="UTF-8"?>
<status>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
<items>
<item>
<productId>1234</productId>
<quantity>2</quantity>
<price>51</price>
</item>
<item>
<productId>1235</productId>
<quantity>1</quantity>
<price>105</price>
</item>
</items>
<status>approved</status>
</status>
В корзине изменилась цена на одну из позиций и не хватает одной из позиций JSON
В этом случае корзина будет отличаться от исходной количеством товара 1234
, 2 -> 1
и ценой товара 1235
,
78 -> 105
. Ютека отправит пользователю уведомление и будет ждать его ответа. В зависимости от ответа пришлёт либо
отмену, либо продолжит опрашивать статусы. При продолжении опроса корзины из ответов будут сравниваться уже с новой
корзиной.
Вместе (либо вместо) с productId
дополнительно (но не обязательно) можно использовать номера партий partNumber
для
более точного сравнения корзин. Допускается использование названий полей, типов их содержания и не по шаблону (
обговаривается с Партнёром в индивидуальном порядке).
Content-Type: application/json
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
{
"partnerOrderId": "228",
"utekaOrderId": "1234",
"items": [
{
"productId": "1234",
"quantity": 1,
"price": 51
},
{
"productId": "1235",
"quantity": 1,
"price": 105
}
],
"status": "approved"
}
В корзине изменилась цена на одну из позиций и не хватает одной из позиций XML
В этом случае корзина будет отличаться от исходной количеством товара 1234
, 2 -> 1
и ценой товара 1235
,
78 -> 105
. Ютека отправит пользователю уведомление и будет ждать его ответа. В зависимости от ответа пришлёт либо
отмену, либо продолжит опрашивать статусы. При продолжении опроса корзины из ответов будут сравниваться уже с новой
корзиной.
Вместе (либо вместо) с productId
дополнительно (но не обязательно) можно использовать номера партий partNumber
для
более точного сравнения корзин. Допускается использование названий полей, типов их содержания и не по шаблону (
обговаривается с Партнёром в индивидуальном порядке).
Content-Type: application/xml; charset=utf-8
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
<?xml version="1.0" encoding="UTF-8"?>
<status>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
<items>
<item>
<productId>1234</productId>
<quantity>1</quantity>
<price>51</price>
</item>
<item>
<productId>1235</productId>
<quantity>1</quantity>
<price>105</price>
</item>
</items>
<status>approved</status>
</status>
Batch получение статусов по заказам от Парнёра REST #
Это необязательный вариант реализации обмена статусами по заказам, когда Ютека в одном запросе передаёт сразу несколько IDs заказов, по которым ожидает получить статусы.
По сути, отличие от реализации обмена статусами по заказам состоит только в том, что Ютека будет передавать массив объектов заказов вместо одного заказа, по которым нужно получить статусы, а инфраструктура Партнёра будет возвращать в ответе массив объектов заказов, вместо одного заказа, с актуальными статусами.
Этот процесс так же может поддерживать и механизм частичных сборок по аналогии с обычной реализацией обмена статусами по заказам
Ютека будет отправлять запросы, которые будут состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
"orderIds" |
[]Object | ✔️ | Массив объектов заказов, по которым нужно получить статусы |
"orderIds".partnerOrderId |
string | ✔️ | ID заказа в инфраструктуре Партнёра. Можно использовать ID Ютеки (обсуждается перед реализацией) |
"orderIds".utekaOrderId |
string | ️ | ID заказа Ютеки |
Допускается использование названий полей, типов их содержания и не по шаблону (обговаривается с Партнёром в индивидуальном порядке).
Допускается возможность авторизации разными способами, например, Basic
, Bearer
и др.
Допускается наличие у партнёра отдельного авторизационного эндпоинта, который будет возвращать временный токен доступа, а Ютека будет обновлять его по истечению срока его жизни.
Допускается использование комбинированных подходов, дополнительных заголовков и др.
Пример JSON
POST-запрос:
POST https://partner-host.ru/orders/status
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
{
"orderIds": [
{
"partnerOrderId": "228",
"utekaOrderId": "1234"
},
{
"partnerOrderId": "339",
"utekaOrderId": "1235"
},
{
"partnerOrderId": "440",
"utekaOrderId": "1236"
}
]
}
Или GET-запрос:
GET https://partner-host.ru/orders/status?partnerOrderIds=228,339,440
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
-
partner-host.ru
- хост партнёра, -
orders/status
- адрес ресурса для получения статусов по заказам, -
X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Пример XML
POST https://partner-host.ru/orders/status
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/xml; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<orderIds>
<orderId>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
</orderId>
<orderId>
<partnerOrderId>339</partnerOrderId>
<utekaOrderId>1235</utekaOrderId>
</orderId>
<orderId>
<partnerOrderId>440</partnerOrderId>
<utekaOrderId>1236</utekaOrderId>
</orderId>
</orderIds>
Или GET-запрос:
GET https://partner-host.ru/orders/status?partnerOrderIds=228,339,440
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
-
partner-host.ru
- хост партнёра, -
orders/status
- адрес ресурса для получения статусов по заказам, -
X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Ответ должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
orderIds |
[]Object | ✔️ | Массив объектов заказов со статусами |
orderIds.partnerOrderId |
string | ✔️ | ID заказа в инфраструктуре Партнёра. Можно использовать ID Ютеки (обсуждается перед реализацией) |
orderIds.utekaOrderId |
string | ️ | ID заказа Ютеки |
orderIds.status |
string | ✔️ | Статус заказа |
orderIds.deliveryDate |
string | Дата доставки корзины в аптеку, если она не совпадает с датой из выгрузки аптек | |
orderIds.items |
[]Object | Актуальная корзина при частичной сборки заказа | |
orderIds.items.productId |
string | ✔️(если заполнен items ) |
ID товара из выгрузок товаров |
orderIds.items.consignment или items.partNumber |
string | ️ | Номер партии товара при наличии из выгрузки остатков |
orderIds.items.quantity |
int | ✔️(если заполнен items ) |
Количество текущего ID товара в корзине |
orderIds.items.price |
float64 | ✔️(если заполнен items ) |
Цена за единицу текущего ID товара из выгрузки остатков |
Batch статусы по заказам JSON
Content-Type: application/json
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
{
"orderIds": [
{
"partnerOrderId": "228",
"utekaOrderId": "1234",
"status": "approved"
},
{
"partnerOrderId": "339",
"utekaOrderId": "1235",
"status": "ready"
},
{
"partnerOrderId": "440",
"utekaOrderId": "1236",
"status": "cancelled"
}
]
}
Batch статусы по заказам XML
Content-Type: application/xml; charset=utf-8
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
<?xml version="1.0" encoding="UTF-8"?>
<orderStatuses>
<order>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
<status>approved</status>
</order>
<order>
<partnerOrderId>339</partnerOrderId>
<utekaOrderId>1235</utekaOrderId>
<status>ready</status>
</order>
<order>
<partnerOrderId>440</partnerOrderId>
<utekaOrderId>1236</utekaOrderId>
<status>cancelled</status>
</order>
</orderStatuses>
Отмена заказа REST #
После того, как Ютека передала заказ в инфраструктуру партнёра, Ютека может отправлять запросы на отмену этих заказов. Отмена может инициироваться пользователем, либо дургими процессами, например, когда у заказа истёк дедлайн сборки.
Для отправки статусов по заказам со стороны Партнёра должен быть реализован эндпоинт, принимающий DELETE или
POST-запросы, и
возвращающий результат отмены в инфраструктуре партнёра с content-type: application/json
(или xml
, на усмотрение
Партнёра).
Ютека будет отправлять запросы, которые будут состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
partnerOrderId |
string | ✔️ | ID заказа в инфраструктуре Партнёра. Можно использовать ID Ютеки (обсуждается перед реализацией) |
utekaOrderId |
string | ️ | ID заказа Ютеки |
status |
string | ️ | Передача статуса отмены явно, если это требуется |
Допускается использование названий полей, типов их содержания и не по шаблону (обговаривается с Партнёром в индивидуальном порядке).
Допускается возможность авторизации разными способами, например, Basic
, Bearer
и др.
Допускается наличие у партнёра отдельного авторизационного эндпоинта, который будет возвращать временный токен доступа, а Ютека будет обновлять его по истечению срока его жизни.
Допускается использование комбинированных подходов, дополнительных заголовков и др.
Пример JSON
POST-запрос:
POST https://partner-host.ru/orders/cancel
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
{
"partnerOrderId": "228",
"utekaOrderId": "1234"
}
Или DELETE-запрос:
DELETE https://partner-host.ru/orders/cancel?partnerOrderId=228
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
-
partner-host.ru
- хост партнёра, -
orders/cancel
- адрес ресурса для отмены заказа, -
X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Пример XML
POST https://partner-host.ru/orders/cancel
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/xml; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<orderStatus>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
</orderStatus>
Или DELETE-запрос:
DELETE https://partner-host.ru/orders/cancel?partnerOrderId=228
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
-
partner-host.ru
- хост партнёра, -
orders/cancel
- адрес ресурса для отмены заказа, -
X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Ответ должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
partnerOrderId |
string | ID заказа в инфраструктуре Партнёра. Можно использовать ID Ютеки (обсуждается перед реализацией) | |
utekaOrderId |
string | ️ | ID заказа Ютеки |
status |
string | ️ | Передача статуса отмены явно, если это требуется |
Тело ответа при этом не обязательно. Но можно заполнить поля с IDs и статусом, чтобы на стороне Ютеки сделать
дополнительные проверки, что заказ вернулся именно тот, по которому отправили отмену и что заказ действительно стал
отменённым. Хотя будет и достаточно 2xx
статуса в ответе на запрос отмены.
Заказ отменён со стороны Ютеки JSON
Content-Type: application/json
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
{
"partnerOrderId": "228",
"utekaOrderId": "1234",
"status": "cancelled"
}
Заказ отменён со стороны Ютеки XML
Content-Type: application/xml; charset=utf-8
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
<status>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
<status>cancelled</status>
</status>
Batch отмена заказов REST #
Это необязательный вариант реализации отмен заказов, когда Ютека в одном запросе передаёт сразу несколько IDs заказов, которые нужно отменить в инфраструктуре Партнёра.
По сути, отличие от реализации отмен заказов состоит только в том, что Ютека будет передавать массив объектов заказов вместо одного заказа, которые нужно отменить, а инфраструктура Партнёра сможет обрабатывать такие запросы.
Ютека будет отправлять запросы, которые будут состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
"orderIds" |
[]Object | ✔️ | Массив объектов заказов, которые нужно отменить |
orderIds.partnerOrderId |
string | ✔️ | ID заказа в инфраструктуре Партнёра. Можно использовать ID Ютеки (обсуждается перед реализацией) |
orderIds.utekaOrderId |
string | ️ | ID заказа Ютеки |
orderIds.status |
string | ️ | Передача статуса отмены явно, если это требуется |
Допускается использование названий полей, типов их содержания и не по шаблону (обговаривается с Партнёром в индивидуальном порядке).
Допускается возможность авторизации разными способами, например, Basic
, Bearer
и др.
Допускается наличие у партнёра отдельного авторизационного эндпоинта, который будет возвращать временный токен доступа, а Ютека будет обновлять его по истечению срока его жизни.
Допускается использование комбинированных подходов, дополнительных заголовков и др.
Пример JSON
POST-запрос:
POST https://partner-host.ru/orders/cancel
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
{
"orderIds": [
{
"partnerOrderId": "228",
"utekaOrderId": "1234"
},
{
"partnerOrderId": "339",
"utekaOrderId": "1235"
},
{
"partnerOrderId": "440",
"utekaOrderId": "1236"
}
]
}
Или DELETE-запрос:
DELETE https://partner-host.ru/orders/cancel?partnerOrderId=228,339,440
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
-
partner-host.ru
- хост партнёра, -
orders/cancel
- адрес ресурса для отмены заказа, -
X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Пример XML
POST https://partner-host.ru/orders/cancel
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/xml; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<orderIds>
<orderId>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
</orderId>
<orderId>
<partnerOrderId>339</partnerOrderId>
<utekaOrderId>1235</utekaOrderId>
</orderId>
<orderId>
<partnerOrderId>440</partnerOrderId>
<utekaOrderId>1236</utekaOrderId>
</orderId>
</orderIds>
Или DELETE-запрос:
DELETE https://partner-host.ru/orders/cancel?partnerOrderId=228,339,440
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
Content-Type: application/json
, где:
-
partner-host.ru
- хост партнёра, -
orders/cancel
- адрес ресурса для отмены заказа, -
X-Request-ID
- специальный заголовок для трассировки запросов. При его наличии, в ответе на запрос должен быть точно такой же заголовок с таким же значением. Если со стороны Ютеки этот заголовок не пришёл, Партнёру нужно сгенерировать и отправить его со своей стороны самостоятельно.
Ответ должен состоять из следующих полей:
Поле | Тип | Обязательное | Описание |
---|---|---|---|
orderIds |
[]Object | Массив объектов заказов со статусами | |
orderIds.partnerOrderId |
string | ID заказа в инфраструктуре Партнёра. Можно использовать ID Ютеки (обсуждается перед реализацией) | |
orderIds.utekaOrderId |
string | ️ | ID заказа Ютеки |
orderIds.status |
string | ️ | Передача статуса отмены явно, если это требуется |
Тело ответа при этом не обязательно. Но можно заполнить поля с IDs и статусом, чтобы на стороне Ютеки сделать
дополнительные проверки, что заказ вернулся именно тот, по которому отправили отмену и что заказ действительно стал
отменённым. Хотя будет и достаточно 2xx
статуса в ответе на запрос отмены.
Batch ответы на отмены JSON
Content-Type: application/json
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
{
"orderIds": [
{
"partnerOrderId": "228",
"utekaOrderId": "1234",
"status": "cancelled"
},
{
"partnerOrderId": "339",
"utekaOrderId": "1235",
"status": "cancelled"
},
{
"partnerOrderId": "440",
"utekaOrderId": "1236",
"status": "cancelled"
}
]
}
Batch ответы на отмены XML
Content-Type: application/xml; charset=utf-8
X-Request-ID: TQaWgDfqCyWufZPvilhiyznyGfoLTDKP
<?xml version="1.0" encoding="UTF-8"?>
<orderStatuses>
<order>
<partnerOrderId>228</partnerOrderId>
<utekaOrderId>1234</utekaOrderId>
<status>cancelled</status>
</order>
<order>
<partnerOrderId>339</partnerOrderId>
<utekaOrderId>1235</utekaOrderId>
<status>cancelled</status>
</order>
<order>
<partnerOrderId>440</partnerOrderId>
<utekaOrderId>1236</utekaOrderId>
<status>cancelled</status>
</order>
</orderStatuses>