From 1db997f93c366cc0b3fcf412666a4cc4aee33c6a Mon Sep 17 00:00:00 2001 From: Sergey Martynov Date: Mon, 13 Apr 2026 19:41:46 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D0=91=D0=B8=D1=82=D1=80=D0=B8?= =?UTF-8?q?=D0=BA=D1=81:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B2?= =?UTF-8?q?=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - order-create: добавлена проверка IS_NEW для защиты от дубликатов заказов - list-cart, order-create: убран мёртвый код is_object($price)/getAmount() - customer-edit: краткий фрагмент теперь очищает телефон как полный пример - customer-register, customer-edit: явное предупреждение что $isSync=true по умолчанию - customer-auth: убрана лишняя ссылка & у $arUser в OnAfterUserAuthorize - list-cart: global $USER заменён на Fuser API из объекта корзины - order-create: пояснение о различии setPrice (заказ) vs setPricePerItem (корзина) - все файлы: исправлено форматирование bold внутри бэктиков Made-with: Cursor --- docs/README.md | 37 ++++--- docs/bitrix/README.md | 173 ++++++++++++++++++++++++++++++ docs/bitrix/customer-auth.md | 112 +++++++++++++++++++ docs/bitrix/customer-edit.md | 130 ++++++++++++++++++++++ docs/bitrix/customer-register.md | 93 ++++++++++++++++ docs/bitrix/customer-subscribe.md | 115 ++++++++++++++++++++ docs/bitrix/list-cart.md | 152 ++++++++++++++++++++++++++ docs/bitrix/list-favorites.md | 113 +++++++++++++++++++ docs/bitrix/order-create.md | 164 ++++++++++++++++++++++++++++ 9 files changed, 1072 insertions(+), 17 deletions(-) create mode 100644 docs/bitrix/README.md create mode 100644 docs/bitrix/customer-auth.md create mode 100644 docs/bitrix/customer-edit.md create mode 100644 docs/bitrix/customer-register.md create mode 100644 docs/bitrix/customer-subscribe.md create mode 100644 docs/bitrix/list-cart.md create mode 100644 docs/bitrix/list-favorites.md create mode 100644 docs/bitrix/order-create.md diff --git a/docs/README.md b/docs/README.md index 69b7545..5edb8ac 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,36 +3,39 @@ Mindbox PHP SDK - это библиотека инкапсулирующая детали сетевого взаимодействия и нюансы реализации API Mindbox от конечного разработчика. Ключевые возможности библиотеки: -* Поддержка двух версий API: v2.1, v3; -* Отправка произвольных запросов к API Mindbox (POST и GET запросы); -* Удобные обёртки для наиболее часто используемых операций Mindbox; -* Логирование запросов и ответов API; -* Синхронные и асинхронные вызовы операций для API v3; -* Возможность подключить собственный логгер (реализующий \Psr\Log\LoggerInterface); -* Возможность выбора HTTP клиента: [PHP cURL](http://php.net/manual/ru/book.curl.php), [PHP Stream](http://php.net/manual/ru/book.stream.php). + +- Поддержка двух версий API: v2.1, v3; +- Отправка произвольных запросов к API Mindbox (POST и GET запросы); +- Удобные обёртки для наиболее часто используемых операций Mindbox; +- Логирование запросов и ответов API; +- Синхронные и асинхронные вызовы операций для API v3; +- Возможность подключить собственный логгер (реализующий \Psr\Log\LoggerInterface); +- Возможность выбора HTTP клиента: [PHP cURL](http://php.net/manual/ru/book.curl.php), [PHP Stream](http://php.net/manual/ru/book.stream.php). С полной документацией API Mindbox можно ознакомиться [здесь](https://developers.mindbox.ru/docs/v3). Для начала работы с Mindbox SDK ознакомтесь с [Гайдом по установке и настройке Mindbox PHP SDK](./getting_started.md), а также с примерами использования SDK, приведёнными ниже. +Интеграция с **1С-Битрикс** (настройка SDK, клиент, списки, заказ) описана в отдельном разделе: **[документация по Битрикс](./bitrix/README.md)**. + --- ## Примеры использования SDK - **Формирование тела запроса** - - [Установка тела запроса через конструктор DTO](./examples/dto_constructor.md) - - [Установка тела запроса через сеттеры DTO](./examples/dto_setters.md) - - [Формирование тела запроса без использования DTO](./examples/send_prepared_request.md) + - [Установка тела запроса через конструктор DTO](./examples/dto_constructor.md) + - [Установка тела запроса через сеттеры DTO](./examples/dto_setters.md) + - [Формирование тела запроса без использования DTO](./examples/send_prepared_request.md) - **Универсальные методы отправки запросов** - - [Отправка запросов на Mindbox API v2.1](./examples/send_request_to_v2.md) - - [Отправка запросов на Mindbox API v3](./examples/send_request_to_v3.md) + - [Отправка запросов на Mindbox API v2.1](./examples/send_request_to_v2.md) + - [Отправка запросов на Mindbox API v3](./examples/send_request_to_v3.md) - **Хелперы для стандартных операций Mindbox** - - [Операции над потребителем](./examples/customer_helper.md) - - [Операции связанные с процессингом заказов](./examples/order_helper.md) - - [Операции связанные с обновлением списка продуктов в корзине](./examples/product_list_helper.md) + - [Операции над потребителем](./examples/customer_helper.md) + - [Операции связанные с процессингом заказов](./examples/order_helper.md) + - [Операции связанные с обновлением списка продуктов в корзине](./examples/product_list_helper.md) - **Работа с ответом от Mindbox** - - [Доступные методы для работы с ответом](./examples/response.md) - - [Список возможных исключений при отправке запроса](./examples/exceptions.md) + - [Доступные методы для работы с ответом](./examples/response.md) + - [Список возможных исключений при отправке запроса](./examples/exceptions.md) ## Справочник API SDK diff --git a/docs/bitrix/README.md b/docs/bitrix/README.md new file mode 100644 index 0000000..1c68897 --- /dev/null +++ b/docs/bitrix/README.md @@ -0,0 +1,173 @@ +# Интеграция Mindbox PHP SDK с Битрикс + +Краткая последовательность: установка SDK → конфиг в `.settings.php` → один файл с клиентом Mindbox и регистрацией событий. Дальше — сценарии по операциям в отдельных страницах ниже. + +--- + +## 1. Установить SDK + +```sh +composer require mindbox/sdk +``` + +При необходимости SDK можно скопировать в каталог проекта (в т.ч. в `local`) и подключать тот же `vendor/autoload.php`. + +В `local/php_interface/init.php` (или другой общей точке входа) подключите автозагрузчик: + +```php +require_once $_SERVER['DOCUMENT_ROOT'] . '/vendor/autoload.php'; +``` + +Требования к PHP и расширениям — в [общем гайде по установке](../getting_started.md#системные-зависимости). + +--- + +## 2. Заполнить конфиг + +В **`local/.settings.php`** добавьте секцию **`mindbox`** (остальные ключи `return` не удаляйте). Значения `endpointId`, `secretKey` и `domain` выдайте у менеджера Mindbox; `api.s.mindbox` в примере — только иллюстрация. + +```php + [ + 'value' => [ + 'endpointId' => '…', + 'secretKey' => '…', + 'domain' => 'api.s.mindbox', + 'domainZone' => 'ru', + ], + 'readonly' => true, + ], +]; +``` + +--- + +## 3. Файл с клиентом и обработчиками событий + +Создайте, например, **`local/php_interface/include/mindbox.php`**. В нём — фабрика клиента и ниже пример регистрации в Mindbox при `OnAfterUserRegister` (подробнее и варианты — [регистрация клиента](./customer-register.md)). Второй аргумент `Mindbox` — любой PSR-3-логгер; ниже для простоты используется `MindboxFileLogger` (лог: `/upload/logs/mindbox.log`). + +Подключите файл в **`init.php`** после автозагрузчика: + +```php +require_once $_SERVER['DOCUMENT_ROOT'] . '/local/php_interface/include/mindbox.php'; +``` + +Пример содержимого `mindbox.php`: + +```php + $cfg['endpointId'], + 'secretKey' => $cfg['secretKey'], + 'domain' => $cfg['domain'], + 'domainZone' => $cfg['domainZone'] ?? 'ru', + ], $logger); + } + + return $client; +} + +EventManager::getInstance()->addEventHandler( + 'main', + 'OnAfterUserRegister', + static function (&$arFields) { + $userId = (int)($arFields['USER_ID'] ?? 0); + if ($userId <= 0) { + return; + } + + $rsUser = \CUser::GetByID($userId); + $arUser = $rsUser->Fetch(); + if (!$arUser) { + return; + } + + $customer = new CustomerRequestDTO(); + + if (!empty($arUser['EMAIL'])) { + $customer->setEmail($arUser['EMAIL']); + } + + if (!empty($arUser['PERSONAL_PHONE'])) { + $phone = preg_replace('/\D+/', '', $arUser['PERSONAL_PHONE']); + if ($phone !== '') { + $customer->setMobilePhone($phone); + } + } + + if (!empty($arUser['NAME'])) { + $customer->setFirstName($arUser['NAME']); + } + + if (!empty($arUser['LAST_NAME'])) { + $customer->setLastName($arUser['LAST_NAME']); + } + + $customer->setId('bitrixId', (string)$arUser['ID']); + + try { + getMindboxClient() + ->customer() + ->register($customer, 'Website.RegisterCustomer', true, false) + ->sendRequest(); + } catch (MindboxClientException $e) { + // Логирование: $e->getMessage(), контекст user id + } + } +); +``` + +--- + +## Оглавление + +### Операции с клиентом + + +| Документ | Описание | +| --------------------------------------------- | ---------------------------------------- | +| [Регистрация клиента](./customer-register.md) | Создание нового клиента в Mindbox | +| [Редактирование клиента](./customer-edit.md) | Обновление данных существующего клиента | +| [Авторизация клиента](./customer-auth.md) | Вход и привязка сессии к клиенту Mindbox | +| [Подписка на рассылки](./customer-subscribe.md) | Подписка через форму (SubscribeCustomer) | + + +### Списки продуктов + + +| Документ | Описание | +| ----------------------------------------- | ------------------------------------ | +| [Список «Корзина»](./list-cart.md) | Установка и синхронизация корзины | +| [Список «Избранное»](./list-favorites.md) | Установка и синхронизация избранного | + + +### Заказ + + +| Документ | Описание | +| ------------------------------------ | ------------------------------- | +| [Создание заказа](./order-create.md) | Оформление заказа через Mindbox | + + +Точные имена операций Mindbox и поля запросов — у менеджера и в [документации API](https://developers.mindbox.ru/docs/v3). \ No newline at end of file diff --git a/docs/bitrix/customer-auth.md b/docs/bitrix/customer-auth.md new file mode 100644 index 0000000..d60e8f9 --- /dev/null +++ b/docs/bitrix/customer-auth.md @@ -0,0 +1,112 @@ +# Авторизация клиента (Битрикс + Mindbox) + +После успешного входа пользователя в Битрикс вызывается операция **AuthorizeCustomer** в Mindbox (имя операции в проекте — **уточните у менеджера**). + +Событие авторизации позволяет Mindbox **склеить анонимный профиль (DeviceUUID)** с известным клиентом и далее персонализировать коммуникации. + +**Предусловия:** выполнен [быстрый старт](./README.md) (SDK, конфиг, `getMindboxClient()`). + +--- + +## Параметры интеграции + + +| Параметр | Значение | +| ------------------------ | ----------------------------------------- | +| Операция Mindbox | `Website.AuthorizeCustomer` | +| DeviceUUID | Да (`addDeviceUUID: true`) | +| Синхронность | Асинхронно (`isSync: false`) | +| Точка интеграции Битрикс | Обработчик события `OnAfterUserAuthorize` | +| Хелпер SDK | `$mindbox->customer()->authorize(...)` | + + +--- + +## Событие + +Используют **`main:OnAfterUserAuthorize`**. В обработчик передаётся массив полей пользователя `$arUser`; для идентификации используйте `ID` (при необходимости дозагрузите профиль через `CUser::GetByID`). + +--- + +## Полный пример + +Ниже — регистрация обработчика (вставьте в `local/php_interface/include/mindbox.php` после блока регистрации или в отдельный подключаемый файл). + +Имя операции `Website.AuthorizeCustomer`, вызов `authorize(..., true, false)` — DeviceUUID в запросе, **асинхронный** вызов v3; подставьте значения из вашей интеграции. + +```php +addEventHandler( + 'main', + 'OnAfterUserAuthorize', + static function ($arUser) { + $userId = (int)($arUser['ID'] ?? 0); + if ($userId <= 0) { + return; + } + + $rsUser = \CUser::GetByID($userId); + $userData = $rsUser->Fetch(); + if (!$userData) { + return; + } + + $customer = new CustomerRequestDTO(); + + if (!empty($userData['EMAIL'])) { + $customer->setEmail($userData['EMAIL']); + } + + $customer->setId('bitrixId', (string)$userData['ID']); + + try { + getMindboxClient() + ->customer() + ->authorize($customer, 'Website.AuthorizeCustomer', true, false) + ->sendRequest(); + } catch (MindboxClientException $e) { + // Логирование: $e->getMessage(), контекст user id + } + } +); +``` + +Краткий фрагмент только вызова Mindbox (без события Битрикс): + +```php +$customer = new \Mindbox\DTO\V3\Requests\CustomerRequestDTO(); +$customer->setEmail($arUser['EMAIL']); +$customer->setId('bitrixId', (string)$arUser['ID']); + +try { + $response = getMindboxClient() + ->customer() + ->authorize($customer, 'Website.AuthorizeCustomer', true, false) + ->sendRequest(); +} catch (\Mindbox\Exceptions\MindboxClientException $e) { + // Логирование ошибки +} +``` + +--- + +## Параметры `authorize()` + +Сигнатура хелпера: `authorize(CustomerRequestDTO $customer, $operationName, $addDeviceUUID = true, $isSync = false)`. + +- **`$addDeviceUUID`** — передавать ли DeviceUUID в запросе (для склейки с анонимным профилем обычно `true`). +- **`$isSync`** — `false` (по умолчанию): асинхронный запрос к API v3, `true`: синхронный. + +--- + +## Ошибки и отладка + +- Ошибка Mindbox при авторизации **не обязана** блокировать вход в Битрикс — правило логирования и влияния на UX задайте в проекте. +- Перехват `MindboxClientException` и запись в лог (без утечки ПДн). +- См. [исключения SDK](../examples/exceptions.md), [CustomerHelper в общих примерах](../examples/customer_helper.md). + diff --git a/docs/bitrix/customer-edit.md b/docs/bitrix/customer-edit.md new file mode 100644 index 0000000..f37f36b --- /dev/null +++ b/docs/bitrix/customer-edit.md @@ -0,0 +1,130 @@ +# Редактирование профиля (Битрикс + Mindbox) + +При обновлении данных пользователя в Битрикс вызывается операция **EditCustomer** в Mindbox (имя операции в проекте — **уточните у менеджера**). + +**Предусловия:** выполнен [быстрый старт](./README.md) (SDK, конфиг, `getMindboxClient()`). + +--- + +## Параметры интеграции + +| Параметр | Значение | +|----------|----------| +| Операция Mindbox | `Website.EditCustomer` | +| DeviceUUID | Да (`addDeviceUUID: true`) | +| Синхронность | Асинхронно (`isSync: false`) | +| Точка интеграции Битрикс | Обработчик события `OnAfterUserUpdate` | +| Хелпер SDK | `$mindbox->customer()->edit(...)` | + +--- + +## Событие + +Используют **`main:OnAfterUserUpdate`**. В обработчик передаётся **`&$arFields`** — поля обновляемого пользователя; для актуального профиля в Mindbox удобно после проверки **`ID`** дозагрузить пользователя через **`CUser::GetByID`**. + +--- + +## Полный пример + +Вставьте в `local/php_interface/include/mindbox.php` (или отдельный подключаемый файл) рядом с другими обработчиками Mindbox. + +Имя операции `Website.EditCustomer`, вызов `edit(..., true, false)` — DeviceUUID в запросе, **асинхронный** вызов v3. + +```php +addEventHandler( + 'main', + 'OnAfterUserUpdate', + static function (&$arFields) { + $userId = (int)($arFields['ID'] ?? 0); + if ($userId <= 0) { + return; + } + + $rsUser = \CUser::GetByID($userId); + $userData = $rsUser->Fetch(); + if (!$userData) { + return; + } + + $customer = new CustomerRequestDTO(); + + $customer->setId('bitrixId', (string)$userData['ID']); + + if (!empty($userData['EMAIL'])) { + $customer->setEmail($userData['EMAIL']); + } + + if (!empty($userData['PERSONAL_PHONE'])) { + $phone = preg_replace('/\D+/', '', $userData['PERSONAL_PHONE']); + if ($phone !== '') { + $customer->setMobilePhone($phone); + } + } + + if (!empty($userData['NAME'])) { + $customer->setFirstName($userData['NAME']); + } + + if (!empty($userData['LAST_NAME'])) { + $customer->setLastName($userData['LAST_NAME']); + } + + try { + getMindboxClient() + ->customer() + ->edit($customer, 'Website.EditCustomer', true, false) + ->sendRequest(); + } catch (MindboxClientException $e) { + // Логирование: $e->getMessage(), контекст user id + } + } +); +``` + +Краткий фрагмент только вызова Mindbox: + +```php +$customer = new \Mindbox\DTO\V3\Requests\CustomerRequestDTO(); +$customer->setId('bitrixId', (string)$arUser['ID']); +$customer->setEmail($arUser['EMAIL']); + +$phone = preg_replace('/\D+/', '', $arUser['PERSONAL_PHONE']); +if ($phone !== '') { + $customer->setMobilePhone($phone); +} + +$customer->setFirstName($arUser['NAME']); +$customer->setLastName($arUser['LAST_NAME']); + +try { + $response = getMindboxClient() + ->customer() + ->edit($customer, 'Website.EditCustomer', true, false) + ->sendRequest(); +} catch (\Mindbox\Exceptions\MindboxClientException $e) { + // Логирование ошибки +} +``` + +--- + +## Параметры `edit()` + +Сигнатура хелпера: `edit(CustomerRequestDTO $customer, $operationName, $addDeviceUUID = true, $isSync = true)`. + +- **`$addDeviceUUID`** — передавать ли DeviceUUID в запросе. +- **`$isSync`** — `true` (по умолчанию): синхронный запрос к API v3, `false`: асинхронный. В примере выше явно передаётся `false` — без этого вызов будет синхронным. + +--- + +## Ошибки и отладка + +- Логировать исключения Mindbox отдельно от ошибок сохранения в Битрикс. +- Учитывать конфликты данных (дубликаты телефона/email в Mindbox). +- См. [исключения SDK](../examples/exceptions.md), [CustomerHelper в общих примерах](../examples/customer_helper.md). diff --git a/docs/bitrix/customer-register.md b/docs/bitrix/customer-register.md new file mode 100644 index 0000000..76435e0 --- /dev/null +++ b/docs/bitrix/customer-register.md @@ -0,0 +1,93 @@ +# Регистрация клиента (Битрикс + Mindbox) + +После успешной регистрации пользователя в Битрикс вызывается операция Mindbox (например `Website.RegisterCustomer` — имя **уточните у менеджера** под ваш проект). + +**Предусловия:** выполнен [быстрый старт](./README.md) (SDK, конфиг, `getMindboxClient()`). + +--- + +## Событие + +Обычно используют `main:OnAfterUserRegister`. В обработчик передаётся `&$arFields`; после создания пользователя в массиве есть `USER_ID`. + +--- + +## Полный пример + +Ниже — тело обработчика (можно вставить в `local/php_interface/include/mindbox.php` после `getMindboxClient()` или вынести в отдельный файл и подключить `require`). + +Имя операции `Website.RegisterCustomer`, флаги `register(..., true, false)` (DeviceUUID в запросе, **асинхронный** вызов v3) — приведены как в типовой схеме; подставьте значения из вашей интеграции. + +```php +addEventHandler( + 'main', + 'OnAfterUserRegister', + static function (&$arFields) { + $userId = (int)($arFields['USER_ID'] ?? 0); + if ($userId <= 0) { + return; + } + + $rsUser = \CUser::GetByID($userId); + $arUser = $rsUser->Fetch(); + if (!$arUser) { + return; + } + + $customer = new CustomerRequestDTO(); + + if (!empty($arUser['EMAIL'])) { + $customer->setEmail($arUser['EMAIL']); + } + + if (!empty($arUser['PERSONAL_PHONE'])) { + $phone = preg_replace('/\D+/', '', $arUser['PERSONAL_PHONE']); + if ($phone !== '') { + $customer->setMobilePhone($phone); + } + } + + if (!empty($arUser['NAME'])) { + $customer->setFirstName($arUser['NAME']); + } + + if (!empty($arUser['LAST_NAME'])) { + $customer->setLastName($arUser['LAST_NAME']); + } + + $customer->setId('bitrixId', (string)$arUser['ID']); + + try { + getMindboxClient() + ->customer() + ->register($customer, 'Website.RegisterCustomer', true, false) + ->sendRequest(); + } catch (MindboxClientException $e) { + // Логирование: $e->getMessage(), контекст user id + } + } +); +``` + +--- + +## Параметры `register()` + +Сигнатура хелпера: `register(CustomerRequestDTO $customer, $operationName, $addDeviceUUID = true, $isSync = true)`. + +- **`$addDeviceUUID`** — передавать ли DeviceUUID в запросе (`true` / `false` по правилам проекта). +- **`$isSync`** — `true` (по умолчанию): синхронный запрос к API v3, `false`: асинхронный. В примере выше явно передаётся `false` — без этого вызов будет синхронным. + +--- + +## Ошибки и отладка + +- Перехват `MindboxClientException` и запись в лог (без утечки ПДн в публичные ответы). +- Сверка тела запроса с [документацией Mindbox](https://developers.mindbox.ru/docs/v3). +- Дополнительные сценарии: [CustomerHelper в общих примерах SDK](../examples/customer_helper.md). diff --git a/docs/bitrix/customer-subscribe.md b/docs/bitrix/customer-subscribe.md new file mode 100644 index 0000000..061f2d4 --- /dev/null +++ b/docs/bitrix/customer-subscribe.md @@ -0,0 +1,115 @@ +# Подписка на рассылки (SubscribeCustomer) + +Подписка через форму на сайте (футер, попап и т.д.) вызывает операцию **SubscribeCustomer** в Mindbox (имя операции в проекте — **уточните у менеджера**). + +**Предусловия:** выполнен [быстрый старт](./README.md) (SDK, конфиг, `getMindboxClient()`). + +В ядре Битрикс **нет одного стандартного события** «отправлена форма подписки» — обработчик вешайте на **свой** компонент / контроллер / `ajax.php`, где принимаете email и вызываете код ниже. + +--- + +## Параметры интеграции + +| Параметр | Значение | +|----------|----------| +| Операция Mindbox | `Website.SubscribeCustomer` | +| DeviceUUID | Да (`addDeviceUUID: true`) | +| Синхронность | Синхронно (`isSync: true`) | +| Точка интеграции Битрикс | Форма подписки (футер, попап, отдельная страница) | +| Хелпер SDK | `$mindbox->customer()->subscribe(...)` | + +Сигнатура: + +```php +public function subscribe( + CustomerRequestDTO $customer, + $operationName, + $addDeviceUUID = false, + $isSync = true +) +``` + +Для сценария из таблицы вызывайте: **`subscribe($customer, 'Website.SubscribeCustomer', true, true)`**. + +--- + +## Данные запроса + +| Поле | Описание | +|------|----------| +| Клиент | `CustomerRequestDTO`: как минимум **`setEmail()`** | +| Подписка | `SubscriptionRequestDTO` с **`setPointOfContact()`** (например `'Email'`) — передаётся в **`CustomerRequestDTO::setSubscriptions(new SubscriptionRequestCollection([...]))`** | + +Точный набор полей подписки — по операции в Mindbox. + +--- + +## Полный пример + +Обработка после валидации email из формы (`$email` — строка из `$_POST` / параметра action): + +```php +setEmail($email); + +$subscription = new SubscriptionRequestDTO(); +$subscription->setPointOfContact('Email'); + +$customer->setSubscriptions(new SubscriptionRequestCollection([$subscription])); + +try { + getMindboxClient() + ->customer() + ->subscribe($customer, 'Website.SubscribeCustomer', true, true) + ->sendRequest(); +} catch (MindboxClientException $e) { + // Логирование ошибки +} +``` + +--- + +## Краткий фрагмент только вызова Mindbox + +```php +$customer = new \Mindbox\DTO\V3\Requests\CustomerRequestDTO(); +$customer->setEmail($email); + +$subscription = new \Mindbox\DTO\V3\Requests\SubscriptionRequestDTO(); +$subscription->setPointOfContact('Email'); +$customer->setSubscriptions( + new \Mindbox\DTO\V3\Requests\SubscriptionRequestCollection([$subscription]) +); + +try { + $response = getMindboxClient() + ->customer() + ->subscribe($customer, 'Website.SubscribeCustomer', true, true) + ->sendRequest(); +} catch (\Mindbox\Exceptions\MindboxClientException $e) { + // Логирование ошибки +} +``` + +--- + +## Параметры `subscribe()` + +- **`$addDeviceUUID`** — передавать ли DeviceUUID (`true` в таблице выше). +- **`$isSync`** — `true`: синхронный запрос к API v3, `false`: асинхронный. + +--- + +## Ошибки и отладка + +- Валидация email и защита от спама на стороне сайта до вызова Mindbox. +- См. [CustomerHelper в общих примерах SDK](../examples/customer_helper.md), [исключения SDK](../examples/exceptions.md). diff --git a/docs/bitrix/list-cart.md b/docs/bitrix/list-cart.md new file mode 100644 index 0000000..741a919 --- /dev/null +++ b/docs/bitrix/list-cart.md @@ -0,0 +1,152 @@ +# Установка состава корзины (Битрикс + Mindbox) + +При изменении корзины в Битрикс вызывается операция установки списка (**SetCart**). Mindbox **полностью подменяет** сохранённый ранее состав корзины переданным набором позиций. + +Имя операции в проекте — **уточните у менеджера** (ниже — пример `Website.SetCart`). + +**Предусловия:** выполнен [быстрый старт](./README.md) (SDK, конфиг, `getMindboxClient()`). + +--- + +## Параметры интеграции + + +| Параметр | Значение | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | +| Операция Mindbox | `Website.SetCart` | +| DeviceUUID | Да (`addDeviceUUID: true`) | +| Синхронность | Запрос формируется **асинхронно** — в `ProductListHelper::setProductList()` отдельного аргумента `isSync` нет, режим зашит в SDK | +| Точка интеграции Битрикс | `OnSaleBasketSaved` и/или AJAX-обработчик после изменения корзины | +| Хелпер SDK | `ProductListHelper::setProductList(...)` | + + +При каждом изменении корзины (добавление, удаление, количество) отправляйте **полный актуальный** состав позиций. + +--- + +## Передаваемые данные по товару + + +| Поле | Описание | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `productId` | Внешний ID товара (часто ID элемента каталога Битрикс) — `ProductRequestDTO::setId('productId', …)` | +| `name` | Наименование — `ProductRequestDTO::setName()` | +| `count` | Количество — `ProductListItemRequestDTO::setCount()` | +| `price` | Цена за единицу — на позиции списка `ProductListItemRequestDTO::setPricePerItem()` (или `setPrice()` — по согласованию с полями операции) | + + +--- + +## Событие + +Модуль **`sale`**, событие **`OnSaleBasketSaved`**. В обработчик передаётся `Main\Event`; корзина — `$event->getParameter('ENTITY')` (объект `Bitrix\Sale\Basket`). См. [события сохранения корзины](https://dev.1c-bitrix.ru/api_d7/bitrix/sale/events/basket_saved.php). + +Ту же логику можно вызвать вручную после `Basket::save()` или из AJAX — без регистрации события. + +--- + +## Полный пример + +Вставьте в `local/php_interface/include/mindbox.php` (или отдельный подключаемый файл) рядом с другими обработчиками Mindbox. + +Коллекция — `ProductListItemRequestCollection` из массива `ProductListItemRequestDTO`. У каждой строки: вложенный `ProductRequestDTO` (id + имя), `count`, цена за единицу. Для авторизованного пользователя — `CustomerIdentityRequestDTO` с `bitrixId`; для гостя — `null`, если сценарий в Mindbox это допускает. + +Имя операции `Website.SetCart`, четвёртый аргумент `setProductList` — `true` (DeviceUUID). + +```php +addEventHandler( + 'sale', + 'OnSaleBasketSaved', + static function (Event $event) { + /** @var \Bitrix\Sale\Basket $basket */ + $basket = $event->getParameter('ENTITY'); + if (!$basket) { + return; + } + + $fUserId = $basket->getFUserId(); + $userId = (int)\Bitrix\Sale\Fuser::getUserIdById($fUserId); + + $items = []; + foreach ($basket as $basketItem) { + $line = new ProductListItemRequestDTO(); + $product = new ProductRequestDTO(); + $product->setId('productId', (string)$basketItem->getProductId()); + $name = $basketItem->getField('NAME'); + $product->setName($name !== null && $name !== '' ? $name : ('#' . $basketItem->getProductId())); + $line->setProduct($product); + $line->setCount($basketItem->getQuantity()); + + $line->setPricePerItem((float)$basketItem->getPrice()); + + $items[] = $line; + } + + $productList = new ProductListItemRequestCollection($items); + + $customerIdentity = null; + if ($userId > 0) { + $customerIdentity = new CustomerIdentityRequestDTO(); + $customerIdentity->setId('bitrixId', (string)$userId); + } + + try { + getMindboxClient() + ->productList() + ->setProductList($productList, 'Website.SetCart', $customerIdentity, true) + ->sendRequest(); + } catch (MindboxClientException $e) { + // Логирование: $e->getMessage(), контекст корзины + } + } +); +``` + +Имя поля позиции и способ получения `$userId` уточните под вашу версию `sale`. + +--- + +## Краткий фрагмент только вызова Mindbox + +```php +$productList = new \Mindbox\DTO\V3\Requests\ProductListItemRequestCollection(/* массив ProductListItemRequestDTO */); + +try { + $response = getMindboxClient() + ->productList() + ->setProductList($productList, 'Website.SetCart', $customerIdentity, true) + ->sendRequest(); +} catch (\Mindbox\Exceptions\MindboxClientException $e) { + // Логирование ошибки +} +``` + +Четвёртый аргумент `setProductList` — `$addDeviceUUID` (`true` / `false`). Отдельного параметра синхронности у метода нет — см. таблицу выше. + +--- + +## Параметры `setProductList()` + +Сигнатура: `setProductList(ProductListItemRequestCollection $products, $operationName, CustomerIdentityRequestDTO $customerIdentity = null, $addDeviceUUID = true)`. + +- **`$customerIdentity`** — `null`, если клиент идентифицируется только по DeviceUUID (или по правилам операции). +- **`$addDeviceUUID`** — передавать ли DeviceUUID в запросе. + +--- + +## Ошибки и отладка + +- Расхождение ID товара Битрикс ↔ Mindbox — проверить маппинг и имя внешнего id (`productId`). +- Большая корзина — следить за размером тела запроса и таймаутами. +- См. [операции со списком продуктов в SDK](../examples/product_list_helper.md). + diff --git a/docs/bitrix/list-favorites.md b/docs/bitrix/list-favorites.md new file mode 100644 index 0000000..4df50bf --- /dev/null +++ b/docs/bitrix/list-favorites.md @@ -0,0 +1,113 @@ +# Установка списка избранного (SetWishList) + +При изменении избранного в Битрикс вызывается операция **SetWishList** в Mindbox. Mindbox **полностью подменяет** сохранённый ранее список избранного переданным набором позиций — **как для [корзины](./list-cart.md)**, но другая операция и метод хелпера. + +Имя операции в проекте — **уточните у менеджера** (ниже — пример `Website.SetWishList`). + +**Предусловия:** выполнен [быстрый старт](./README.md) (SDK, конфиг, `getMindboxClient()`). + +В ядре Битрикс **нет единого стандартного события** «избранное изменилось»: подписку через `EventManager` здесь не приводим. Вызов Mindbox вставляйте в **свой** код добавления/удаления в избранное (компонент, AJAX, highload-блок и т.д.). + +--- + +## Параметры интеграции + +| Параметр | Значение | +|----------|----------| +| Операция Mindbox | `Website.SetWishList` | +| DeviceUUID | Да (`addDeviceUUID: true`) | +| Синхронность | Запрос формируется **асинхронно** — в `ProductListHelper::setWishList()` отдельного аргумента `isSync` нет, режим зашит в SDK | +| Точка интеграции Битрикс | Обработчик добавления / удаления из избранного (ваш код, не событие ядра) | +| Хелпер SDK | **`ProductListHelper::setWishList(...)`** — тот же состав DTO, что и у корзины, другой метод и имя операции | + +По смыслу то же, что **SetCart**: при каждом изменении списка отправляйте **полный актуальный** набор позиций. + +--- + +## Передаваемые данные по товару + +Как для корзины: см. таблицу в [корзине](./list-cart.md#передаваемые-данные-по-товару) (`productId`, `name`, `count`, цена за единицу — если операция в Mindbox ожидает цену для строки избранного). + +--- + +## Полный пример + +Сборка **`ProductListItemRequestCollection`** — так же, как в [корзине](./list-cart.md). Источник **`$wishlistItems`** зависит от вашей реализации избранного (массив строк с полями вроде `NAME`, `PRODUCT_ID`, `QUANTITY`, `PRICE` — подставьте свои). + +```php +setName($item['NAME']); + $product->setId('productId', (string)$item['PRODUCT_ID']); + $line->setProduct($product); + $line->setCount($item['QUANTITY']); + $line->setPricePerItem($item['PRICE']); + + $items[] = $line; +} + +$productList = new ProductListItemRequestCollection($items); + +$customerIdentity = null; +if ($userId > 0) { + $customerIdentity = new CustomerIdentityRequestDTO(); + $customerIdentity->setId('bitrixId', (string)$userId); +} + +try { + getMindboxClient() + ->productList() + ->setWishList($productList, 'Website.SetWishList', $customerIdentity, true) + ->sendRequest(); +} catch (MindboxClientException $e) { + // Логирование ошибки +} +``` + +--- + +## Краткий фрагмент только вызова Mindbox + +```php +$productList = new \Mindbox\DTO\V3\Requests\ProductListItemRequestCollection(/* массив ProductListItemRequestDTO */); + +try { + $response = getMindboxClient() + ->productList() + ->setWishList($productList, 'Website.SetWishList', $customerIdentity, true) + ->sendRequest(); +} catch (\Mindbox\Exceptions\MindboxClientException $e) { + // Логирование ошибки +} +``` + +Четвёртый аргумент — **`$addDeviceUUID`**. Отдельного параметра синхронности у метода нет. + +--- + +## Параметры `setWishList()` + +Сигнатура: `setWishList(ProductListItemRequestCollection $products, $operationName, CustomerIdentityRequestDTO $customerIdentity = null, $addDeviceUUID = true)`. + +Отличие от корзины: метод **`setWishList`**, операция **`Website.SetWishList`**. У **`setProductList`** (корзина) и **`setWishList`** (избранное) одинаковые аргументы и то же тело операции в API v3 по смыслу «полный список». + +--- + +## Ошибки и отладка + +- Те же замечания, что для [корзины](./list-cart.md): маппинг `productId`, размер запроса. +- Гости: если избранное только у авторизованных — не вызывайте Mindbox без `CustomerIdentity` / по правилам проекта. +- См. [операции со списком продуктов в SDK](../examples/product_list_helper.md). diff --git a/docs/bitrix/order-create.md b/docs/bitrix/order-create.md new file mode 100644 index 0000000..33584b3 --- /dev/null +++ b/docs/bitrix/order-create.md @@ -0,0 +1,164 @@ +# Создание заказа (CreateAuthorizedOrder) + +После сохранения заказа в Битрикс вызывается операция **CreateAuthorizedOrder** в Mindbox (имя операции в проекте — **уточните у менеджера**). + +**Предусловия:** выполнен [быстрый старт](./README.md) (SDK, конфиг, `getMindboxClient()`). + +--- + +## Параметры интеграции + +| Параметр | Значение | +|----------|----------| +| Операция Mindbox | `Website.CreateAuthorizedOrder` | +| DeviceUUID | Да — в `createAuthorizedOrder` запрос к API v3 формируется **с DeviceUUID** (реализация в SDK) | +| Синхронность | **Асинхронно** (`isSync: false`) — задаётся внутри `OrderHelper::createAuthorizedOrder()`, отдельного параметра нет | +| Точка интеграции Битрикс | Обработчик **`sale:OnSaleOrderSaved`** (при необходимости отфильтруйте только **новый** заказ / первое сохранение — по правилам проекта) | +| Хелпер SDK | `$mindbox->order()->createAuthorizedOrder($order, $operationName)` — **два** аргумента | + +Сигнатура в коде: + +```php +public function createAuthorizedOrder(OrderCreateRequestDTO $order, $operationName) +``` + +--- + +## Передаваемые данные заказа + +| Поле | Описание | +|------|----------| +| `orderId` | Внешний ID заказа — `OrderCreateRequestDTO::setId('bitrixId', …)` (или другое имя внешнего id по договорённости с Mindbox) | +| `customer` | Покупатель — `CustomerRequestDTO`: email, телефон, `bitrixId` и др. | +| `lines` | Состав — `LineRequestCollection` из `LineRequestDTO` (товар, количество, цены — по полям операции) | +| `totalPrice` | Итог — при необходимости через поля операции / `customFields` (в базовом `OrderCreateRequestDTO` отдельного `setTotalPrice` может не быть — уточните у менеджера) | +| `discountAmount` | Скидки — `setDiscounts` / поля операции | +| `deliveryType` | Доставка — например `setDeliveryCost`, кастомные поля | +| `paymentType` | Оплата — `setPayments` (`PaymentRequestCollection`) | + +Конкретный набор полей зависит от настройки операции в Mindbox. + +--- + +## Событие + +Модуль **`sale`**, событие **`OnSaleOrderSaved`**. В обработчик передаётся **`Main\Event`**; объект заказа обычно доступен как **`$event->getParameter('ENTITY')`** (`\Bitrix\Sale\Order`). Уточните имя параметра в [документации событий sale](https://dev.1c-bitrix.ru/api_d7/bitrix/sale/) для вашей версии. + +Событие может срабатывать при **каждом** сохранении заказа — добавьте условие, чтобы не дублировать создание в Mindbox (например только новый заказ или смена статуса). + +--- + +## Полный пример + +Вставьте в `local/php_interface/include/mindbox.php` (или отдельный файл) рядом с другими обработчиками. + +```php +addEventHandler( + 'sale', + 'OnSaleOrderSaved', + static function (Event $event) { + /** @var \Bitrix\Sale\Order $saleOrder */ + $saleOrder = $event->getParameter('ENTITY'); + if (!$saleOrder instanceof \Bitrix\Sale\Order) { + return; + } + + if (!$event->getParameter('IS_NEW')) { + return; + } + + $orderDto = new OrderCreateRequestDTO(); + $orderDto->setId('bitrixId', (string)$saleOrder->getId()); + + $customer = new CustomerRequestDTO(); + $customer->setId('bitrixId', (string)$saleOrder->getUserId()); + $orderDto->setCustomer($customer); + + $lines = []; + $basket = $saleOrder->getBasket(); + foreach ($basket as $basketItem) { + $line = new LineRequestDTO(); + $product = new ProductRequestDTO(); + $product->setId('productId', (string)$basketItem->getProductId()); + $name = $basketItem->getField('NAME'); + $product->setName($name !== null && $name !== '' ? $name : ('#' . $basketItem->getProductId())); + $line->setProduct($product); + $line->setQuantity($basketItem->getQuantity()); + + $product->setPrice((float)$basketItem->getPrice()); + + $lines[] = $line; + } + + $orderDto->setLines(new LineRequestCollection($lines)); + + try { + getMindboxClient() + ->order() + ->createAuthorizedOrder($orderDto, 'Website.CreateAuthorizedOrder') + ->sendRequest(); + } catch (MindboxClientException $e) { + // Логирование: $e->getMessage(), ID заказа + } + } +); +``` + +Цена в заказе указывается на `ProductRequestDTO::setPrice()` (цена за единицу товара внутри строки заказа). Это отличается от корзины, где используется `ProductListItemRequestDTO::setPricePerItem()` — на уровне позиции списка. + +Итоговая сумма, скидки, доставка и оплата в примере не заполняются — добавьте по контракту операции Mindbox (`setDeliveryCost`, `setPayments`, `setDiscounts` и т.д.). + +--- + +## Краткий фрагмент только вызова Mindbox + +```php +$order = new \Mindbox\DTO\V3\Requests\OrderCreateRequestDTO(); +$order->setId('bitrixId', (string)$arOrder['ID']); + +$customer = new \Mindbox\DTO\V3\Requests\CustomerRequestDTO(); +$customer->setId('bitrixId', (string)$arOrder['USER_ID']); +$order->setCustomer($customer); + +$lines = []; +foreach ($arOrder['BASKET'] as $item) { + $line = new \Mindbox\DTO\V3\Requests\LineRequestDTO(); + $product = new \Mindbox\DTO\V3\Requests\ProductRequestDTO(); + $product->setId('productId', (string)$item['PRODUCT_ID']); + $product->setName($item['NAME']); + $product->setPrice((float)$item['PRICE']); + $line->setProduct($product); + $line->setQuantity($item['QUANTITY']); + $lines[] = $line; +} +$order->setLines(new \Mindbox\DTO\V3\Requests\LineRequestCollection($lines)); + +try { + $response = getMindboxClient() + ->order() + ->createAuthorizedOrder($order, 'Website.CreateAuthorizedOrder') + ->sendRequest(); +} catch (\Mindbox\Exceptions\MindboxClientException $e) { + // Логирование ошибки +} +``` + +`$arOrder['BASKET']` в Битрикс обычно **нет** в таком виде — строки берут из **`Sale\Order::getBasket()`**, как в полном примере выше. + +--- + +## Ошибки и отладка + +- Идемпотентность: не создавайте дубликаты в Mindbox при повторном `OnSaleOrderSaved`. +- См. [OrderHelper в общих примерах SDK](../examples/order_helper.md), [исключения SDK](../examples/exceptions.md).