Los pedidos duplicados y los cobros duplicados están relacionados, pero no son iguales. Dos pedidos pueden apuntar a un único pago, un pedido puede tener dos cargos, o ambos sistemas pueden duplicarse.
Detén las pruebas y concilia cada transacción antes de borrar, reembolsar o cambiar estados.
Clasifica el patrón
Crea una tabla anonimizada con ID de pedido, importe, hora, referencia, ID de transacción y estado. Comprueba si productos y dirección coinciden.
Patrón A: dos pedidos, una transacción
Patrón B: un pedido, dos transacciones
Patrón C: dos pedidos, dos transacciones
Patrón D: un cobro y una autorización temporal
Confirma en el proveedor qué pagos están capturados. Una retención pendiente no siempre es un segundo cargo definitivo.
Inspecciona la conducta del envío
Una respuesta lenta anima a tocar varias veces, recargar o abrir otra pestaña. Network puede mostrar peticiones de checkout separadas por segundos.
El botón debe mostrar estado ocupado después de un envío válido, pero deshabilitarlo solo protege la interfaz. El servidor debe procesar con seguridad cualquier reintento.
Revisa móvil y visibilidad de errores: el primer intento quizá terminó sin que el cliente lo viera.
Rastrea la idempotencia de la pasarela
Muchas API aceptan una clave de idempotencia para devolver el resultado original al repetir, sin volver a cobrar. Revisa logs y referencias.
Un intento lógico debe conservar:
- una referencia de pedido
- una clave de idempotencia cuando exista
- un ID de transacción almacenado
No registres secrets ni payloads completos. La clave debe ser estable durante reintentos de red y distinta para una compra realmente nueva.
Comprueba que no se regenere al recargar JavaScript.
Comprueba la repetición de webhooks
El proveedor reintenta cuando recibe timeout o estado no exitoso. El mismo evento puede llegar varias veces y el handler debe reconocerlo.
Revisa historial de entrega y notas. Si el mismo ID genera varias preparaciones, emails o cambios, corrige idempotencia antes de reproducirlo.
Responde pronto y encola ERP o tareas lentas. Esperar una exportación antes del 2xx invita a nuevos intentos.
Revisa hooks personalizados
El código puede crear pedidos en dos hooks, llamar al pago desde navegador y servidor, o ejecutarse cada vez que se actualiza el checkout.
Busca wc_create_order(), llamadas a la pasarela y hooks de checkout o página de gracias. Esta página puede recargarse y nunca debe iniciar otro cobro.
No edites WooCommerce core. Mantén la corrección en código versionado con logs mínimos y pruebas.
Examina tareas y sistemas externos
Action Scheduler puede reintentar una tarea, mientras ERP o automatización reenvían la creación tras un timeout. Compara ID de correlación.
Timeout significa resultado desconocido, no “no ocurrió nada”. Antes de repetir, consulta por referencia estable o usa una operación idempotente.
Comprueba también colas del proveedor y webhooks de fulfillment.
Concilia el impacto
Decide qué pedido y cargo deben permanecer. Reembolsa capturas duplicadas mediante la pasarela y añade notas privadas enlazando referencias.
Restaura stock una sola vez y evita preparación doble. No envíes el pedido a la papelera sin más: contabilidad, proveedor, inventario y emails divergirían.
Explica al cliente hechos y plazo del reembolso sin culparle por pulsar dos veces.
Verifica la reparación
Prueba un pago normal, doble clic deliberado, reintento del navegador y webhook repetido en sandbox. Cada intento lógico debe producir un pedido, un cargo capturado y una ruta de preparación.
Comprueba que alertas y logs no generen efectos adicionales.
El mantenimiento recurrente debe detectar pedidos del mismo cliente e importe en un intervalo corto, referencias reutilizadas y varias capturas por pedido. La detección complementa la idempotencia, no la sustituye.