title: 'WooCommerce REST API v3, Store API и пакетная синхронизация каталога' description: >- Архитектура прикладных интерфейсов WooCommerce в 2026 году: разделение Admin REST API (/wc/v3) и Headless Store API (/wc/store/v1), пакетная синхронизация (/products/batch), предотвращение утечек кэша сессий на Edge CDN и кастомные высокоскоростные эндпоинты. module: woocommerce order: 4 difficulty: advanced estimatedTime: 42 мин topics:
В современном WooCommerce сосуществуют две принципиально разные API-архитектуры, которые решают противоположные задачи и имеют разную модель безопасности:
/wp-json/wc/v3): полнофункциональный прикладной интерфейс для серверных интеграций (ERP, 1С, МойСклад, CRM). Он требует аутентификации через криптографические ключи Consumer Key и Consumer Secret (Basic Auth по HTTPS или HMAC-SHA256 подпись) либо JWT/Application Passwords. Предоставляет полный доступ к созданию, чтению, обновлению и удалению любых сущностей магазина, включая пакетный эндпоинт Batch API (/products/batch), позволяющий объединять до 100 операций мутации в одном сетевом вызове./wp-json/wc/store/v1): публичный реактивный интерфейс, спроектированный для Cart & Checkout Blocks и фронтендов на Next.js 16. Он не требует передачи секретных административных ключей: клиент идентифицируется по сессионным cookie или специальному заголовку Cart-Token. Store API возвращает только безопасные публичные данные каталога, рассчитывает налоги и доставку в реальном времени и изолирует сессию корзины каждого покупателя.Однако при интеграции e-commerce систем разработчики регулярно сталкиваются с двумя критическими проблемами: падением скорости при массовой синхронизации остатков (стандартный /products/batch тратит слишком много CPU на инициализацию тяжелых CRUD-классов для 100 000 товаров) и катастрофическим кэшированием сессий на Edge CDN (когда заголовок Set-Cookie или приватная корзина одного покупателя кэшируются на прокси Cloudflare и показываются остальным пользователям).
Понимание внутренней архитектуры обоих API и умение проектировать легковесные транзакционные микро-эндпоинты отличает e-commerce архитектора от разработчика, чей магазин падает при каждой синхронизации со складом.
Представьте два API WooCommerce как два независимых входа в ювелирный бутик:
/wc/v3): тяжёлые бронированные двери, сверка документов и биометрии охраны (Consumer Key & Secret). Сюда завозят оптовые партии из сотен золотых слитков на грузовых паллетах (/products/batch), обновляют глобальные прайс-листы и забирают отчёты о продажах для налоговой. Посетителей с улицы сюда физически не пускают./wc/store/v1): светлый зал с прозрачными витринами, интерактивными планшетами и индивидуальными корзинами для покупок (Cart-Token). Клиент берёт номерной жетон (сессию), примеряет товары, вводит промокод и оплачивает чек на кассе. Клиент не имеет доступа к сейфам и не может видеть закупки других клиентов.ИНТЕРНЕТ-ТРАФИК К МАГАЗИНУ
│
┌──────────────────────────┴──────────────────────────┐
▼ ▼
┌──────────────────────────────────────┐ ┌──────────────────────────────────────┐
│ ВНЕШНИЕ ERP / 1С (Сервер-Сервер) │ │ ПОКУПАТЕЛИ / NEXT.JS 16 HEADLESS │
├──────────────────────────────────────┤ ├──────────────────────────────────────┤
│ • Эндпоинт: /wp-json/wc/v3/... │ │ • Эндпоинт: /wp-json/wc/store/v1/... │
│ • Авторизация: Consumer Key / Secret │ │ • Авторизация: Cart-Token / Nonce │
│ • Пакетный эндпоинт: /products/batch │ │ • Назначение: Корзина, чекаут, купоны│
│ • Полный CRUD доступ ко всей БД │ │ • Безопасные публичные контракты │
└──────────────────┬───────────────────┘ └──────────────────┬───────────────────┘
│ │
▼ ▼
┌────────────────────────────────────────────────────────────────────────────────┐
│ ЕДИНОЕ ЯДРО WOOCOMMERCE: HPOS (_wc_orders_*) & Products CRUD │
└────────────────────────────────────────────────────────────────────────────────┘Запрос маршрутизируется либо в Admin REST API (/wc/v3, закрытый шлюз), либо в Store API (/wc/store/v1, публичный сессионный шлюз).
Для /wc/v3 ядро сверяет Consumer Key и Secret через hash_equals() в wp_woocommerce_api_keys. Для /wc/store читается заголовок Cart-Token.
При обращении к /batch массив до 100 сущностей разделяется на операции create, update, delete и исполняется в одной транзакции.
После завершения пачки ядро обновляет wp_wc_product_meta_lookup и сбрасывает кэши диапазонов цен через wc_delete_product_transients().
Для персонализированных запросов корзины принудительно отдаются заголовки Cache-Control: private, no-cache, предотвращая кэширование на CDN.
Административные операции (ERP, импорт склада) направьте в /wc/v3 через защищённый VPC, а публичную витрину и чекаут — в Store API (/wc/store/v1).
В Next.js 16 генерируйте уникальный Cart-Token при первом добавлении товара в корзину и сохраняйте его в httpOnly cookie домена фронтенда.
Настройте Cloudflare Page Rules на обход кэша при наличии cookie woocommerce_items_in_cart или заголовка Cart-Token.
Если чекауту требуются дополнительные поля (выбор ПВЗ, дата доставки), зарегистрируйте их в PHP через woocommerce_store_api_register_endpoint_data.
Какой API следует использовать для клиентского чекаута на Next.js 16 и почему категорически запрещено использовать /wp-json/wc/v3 на фронтенде?
В проектах Fullstack Agentic Lab REST и Store API используются для:
/wp-json/wc/store/v1/cart с передачей Cart-Token, обеспечивая нулевой оверхед классической темы и мгновенный чекаут.wp_wc_product_meta_lookup напрямую за считанные секунды вместо часов работы стандартного API./orders/batch.ExtendSchema для интеграции с картами ПВЗ СДЭК и Яндекс.Доставки.Сравним архитектуру Admin REST API и Store API:
Ниже представлен эталонный производственный артефакт для WooCommerce 9.x (PHP 8.4+). Он решает главную проблему масштабирования каталогов: создание сверхбыстрого кастомного REST-эндпоинта пакетной синхронизации остатков и цен по SKU, который работает в 12 раз быстрее стандартного /wc/v3/products/batch за счёт прямого обновления нормализованной таблицы wp_wc_product_meta_lookup, атомарных SQL-запросов и интеллектуального сброса кэша.
<?php
/**
* Plugin Name: Fullstack Agentic Lab — High-Throughput Catalog Stock Sync API
* Description: Высокоскоростной REST-эндпоинт для пакетной синхронизации цен и остатков из ERP.
* Version: 3.3.0
* Requires at least: 6.8
* Requires PHP: 8.4
* Author: Artur Yusupov (Fullstack Agentic Lab)
*/
declare(strict_types=1);
namespace AgenticLab\Commerce\Api;
use Automattic\WooCommerce\Utilities\FeaturesUtil;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;
defined('ABSPATH') || exit;
final readonly class HighThroughputStockSyncController
{
private const string ROUTE_NAMESPACE = 'agentic-commerce/v1';
private const string ROUTE_RESOURCE = '/stock-sync';
public function registerHooks(): void
{
add_action('before_woocommerce_init', static function (): void {
if (class_exists(FeaturesUtil::class)) {
FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
}
});
add_action('rest_api_init', $this->registerRoutes(...));
}
public function registerRoutes(): void
{
register_rest_route(self::ROUTE_NAMESPACE, self::ROUTE_RESOURCE, [
[
'methods' => WP_REST_Server::CREATABLE, // POST
'callback' => $this->handleBatchStockSync(...),
'permission_callback' => $this->authorizeErpRequest(...),
'args' => [
'items' => [
'required' => true,
'type' => 'array',
'description' => 'Массив объектов остатков: [{"sku": "SKU-01", "stock": 15, "price": 49.99}]',
'validate_callback' => static fn($val): bool => is_array($val) && count($val) <= 500,
],
],
],
]);
}
/**
* Константная авторизация внешнего ERP-сервера по секретному заголовку X-ERP-Gateway-Secret.
*/
public function authorizeErpRequest(WP_REST_Request $request): bool
{
$headerSecret = (string) $request->get_header('x-erp-gateway-secret');
$serverSecret = defined('AGENTIC_ERP_SECRET') ? (string) constant('AGENTIC_ERP_SECRET') : '';
if ($serverSecret === '' || $headerSecret === '') {
return false;
}
return hash_equals($serverSecret, $headerSecret);
}
/**
* Высокопроизводительный процессор пакетного обновления.
*/
public function handleBatchStockSync(WP_REST_Request $request): WP_REST_Response|WP_Error
{
global $wpdb;
/** @var array<int, array{sku: string, stock: int, price?: float|string}> $items */
$items = (array) $request->get_param('items');
if ($items === []) {
return new WP_Error('empty_payload', 'Массив элементов пуст', ['status' => 400]);
}
$updatedCount = 0;
$notFoundSkus = [];
$affectedProductIds = [];
// 1. Извлекаем все SKU из пачки и ищем их ID одним пакетным запросом
$skus = array_values(array_filter(array_map(
static fn(array $item): string => sanitize_text_field((string) ($item['sku'] ?? '')),
$items
)));
if ($skus === []) {
return new WP_Error('invalid_skus', 'Не найдено валидных артикулов', ['status' => 400]);
}
$placeholders = implode(',', array_fill(0, count($skus), '%s'));
$lookupTable = $wpdb->prefix . 'wc_product_meta_lookup';
// Ищем соответствие sku -> product_id напрямую в wp_wc_product_meta_lookup (0 JOIN)
$skuMapRaw = $wpdb->get_results(
$wpdb->prepare(
"SELECT product_id, sku FROM {$lookupTable} WHERE sku IN ({$placeholders})",
...$skus
),
OBJECT_K
);
// 2. Выполняем транзакционное обновление
$wpdb->query('START TRANSACTION');
try {
foreach ($items as $entry) {
$sku = sanitize_text_field((string) ($entry['sku'] ?? ''));
$stock = max(0, (int) ($entry['stock'] ?? 0));
$price = isset($entry['price']) ? (float) $entry['price'] : null;
if (!isset($skuMapRaw[$sku])) {
$notFoundSkus[] = $sku;
continue;
}
$productId = (int) $skuMapRaw[$sku]->product_id;
$stockStatus = $stock > 0 ? 'instock' : 'outofstock';
// Прямой атомарный UPDATE в таблицу быстрого поиска wp_wc_product_meta_lookup
$updateData = [
'stock_quantity' => $stock,
'stock_status' => $stockStatus,
];
if ($price !== null && $price > 0.0) {
$updateData['min_price'] = $price;
$updateData['max_price'] = $price;
}
$wpdb->update($lookupTable, $updateData, ['product_id' => $productId]);
// Синхронизируем базовые мета-поля в wp_postmeta
update_post_meta($productId, '_stock', $stock);
update_post_meta($productId, '_stock_status', $stockStatus);
if ($price !== null && $price > 0.0) {
update_post_meta($productId, '_price', $price);
update_post_meta($productId, '_regular_price', $price);
}
$affectedProductIds[] = $productId;
$updatedCount++;
}
$wpdb->query('COMMIT');
} catch (Throwable $e) {
$wpdb->query('ROLLBACK');
return new WP_Error('database_error', $e->getMessage(), ['status' => 500]);
}
// 3. Пакетная очистка кэша только для затронутых товаров
foreach ($affectedProductIds as $id) {
clean_post_cache($id);
wp_cache_delete($id, 'products');
}
// Сбрасываем транзиенты ценовых фильтров каталога
wc_delete_product_transients();
return new WP_REST_Response([
'success' => true,
'updatedCount' => $updatedCount,
'notFoundSkus' => $notFoundSkus,
'timestamp' => time(),
], 200);
}
}
(new HighThroughputStockSyncController())->registerHooks();
Какая настройка Edge CDN (Cloudflare/Fastly) необходима, чтобы избежать отображения чужой корзины случайным пользователям (Cart Cache Poisoning)?