openapi: 3.0.0 info: title: MAXMA Webhooks version: '2025-06-25' x-logo: url: logo.png description: | # Общие сведения Процессинг MAXMA может выступать в роли клиента, чтобы сообщать вам о произошедших событиях. Для этого в вашей системе нужно организовать веб-сервер с публичным IP-адресом, который может принимать и обрабатывать запросы в описанном в этом документе формате. ## События | Код события | Описание и дополнительные данные | |-----------------------------------|---------------------------------------------------------------------------| | `EVENT_CLIENT_EMAIL_SUBSCRIBED` | Клиент подтвердил согласие на маркетинговые рассылки по электронной почте | | `EVENT_CLIENT_EMAIL_UNSUBSCRIBED` | Клиент отписался от маркетинговых рассылок по электронной почте | | `EVENT_CLIENT_NEW` | Создан клиент | | `EVENT_CLIENT_CHANGED` | Данные о клиенте изменились | | `EVENT_CLIENT_BONUSES_CHANGED` | Количество бонусов на счете клиента изменилось | | `EVENT_PURCHASE_NEW` | Создана новая покупка | ## Аутентификация запросов Доступ к веб-серверу рекомендуем закрыть HTTP-авторизацией, чтобы никто кроме нас не мог отправлять на него сообщения. Если авторизация не пройдена, возвращайте ```401 Unauthorized``` или ```403 Forbidden```, так мы поймем, что что-то пошло не так, но при этом не будем повторять попытки отправки до исправления ошибок. Также можно разрешить доступ к веб-серверу только для нашего диапазона IP-адресов (диапазон предоставляем по запросу). ## Форматы данных Сообщение закодировано в JSON и передается в теле POST-запроса. #### Строки Для строк используется кодировка UTF-8. #### Дата и время Дата и время форматируется в соответствии с RFC 3339 "Date and Time on the Internet: Timestamps" (http://tools.ietf.org/html/rfc3339). Пример: ```1996-12-19T16:39:57-08:00``` #### Денежные величины Все денежные величины (стоимость товаров, величина скидки) указываются в единой заранее оговоренной валюте. Суммы передаются вещественным числом с фиксированной точностью 2 знака после запятой. ## Обработка ошибок Если сообщение успешно принято и обработано у вас, сообщайте код ответа ```200 OK```. Тело может быть любым, мы на него не смотрим. Если произошла ошибка в вашей системе, возвращайте код 5XX, например ```500 Internal Server Error```, в этом случае мы повторим запрос через некоторое время. Всего мы делаем 10 попыток доставки сообщения с увеличивающимся интервалом между попытками. Если на десятую попытку ваша система ответила ошибкой, мы прекращаем доставку этого сообщения. Если вам пришло некорректное сообщение, которая ваша система не может обработать, или если авторизация не пройдена, возвращайте код 4XX, например ```400 Bad Request```. Мы не будем повторять попытку доставить такое сообщение и разберемся с причинами, почему оно не доставлено. На статусы 3XX мы не реагируем, по редиректу не переходим, попытку доставки в случае такого ответа не повторяем. Таймаут на ожидание ответа от вашего сервера - 30 секунд. Не дождавшись ответа, мы попытаемся отправить запрос повторно (так же, как при ошибке 5XX). Из-за специфики сетевого обмена одно и то же сообщение может быть доставлено вам несколько раз. Если вам важно этого избежать, записывайте значение поля eventId для обработанных сообщений и игнорируйте повторно пришедшее сообщение с этим кодом. contact: name: Поддержка email: support@maxma.com servers: - url: https://your-host.com description: Ваш сервер paths: /your-path: post: summary: Уведомление о событии description: | Запрос делается: с сервера MAXMA на ваш сервер. URL для вызова может быть любым. Вызывается, когда происходит событие, на которое вы подписаны. Содержит код произошедшего события и его атрибуты. requestBody: content: application/json: schema: $ref: '#/components/schemas/EventRequest' responses: 200: description: Запрос обработан успешно 403: description: Запрос не обработан из-за ошибки авторизации 500: description: Ошибка на сервере, запрос нужно повторить components: schemas: EventRequest: type: array items: type: object properties: event: type: string description: | Код произошедшего события. Коды событий и рсшифровки приведены в [таблице "События"](#section/Obshie-svedeniya/Sobytiya). eventId: type: string description: | Уникальный идентификатор события. Можно использовать для того, чтобы игнорировать ранее обработанное, но повторно полученное событие. eventTime: type: string format: date-time description: Дата и время произошедшего события source: type: string enum: - api - internal description: | Источник изменения. Возможные значения: - api - изменение сделано извне по API; - internal - изменение произошло внутри системы. EVENT_CLIENT_EMAIL_SUBSCRIBED: $ref: '#/components/schemas/subscriptionEventPayload' EVENT_CLIENT_EMAIL_UNSUBSCRIBED: $ref: '#/components/schemas/subscriptionEventPayload' EVENT_CLIENT_NEW: type: object properties: clientData: $ref: '#/components/schemas/clientData' required: - clientData EVENT_CLIENT_CHANGED: type: object properties: client: $ref: '#/components/schemas/clientQuery' clientData: $ref: '#/components/schemas/clientData' required: - client - clientData EVENT_CLIENT_BONUSES_CHANGED: type: object properties: client: $ref: '#/components/schemas/clientQuery' clientBonuses: $ref: '#/components/schemas/clientBonuses' bonusesDelta: type: object description: Информация об изменении бонусного счета properties: availableDelta: type: number format: int32 description: > Разница, на которую произошло изменение доступных бонусов: - положительное значение, если произошло увеличение кол-ва бонусов; - отрицательное значение, если произошло уменьшение кол-ва бонусов; - ноль, если изменения этого значения не было. pendingDelta: type: number format: int32 description: > Разница, на которую произошло изменение бонусов в ожидании: - положительное значение, если произошло увеличение кол-ва бонусов; - отрицательное значение, если произошло уменьшение кол-ва бонусов; - ноль, если изменения этого значения не было. required: - client - clientBonuses - bonusesDelta EVENT_PURCHASE_NEW: type: object properties: client: $ref: '#/components/schemas/clientQuery' shop: $ref: '#/components/schemas/shopQuery' cashier: $ref: '#/components/schemas/cashierQuery' txid: type: string description: Уникальный идентификатор покупки executedAt: type: string format: date-time description: Дата и время выполнения покупки totalAmount: type: number format: float description: Сумма покупки totalDiscount: type: number format: float description: Сумма скидки discounts: $ref: '#/components/schemas/purchaseDiscounts' bonuses: $ref: '#/components/schemas/purchaseBonuses' promocode: type: string description: Промокод, который был использован при покупке rows: type: array items: $ref: '#/components/schemas/purchaseRow' required: - shop - txid - executedAt - totalAmount - totalDiscount - discounts required: - event - eventId - eventTime - source example: - event: EVENT_CLIENT_EMAIL_UNSUBSCRIBED eventId: 3f94c77c-5e2a-4a6e-85cd-730d0c7b33aa eventTime: '2016-12-19T16:39:57+03:00' EVENT_CLIENT_EMAIL_UNSUBSCRIBED: client: phoneNumber: "+79991234567" card: '200000000446' email: zhiglov20@gmail.com - event: EVENT_CLIENT_CHANGED eventId: 0a353b66-5ac9-4cbb-9c52-596f46bfac62 eventTime: '2016-12-19T17:24:32+03:00' EVENT_CLIENT_CHANGED: client: phoneNumber: '+79991234567' card: '200000000446' clientData: phoneNumber: '+79991234567' card: '200000000447' email: zhiglov20@gmail.com surname: Жиглов name: Вениамин patronymicName: Карлович fullName: Вениамин Карлович Жиглов gender: 1 birthdate: '1967-11-21T00:00:00+03:00' city: Реутов street: Победы - event: EVENT_CLIENT_BONUSES_CHANGED eventId: 00f09507-2aea-4bc4-aad9-c68e83a8448c eventTime: '2016-12-19T17:24:44+03:00' EVENT_CLIENT_BONUSES_CHANGED: client: phoneNumber: '+79991234567' card: '200000000447' clientBonuses: totalAmount: 800 availableAmount: 600 pendingAmount: 200 bonusesDelta: availableDelta: -100 pendingDelta: 0 - event: EVENT_PURCHASE_NEW eventId: 0a353b66-5ac9-4cbb-9c52-596f46bfac62 eventTime: '2016-12-19T17:24:32+03:00' EVENT_PURCHASE_NEW: client: phoneNumber: '+79991234567' card: '200000000447' shop: code: '1' name: Магазин 1 cashier: code: '1' name: Кассир 1 txid: 00f09507-2aea-4bc4-aad9-c68e83a8448c executedAt: '2016-12-19T17:24:32+03:00' totalAmount: 1000 totalDiscount: 200 discounts: initial: 0 bonuses: 200 promocode: 0 offer: 0 rounding: 0 bonuses: applied: 200 collected: 120 subscriptionEventPayload: type: object properties: client: $ref: '#/components/schemas/clientQuery' email: type: string description: Адрес электронной почты required: - client - email clientQuery: type: object description: Данные о клиенте properties: phoneNumber: type: string description: Номер телефона клиента card: type: string description: Номер карты externalId: type: string description: Внешний идентификатор clientData: type: object description: Информация о клиенте properties: phoneNumber: type: string description: Номер телефона клиента card: type: string description: Номер карты externalId: type: string description: Внешний идентификатор email: type: string description: Адрес электронной почты surname: type: string description: Фамилия отдельно name: type: string description: Имя отдельно patronymicName: type: string description: Отчество отдельно fullName: type: string description: Фамилия, имя и отчество gender: type: number format: int32 enum: - 0 - 1 - 2 description: | Пол клиента. Поддерживаемые значения: - 0 - пол неизвестен, - 1 - мужской, - 2 - женский birthdate: type: string format: date description: | Дата рождения. Формат даты: ```1967-11-21```. Если нам известны только день и месяц рождения, а год рождения клиент не предоставил, год в этом поле будет - 1900, например, ```1900-11-21```. level: type: number format: int32 description: Уровень клиента в программе лояльности isEmailSubscribed: type: boolean description: Флаг, подписан ли клиент на маркетинговые рассылки по электронной почте isPhoneSubscribed: type: boolean description: Флаг, подписан ли клиент на маркетинговые рассылки по SMS или Viber isChatSubscribed: type: boolean description: Флаг, подписан ли клиент на коммуникацию с чат-ботами extraFields: type: object description: Дополнительные параметры клиента clientBonuses: type: object description: Информация о бонусном счете properties: totalAmount: type: number format: int32 description: Всего бонусов у клиента (available + pending) availableAmount: type: number format: int32 description: Количество доступных для расходования бонусов pendingAmount: type: number format: int32 description: Количество бонусов, которые начислены, но пока недоступны shopQuery: type: object description: Данные о торговой точке properties: code: type: string description: Код name: type: string description: Название required: - code - name cashierQuery: type: object description: Данные о кассире properties: code: type: string description: Код или табельный номер name: type: string description: Имя required: - code - name purchaseDiscounts: description: Детализация по скидкам type: object properties: initial: description: Скидка кассы type: number format: float bonuses: description: Скидка по бонусам type: number format: float promocode: description: Скидка по промокоду type: number format: float offer: description: Скидка по акции type: number format: float rounding: description: Округление type: number format: float required: - initial - bonuses - promocode - offer - rounding purchaseBonuses: description: Детализация по начисленным бонусам type: object properties: applied: description: Применено type: integer format: int32 minimum: 0 collected: description: Накоплено type: integer format: int32 minimum: 0 required: - applied - collected purchaseRow: description: Позиция чека type: object properties: externalId: type: string description: Внешний идентификатор товара или услуги title: type: string description: Наименование товара или услуги sku: type: string description: Артикул или код товара или услуги qty: description: Количество type: number format: float price: description: Цена продажи type: number format: float buyingPrice: description: Закупочная цена type: number format: float totalAmount: description: Итого по строке (до вычета скидок) type: number format: float totalDiscount: type: number format: float description: Сумма скидки discounts: $ref: '#/components/schemas/purchaseDiscounts' bonuses: $ref: '#/components/schemas/purchaseBonuses' required: - sku - qty - price - totalAmount - totalDiscount - discounts