
🛒 Pedidos duplicados en WooCommerce: 3 scripts para bloquear a nivel de carrito
El cliente hizo clic en «Realizar pedido» dos veces y ahora aparecen dos pedidos idénticos en el panel de administración con apenas un par de segundos de diferencia. Se le cobró el dinero dos veces, el cliente está furioso y usted tiene que gestionar un reembolso.
Los pedidos duplicados no son una situación hipotética. Aproximadamente una de cada tres tiendas en WooCommerce se enfrenta a ellos, especialmente con un alojamiento inestable o pasarelas de pago lentas. Pero incluso en un servidor rápido, un usuario puede volver a añadir un producto mediante una URL o comprar un producto digital gratuito decenas de veces.
Hemos preparado tres scripts funcionales que cubren diferentes escenarios de duplicación: desde forzar la venta de una sola unidad hasta bloquear adiciones repetidas por URL. A continuación, un desglose completo con el código.
Por qué se duplican los pedidos en WooCommerce
💡 Resumen rápido:
- Aprenderá por qué se duplican los pedidos a nivel de carrito, URL y compra repetida
- Instalará un script para forzar la venta de una sola unidad mediante campos meta
- Evitará compras repetidas de variaciones gratuitas (productos digitales, cupones)
- Eliminará la duplicación por URL con
add-to-cartal refrescar la página - Probará la protección y sabrá qué más puede causar duplicados a nivel de servidor
Los pedidos duplicados surgen de tres escenarios principales.
Primero, el usuario hace clic en «Realizar pedido» varias veces. Esto ocurre cuando la página se ralentiza y la persona no ve respuesta y vuelve a hacer clic en el botón. En un alojamiento lento, una historia habitual.
Segundo escenario, productos gratuitos. Si tiene un producto digital gratuito, nada impide que un mismo usuario lo «compre» diez veces seguidas. WooCommerce por defecto no registra si este cliente ya ha adquirido antes el mismo producto gratuito.
Tercero, un enlace directo con el parámetro ?add-to-cart=123. Se refrescó la página del carrito, el producto se añadió otra vez. Se siguió un enlace de una newsletter dos veces, dos copias en el carrito.
Los tres escenarios se resuelven del lado del servidor mediante hooks de WooCommerce. Sin plugins, solo código.
Método 1: Forzar la venta de una sola unidad mediante campos meta

El primer script obliga a WooCommerce a tratar un producto específico como «vendido individualmente», incluso si esa casilla no está marcada en los ajustes del producto. El control se realiza mediante un campo meta.
Cuando el campo meta maximum_allowed_quantity es igual a 1, el filtro woocommerce_is_sold_individually devuelve true y el propio WooCommerce bloquea la adición de una segunda unidad al carrito. Funciona tanto para productos simples como para variaciones.
Añada el código al archivo functions.php de su tema hijo o mediante el plugin Code Snippets:
1 function sd_woocommerce_is_sold_individually( $return, $product ) { 2 switch ( $product->get_type() ) { 3 case 'variation': 4 if ( $product->get_meta( 'variation_maximum_allowed_quantity' ) == 1 ) { 5 return true; 6 } 7 break; 8 case 'simple': 9 if ( $product->get_meta( 'maximum_allowed_quantity' ) == 1 ) { 10 return true; 11 } 12 break; 13 } 14 return $return; 15 } 16 add_filter( 'woocommerce_is_sold_individually', 'sd_woocommerce_is_sold_individually', 10, 2 );
Lo que ocurre aquí: el hook woocommerce_is_sold_individually se ejecuta cada vez que WooCommerce verifica si un producto puede añadirse al carrito más de una vez. La función busca en los campos meta del producto y, si encuentra maximum_allowed_quantity = 1, activa forzosamente el modo «Vendido individualmente». Para las variaciones, el campo se llama variation_maximum_allowed_quantity.
Cómo aplicarlo. Acceda a la edición de cualquier producto o variación, añada un campo personalizado con el nombre maximum_allowed_quantity (o variation_maximum_allowed_quantity para una variación) y el valor 1. Guarde. Eso es todo, ahora este producto no se puede añadir al carrito dos veces.
Método 2: Evitar compras repetidas de variaciones gratuitas
Los productos digitales gratuitos son la principal brecha en la protección contra duplicados. El usuario ha iniciado sesión, el precio del producto es cero, WooCommerce deja pasar el pedido sin preguntar. Diez pedidos así de un mismo cliente, diez entradas idénticas en el panel de administración.
El siguiente script verifica mediante wc_customer_bought_product() si este usuario ya ha comprado el mismo producto antes. Si es así, se bloquea la adición al carrito con un aviso claro.
1 function sd_disable_repeat_purchase_for_free_products( $passed, $product_id, $quantity, $variation_id = 0 ) { 2 $current_user = wp_get_current_user(); 3 $product = $variation_id > 0 ? wc_get_product( $variation_id ) : wc_get_product( $product_id ); 4 $target_id = $variation_id > 0 ? $variation_id : $product_id; 5 6 if ( floatval( $product->get_price() ) == 0 ) { 7 if ( wc_customer_bought_product( $current_user->user_email, $current_user->ID, $target_id ) ) { 8 wc_add_notice( 9 sprintf( __( 'You have already purchased "%s" and cannot purchase it again.', 'sd' ), $product->get_name() ), 10 'error' 11 ); 12 return false; 13 } 14 } 15 return $passed; 16 } 17 add_filter( 'woocommerce_add_to_cart_validation', 'sd_disable_repeat_purchase_for_free_products', 10, 4 );
Tras la instalación, el usuario ve un mensaje de error al intentar añadir al carrito un producto gratuito ya comprado. El pedido en sí no se crea.
Dos condiciones importantes. Primero, el cliente debe haber iniciado sesión. Para los invitados, wp_get_current_user() devuelve un objeto vacío y la comprobación no funcionará. Segundo, el precio del producto debe ser exactamente cero (floatval elimina problemas de formato). El script no afectará a productos con un precio superior a cero.
Método 3: Protección contra la duplicación por URL con add-to-cart
WooCommerce permite añadir un producto al carrito mediante un enlace directo: /cart/?add-to-cart=123. Es práctico para el marketing, pero si el usuario refresca la página o sigue el enlace de nuevo, el producto se añade otra vez.
Este script intercepta la carga de la página del carrito o del checkout y, si el producto del parámetro URL ya está en el carrito, elimina silenciosamente los parámetros de consulta y redirige a una URL limpia.
1 function sd_prevent_duplicate_add_to_cart_redirect() { 2 if ( ( is_cart() || is_checkout() ) && isset( $_GET['add-to-cart'] ) ) { 3 $current_url = home_url( add_query_arg( null, null ) ); 4 $product_id = intval( $_GET['add-to-cart'] ); 5 $variation_id = isset( $_GET['variation_id'] ) ? intval( $_GET['variation_id'] ) : 0; 6 7 foreach ( WC()->cart->get_cart() as $cart_item ) { 8 if ( $cart_item['product_id'] == $product_id 9 && ( $variation_id == 0 || $cart_item['variation_id'] == $variation_id ) ) { 10 wp_redirect( remove_query_arg( array( 'add-to-cart', 'variation_id' ), $current_url ) ); 11 exit; 12 } 13 } 14 } 15 } 16 add_action( 'template_redirect', 'sd_prevent_duplicate_add_to_cart_redirect' );
El hook template_redirect se ejecuta antes de que WordPress comience a renderizar la página. El script verifica si el parámetro add-to-cart está en la URL, busca ese producto en el contenido del carrito (WC()->cart->get_cart()) y, si lo encuentra, realiza un wp_redirect a la misma página sin parámetros de consulta.
La adición repetida por URL queda eliminada. Al mismo tiempo, la primera adición (cuando el producto aún no está en el carrito) funciona como de costumbre.
Unificando todo
Los tres scripts no entran en conflicto y funcionan de forma independiente:
- El primero limita la cantidad de un solo producto a 1, protección contra adiciones múltiples.
- El segundo bloquea las compras repetidas de productos gratuitos, protección contra el «spam de clics» en regalos.
- El tercero elimina la duplicación por URL, protección contra adiciones repetidas accidentales.
Añada el código mediante el plugin Code Snippets en WP.org, no directamente en el functions.php del tema. El plugin le permite activar y desactivar fragmentos uno a uno sin romper el sitio ante un error, y no pierde el código al cambiar de tema. Cree tres fragmentos separados, así es más fácil depurarlos.
Qué más revisar si los duplicados persisten
Los scripts anteriores cubren los duplicados a nivel de carrito y checkout. Pero hay dos fuentes más del problema que el código no puede solucionar.
Caché de las páginas de carrito y checkout. Plugins de caché como WP Rocket o LiteSpeed Cache excluyen estas páginas por defecto, pero tras una actualización de ajustes la exclusión puede restablecerse. Revise: carrito, checkout y mi cuenta no deben estar en caché a nivel de plugin, de servidor ni en el CDN.
Reintentos desde la pasarela de pago. Si la pasarela (Stripe, PayPal) no recibe confirmación del servidor a tiempo, envía la notificación de nuevo y WooCommerce crea un segundo pedido. La causa suele ser un alojamiento lento o un firewall que bloquea las solicitudes de callback. Solución: revise los registros de la pasarela en busca de envíos repetidos de webhooks y asegúrese de que la URL de callback sea accesible desde el exterior.
Cómo probar la protección
Después de instalar los scripts, asegúrese de probar cada escenario en un sitio de pruebas.
Método 1, cree un producto con el campo meta maximum_allowed_quantity = 1 e intente añadir dos unidades al carrito. Solo debería añadirse una.
Método 2, inicie sesión, compre un producto gratuito y luego intente comprarlo de nuevo. Debería ver un mensaje de error.
Método 3, añada un producto al carrito mediante un enlace directo con ?add-to-cart=ID y luego refresque la página. El número de artículos en el carrito no debería cambiar.
Pruebe en una instalación separada o en un sitio de staging. En una tienda en producción, solo después de una comprobación completa.
⁉️🤔 Preguntas frecuentes
¿Por qué se duplican los pedidos si el botón «Realizar pedido» se pulsó una sola vez?
La razón es casi siempre una respuesta lenta del servidor. El usuario hace clic en el botón, la solicitud se envía, pero la página no se refresca durante varios segundos. La persona cree que no hizo clic y vuelve a pulsar. Ambas solicitudes llegan a WooCommerce y se crean dos pedidos. Los scripts de este artículo no cubren esta situación; aquí ayuda la caché del lado del cliente (deshabilitar el botón tras el clic) o una verificación de unicidad del pedido del lado del servidor mediante un token nonce.
¿Puede una pasarela de pago crear duplicados por sí sola?
Sí. Si la pasarela no recibe un HTTP 200 de su servidor en respuesta a un IPN/webhook, repite el envío. En cada reintento, WooCommerce puede crear un nuevo pedido. El problema se soluciona acelerando el alojamiento y verificando que la URL de callback de la pasarela no esté bloqueada por un firewall y no requiera autenticación HTTP.
¿Esta protección bloquea pedidos repetidos legítimos de un cliente?
No. Los scripts actúan en la fase de añadir al carrito, no en la de creación del pedido. Si un cliente hizo un pedido deliberadamente y una semana después hace otro, los scripts no interferirán. Excepción: el método 2, que prohíbe la compra repetida de un producto gratuito por el mismo usuario.
¿Es obligatorio usar Code Snippets o se puede poner en functions.php?
Puede usar
functions.php, pero recomendamos Code Snippets. Con un error tipográfico en el código defunctions.phpobtiene una pantalla en blanco y acceso solo por FTP. El plugin Code Snippets captura los errores fatales y desactiva automáticamente el fragmento problemático, el sitio se mantiene en pie.
¿Ayudan los plugins para prevenir duplicados?
Plugins especializados como WooCommerce Duplicate Order Blocker funcionan a otro nivel; rastrean duplicados por IP, correo electrónico o huella digital del navegador en la fase de checkout. Esto cubre el escenario del doble clic que nuestros scripts no cubren. Sin embargo, los plugins no resuelven el problema de duplicados por URL add-to-cart ni las compras repetidas de un producto gratuito. La opción ideal: los scripts de este artículo más un plugin bloqueador en el checkout.
Resumen: tres niveles de protección contra duplicados
Tres scripts cubren tres escenarios específicos de duplicación de pedidos. Instálelos todos a la vez; cada uno es responsable de su propio ámbito y juntos proporcionan una protección sólida a nivel de carrito.
Si después de la instalación siguen apareciendo duplicados, dirija la mirada hacia el alojamiento y la pasarela de pago. Un doble clic en el botón «Realizar pedido» no se puede solucionar con código a nivel de carrito; aquí necesita o bien un plugin bloqueador o una protección personalizada del botón mediante JavaScript.
Pruebe la protección en un sitio de pruebas y luego pásela a producción. Quince minutos para instalar tres fragmentos ahorran horas de gestionar reembolsos.


