Ozon Доставка для WooCommerceнезависимая разработка, не продукт Ozon Закрыть
Установить и настроить · Для техлида

Архитектура и безопасность плагина «Ozon Доставка для WooCommerce»

Страница для техлида и разработчика, который решает, ставить ли плагин на рабочий магазин. Что плагин хранит в базе сайта, сколько запросов делает на оформлении заказа, какие фоновые задачи запускает, что передаёт на сервер лицензий и как защищены ключи и журналы.

Коротко

ЧтоКак устроено
Справочник пунктовСвоя таблица {prefix}ozwc_points: около 88 тысяч строк, индексы по координатам, городу и номеру прохода синхронизации
Окно выбора пунктаПоиск, «Рядом со мной» и карта работают только с этой таблицей, без запросов к Ozon
Запросы к Ozon на оформленииРасчёт цены и проверка пункта; ответы кэшируются в сессии WooCommerce на 20 минут, таймаут 4 секунды
Фоновые задачиAction Scheduler WooCommerce, группа ozon-delivery-woo
Сервер лицензийНа оформлении заказа не вызывается; ответы подписаны Ed25519
Без сервера лицензийДо 14 дней по последнему подписанному статусу годовой лицензии
Ключи и журналыКлючи — в опциях без автозагрузки, в HTML и журналах их нет; телефоны в журналах маскируются
ЗаказыТолько CRUD-методы WooCommerce, совместимость с HPOS и блочным оформлением объявлена

Таблица пунктов выдачи и индексы

Ozon не отдаёт пункты по городу и не даёт готовой карты, поэтому справочник хранится в базе сайта. Таблица создаётся при активации через dbDelta с кодировкой базы WordPress:

CREATE TABLE {prefix}ozwc_points (
  point_id bigint(20) unsigned NOT NULL, -- номер пункта Ozon, 16 цифр
  type varchar(16), name varchar(191), point_number varchar(64),
  address varchar(500), city varchar(191),
  lat decimal(9,6), lon decimal(9,6),
  is_bulky tinyint(1), storage_days smallint(5) unsigned, fitting_rooms smallint(5) unsigned,
  schedule varchar(255), -- недельный график одной строкой
  max_weight_g int(10) unsigned, max_length_mm int(10) unsigned,
  max_width_mm int(10) unsigned, max_height_mm int(10) unsigned,
  max_price decimal(12,2), min_weight_g int(10) unsigned, min_price decimal(12,2),
  sync_run int(10) unsigned, -- номер прохода синхронизации
  info_at int(10) unsigned, -- когда обновлялись подробности
  PRIMARY KEY (point_id),
  KEY geo (lat, lon),
  KEY city (city),
  KEY sync_run (sync_run)
);
  • geo (lat, lon) — поиск ближайших пунктов и пунктов в области карты.
  • city — подсказка для города покупателя до выбора пункта: сколько пунктов и примерная цена.
  • sync_run — удаление пунктов, которых больше нет в Ozon, после завершённого прохода.

В строке — только то, что нужно окну выбора: адрес, город, координаты, срок хранения, примерочные, график и пределы веса, размеров и стоимости. Исходный ответ Ozon (около 2 КБ на пункт) не хранится, график Ozon (даты примерно на три недели вперёд) сжимается в недельную строку. В таблицу попадают только активные пункты, куда возит ваш метод доставки. Имя таблицы можно сменить фильтром ozwc_points_table — он нужен только для тестов, чтобы тест не тронул рабочий справочник.

Как обновляется справочник

ЧтоКак
КогдаРаз в сутки ночью, между 00:20 и 02:20 UTC; минута зависит от адреса сайта, чтобы магазины не приходили к Ozon одновременно
Источникdelivery-point/list — только номера пунктов и методы доставки, по 100 на страницу; delivery-point/info — подробности пачками до 100
ОбъёмПолный проход по list; info — только для новых пунктов и тех, чьи данные старше 7 дней, не больше 15 000 за проход
УдалениеПункты, которых нет в list, удаляются только после завершённого прохода. Пока проход идёт, покупатели видят прежний справочник
ШагиЗадачи Action Scheduler до 15 секунд; состояние и курсор сохраняются после каждой страницы, от параллельного запуска защищает блокировка
ОшибкиВременные ошибки — до 5 повторов с паузой. Если Ozon отвечает 404 на пачку из-за пропавших пунктов, плагин убирает их из пачки и повторяет запрос
Таймаутыlist — 4 секунды, info — 6 секунд

Первая загрузка занимает 5–15 минут: пока открыта вкладка «Пункты выдачи», загрузка идёт быстрее, если закрыть — продолжится в фоне.

Одно оформление заказа: запросы к Ozon и к базе

МоментЗапросы к базе сайтаЗапросы к Ozon
Покупатель на оформлении, пункт ещё не выбранОдна сводка по городу: WHERE city = … с фильтром по посылкеЕсли указан телефон — order/checkout для одного пункта города: примерная цена и срок
Окно выбора: «Рядом со мной», поиск, картаБлижайшие — по индексу geo, от 1 до 5 запросов с расширяющейся областью, не больше 2000 строк в каждом; поиск — по словам в городе, адресе и номере пункта; карта — пункты видимой области, а если их больше 2000, сервер сам собирает их в группыНет
Покупатель выбрал пунктСтрока пункта по первичному ключуdelivery-point/check-availability и order/checkout
Пересчёт оформления, смена адреса—Нет: ответ берётся из кэша сессии
«Оформить заказ»—order/checkout заново, только если расчёту больше 5 минут
После оплаты—order/create — в фоновой задаче, а не в запросе покупателя

Как устроен кэш и защита:

  • Кэш расчёта — в сессии WooCommerce покупателя, а не в кэше объектов WordPress. Ключ — пункт, вес, размеры и объявленная стоимость посылки, телефон и метод доставки. Запись живёт 20 минут, в сессии хранится не больше 20 записей.
  • Таймауты на оформлении — 4 секунды и один повтор. Любая ошибка Ozon превращается в понятное сообщение покупателю или скрытый способ доставки — оформление заказа не падает.
  • Токен xapi.ozon.ru/oauth/token хранится в базе и обновляется за 5 минут до истечения; при ответе 401 плагин один раз получает новый токен и повторяет запрос.
  • Защита Ozon от DDoS отвечает перенаправлением 302 или 307 с cookie: плагин повторяет тот же POST по адресу из Location, не больше 3 перенаправлений, и хранит cookie по хосту.
  • Лимит номеров: не больше 5 разных номеров телефона на сессию и 15 на IP-адрес в час — сайт нельзя использовать для массовой проверки чужих номеров в Ozon. Счётчик по IP хранится во временных данных WordPress (transient): если на сайте подключён внешний кэш объектов, например Redis, WordPress хранит их там.
  • Сервер лицензий на оформлении заказа не вызывается: плагин читает сохранённый статус из небольшой опции с автозагрузкой.

Фоновые задачи Action Scheduler

Все задачи — в группе ozon-delivery-woo планировщика Action Scheduler, который входит в WooCommerce и работает поверх WP-Cron. Если сайт посещают редко, настройте системный cron.

ХукКогдаЧто делает
ozwc_points_sync_dailyРаз в сутки, 00:20–02:20 UTCНачинает проход по справочнику
ozwc_points_sync_stepШагами до 15 секундЗагружает страницы list и info
ozwc_order_createПосле оплаты; при сбое — повторы через 1, 5 и 15 минут, 1 и 3 часаСоздаёт заказ в Ozon с защитой от дублей
ozwc_order_cancelЗаказ на сайте стал «Отменён» или «Возвращён»Отменяет отправление в Ozon
ozwc_order_refreshПосле «Собрано» и других действий с отправлениемОбновляет статус одного заказа
ozwc_orders_syncКаждые 30 минутОбновляет статусы отправлений
ozwc_returns_checkКаждые 12 часовОбновляет возвраты
ozwc_license_checkinРаз в сутки, минута зависит от сайтаПроверяет лицензию
ozwc_license_orderПосле создания отправления; при сбое связи — повторы с паузами от минуты до сутокСообщает серверу лицензий хэш заказа
ozwc_license_order_cancelПосле отмены отправления до отгрузкиОтмечает отмену на сервере лицензий

Деактивация плагина останавливает фоновые задачи; таблица, настройки и данные заказов остаются.

Что уходит на сервер лицензий

Сервер лицензий — https://api.ev-gerasimov.ru. Он нужен для пробного периода, проверки лицензии и обновлений. Таймаут запроса — 8 секунд, в каждый запрос плагин добавляет одноразовый nonce.

ЗапросКогдаЧто передаётся
/api/v1/checkinВскоре после активации, затем раз в суткиСлучайный номер установки, домен и адрес сайта, версии плагина, WordPress, WooCommerce и PHP, ключ лицензии и метка сборки, число отправлений
/api/v1/activateПри вводе ключаКлюч, номер установки, домен и адрес сайта
/api/v1/deactivateПо кнопке «Отвязать сайт»Ключ, номер установки, домен
/api/v1/orderПосле создания отправленияНомер установки, домен, ключ, метка сборки и order_ref = sha256(домен + номер заказа + соль установки)
/api/v1/order-cancelПосле отмены отправления до приёма в пункте отгрузкиТо же, с тем же order_ref
ОбновленияКогда WordPress проверяет обновленияСсылку на архив плагин запрашивает у сервера прямо перед скачиванием

Имена, телефоны, адреса, e-mail, суммы и состав заказов покупателей на сервер не передаются; IP-адрес сайта сервер видит при запросе. Тот же перечень — в п. 12.5 оферты и на вкладке «Лицензия» в плагине.

Подпись ответов: Ed25519

  • Все JSON-ответы сервера лицензий, включая ошибки, подписаны Ed25519. Поле sig — подпись канонического JSON без самого sig: ключи объектов отсортированы на всех уровнях, списки — в исходном порядке, без пробелов, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE.
  • Плагин проверяет подпись функцией sodium_crypto_sign_verify_detached, а если расширения sodium нет — через sodium_compat из WordPress.
  • Ответ принимается, только если подпись верна и в нём те же номер установки, домен и nonce, что в запросе: старый ответ подставить нельзя.
  • Адрес сервера и открытый ключ зашиты в сборку плагина и из сети не загружаются. Открытый ключ (base64, 32 байта) — DQk2vmKAOr3sJXAlaU6uUbmFOxO78EiuAO5w4CpoAIA=; его же отдаёт сервер по адресу https://api.ev-gerasimov.ru/api/v1/pubkey.

Если сервер лицензий недоступен

  • Оформление заказов от сервера не зависит: статус лицензии хранится на сайте.
  • Годовая лицензия работает по последнему подписанному статусу до 14 дней, бессрочная — без ограничения срока.
  • Пробные заказы, пока сервер молчит, считаются на сайте, а отчёты о них досылаются, когда связь вернётся.
  • Если проверки не было больше 3 дней, в админке появляется предупреждение о связи.
  • Через 14 дней без связи годовая лицензия считается непроверенной: работает остаток пробного периода, если он есть, иначе способ Ozon скрывается на оформлении. Уже созданные отправления доводятся до конца.

Ключи, журналы и персональные данные

  • Ключи Ozon. Настройки плагина — в опции без автозагрузки, Client Secret — в отдельной опции без автозагрузки, токен доступа — тоже. Ключей Ozon нет в HTML, JavaScript и журналах: все запросы к Ozon идут с сервера сайта.
  • Ключи карт. Публичные ключи Яндекс.Карт и 2ГИС попадают в код страницы оформления, только если выбранная карта может понадобиться. Ограничьте их адресом сайта в кабинетах этих сервисов. Для DaData нужен только публичный ключ.
  • Журнал — WooCommerce → Статус → Журналы, источник ozon-delivery-woo. Перед записью маскируются заголовок Bearer …, поля client_secret, access_token, refresh_token, любые поля, в названии которых есть secret, token, authorization, password или cookie, и российские телефоны в любом формате — от номера остаются код оператора и последние две цифры. Отладочные сообщения пишутся, только если включено подробное логирование. Сроком хранения журналов управляет WooCommerce.
  • Данные в Ozon — ФИО и телефон получателя, выбранный пункт, вес, размеры и объявленная стоимость посылки и описание без персональных данных (по шаблону с {order} и {site}).
  • Мета заказа — номер и адрес выбранного пункта, номер отправления, статус и история статусов, без списка всех пунктов города.
  • Удаление. Деактивация данные не удаляет. Удаление плагина стирает настройки, справочник и данные лицензии на сайте, только если на вкладке «Журнал и данные» отмечено «Удалить настройки и справочник пунктов». Данные заказов не удаляются никогда.

Совместимость: HPOS, блоки, PHP

  • HPOS. Плагин работает с заказами только через CRUD-методы WooCommerce и объявляет совместимость с HPOS (custom_order_tables). Включён ли HPOS, определяется через OrderUtil в try/catch.
  • Блочное оформление. Совместимость с cart_checkout_blocks объявлена, данные корзины идут через Store API (extensions.ozwc), без отдельной сборки JavaScript. Подробно — в статье Блочное и классическое оформление.
  • PHP 7.4–8.3, 64-битный. Номера Ozon 16-значные. На 32-битном PHP плагин не включается и пишет об этом в админке, сайт работает как раньше.
  • Код — без обфускации. Дорабатывать его под свой сайт можно, кроме проверки лицензии, подсчёта пробных отправлений, передачи технических данных и метки сборки (п. 4.7 и 4.10 оферты).

Вопросы техлида

Нагрузит ли плагин базу и Ozon на распродаже?

Окно выбора пункта работает только со своей таблицей: поиск ближайших пунктов в справочнике на 88 тысяч строк занимает несколько миллисекунд. Запросы к Ozon на оформлении — расчёт цены и проверка выбранного пункта, повторные пересчёты берут ответ из кэша сессии.

Нужен ли Redis или другой кэш объектов?

Нет. Кэш расчёта хранится в сессии WooCommerce, а счётчик номеров по IP — во временных данных WordPress: с внешним кэшем объектов они хранятся в нём, без него — в базе сайта.

Что будет с оформлением, если Ozon не отвечает?

Через 4 секунды и один повтор покупатель увидит сообщение, а остальные способы доставки работают. Ozon иногда «зависает» на отдельных запросах, поэтому таймауты короткие, а фоновые задачи повторяют запросы сами.

Как проверить, что ответ пришёл от настоящего сервера лицензий?

Сверьте открытый ключ из раздела «Подпись ответов» с ответом https://api.ev-gerasimov.ru/api/v1/pubkey. Плагин принимает только ответы с верной подписью этим ключом.

Читайте дальше

Ozon Доставка на вашем сайте

Скачайте плагин или доверьте подключение мне

Первые 50 заказов через плагин — бесплатно, дальше ключ лицензии — 19 990 ₽ в год за сайт, оптом дешевле. Под ключ: разбор сайта, помощь с кабинетом Ozon, настройка и приёмка тестовым заказом.

Ozon и Ozon Доставка — товарные знаки их правообладателя. Плагин — независимая разработка: он работает через официальный Ozon Delivery API и не является продуктом Ozon. Тарифы и условия Ozon могут меняться — актуальные смотрите в кабинете Ozon Доставки.