Тема
Интеграции с внешними сервисами
Краткий обзор РФ-сервисов — в ru-specific.md; здесь — детали интеграций, паттерны реализации и разбор конкретных пакетов. Рамка та же: официальный PHP SDK сервиса + тонкая своя интеграция надёжнее толстой community-обёртки laravel-*, брошенной автором — в интеграциях это особенно критично, т.к. на кону деньги (платежи) или блокировка обмена (1С).
Платёжные системы
Паттерн интеграции
Не подключать SDK каждого шлюза напрямую из контроллера — доменный интерфейс + реализации:
php
interface PaymentGateway
{
public function createPayment(Money $amount, string $orderId, string $returnUrl): PaymentResult;
public function handleWebhook(Request $request): WebhookResult;
}
// app/Services/Payment/YooKassaGateway.php implements PaymentGateway
// app/Services/Payment/TinkoffGateway.php implements PaymentGatewayВыбор реализации — через config('payment.default_gateway') + binding в AppServiceProvider, не через if ($gateway === 'yookassa') по всему коду.
Идемпотентность вебхуков — обязательна, шлюзы ретраят доставку при таймауте ответа:
php
Schema::create('payment_events', function (Blueprint $table) {
$table->id();
$table->string('event_id')->unique(); // ID события от шлюза, не свой UUID
$table->string('gateway'); // yookassa, tinkoff, robokassa...
$table->foreignId('payment_id')->constrained()->cascadeOnDelete();
$table->string('type'); // payment.succeeded, payment.canceled...
$table->json('payload');
$table->timestamp('processed_at')->nullable();
$table->timestamps();
});Обработчик: firstOrCreate(['event_id' => $eventId]), если запись уже была — выйти без повторной обработки (processed_at не null). Без этого двойное списание/начисление бонусов при ретрае вебхука — реальный баг, не гипотетический.
Проверка подписи — до чтения тела как доверенных данных. Каждый шлюз — свой алгоритм (HMAC-SHA256 у одних, IP-whitelist + пароль у Robokassa), логика — в handleWebhook() конкретной реализации, не размазана по middleware.
Разбор по шлюзам
| Шлюз | SDK | Вебхуки | Особенности |
|---|---|---|---|
| ЮKassa | yoomoney/yookassa-sdk-php — официальный, активно поддерживается (релиз 3.14.0, PHP ≥8.0) | HTTP callback, подпись через IP-whitelist ЮKassa + HTTPS; событие payment.succeeded/payment.waiting_for_capture | Двухстадийный платёж (hold → capture) для маркетплейсов/бирж; фискализация чека — в теле запроса создания платежа (receipt) |
| Т-Касса / Т-Банк (бывш. Тинькофф) | Официальный REST API (Init/Confirm/Cancel), community-SDK нестабильны — чаще пишут тонкий клиент на Guzzle за несколько часов | Webhook с полем Token — подпись как HMAC от отсортированных параметров + терминальный пароль | Двухстадийная оплата поддерживается нативно; хорошая песочница для тестов |
| Robokassa | Официальных SDK нет — протокол простой (MD5/SHA-подпись из строки параметров), свой клиент | ResultURL (server-to-server, обязательный источник истины) + SuccessURL/FailURL (только редирект юзера, не источник статуса) | Частая ошибка — доверять SuccessURL: пользователь может закрыть вкладку до перехода, статус подтверждает только ResultURL |
| CloudPayments | Официальный PHP SDK (cloudpayments/php-sdk либо клиент по REST) | Подпись Content-HMAC заголовком, секретный ключ из ЛК | Виджет на фронте создаёт крипто-токен карты — сервер никогда не видит PAN; хорош для рекуррентов |
| Сбербанк-эквайринг (АО «Сбербанк») | Официальный REST API (register.do/getOrderStatus.do), SDK community устаревшие — свой клиент | Callback опционален, чаще опрашивают getOrderStatus.do по расписанию (poll вместо push) | Отдельная песочница 3dsec.sberbank.ru; медленнее внедрять, чем ЮKassa |
| СБП напрямую | Обычно не подключают напрямую (сертификация НСПК) — через ЮKassa/Т-Кассу как СБП-агрегатор | — | Прямое подключение имеет смысл только при большом обороте — экономия на комиссии перевешивает сложность сертификации |
league/omnipay — единый интерфейс к десяткам шлюзов (Stripe, PayPal, часть РФ через community-драйверы). Плюс — не переписывать интерфейс при смене шлюза; минус — РФ-драйверы разного качества и обновляются медленнее официальных SDK. Оправдан, когда проект таргетирует несколько стран с разными шлюзами; для одного РФ-шлюза официальный SDK + свой PaymentGateway проще и прозрачнее в отладке.
laravel/cashier (Stripe/Cashier Paddle) — только для зарубежных заказчиков. Stripe и Paddle не работают с российскими юрлицами и картами — не рассматривать для РФ-проектов.
Рекуррентные платежи и подписки
- Токен карты (recurrent/rebill ID) хранить в отдельной таблице
payment_methods, не вusers/orders— один пользователь может иметь несколько привязанных карт, токен привязан к конкретному шлюзу и не переносится при смене провайдера. - Первый платёж — с явным согласием пользователя на автосписания (текст в форме, чекбокс, таймстемп согласия — для разбора спорных списаний). Каждый шлюз (ЮKassa, Т-Касса, CloudPayments) поддерживает рекуррент через флаг
save_payment_method/аналог при первом платеже + отдельный метод для списания по токену. - Автосписания — через
Jobпо расписанию (schedule:work), не синхронно; статус — событие в тот жеpayment_events, чтобы обработка была той же идемпотентной веткой кода.
54-ФЗ — фискализация
- ЮKassa и Т-Касса фискализируют сами — чек передаётся в теле запроса создания платежа (
receiptс позициями, ставками НДС, предметом расчёта). Дополнительная интеграция не нужна — экономит недели работы. - Если платёжный провайдер чек не формирует (прямой эквайринг, СБП напрямую) — отдельный ОФД: АТОЛ Онлайн (
atol/php-sdk-подобные официальные клиенты от АТОЛ) или OrangeData (официальный PHP SDK от вендора). Оба требуют отдельного заключения с ОФД и кассой (или облачной кассой провайдера). - Модель позиции заказа/платежа должна с самого начала содержать поле «предмет расчёта» (
payment_subject: товар/услуга/платёж и т.д.) и ставку НДС — добавлять постфактум больнее, чем заложить миграцией сразу (см.specs/migrations.mdпро enum-столбцы).
Сервисы Яндекса
| Сервис | Интеграция | Заметка |
|---|---|---|
| Яндекс.Карты (фронт) | JS API v3, <script> с API-ключом | Бесплатный лимит запросов/сутки; для карточек организаций, форм с картой |
| Leaflet + OSM | league/* не нужен — чистый JS (leaflet.js) + бесплатные тайлы OSM/провайдеров | Полностью бесплатная альтернатива без лимитов и ключа; хуже детализация в РФ, чем у Яндекса |
| Яндекс Геокодер | HTTP API (geocode-maps.yandex.ru), свой клиент на Guzzle | Для адрес→координаты в РФ обычно достаточно DaData (см. ru-specific.md); Геокодер — когда нужны именно Яндекс-координаты (совместимость с Яндекс.Картами на фронте) |
| Яндекс.Маркет — фид (YML) | Свой генератор XML (XMLWriter, потоково) | Зрелого Laravel-пакета под YML нет; формат простой (offer/price/categoryId), полдня на генератор — см. паттерн генерации ниже |
| Яндекс.Маркет — Партнёрский API | Официальный REST API (Campaigns API) для остатков/цен/заказов, свой клиент | Отдельно от фида — фид для листинга, API для операционки (заказы, остатки в реальном времени) |
| Яндекс.Доставка | REST API, свой клиент | См. ru-specific.md → Доставка |
| Яндекс ID (OAuth) | socialiteproviders/yandex (community-провайдер под Socialite) | Стандартный Socialite-паттерн, провайдер поддерживается активно |
| Яндекс.Метрика — счётчик | Тег на фронте (mc.yandex.ru/metrika/tag.js) | Клиентская аналитика, без бэкенд-интеграции |
| Яндекс.Метрика — Reporting API | Официальный HTTP API, свой клиент | Для серверных отчётов (дашборды в админке без захода в интерфейс Метрики) |
| Yandex Object Storage | league/flysystem-aws-s3-v3 (S3-совместимый) | Просто другой endpoint в конфиге диска — см. packages.md → Медиа и файлы |
| YandexGPT | — | Кратко: доступен через Yandex Cloud API (аналог OpenAI-совместимого интерфейса); детали интеграции LLM — ai-llm.md |
Сервисы Google
| Сервис | Интеграция | Заметка |
|---|---|---|
| Google Sheets | revolution/laravel-google-sheets — проверено: активно поддерживается, версия 7.2.0 (релиз февраль 2026), требует PHP ^8.3 и illuminate/support ^12.0|^13.0. Не abandoned | Кейс: выгрузка заявок из формы в таблицу для клиента без доступа к БД — Job раз в N минут догружает новые записи. Альтернатива — google/apiclient (официальный) напрямую, если нужен только простой append без Laravel-обёрток |
| Google Tables/Sheets — офиц. клиент | google/apiclient | Тяжелее в настройке (OAuth service account, JSON-ключ), но всегда актуален под новый Sheets API — работает и когда community-пакет отстаёт |
| Google Merchant / Shopping (фид) | spatie/laravel-feed как база или свой XML по спецификации Google Merchant | Формат близок к RSS/Atom — spatie/laravel-feed экономит на структуре, но кастомные поля Merchant (g:price, g:availability) всё равно пишутся вручную |
| Google Analytics 4 — счётчик | gtag.js на фронте | Клиентская аналитика |
| Google Analytics 4 — серверные отчёты | spatie/laravel-analytics — проверено: активно поддерживается, версия 5.7.1 (релиз апрель 2026), требует PHP ^8.3, Laravel ^12.40.1|^13.0. Не abandoned | Пакет уже переведён на GA4 (google/analytics-data), старый Universal Analytics не поддерживает — не актуально для новых интеграций и не нужно |
| Google Maps / Geocoding | spatie/geocoder или прямой google/apiclient | Для РФ обычно хуже, чем Яндекс/DaData (точность адресов, цена лимитов) — см. ru-specific.md → Поиск и карты; рассматривать Google Maps только если у проекта иностранная аудитория |
| Google reCAPTCHA | — не использовать | Нестабильна в РФ (блокировки/задержки виджета) → Cloudflare Turnstile, hCaptcha — см. packages.md → Формы |
| Google OAuth | laravel/socialite (first-party, провайдер Google встроен) | Стандартный вход через Google-аккаунт |
Импорт / экспорт / синхронизация
Форматы файлов — когда что
| Формат/задача | Пакет | Когда |
|---|---|---|
| Excel с формулами, стилями, множеством листов | maatwebsite/excel | Админка формирует отчёт для бухгалтерии — нужны стили, форматирование ячеек |
| Большие выгрузки (100k+ строк), ETL | spatie/simple-excel | Потоковая запись/чтение — не держит весь файл в памяти, критично при импорте каталогов |
| CSV без Excel-специфики | league/csv | Максимальный контроль над форматом, нет зависимости от PhpSpreadsheet |
Детали пакетов — packages.md. Здесь — принцип выбора: maatwebsite/excel для «человек открывает в Excel», spatie/simple-excel/league/csv для машинного ETL, где важна память и скорость, а не форматирование.
Обмен с 1С (CommerceML 2)
Протокол обмена «1С-Битрикс: Управление сайтом» / стандартный CommerceML-обмен через HTTP-запросы к /1c_exchange.php-подобному endpoint'у с параметрами type и mode:
checkauth— проверка логина/пароля, возвратcookie name/valueили сессииinit— отдача параметров (zip=no,file_limit)file— приём файлов частями (import.xml,offers.xml, картинки) черезPUT-подобную запись потока вstorage/1c-exchange/import— разбор присланных XML, апсерт каталога/остатков в БД
Реализуется собственным контроллером за 1–2 дня — сам протокол несложный, основная работа в парсинге XML большого объёма (XMLReader потоково, не SimpleXML на весь файл при каталогах 10k+ товаров).
Пакеты — проверено через Packagist, картина хуже, чем можно было ожидать:
| Пакет | Статус |
|---|---|
zenwalker/commerceml (не php-commerceml — пакета с таким именем на Packagist нет) | 🔴 Мёртв: последний релиз 2015-08-24, 3 загрузки/месяц. Не брать — писать свой парсер на XMLReader |
mavsan/laravel-1c-protocol | 🟢 Активен: релиз 11.0.12 от 2025-12-08, несколько поддерживаемых веток (v5.x-dev, v7.x-dev). Реализует именно протокол обмена (checkauth/init/file/import) как Laravel-пакет — лучший кандидат из готовых, если не писать свой контроллер |
bigperson/laravel-exchange1c, sv1ft/exchange1c, imrev-agency/exchange1c (форки одной линии) | Не проверялись детально — набор форков одного пакета «Catalog Loader from 1c»; при выборе проверять дату последнего релиза и открытые issues перед внедрением, паттерн форков часто означает заброшенный апстрим |
Вывод: zenwalker/php-commerceml, который иногда всплывает в старых статьях/памяти как рекомендация — миф или устаревшее название, реального живого пакета под этим именем нет. Если брать готовое, а не свой парсер — mavsan/laravel-1c-protocol единственный, кто подтверждённо жив на июль 2026. При сомнении — свой контроллер надёжнее любого из форков.
МойСклад
Официальный REST API (JSON, хорошая документация, OAuth2 или пары логин/пароль) — свой клиент на Guzzle. Часто выбирается вместо прямого обмена с 1С, когда у клиента МойСклад как основная учётная система, а 1С нет вообще.
Маркетплейсы: Wildberries, Ozon, Яндекс.Маркет
Партнёрские API есть у всех трёх (остатки, цены, заказы, статусы отгрузки) — зрелых Laravel- пакетов под них обычно нет или они узкоспециализированные и быстро устаревают вслед за частыми изменениями API маркетплейсов. Паттерн:
- Свой HTTP-клиент на Guzzle под каждый маркетплейс (
WildberriesClient,OzonClient) — тонкая обёртка методов API, без попытки покрыть всё API сразу, только используемые эндпоинты Jobпо расписанию (schedule:work) синхронизирует остатки/цены в одну сторону (сайт → МП) или в обе, если МП — источник заказов- Таблица sync-состояния для отслеживания и отладки рассинхронов:
php
Schema::create('marketplace_sync_logs', function (Blueprint $table) {
$table->id();
$table->string('marketplace'); // wildberries, ozon, yandex_market
$table->string('entity_type'); // stock, price, order
$table->string('external_id')->nullable();
$table->enum('status', ['pending', 'success', 'failed']);
$table->text('error')->nullable();
$table->timestamp('synced_at')->nullable();
$table->timestamps();
$table->index(['marketplace', 'entity_type', 'status']);
});Без такой таблицы рассинхрон остатков (продали на сайте, на маркетплейсе висит «в наличии») обнаруживается только по жалобе клиента — с таблицей это плановая проверка status = failed.
Генерация фидов (YML, Google Merchant, Авито) — паттерн
Для каталогов от нескольких тысяч позиций — не собирать фид в память Blade-шаблоном (view()->render() на 50k товаров = OOM), а писать потоково через XMLWriter напрямую в response stream:
php
return response()->streamDownload(function () {
$writer = new XMLWriter();
$writer->openURI('php://output');
$writer->startDocument('1.0', 'UTF-8');
$writer->startElement('yml_catalog');
// ...
Product::query()->where('is_active', true)
->lazy(500) // chunk без держания всей коллекции в памяти
->each(fn ($product) => /* $writer->startElement('offer'), ... */ null);
$writer->endElement();
$writer->endDocument();
echo $writer->outputMemory(true);
}, 'yml.xml', ['Content-Type' => 'application/xml']);Ключевое — lazy()/chunk() вместо get() на больших каталогах (см. specs/code-quality.md про массовые операции) и XMLWriter вместо строковой конкатенации/DOM в памяти. Детали конкретных фидов (YML-схема, обязательные поля) — см. ru-specific.md.
Общий чеклист перед интеграцией
- Официальный SDK есть? → брать его, не community-обёртку под Laravel
- Community-пакет — проверить
abandonedв Packagist API, дату последнего релиза, monthly downloads, поддержку текущего PHP/Laravel — как в разборе CommerceML-пакетов выше - Вебхуки — подпись проверяется до обработки, идемпотентность через таблицу событий с уникальным
event_id - Секреты (API-ключи, токены шлюзов) — только
.env→config(), никогда в коде (см.specs/anti-hardcode.md) - Внешний API может быть недоступен/медленным — синхронный вызов из контроллера только для быстрых операций (создание платежа); тяжёлое/ненадёжное — в
Jobс$tries/backoff/failed()(см.specs/services.md)