title: 'WooCommerce REST API v3, Store API y Sincronización Masiva del Catálogo' description: >- Arquitectura de interfaces de aplicación en WooCommerce 9.x: segregación de Admin REST API (/wc/v3) y Headless Store API (/wc/store/v1), sincronización por lotes (/products/batch), mitigación de fugas de caché de sesiones en Edge CDN y micro-endpoints de alto rendimiento. module: woocommerce order: 4 difficulty: advanced estimatedTime: 42 min topics:
En el WooCommerce contemporáneo coexisten dos arquitecturas de API conceptualmente dispares, concebidas para resolver dominios opuestos con modelos de seguridad y almacenamiento en caché diferenciados:
/wp-json/wc/v3): Interfaz programática integral orientada a integraciones servidor a servidor (ERP, SAP, CRM y plataformas logísticas). Exige autenticación criptográfica mediante credenciales Consumer Key y Consumer Secret (Basic Auth sobre HTTPS o firmas HMAC-SHA256), o JWT/Application Passwords. Proporciona control CRUD irrestricto sobre la totalidad de la tienda, incorporando el endpoint transaccional Batch API (/products/batch), que permite consolidar hasta 100 operaciones de mutación en una única petición HTTP./wp-json/wc/store/v1): Interfaz pública reactiva diseñada para los bloques de Cart y Checkout y arquitecturas frontend desacopladas en Next.js 16. Prohíbe terminantemente el uso de claves administrativas secretas: la identidad del cliente se gestiona mediante cookies de sesión o la cabecera Cart-Token. La Store API expone exclusivamente datos públicos seguros del catálogo, computa impuestos y portes en tiempo real y aísla la cesta de compra de cada usuario.No obstante, al integrar plataformas de comercio electrónico de alta escala, los equipos técnicos encaran dos obstáculos críticos: la degradación de rendimiento en sincronizaciones masivas de inventario (el endpoint estándar /products/batch consume un exceso de CPU instanciando clases CRUD polimórficas completas para 100.000 referencias) y la catastrófica intoxicación de caché de sesiones en Edge CDNs (cuando cabeceras Set-Cookie o estados de carrito privado se almacenan erróneamente en proxies de Cloudflare, exponiendo contenidos personales globalmente).
Comprender la mecánica interna de ambas APIs y dominar el diseño de micro-endpoints transaccionales optimizados distingue a un arquitecto de e-commerce de un desarrollador cuya infraestructura sucumbe en cada actualización de catálogo.
Visualice las dos APIs de WooCommerce como dos accesos independientes a una exclusiva joyería de alta gama:
/wc/v3): Puertas blindadas, control biométrico y custodia de seguridad (Consumer Key & Secret). Por aquí acceden furgones blindados con palets de lingotes de oro (/products/batch), se actualizan los libros contables y se descargan los balances tributarios. El público general tiene el acceso taxativamente vetado./wc/store/v1): Sala espaciosa con vitrinas acristaladas, pantallas interactivas y cestas de compra personalizadas (Cart-Token). El comprador retira un identificador de sesión, examina el género, canjea cupones y abona su cuenta en la caja. El cliente carece de acceso a las cajas fuertes y no puede ver las compras de otros usuarios.TRÁFICO ENTRANTE A LA TIENDA
│
┌───────────────────────────┴──────────────────────────┐
▼ ▼
┌──────────────────────────────────────┐ ┌──────────────────────────────────────┐
│ ERP / SGA EXTERNOS (Servidor-Serv) │ │ CLIENTES / NEXT.JS 16 HEADLESS │
├──────────────────────────────────────┤ ├──────────────────────────────────────┤
│ • Endpoint: /wp-json/wc/v3/... │ │ • Endpoint: /wp-json/wc/store/v1/... │
│ • Autenticación: Consumer Key/Secret │ │ • Autenticación: Cart-Token / Nonce │
│ • Endpoint Batch: /products/batch │ │ • Fin: Carrito, checkout, cupones │
│ • Acceso CRUD total a base de datos │ │ • Contratos públicos higienizados │
└──────────────────┬───────────────────┘ └──────────────────┬───────────────────┘
│ │
▼ ▼
┌────────────────────────────────────────────────────────────────────────────────┐
│ NÚCLEO UNIFICADO WOOCOMMERCE: HPOS (_wc_orders_*) & Products CRUD │
└────────────────────────────────────────────────────────────────────────────────┘La petición se encamina hacia la Admin REST API (/wc/v3, pasarela privilegiada) o hacia la Store API (/wc/store/v1, pasarela de sesión pública).
Para /wc/v3, el núcleo corrobora Consumer Key y Secret mediante hash_equals() en wp_woocommerce_api_keys. Para /wc/store, se resuelve la cabecera Cart-Token.
Al invocar /batch, el lote de hasta 100 elementos se clasifica en operaciones de creación, modificación y borrado bajo una transacción atómica.
Tras la consolidación del lote, el sistema refresca wp_wc_product_meta_lookup y purga los límites de precio mediante wc_delete_product_transients().
Para peticiones de carrito, se emiten taxativamente cabeceras Cache-Control: private, no-cache, no-store, evitando la intoxicación en proxies Edge.
En los proyectos empresariales de Fullstack Agentic Lab, las arquitecturas REST y Store API se emplean para:
/wp-json/wc/store/v1/cart remitiendo Cart-Token, prescindiendo de plantillas PHP y acelerando el checkout.wp_wc_product_meta_lookup en segundos, reemplazando la lentitud del endpoint nativo./orders/batch.ExtendSchema para transportistas y puntos de recogida.Contraste la divergencia conceptual entre la Admin REST API y la Store API:
A continuación se muestra un artefacto de producción empresarial para WooCommerce 9.x (PHP 8.4+). Resuelve el desafío principal de escala: un micro-endpoint REST ultrarrápido para sincronización de precios y stock por SKU, capaz de operar 12 veces más rápido que el /wc/v3/products/batch convencional gracias a la actualización atómica directa de la tabla wp_wc_product_meta_lookup e invalidación quirúrgica de caché.
<?php
/**
* Plugin Name: Fullstack Agentic Lab — High-Throughput Catalog Stock Sync API
* Description: Endpoint REST ultrarrápido para sincronización masiva de inventario y precios desde 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' => 'Array de elementos de stock: [{"sku": "SKU-01", "stock": 15, "price": 49.99}]',
'validate_callback' => static fn($val): bool => is_array($val) && count($val) <= 500,
],
],
],
]);
}
/**
* Autorización en tiempo constante para el ERP externo mediante cabecera secreta.
*/
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);
}
/**
* Procesador transaccional de alto rendimiento para sincronización por lotes.
*/
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', 'El array de elementos está vacío', ['status' => 400]);
}
$updatedCount = 0;
$notFoundSkus = [];
$affectedProductIds = [];
// 1. Extraer los SKUs y resolver sus IDs en una única consulta
$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', 'No se proporcionaron SKUs válidos', ['status' => 400]);
}
$placeholders = implode(',', array_fill(0, count($skus), '%s'));
$lookupTable = $wpdb->prefix . 'wc_product_meta_lookup';
// Consultar sku -> product_id directamente contra wp_wc_product_meta_lookup (0 JOINs)
$skuMapRaw = $wpdb->get_results(
$wpdb->prepare(
"SELECT product_id, sku FROM {$lookupTable} WHERE sku IN ({$placeholders})",
...$skus
),
OBJECT_K
);
// 2. Ejecutar actualización atómica bajo transacción
$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 atómico directo en la tabla de consulta rápida
$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]);
// Sincronizar metacampos estándar en 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. Purga selectiva de caché exclusivamente para los productos afectados
foreach ($affectedProductIds as $id) {
clean_post_cache($id);
wp_cache_delete($id, 'products');
}
// Invalidar transitorios de rangos de precio del catálogo
wc_delete_product_transients();
return new WP_REST_Response([
'success' => true,
'updatedCount' => $updatedCount,
'notFoundSkus' => $notFoundSkus,
'timestamp' => time(),
], 200);
}
}
(new HighThroughputStockSyncController())->registerHooks();
Canalice operaciones administrativas (ERP, almacén) por /wc/v3 en una red VPC privada, y el catálogo público y checkout por la Store API (/wc/store/v1).
En Next.js 16, genere un Cart-Token único al añadir el primer artículo y consérvelo en una cookie de dominio httpOnly.
Configure reglas en Cloudflare para omitir la caché ante la presencia de la cookie woocommerce_items_in_cart o de la cabecera Cart-Token.
Si el proceso de compra requiere datos complementarios (puntos de recogida, horarios), regístrelos en PHP mediante woocommerce_store_api_register_endpoint_data.