antonowano / mindbox
Mindbox API integration library
Requires
- php: ^8.2
- ext-json: *
- psr/http-client: ^1.0
- psr/http-factory: ^1.1
- psr/log: ^1.0 || ^2.0 || ^3.0
Requires (Dev)
- guzzlehttp/guzzle: ^8.0
- phpunit/phpunit: ^11.5
README
Mindbox - это российская экосистема для email-, sms-, push-рассылок с персонализацией маркетинга и цен.
Эта библиотека предоставляет удобные классы для быстрой интеграции с API-методами Mindbox на PHP. А продуманные интерфейсы помогут быстро расширить функциональность библиотеки.
Установка
composer require antonowano/mindbox
Библиотека зависит только от интерфейсов PSR. Для работы нужны реализации:
- PSR-18 / PSR-17 - HTTP-клиент и фабрики запросов/стримов (например, Guzzle:
composer require guzzlehttp/guzzle); - PSR-3 - логгер, если используете
SilentApiClient(например, Monolog:composer require monolog/monolog).
SilentApiClient оборачивает обычный клиент, ловит исключения и пишет их в лог вместо проброса наружу.
Параметры интеграции
Для работы клиента нужны:
| Параметр | Описание |
|---|---|
endpointId |
Идентификатор эндпоинта в Mindbox |
secretKey |
Секретный ключ API |
defaultPrefix |
Префикс операций по умолчанию (например, Website.) |
deviceUuid |
UUID устройства для операций с needDeviceUuid: true |
В боевом приложении deviceUuid берётся из cookie mindboxDeviceUUID
(его выставляет JS SDK Mindbox).
Примеры использования
Синхронный запрос (получить профиль)
$data = $apiClient->sync(new DefaultJsonOperation( name: '{prefix}GetProfile', needDeviceUuid: false, requestData: [ 'customer' => [ 'ids' => [ 'webID' => 3165145, ], ], ], ));
Асинхронный запрос (просмотр товара)
$data = $apiClient->async(new DefaultJsonOperation( name: '{prefix}ViewProduct', needDeviceUuid: true, requestData: [ 'viewProduct' => [ 'productGroup' => [ 'ids' => [ 'brandProducts' => 3914381, ], ], ], ], ));
Bulk-операция (массовое редактирование клиентов)
$csvFile = tmpfile(); fputcsv($csvFile, [ 'ExternalIdentityWebID', 'IsSubscribedByEmail', 'IsSubscribedByEmailOnService', 'IsSubscribedByEmailOnTrigger', 'IsSubscribedByEmailOnLoyalty', ], ';', '"', '\\'); fputcsv($csvFile, [ '3165145', '0', '1', '1', '1', ], ';', '"', '\\'); rewind($csvFile); $data = $apiClient->bulk( new DefaultBulkOperation( name: 'DirectCrm.Customers.Edit', resource: $csvFile, ), new MindboxTransactionId() );
Эти же примеры с инициализацией можно найти в папке scripts/.
Свои классы
Для более элегантной работы с библиотекой лучше реализовывать собственные классы
вместо DefaultIntegration, DefaultJsonOperation и других дефолтных реализаций.
Интерфейсы Integration, AsyncOperation, SyncOperation и BulkOperation
как раз для этого и предусмотрены.
CustomIntegration
Вместо DefaultIntegration можно описать своё поведение -
например, читать deviceUuid из cookie:
use Antonowano\Mindbox\Integration; readonly class CustomIntegration implements Integration { public function endpointId(): string { return env('MINDBOX_ENDPOINT_ID'); } public function secretKey(): string { return env('MINDBOX_SECRET_KEY'); } public function defaultPrefix(): string { return env('MINDBOX_DEFAULT_PREFIX'); } public function deviceUuid(): ?string { return $_COOKIE['mindboxDeviceUUID'] ?? null; } }
ViewProduct
Вместо передачи массива в DefaultJsonOperation можно инкапсулировать операцию
в отдельный класс:
use Antonowano\Mindbox\AsyncOperation; readonly class ViewProduct implements AsyncOperation { public function __construct( private int $productId, ) { } public function name(string $defaultPrefix): string { return $defaultPrefix . 'ViewProduct'; } public function needDeviceUuid(): bool { return true; } public function requestData(): array { return [ 'viewProduct' => [ 'productGroup' => [ 'ids' => [ 'brandProducts' => $this->productId, ], ], ], ]; } }
Использование:
$data = $apiClient->async(new ViewProduct($productId));
Такой подход делает вызовы API читаемее: в код передаются доменные данные, а детали формирования запроса остаются в классе операции.
Установка на Laravel
1. Установка пакета
composer require antonowano/mindbox
Отдельно ставить Guzzle и логгер не нужно: в Laravel уже есть Guzzle (через фреймворк) и PSR-3 логгер (Log).
2. Переменные окружения
Добавьте в .env:
MINDBOX_ENDPOINT_ID=company_endpoint_web MINDBOX_SECRET_KEY=secret_key MINDBOX_DEFAULT_PREFIX=Website.
3. Настройка конфига
Добавьте секцию mindbox в config/services.php:
'mindbox' => [ 'endpoint_id' => env('MINDBOX_ENDPOINT_ID'), 'secret_key' => env('MINDBOX_SECRET_KEY'), 'default_prefix' => env('MINDBOX_DEFAULT_PREFIX'), ],
После изменения .env в production не забудьте пересобрать кэш конфигурации:
php artisan config:cache
В коде значения читаются через config('services.mindbox.*'), а не через env() напрямую.
4. Регистрация в контейнере
В AppServiceProvider (или отдельном сервисе-провайдере):
use Antonowano\Mindbox\ApiClient; use Antonowano\Mindbox\DefaultIntegration; use Antonowano\Mindbox\Integration; use Antonowano\Mindbox\MindboxApiClient; use Antonowano\Mindbox\MindboxEndpointUrl; use Antonowano\Mindbox\SilentApiClient; use GuzzleHttp\Client; use GuzzleHttp\Psr7\HttpFactory; use Illuminate\Support\Facades\Log; public function register(): void { $this->app->singleton(Integration::class, function () { return new DefaultIntegration( endpointId: config('services.mindbox.endpoint_id'), secretKey: config('services.mindbox.secret_key'), defaultPrefix: config('services.mindbox.default_prefix'), deviceUuid: $_COOKIE['mindboxDeviceUUID'] ?? null, ); }); $this->app->singleton(ApiClient::class, function ($app) { $integration = $app->make(Integration::class); $httpFactory = new HttpFactory(); $client = new MindboxApiClient( integration: $integration, endpointUrl: new MindboxEndpointUrl($integration), httpClient: new Client(), requestFactory: $httpFactory, streamFactory: $httpFactory, ); return $client; // или, если никогда не хотите обрабатывать Exception, тогда так: // return new SilentApiClient($client, Log::channel()); }); }
Если вы не хотите обрабатывать исключения с помощью try..catch,
тогда оберните зарегистрированный клиент классом SilentApiClient.
Все Exception будут направлены в логгер, а само приложение не упадёт.
public function product(int $productId, ApiClient $client) { $client = new SilentApiClient($client, Log::channel()); $client->async(new ViewProduct($productId)); }