Loyalty Platform — руководство пользователя

Интеграции

Содержание раздела
  1. Зачем это бизнесу
  2. Список интеграций
  3. Два типа интеграций
  4. Карточка интеграции и API-ключи
  5. Кто что может

← Оглавление

Раздел «Интеграции» — точка подключения внешних систем к платформе. Интеграция — это касса, сайт или мобильное приложение, которые проводят заказы, начисляют бонусы, читают данные клиентов и узнают акции для гостя через API. Каждая интеграция получает свои API-ключи — «пропуска» с настраиваемыми правами, привязкой к филиалам и сроком действия. Заказы в разделе «Заказы» появляются именно отсюда: их создают системы, подключённые через интеграции.

Зачем это бизнесу#

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

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

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

Заголовок страницы — «Интеграции», подзаголовок — «Управление интеграциями и API-ключами». В правом верхнем углу — кнопка «Создать интеграцию», над таблицей — поиск и фильтры по статусу, типу и каналу (см. ограничения ниже).

В таблице — колонки Название (ссылка на карточку), Тип, Каналы, Статус, Ключей и Создана. На скриншоте — интеграция демо-организации «Demo POS»: серверная, активная, с каналами pos, web, mobile и двумя ключами.

Два типа интеграций#

Тип Ключи Для чего
Серверная sk_live_... (secret key) Бэкенды: кассовое ПО, сервер сайта, 1С. Ключ хранится на сервере и никогда не попадает к пользователям
Клиентская pk_live_... (publishable key) Код, работающий у клиента: сайт в браузере, мобильное приложение. Дополнительно защищается списком «Разрешённых доменов»

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

У каждой интеграции задаются каналы продаж — они ограничивают, какие заказы система может проводить: интеграция с каналом «POS» не сможет прислать заказ с пометкой «Website». Канал заказа всегда должен входить в каналы интеграции.

Что касса передаёт и получает при расчёте. Для каждой позиции чека достаточно артикула, названия, цены и количества. Пределы длины: артикул — до 100 символов, название, категория и бренд — до 255; позиция с более длинным значением отклоняется ошибкой проверки (400 VALIDATION_ERROR с путём к полю, например items[0].name) и в расчёте, и в подтверждении. Точка продаж и касса описаны ниже, в «Точка продаж и касса в заказе». Категорию передавать не обязательно: платформа сама находит товар в «Каталоге» по артикулу и берёт оттуда категорию (вместе с родительскими), бренд и теги, поэтому акции по категории работают и на кассе, которая категорию не присылает. Но передавать категорию рекомендуется: подтверждённые заказы синхронизируют каталог с кассой — новые артикулы заводятся товарами с названием и категорией из чека, у существующих товаров обновляются название и переданная категория, недостающие категории создаются сами. Без категории новые товары появятся в каталоге без неё, и раскладывать их придётся вручную. Ручные правки названий в каталоге следующий чек перезапишет — правьте их в учётной системе кассы. Цена всегда берётся из чека. Если сработала акция «Подарок к покупке», в ответе придёт артикул, название и количество подарочного товара, идентификатор (promotionId) и название акции; цены у подарка нет — касса подставляет её сама по артикулу. Подарок — зона платформы: она решает, положен ли он, и при подтверждении сама сохраняет его в заказе. Касса подарок не передаёт ни в расчёт, ни в подтверждение: в items только позиции, которые клиент покупает; строка подарка в чеке нужна лишь для показа клиенту и печати. Добавляйте её с нулевой ценой и не включайте в подытог и в процент скидки: если поставить подарку каталожную цену и скидку 100%, скидка в чеке разойдётся с платформой ровно на цену подарка (платформа подарок скидкой не считает, он учитывается как «подарок × N»).

Идентификатор строки чека. Для каждой позиции касса может передать необязательное поле:

Поле Что передавать Зачем
positionId Свой идентификатор строки чека — номер или код строки в кассовой программе, например line-2. До 64 символов; внутри одного чека значения не должны повторяться Платформа возвращает его в той же строке ответа расчёта и подтверждения, а также при чтении заказа. По нему касса точно знает, к какой своей строке относится скидка, когда в чеке несколько строк с одним артикулом — например, две пиццы и к каждой модификатор «Пепперони»: строки не сливаются, у каждой своя скидка. В админке идентификатор виден в карточке заказа под артикулом позиции

Пробелы по краям обрезаются, пустое значение равносильно непереданному. Повтор идентификатора в одном чеке или значение длиннее 64 символов отклоняются ошибкой проверки 400 VALIDATION_ERROR; для повтора в ответе указаны обе строки, например items[2].positionId — duplicates items[1].positionId. В разных чеках идентификаторы могут совпадать. В ответах расчёта и первого подтверждения строки идут в порядке запроса, но при чтении сохранённого заказа и при повторе подтверждения порядок строк не гарантирован — сопоставляйте строки по positionId, а не по номеру строки или артикулу. У подарков (giftItems) этого поля нет.

"items": [
  { "sku": "PIZZA", "name": "Пицца", "price": 500, "quantity": 1, "positionId": "line-1" },
  { "sku": "PEPPERONI", "name": "Пепперони", "price": 100, "quantity": 1, "positionId": "line-2" },
  { "sku": "PIZZA", "name": "Пицца", "price": 500, "quantity": 1, "positionId": "line-3" },
  { "sku": "PEPPERONI", "name": "Пепперони", "price": 150, "quantity": 2, "positionId": "line-4" }
]

Точка продаж и касса в заказе. Касса передаёт три необязательных поля:

Поле Где Что передавать
storeId Расчёт и подтверждение Свой код точки — ID заведения в кассовой системе, до 100 символов: NAMANGAN-01 или, в rPos, строка вида 65f8de27-da49-70e5-01a0-19ba4455186f. Платформа ищет точку из раздела «Филиалы», у которой «Внешний ID» совпадает с этим кодом (пробелы по краям отбрасываются). Внутренний идентификатор точки платформы в storeId не подходит. Неактивная точка принимается
storeName Расчёт и подтверждение Название точки, до 255 символов, например Наманган, ул. Навои 12. Нужно только для точки, которой ещё нет в справочнике: платформа создаёт её с этим названием и кодом storeId, и заказ проходит. У существующей точки название не меняется — переименовывают в админке. Без storeName неизвестный storeId даёт 404 LOCATION_NOT_FOUND (Location with externalId '…' not found). Создавать точку может только ключ с правом на запись заказов (orders:write, а также orders:* или *): в расчёте ключ только с orders:read при неизвестном storeId и заполненном storeName получает 403 API_KEY_SCOPE_REQUIRED — так же, как создание клиента в расчёте требует customers:write. Для подтверждения orders:write нужен и так
salePointName Только подтверждение Номер или имя кассы (терминала), как оно настроено в кассовой программе, до 100 символов, например POS-2. Сохраняется в заказе как есть — справочника касс в платформе нет. Пробелы по краям обрезаются, пустое значение не сохраняется. Возвращается в ответе подтверждения и при чтении заказа в поле salePointName

Рекомендуем всегда передавать storeName вместе с storeId и выдавать кассовому ключу право orders:write: тогда открытие новой точки не потребует заранее заводить её в админке. Автоматически созданная точка помечена в разделе «Филиалы» тегом «Создан кассой» — администратор дополняет её адрес. В ответах и вебхуках точка заказа приходит уже внутренним идентификатором платформы.

{
  "channel": "pos",
  "storeId": "NAMANGAN-01",
  "storeName": "Наманган, ул. Навои 12",
  "salePointName": "POS-2",
  "orderNumber": "674",
  "items": [{ "sku": "LAT-001", "name": "Латте", "price": 550.00, "quantity": 1 }]
}

Точка влияет и на акции: акция, ограниченная филиалами (см. «Промо-акции» → «Где действует»), применяется, только если storeId указывает на точку из её списка.

В списке применённых акций ответа (appliedPromotions) только акции, которые что-то дали: скидку больше нуля, подарок, бесплатную единицу или бонусы. Акция, подошедшая по условиям, но не давшая выгоды, в список не попадает.

Что приходит в ответе подтверждения и при чтении заказа. Подтверждённый заказ хранит сработавшие акции и подарки. Ответ подтверждения, чтение заказа по идентификатору или внешнему номеру и ответ возврата содержат appliedPromotions — по объекту на акцию: идентификатор, название, скидка на позициях, начисленные бонусы, число бесплатных единиц и подарки этой акции, — и общий список подарков giftItems в том же формате, что и в расчёте. Обратите внимание: в ответе расчёта appliedPromotions — это список идентификаторов акций, а в ответе подтверждения и чтения заказа — список объектов. Если касса разбирает оба ответа одним кодом, учтите разницу.

Подтверждение заказа: передавайте суммы из расчёта. При подтверждении платформа всегда пересчитывает заказ заново. Между расчётом и подтверждением суммы могут измениться — например, акция закончилась или её приостановили. Чтобы гость не оплатил сумму, отличную от показанной на кассе, передавайте в подтверждении итоговую сумму и сумму скидки из ответа расчёта (expectedFinalTotal, expectedDiscountTotal). Если они разошлись с пересчётом, заказ не подтверждается: касса получает ошибку 409 ORDER_CALCULATION_DRIFT с актуальными суммами, показывает гостю новую сумму и подтверждает заказ повторно. Без этих полей заказ подтверждается молча с новыми суммами.

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

Поле Что передавать Зачем
externalOrderId Внутренний идентификатор заказа в кассовой системе — то, по чему касса сама отличает заказы (rPos, например, присылает UUID заказа). Буквы, цифры, ., _, :, -, до 100 символов Уникален в пределах организации: по нему касса читает заказ и оформляет возврат, повтор с тем же значением и новым ключом идемпотентности отклоняется (409 ORDER_EXTERNAL_ID_CONFLICT). В админке — строка «Внешний ID заказа» в карточке
orderNumber Номер, напечатанный на чеке, — например 674 или web-49803. До 100 символов, без переводов строки и других управляющих символов По нему менеджер находит заказ в разделе «Заказы», когда гость приходит с чеком. Номер не уникален: на кассе номера повторяются между сменами и точками, платформа такие повторы принимает. В админке — колонка и строка «Номер заказа»

Интеграторам rPos: в externalOrderId по-прежнему передаётся внутренний id заказа rPos, номер с чека — в orderNumber. Пробелы по краям номера обрезаются, пустой номер не сохраняется. Номер записывается только при подтверждении (в расчёте его нет); при повторе подтверждения с тем же ключом идемпотентности остаётся номер, сохранённый первым запросом. В ответах подтверждения, чтения и возврата заказа номер приходит в поле orderNumber.

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

Карточка интеграции и API-ключи#

Карточка интеграции с API-ключами
Карточка интеграции с API-ключами

Карточка показывает таблицу «API-ключи» и блок «Сведения» (количество ключей, каналы, разрешённые домены, даты создания и изменения, идентификатор); тип и статус интеграции — в шапке. На скриншоте — карточка «Demo POS» с двумя показательными ключами:

  • Глобальный ключ «Demo POS Key» — права «*» (полный доступ), филиалы «Все филиалы», без срока действия;
  • Филиальный ключ «Касса — Кофейня в Magic City» — гранулярные права orders:write, customers:read, bonuses:write, привязка к филиалу «Кофейня в Magic City» и срок действия до 1 июля 2027 года.

Колонки таблицы ключей:

Колонка Что показывает
Название Название ключа и рядом статус: «Активен», «Отозван» или «Истёк». Под ними — маска ключа (значок рядом есть только у активных ключей и копирует полный ключ: админка запрашивает его у сервера, а если браузер не дал записать в буфер обмена, открывает окно с ключом для ручного копирования) и строка «Использован: ‹дата и время›» или «Ещё не использовался»
Доступ Права ключа тегами, под ними — филиалы: «Все филиалы» у глобального ключа или названия точек продаж через запятую
Действует Дата создания ключа, под ней — «до ‹дата›» или «бессрочно»

Последняя колонка — действия, она закреплена у правого края таблицы. У активных ключей в ней кнопки «Показать ключ» (иконка глаза) и «Отозвать», у отозванных и истёкших она пустая. Если у интеграции ещё нет ключей, вместо таблицы — надпись «Ключей пока нет».

При создании ключа настраиваются:

Настройка Варианты
Права Полный доступ («*») или гранулярно: клиенты / заказы / бонусы × чтение / запись, «Карты лояльности: чтение / запись» (loyalty-cards:read, loyalty-cards:write; см. «Карты лояльности»), а также «Акции: чтение» (promotions:read)
Филиалы Список точек продаж, от имени которых работает ключ. Пусто = все филиалы
Срок действия Дата, после которой ключ перестаёт работать. Удобно для временных подрядчиков

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

Кнопка «Отозвать» прекращает действие ключа необратимо — восстановить отозванный ключ нельзя, только выпустить новый. Это осознанно: при малейшем подозрении на утечку ключ отзывается без колебаний.

Право «Акции: чтение»#

Выбор права «Акции: чтение» при создании API-ключа
Выбор права «Акции: чтение» при создании API-ключа

На скриншоте — окно «Создать API-ключ» интеграции «Demo POS»: в списке прав выбраны «Клиенты: чтение» и «Акции: чтение». В форме ключа у права «Акции: чтение» подсказка «Список актуальных акций для клиента по телефону». Оно нужно кассе, сайту или приложению, которые показывают гостю его акции ещё до оформления покупки. Гость называет телефон, и система возвращает акции, которые действуют прямо сейчас. Каждая акция в списке отмечена одним из двух способов:

  • Персональная — действует именно на этого гостя: он состоит в сегменте, на который нацелена акция; или подходит под условия акции по своему профилю (уровень, возраст, день рождения, число заказов); или цепочка выдала ему личный промокод. Такие акции касса видит первыми, вместе с личными промокодами гостя.
  • Публичная — работает для всех. Если для неё нужен промокод (например, общий WELCOME10), это тоже видно в списке.

Акции, которые гостю заведомо не подходят, в список не попадают: например, акция для сегмента «VIP» не покажется гостю вне этого сегмента. Также учитываются ограничения акции по интеграциям: акция «только для мобильного приложения» кассе не покажется.

Передавайте код своей точки (storeId). Касса может добавить в запрос акций тот же storeId, что и в расчёте заказа. Тогда в списке только акции, которые действуют в этой точке: акции с условием «Точка продаж» на другую точку и акции, ограниченные другими филиалами, не придут, а промокод такой акции получит ответ «акция гостю сейчас недоступна». Без storeId условие «Точка продаж» список не сужает, а акции, ограниченные полем «Филиалы», не показываются вовсе — поэтому касса, работающая в конкретной точке, должна передавать storeId всегда. Точку этот запрос не создаёт: неизвестный код даёт 404 LOCATION_NOT_FOUND, storeName здесь не передаётся. Пример анонимного запроса: GET /v1/api/promotions/available?storeId=NAMANGAN-01; с телефоном: POST с телом {"phone": "+998 90 100-00-01", "storeId": "NAMANGAN-01"}.

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

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

Как проверить на демо-данных. В организации, заполненной демо-данными (см. «Настройки» → «Демо-данные»), есть интеграция «Demo POS» с ключом полного доступа, право «Акции: чтение» в него входит. Подключите кассу или приложение этим ключом и запросите акции для нескольких гостей:

  • +998 90 100-00-03, Тимур Рахимов — VIP-гость: первой придёт персональная акция «VIP: +5% бонусов».
  • +998 90 100-00-01, Алишер Каримов — у него есть личный промокод: персональной придёт «−15% по промокоду из письма» вместе с кодом вида GIFT-XXXXXX.
  • +998 90 100-00-19, Азиз Хакимов — у него один заказ: персональной придёт «Второй визит: 10 000 бонусов».
  • +998 90 999-99-99 — такого гостя в базе нет: придут только публичные акции, ошибки не будет.

Кроме персональных, все четверо получат одни и те же публичные акции, например «Базовый кешбэк 5%» и «Каждый третий кофе — бесплатно». Сам промокод Алишера при каждом заполнении новый, постоянно только то, что он есть. Если в разделе «Промо-акции» нет акции «Второй визит: 10 000 бонусов», демо-данные заполнены более ранней версией. Чтобы результат совпал с описанным, удалите данные демонстрационной организации и заполните её заново.

Кто что может#

Роль Доступ к разделу
Администратор организации (TENANT_ADMIN) Полное управление: интеграции, ключи, отзыв, отключение
Все остальные роли Раздел не виден

API-ключи — самый чувствительный элемент безопасности организации, поэтому доступ максимально узкий: раздел видит только Администратор организации.


← Оглавление

Loyalty Platform — руководство пользователя