Перейти к содержимому
Разделы

Руководства

Доступ к столу и заказы по QR

Присоединитесь к текущей сессии стола, настройте опубликованные позиции и восстановите собственный запрос заказа.

Требуются текущие полномочия домена и представления

Уполномоченный сотрудник включает QR-политику заведения, активирует публичный доступ к текущей сессии и предоставляет locator физического стола через действующий типизированный ресурс представления. Публичные методы не настраивают политику, не выдают PIN сессии, не одобряют гостя и не выбирают операционное место гостя. Политика допуска и последующее подтверждение заказов — разные правила.

Locator — несекретная физическая ссылка, а не ключ к счёту. Для разрешения требуются фактический Origin браузера и текущая привязка Storefront. Переданный connectionId служит несекретным идентификатором настроенного подключения, а не разрешением использовать его. В браузере указывайте credentials: 'omit' и позволяйте браузеру отправить Origin; не подставляйте вымышленный Origin или токен аккаунта.

В кабинете заведения откройте Floor (/venues/:venueId/floor), выберите стол и панель доступа по QR. Настройте политику заведения и способ допуска гостевых заказов, затем активируйте гостевой доступ к текущей сессии. DIRECT и STAFF_CONFIRM задают подтверждение заказа отдельно от присоединения по PIN или решению сотрудника. Эти QR-настройки требуют действующего права операций зала. Для опубликованной страницы на своём домене отдельно нужны текущий ресурс QR и проверенное подключение домена в настройках Storefront (/venues/:venueId/settings/storefront), доступ через venueSettings и собственные разрешения Storefront. Гостевой API не выдаёт административных полномочий.

Определите стол и запросите допуск

Разрешение locator возвращает публичные подписи, доступные сейчас способы присоединения и непрозрачный joinContext на 60 секунд. Оно не раскрывает приватное участие или счёт стола. Не разбирайте context и не выводите из него идентификатор сессии.

Для SESSION_PIN нужен действующий шестизначный PIN сессии обслуживания. STAFF_APPROVAL не принимает PIN, выбранное клиентом место или решение сотрудника. Пример выбирает STAFF_APPROVAL только после проверки возвращённой политики. Все идентификаторы и ключ идемпотентности вымышлены; документация не отправляет запросы. Настоящий клиент создаёт отдельный ключ на команду и сохраняет его для точных повторов.

  1. Разрешите настоящий locator из 24 символов из QR ресторана, указав настроенное подключение, когда оно требуется.
  2. Проверьте joinAvailable, разрешённый способ и непустой joinContext. Остановитесь, если текущая сессия обслуживания не допускает присоединение.
  3. Создайте запрос с исходным context, разрешённым способом и стабильным Idempotency-Key. Передавайте PIN только для SESSION_PIN.
  4. Сохраните возвращённый requestToken внутри приватного гостевого процесса. Он разрешает только чтение и принятие этого конкретного запроса допуска.
  5. При необходимости прочитайте решение по запросу. Получайте участие только после одобрения, используя токен запроса и отдельный стабильный ключ команды claim.
Примеры запросов
const locator = 'qr_demo_locator_00000001';
const connectionId = '0604de3a-da74-4ad5-89f6-2dfe000d5391';
const url = new URL(
  `/public/table-locators/${encodeURIComponent(locator)}`,
  'https://api.hallify.co'
);
url.searchParams.set('connectionId', connectionId);

const resolved = await fetch(url, { credentials: 'omit', cache: 'no-store' });

if (!resolved.ok) {
  throw new Error(`HTTP ${resolved.status}`);
}

const context = await resolved.json();

if (
  !context.joinAvailable ||
  !context.allowStaffApprovalJoin ||
  !context.joinContext
) {
  throw new Error('Staff-approved admission is unavailable.');
}

const response = await fetch(`${url.origin}${url.pathname}/joins`, {
  method: 'POST',
  credentials: 'omit',
  cache: 'no-store',
  headers: {
    'Content-Type': 'application/json',
    'Idempotency-Key': 'docs-table-admission-001',
  },
  body: JSON.stringify({
    joinContext: context.joinContext,
    method: 'STAFF_APPROVAL',
  }),
});

if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}

const join = await response.json();

Получите исходное участие и учитывайте отзыв доступа

PENDING_APPROVAL ещё не означает участия. APPROVED разрешает первое принятие до expiresAt. Claim создаёт одно участие TableSessionGuest и возвращает отдельный токен текущей сессии. Токен запроса допуска не становится общим ключом ресторана или счёта. Для наблюдения TABLE_SESSION используется READ-доступ полученного участия, а не requestToken запроса.

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

При чтении участия повторно проверяются текущее заведение, сессия, место гостя, эпохи полномочий и точный Origin привязки. Закрытие, повторная посадка, transfer/merge, отключение доступа или явный отзыв могут закрыть устаревшие полномочия. Обычная ревизия конфигурации или продление проверки домена сами по себе не создают нового участия.

При 429 соблюдайте Retry-After. Лимиты неверного PIN и попыток присоединения принадлежат доменному допуску; смена ключей их не обходит. При 409 отличайте конфликт идемпотентности от устаревшей ревизии, изменившегося запроса или занятого места по документированному машинному коду. Сохраняйте исходную команду, пока проверяете её результат.

Используйте тот же сценарий на проверенном домене

Hallify обслуживает каноническую страницу /table/:locator. На опубликованном проверенном домене ресторана или сети доступен тот же путь: подключение определяется текущей публикацией домена. Явный connectionId должен совпадать с этой привязкой. Подстановки другого подключения или допуска через канонический Origin нет.

Страница сохраняет фактический проверенный Origin и не перенаправляет сценарий стола на другой канонический домен. Снимок публикации определяет только подключение представления: locator, нативное участие, область ресурса и текущий разрешённый Origin по-прежнему проверяются для каждого гостевого запроса. Неопубликованная отдельная QR-страница на своём домене не входит в этот hosted-маршрут; отдельно настроенный собственный frontend использует те же нативные полномочия API.

Настройте позиции текущего меню и проверьте расчёт

После получения участия прочитайте order-menu с его токеном. Используйте menuVersionId и graphHash именно этого каталога. Передавайте от 1 до 100 строк с уникальными идентификаторами: каждая выбирает ровно один menuItemId или menuVariantId и целое количество от 1 до 999. Модификаторы и составные позиции выбираются из опубликованного конфигуратора; сервер проверяет допустимость и все суммы.

Создание quote не создаёт Order, кухонный спрос или сумму к оплате. Ответ содержит исходный нативный расчёт, режим DIRECT либо STAFF_CONFIRM, фактический expiresAt и непрозрачный proof. Срок составляет не более пяти минут и сокращается, если полномочия источника истекают раньше. Цены каталога нужны для отображения; окончательные суммы определяет серверный quote.

Пример для браузера принимает уже полученные сессию и токен, действующий каталог и выбор вашего конфигуратора. Документация не выполняет этот вызов. Он собирает локальный submission с исходными selection, quote и proof, сохраняя рядом серверные mode и фактический expiresAt для проверки. В POST передавайте только submission: режим и внешний срок — метаданные ответа, а не поля тела отправки. Для отсчёта времени проверки используйте внешний expiresAt, а не потенциально более поздний quote.expiresAt нативного расчёта. Сохраняйте исходный submission без изменений; не пересобирайте расчёт и не выбирайте другой tenant, канал, место гостя или сотрудника.

Примеры запросов
async function quoteTableOrder({ sessionId, capability, catalog, items }) {
  const selection = {
    items,
    clientMenuVersionId: catalog.menuVersionId,
    clientGraphHash: catalog.graphHash,
  };

  const response = await fetch(
    `https://api.hallify.co/public/table-sessions/${encodeURIComponent(sessionId)}/order-requests/quote`,
    {
      method: 'POST',
      credentials: 'omit',
      cache: 'no-store',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${capability}`,
      },
      body: JSON.stringify(selection),
    }
  );

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }

  const quoted = await response.json();

  return {
    submission: {
      selection,
      quote: quoted.quote,
      proof: quoted.proof,
    },
    mode: quoted.mode,
    expiresAt: quoted.expiresAt,
  };
}

Отправьте заказ один раз и восстановите исходный результат

До POST order-requests сохраните точное тело selection/quote/proof и один новый Idempotency-Key в приватном гостевом хранилище, привязанном к источнику. DIRECT атомарно создаёт один нативный Order для этого набора и возвращает ACCEPTED. STAFF_CONFIRM возвращает PENDING без Order, кухонного спроса и суммы к оплате; уполномоченный сотрудник проверяет запрос и одобряет или отклоняет его в панели доступа текущей сессии.

При UNKNOWN, потере сети, reload или потере ответа сохраняйте исходные источник, тело и ключ и повторяйте ту же команду. Авторизованный точный повтор возвращает исходную неизменяемую квитанцию: он не создаёт второй логический заказ и не расходует бюджет новой отправки. Истечение quote не разрешает заменять неопределённую команду новым ключом или расчётом. Изменение участия и отзыв полномочий разрешаются через нативный доступ, а не токен аккаунта.

Исходная квитанция PENDING остаётся такой и после решения сотрудника. Читайте собственный запрос или ограниченную историю для текущего ACCEPTED, REJECTED либо INVALIDATED и исходного результата нативного допуска, если он есть. Ожидающие запросы могут утратить силу при истечении срока, изменении политики заказов или источника. Правка ещё не отправленной корзины требует нового расчёта; отправленная неопределённая команда остаётся неизменной до выяснения результата.

Обновляйте нативный статус и переходите в отдельный checkout

Observer TABLE_SESSION использует текущие READ-полномочия участия и сообщает об изменении нативных данных. Пустой publicGuest.resourceChanged не содержит истины о заказе или деньгах. Повторно читайте только сводки и детали собственных запросов; отключайте прежний observer при смене источника или истечении доступа. Не создавайте бесконечный цикл опроса.

Чтение, расчёт и новая отправка имеют отдельные нативные бюджеты попыток. Соблюдайте 429 и Retry-After; неуспешная новая работа тоже расходует попытку. Для точного повтора исходной отправки используется восстановление. Новые ключи не обходят лимиты, проверки источника или окончательное решение.

ACCEPTED означает допуск нативного заказа, а не оплату. Исходные quote и outcome остаются историческими. Для текущих назначенных частей либо разрешённого счёта сессии, явного согласия, чаевых и checkout используйте отдельный guest-payments сценарий. Гостевой заказ не меняет чужую корзину или распределение позиций и не отправляет активный черновик сотрудника.

Отделяйте участие от заказа и оплаты

Допуск к участию, нативный заказ и оплата остаются разными полномочиями. Запрос присоединения не добавляет заказ и не раскрывает счёт. Полученное действующее участие разрешает только собственный гостевой заказ; отдельный платёжный допуск QR_MEMBERSHIP использует токен этого участия и UUID сессии стола. Ни один шаг не выбирает операционное место другого гостя.

Уполномоченный сотрудник явно включает paymentScope в QR-политике: по умолчанию DISABLED; ASSIGNED_ITEMS ограничивает оплату текущими назначенными частями, SESSION_BILL допускает текущий счёт сессии в пределах нативных полномочий. Затем используется отдельный guest-payments сценарий счёта, offer и явного согласия. Платёжная политика не изменяет допуск или подтверждение заказов.

Храните join credentials, PIN и membership capability вне URL, аналитики, логов, Redux и аккаунтной авторизации. TableSessionGuest — операционный слот, а не CRM-личность. Не создавайте polling loop и не считайте join observer resource. SDK раскрывает нативные методы; table widget начинает с точного проверенного QR-прикрепления и необязательного публичного локатора.

Widget locator resolution и новый join дополнительно проверяют подписанный context при фактическом frame Origin. Claim и существующее membership используют сохранённые parent/connection/session provenance; новый proof не оживляет отозванное membership. Изолируйте исходный join/order/payment journal каждого стабильного widget slot и не передавайте PIN, native capability или payment credentials в parent events.

Связывайте только собственную исходную QR-историю

После нативного допуска membership прочитайте portal-association-statement с фактического исходного Origin и покажите точные локализованные termsVersion/termsDigest. Явное первое согласие фиксирует commandId, expectedRevision 0, исходные session/membership/seat и выбранный сервером target proof. prepare-portal-booking-proof и associate-portal-history принимают один tableGuestToken или его вместе с guestPortalToken; настоящая BOOKING-проверка с одним table token резервирует точное будущее Portal continuation.

prepare-portal-reentry-proof требует ОБА исходный tableGuestToken И текущий guestPortalToken, а также настоящий использованный PORTAL_REENTRY challenge этой сессии и версии контакта. Недавнее время входа не является proof. Храните OTP приватно, используйте настоящий введённый код и сохраняйте исходные key/body до отправки; неверный код расходует бюджет. Точный повтор потерянного ответа восстанавливает исходное решение association и nullable зарезервированное continuation без новых proof, места или ключа.

Portal QR_VISIT и собственные квитанции запросов остаются за исходными неизменными membership/session/seat после закрытия, переноса или объединения. Они не раскрывают чужие квитанции, SESSION_BILL или native table/payment grant. Чтение/отзыв Portal consent требуют текущего владения; приватная история дополнительно требует свежее подтверждение контакта и последний ACCEPT. Reconsent требует свежий целевой proof и не отменяет hard revoke. Следуйте руководству кабинета; не передавайте gpc bearer через межвкладочный URL или widget parent event.

Hallify использует обязательные cookies и опциональную аналитику.

Обязательные cookies поддерживают вход, язык и тему. Аналитика выключена, пока вы явно ее не разрешите.