Cuando el checkout de WooCommerce falla, no es un fallo leve. Detona ingresos, colas de soporte y tu confianza en la realidad. Los clientes ven spinners, páginas en blanco, “Hubo un error al procesar tu pedido” o el clásico: no pasa nada al hacer clic en Place order.
El truco es dejar de adivinar. El checkout es una cadena: navegador → theme/JS → WooCommerce AJAX → WordPress/PHP → base de datos → pasarela de pago → callbacks de webhook → correos/actualización de stock. Un eslabón débil y todo parece “el checkout no funciona”. Vamos a encontrar el eslabón.
Guía rápida de diagnóstico
Si estás de guardia ahora mismo, comienza aquí. Esta es la secuencia con mayor relación señal/ruido para encontrar el cuello de botella rápidamente.
1) Confirma que es real y acotado
- Prueba un checkout en incógnito con un producto de bajo valor. Anota el síntoma exacto: spinner, redirección, error de pasarela, página en blanco o pedido creado pero pago fallido.
- Prueba desde otra red (hotspot móvil). Si funciona allí, sospecha DNS/WAF/caché corporativo raro en la primera red.
- Verifica en otro navegador. Si solo falla en Safari, probablemente estás ante reglas de cookies cross-site, pagos en iframe o prevención agresiva de rastreo.
2) Mira la consola y la pestaña red antes de tocar el servidor
- Abre DevTools → Consola para errores JS.
- DevTools → Red → filtra por
?wc-ajax=checkout,admin-ajax.phpy endpoints de pasarelas. Observa códigos de estado y cuerpo de respuesta.
3) Revisa los logs de WooCommerce (son aburridos, por eso funcionan)
- WooCommerce → Estado → Registros. Busca logs de pasarelas, errores fatales, fallos de webhook.
- Log de depuración de WordPress y log de errores de PHP-FPM si controlas el servidor.
4) Decide en qué carril estás
La mayoría de fallos de checkout caen en uno de estos carriles:
- SSL/HTTPS o cookies: bucles de redirección, mixed content, “nonce inválido”, spinner infinito.
- Pasarela/API: Stripe/PayPal fallando, webhooks no recibidos, problemas 3DS.
- Plugins/tema/caché: JS roto, AJAX bloqueado, páginas de checkout cacheadas.
- Límites del servidor: timeouts, errores de memoria, BD lenta, disco lleno, CPU saturada.
5) Aplica las mitigaciones más seguras
- Desactiva la caché de página completa para
/cart/,/checkout/,/my-account/y todos los endpoints?wc-ajax=. - Cambia temporalmente a un tema por defecto y desactiva plugins no esenciales en una copia staging (o usa un enfoque “desactivar solo en checkout” si debes hacerlo en producción).
- Restaura el último cambio: actualización de plugin, actualización de tema, regla CDN/WAF, actualización de PHP. Las roturas de checkout adoran los cambios recientes.
Mapa de fallos del checkout: dónde falla y cómo se manifiesta
Capa de navegador y JavaScript
Síntomas: el botón Place order no hace nada, spinner eterno, campos no validan, el elemento de pago no carga.
Causas típicas: minificación de JS mal hecha, bloqueo por mixed content, tema que sobreescribe plantillas de checkout, scripts de pasarela obsoletos, reglas CSP o scripts inyectados por plugins que provocan errores de sintaxis.
Capa AJAX / REST de WooCommerce
Síntomas: 400 o 403 en ?wc-ajax=checkout, “nonce inválido”, sesión perdida, carrito vacío al refrescar.
Causas típicas: caché, cookies bloqueadas, regla WAF del lado servidor, cacheo en Cloudflare/borde o URL del sitio/HTTPS mal configurados.
Capa WordPress / PHP
Síntomas: HTTP 500, página en blanco, “error crítico”, fallos aleatorios bajo carga.
Causas típicas: errores fatales de PHP, límite de memoria bajo, problemas con opcode cache, versiones de plugins incompatibles o agotamiento de workers de PHP-FPM.
Capa de base de datos y almacenamiento
Síntomas: checkout funciona a veces pero se vuelve lento; pedidos creados con metadata faltante; deadlocks; “error de base de datos” intermitente.
Causas típicas: consultas MySQL lentas, IO saturada, disco lleno, índices faltantes o trabajos en background pesados que compiten con las escrituras del checkout.
Capa de pasarela y webhooks
Síntomas: pedido atascado en “pendiente de pago”, pago capturado pero pedido no marcado como pagado, cliente cobrado pero sin email de pedido, reembolsos fallando.
Causas típicas: fallos en la entrega de webhooks, secreto de webhook incorrecto, solicitudes entrantes bloqueadas, claves API en entorno erróneo, flujos 3DS bloqueados o desajuste del reloj del servidor que rompe firmas.
Datos interesantes y breve historia (sí, importa)
- WooCommerce nació como el plugin eCommerce de WooThemes y fue luego adquirido por Automattic; por eso las convenciones de WordPress están por todas partes, para bien y para mal.
- El checkout usa una mezcla de admin-ajax clásico y endpoints AJAX de WooCommerce; aplican suposiciones de seguridad y caché distintas según la ruta usada.
- Los “nonces” en WordPress no son nonces criptográficos; son tokens con ventana temporal ligados a sesiones. Si caches o proxies reponen páginas, los nonces se pudren.
- La presión por cumplimiento PCI hizo que muchas pasarelas movieran la entrada de tarjetas a campos alojados (iframes/elements). Eso hace que los checkouts modernos sean sensibles a CSP, mixed content y reglas de cookies de terceros.
- Los flujos 3-D Secure (3DS) se volvieron más comunes tras las reglas SCA en Europa; las pasarelas ahora requieren pasos adicionales en el navegador que se rompen fácilmente si JS o redirecciones se interfieren.
- HSTS puede convertir “funciona en mi máquina” en una mentira; una vez que un navegador cachea HSTS, forzará HTTPS aunque luego rompas HTTPS.
- Muchos “problemas de checkout” son en realidad bugs de caché: cachear una página personalizada es como fotocopiar el pasaporte de alguien y dárselo al siguiente cliente.
- Los reintentos de webhook no son infinitos. Si tu servidor bloquea callbacks de pasarela durante horas, luego reconciliarás pagos manualmente.
- Los WAFs en la nube cada vez bloquean por defecto más POST “sospechosos”; los formularios de checkout son básicamente un buffet de campos que pueden activar reglas.
SSL y HTTPS: contenido mixto, HSTS y bucles de redirección
Cómo el SSL rompe el checkout específicamente
Los problemas de SSL rara vez aparecen con un mensaje amable “SSL roto”. En su lugar obtienes efectos secundarios:
- Widgets de pago fallan en cargar porque un único script se sirve por HTTP (mixed content bloqueado).
- No se establecen cookies porque rebotas entre HTTP y HTTPS, o entre
wwwy dominio raíz. - La validación de nonces falla porque contenido cacheado se sirve entre esquemas.
- Bucles de redirección porque WordPress piensa que la URL del sitio es HTTP pero el proxy termina TLS, o viceversa.
Contenido mixto: el asesino silencioso del checkout
Los navegadores modernos bloquearán recursos inseguros en páginas seguras. Lo verás en la consola como “Mixed Content: The page was loaded over HTTPS, but requested an insecure resource.” Cuando ese recurso inseguro es el JS de la pasarela o un script de validación del checkout, el checkout se vuelve un baile interpretativo.
Proxies reversos e “is_ssl()” que mienten
Si estás detrás de un balanceador de carga o CDN que termina TLS, el servidor de origen puede ver solo HTTP. WordPress entonces genera URLs HTTP, establece cookies incorrectamente y basa comportamiento seguro en la señal equivocada.
La solución suele ser asegurar que el proxy envíe X-Forwarded-Proto: https, y que WordPress esté configurado para confiar en eso (a menudo vía la pila de hosting). No “arregles” esto codificando redirecciones en cinco sitios. Así obtienes bucles que solo pasan los martes.
Dos reglas precisas
- Regla 1: las páginas de checkout y cuenta deben ser consistentemente HTTPS con un nombre de host canónico.
- Regla 2: nunca cachees páginas que contengan nonces, carritos, sesiones o fragmentos personalizados.
Pagos: pasarelas, webhooks, 3DS y “pedido pendiente para siempre”
Las dos mitades de “pago exitoso”
Un pago exitoso suele ser dos eventos:
- El cliente completa el flujo en sitio (tokenización/autorization/capture).
- La pasarela envía un webhook servidor a servidor confirmando el estado final.
Si el #1 funciona pero el #2 falla, obtienes la pesadilla: los clientes son cobrados, pero los pedidos quedan en “pendiente de pago” o “on-hold”. Eso no es un “problema de la pasarela”. Es un problema de fiabilidad de la integración, y es tu responsabilidad arreglarlo.
Stripe: puntos comunes de falla
- Desajuste del secreto de webhook: los eventos llegan pero fallan la validación de firma.
- Claves API erróneas: sitio en producción usando claves de prueba o viceversa; a veces solo algunos clientes fallan por diferencias en el método de pago.
- Modal 3DS bloqueado: CSP agresivo, JS roto o interferencia de plugins de optimización.
PayPal: puntos comunes de falla
- Problemas con la URL de retorno: esquema/host desajustados provocan fallos al volver desde PayPal o bucles hacia el carrito.
- IPN/webhook bloqueado: callbacks entrantes bloqueados por firewall, WAF o DNS mal enroutado.
Broma #1: Una integración con pasarela de pago es como una amistad: va bien hasta que alguien deja de devolver las llamadas, y entonces todo está “pendiente”.
Desajuste de reloj: una causa sorprendentemente real
Los payloads firmados de webhook y la autenticación API pueden fallar cuando el reloj del servidor deriva. Si NTP está roto, tu pipeline de pedidos se vuelve cuántico: pagado y no pagado al mismo tiempo.
Conflictos de plugins y temas: los asesinos silenciosos
Por qué el checkout atrae conflictos
Las páginas de checkout son donde cada plugin quiere su momento en el escenario: analytics, píxeles, upsells, multi-divisa, validación de direcciones, caché, seguridad, consentimiento de cookies, “mejorar conversiones”, “mejorar rendimiento”, “mejorar felicidad”. La mayoría lo hacen inyectando scripts y filtrando salida. Basta un actor malo para romper todo el runtime.
Cómo pensar en los conflictos
- Conflictos JS: un script lanza una excepción y el resto del JS de checkout deja de ejecutarse.
- Sobreescrituras de plantillas: temas que sobreescriben plantillas de checkout pueden estar desactualizados respecto a tu versión de WooCommerce.
- Hooks de validación: plugins que añaden campos al checkout pero no manejan casos límite, provocando fallos de validación.
- Plugins de seguridad: bloquean endpoints AJAX, peticiones REST o cuerpos POST.
Deja de “optimizar” el checkout con la ruleta de minify/bundle
Los plugins de minificación que concatenan scripts pueden romper campos alojados de pasarelas y fragmentos del checkout. Si no puedes explicar el grafo de dependencias, no permitas que un plugin lo “optimice” automáticamente.
Caché, CDNs y reglas WAF que sabotean el checkout
Qué nunca debe cachéarse
/cart/,/checkout/,/my-account/- Cualquier URL con
add-to-cart=,wc-ajax=o parámetros de consulta que porten sesión - Respuestas que establezcan cookies como
woocommerce_cart_hash,woocommerce_items_in_cart,wp_woocommerce_session_*
Peligros del CDN
Los CDNs son excelentes hasta que deciden que tu checkout es “estático”. Entonces los clientes comparten sesiones como si fuera un jardín comunitario. Además: las reglas WAF gestionadas a veces marcan los POST del checkout como inyección SQL porque, bueno, direcciones y nombres parecen input de usuario (porque lo son).
Cabeceras de seguridad: buenas intenciones, pagos rotos
Content Security Policy (CSP) y ajustes de SameSite en cookies pueden romper flujos de pago de terceros. Puedes usar CSP; solo no lo hagas adivinando. Empieza con modo report-only, captura violaciones y luego endurece.
Broma #2: Lo único más confiado que un WAF es un plugin de marketing que “garantiza” más conversiones.
Fallas del lado servidor: PHP, BD, disco y timeouts
El checkout es una petición con muchas escrituras: crea un pedido, actualiza stock, guarda metadata, quizá habla con una pasarela, quizá dispara APIs de impuestos/envío y luego envía correos. Son muchas piezas dentro de una misma petición HTTP.
PHP-FPM y agotamiento de workers
Cuando PHP-FPM se queda sin workers, las peticiones se encolan. El checkout caduca, los navegadores reintentan y creas pedidos duplicados. Así te despiertas con “¿por qué tenemos 200 pedidos pendientes?”.
Rendimiento y bloqueo en la base de datos
WooCommerce guarda muchos datos en tablas posts y postmeta. Bajo carga, consultas mal indexadas y contención de locks pueden hacer que el checkout sea lento y poco fiable. Si ves picos en CPU de BD o logs de consultas lentas llenos de consultas meta, no necesitas “más caché”. Necesitas arreglar la carga de la base de datos.
Disco lleno: la caída más embarazosa
Si el disco se llena, PHP no puede escribir sesiones, logs o archivos temporales; MySQL no puede volcar; y WordPress comienza a fallar de formas creativas. Las fallas de almacenamiento rara vez se anuncian de forma educada.
Una cita sobre fiabilidad (idea parafraseada)
“La esperanza no es una estrategia.” — idea parafraseada atribuida a líderes de operaciones experimentados. En el mundo del checkout, verificas con logs y trazas, o solo estás vibrando.
Tareas prácticas de diagnóstico (comandos + salida + decisiones)
Estas son tareas reales que puedes ejecutar en un host Linux típico con Nginx/Apache, PHP-FPM y MySQL/MariaDB. Úsalas para pasar de “checkout roto” a “este componente específico está fallando”. Cada tarea incluye: comando, salida de ejemplo y la decisión que tomas.
Task 1: Confirmar DNS y TLS desde el servidor
cr0x@server:~$ dig +short A example-shop.com
203.0.113.10
cr0x@server:~$ echo | openssl s_client -servername example-shop.com -connect example-shop.com:443 2>/dev/null | openssl x509 -noout -subject -issuer -dates
subject=CN = example-shop.com
issuer=CN = R3, O = Let's Encrypt, C = US
notBefore=Nov 1 00:00:00 2025 GMT
notAfter=Jan 30 23:59:59 2026 GMT
Qué significa: DNS apunta donde esperas y el certificado coincide con el hostname con fechas válidas.
Decisión: Si el CN/SAN no coincide, arregla el cert/virtual host. Si DNS apunta a una IP distinta a tu origen, la configuración de CDN/load balancer puede estar mal.
Task 2: Detectar bucles de redirección o confusión HTTP→HTTPS
cr0x@server:~$ curl -I -L -s -o /dev/null -w '%{url_effective} %{http_code} %{num_redirects}\n' http://example-shop.com/checkout/
https://example-shop.com/checkout/ 200 1
cr0x@server:~$ curl -I -s https://example-shop.com/checkout/ | egrep -i 'location:|set-cookie:|strict-transport-security'
strict-transport-security: max-age=31536000; includeSubDomains; preload
set-cookie: wp_woocommerce_session_123abc=...; path=/; secure; HttpOnly
Qué significa: Una redirección a HTTPS está bien. La presencia de cookies Secure en checkout es buena.
Decisión: Si num_redirects es alto o el estado es 301/302 en bucle, arregla WordPress Site URL/Home URL y headers del proxy antes de tocar plugins.
Task 3: Encontrar candidatos a mixed content en el HTML generado
cr0x@server:~$ curl -s https://example-shop.com/checkout/ | egrep -o 'http://[^"]+' | head
http://example-shop.com/wp-content/plugins/some-plugin/js/tracker.js
http://fonts.example.net/somefont.woff2
Qué significa: El HTML del checkout referencia assets por HTTP; los navegadores pueden bloquearlos.
Decisión: Corrige URLs HTTP hardcodeadas en ajustes de tema/plugin, o corrige las URLs de WordPress y ejecuta un reemplazo de contenido apropiado (con cuidado) si hace falta.
Task 4: Vigilar logs de Nginx/Apache para solicitudes de checkout en tiempo real
cr0x@server:~$ sudo tail -f /var/log/nginx/access.log | egrep 'checkout|wc-ajax=checkout|admin-ajax.php'
203.0.113.55 - - [27/Dec/2025:11:02:13 +0000] "POST /?wc-ajax=checkout HTTP/2.0" 400 182 "-" "Mozilla/5.0"
203.0.113.55 - - [27/Dec/2025:11:02:13 +0000] "GET /checkout/ HTTP/2.0" 200 98234 "-" "Mozilla/5.0"
Qué significa: El endpoint AJAX de checkout devuelve 400. Eso no es “un problema del tema” hasta probar lo contrario.
Decisión: Pasa a logs de PHP/Woo para ver el error exacto (nonce, validación, respuesta de pasarela).
Task 5: Inspeccionar salud de PHP-FPM y presión de workers
cr0x@server:~$ sudo systemctl status php8.2-fpm --no-pager
● php8.2-fpm.service - The PHP 8.2 FastCGI Process Manager
Active: active (running) since Sat 2025-12-27 09:10:02 UTC; 1h 52min ago
cr0x@server:~$ sudo grep -i 'server reached pm.max_children' /var/log/php8.2-fpm.log | tail -3
[27-Dec-2025 10:59:01] WARNING: [pool www] server reached pm.max_children setting (20), consider raising it
Qué significa: PHP-FPM se está saturando. Bajo carga, el checkout se encolará o caducará.
Decisión: Aumenta pm.max_children solo si CPU/RAM lo permiten; si no, reduce la carga de plugins, añade caché para páginas no-checkout y arregla peticiones lentas.
Task 6: Localizar errores fatales de PHP durante checkout
cr0x@server:~$ sudo tail -n 80 /var/log/php8.2-fpm.log
[27-Dec-2025 11:02:13] PHP Fatal error: Uncaught Error: Call to undefined function wc_get_order() in /var/www/html/wp-content/plugins/custom-checkout/hooks.php:41
Stack trace:
#0 /var/www/html/wp-includes/class-wp-hook.php(324): custom_checkout_validate()
Qué significa: Un plugin está llamando funciones de WooCommerce incorrectamente o demasiado pronto, provocando un fatal durante el checkout.
Decisión: Desactiva o parchea el plugin. Un error fatal no es un “tal vez”. Es defecto que detiene la línea.
Task 7: Confirmar que WordPress puede escribir sesiones y que el disco no está lleno
cr0x@server:~$ df -h / /var
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 40G 39G 0.9G 98% /
cr0x@server:~$ sudo du -sh /var/log | tail -1
6.2G /var/log
Qué significa: Estás casi sin disco. Las bases de datos y PHP fallarán de formas raras al 99–100%.
Decisión: Libera espacio inmediatamente (rota/comprime logs, borra backups antiguos), luego pon alertas/monitorización. Disco lleno es fallo de política, no un misterio.
Task 8: Comprobar disponibilidad y carga actual de MySQL
cr0x@server:~$ mysqladmin -uroot -p ping
mysqld is alive
cr0x@server:~$ mysql -uroot -p -e "SHOW GLOBAL STATUS LIKE 'Threads_running';"
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| Threads_running | 58 |
+-----------------+-------+
Qué significa: MySQL está activo, pero 58 threads en ejecución suele ser “estamos sufriendo” en una instancia pequeña.
Decisión: Inspecciona consultas lentas y esperas por locks; considera escalar recursos de BD u optimizar consultas/plugins que generan búsquedas meta pesadas.
Task 9: Identificar consultas lentas durante la ventana de checkout
cr0x@server:~$ sudo tail -n 20 /var/log/mysql/slow.log
# Time: 2025-12-27T11:02:13.123456Z
# Query_time: 3.812 Lock_time: 0.002 Rows_sent: 1 Rows_examined: 250000
SELECT post_id FROM wp_postmeta WHERE meta_key='_billing_email' AND meta_value='user@example.com';
Qué significa: Una consulta sobre postmeta está escaneando demasiadas filas. El checkout suele activar estas búsquedas vía plugins (CRM, fraude, emparejamiento de cuentas).
Decisión: Reduce o elimina la funcionalidad del plugin, añade índices apropiados (con cuidado) o reestructura la ruta de consulta. Poner cache en paths de escritura rara vez acaba bien.
Task 10: Confirmar que el endpoint de webhook es alcanzable externamente
cr0x@server:~$ curl -s -o /dev/null -w "%{http_code}\n" https://example-shop.com/?wc-api=wc_stripe
200
cr0x@server:~$ sudo tail -n 50 /var/log/nginx/access.log | egrep 'wc-api=wc_stripe|webhook'
198.51.100.22 - - [27/Dec/2025:11:01:44 +0000] "POST /?wc-api=wc_stripe HTTP/2.0" 200 42 "-" "Stripe/1.0 (+https://stripe.com/docs/webhooks)"
Qué significa: El endpoint responde y puedes ver POSTs entrantes.
Decisión: Si no ves POSTs, la pasarela no te alcanza (DNS/firewall/WAF). Si ves 4xx/5xx, arregla el manejo de la app o las reglas WAF.
Task 11: Detectar WAF o plugin de seguridad bloqueando cuerpos POST
cr0x@server:~$ sudo grep -iE 'modsecurity|access denied|waf|forbidden' /var/log/nginx/error.log | tail -10
2025/12/27 11:02:13 [error] 1234#1234: *8890 ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Rx' with parameter `(?i:select.+from)'" against variable `ARGS:billing_address_1'
Qué significa: Una regla WAF está marcando falsamente campos del checkout como SQLi.
Decisión: Añade una excepción dirigida para endpoints de checkout, no una desactivación global. Mantén la seguridad, pero enséñale modales.
Task 12: Validar URLs del sitio y ajustes HTTPS vía WP-CLI
cr0x@server:~$ cd /var/www/html
cr0x@server:~$ wp option get home
https://example-shop.com
cr0x@server:~$ wp option get siteurl
https://example-shop.com
Qué significa: WordPress cree que está en HTTPS con el host correcto.
Decisión: Si estos son HTTP o el dominio equivocado, arréglalos. URLs desajustadas crean bucles de redirección y problemas de ámbito de cookies.
Task 13: Listar plugins activos rápidamente (y detectar riesgos)
cr0x@server:~$ wp plugin list --status=active
+---------------------------+--------+-----------+---------+
| name | status | update | version |
+---------------------------+--------+-----------+---------+
| woocommerce | active | available | 9.3.2 |
| woocommerce-gateway-stripe| active | none | 8.7.0 |
| wp-rocket | active | none | 3.16.0 |
| wordfence | active | none | 7.11.0 |
| some-minify-plugin | active | none | 2.4.1 |
+---------------------------+--------+-----------+---------+
Qué significa: Puedes ver plugins de caché/seguridad/minify que comúnmente interfieren con checkout.
Decisión: Si el checkout se rompió tras una actualización, revierte ese plugin primero. Si debes probar, desactiva la optimización/minify en las URLs de checkout.
Task 14: Verificar que cron no esté impidiendo envíos de emails de pedidos y actualizaciones de stock
cr0x@server:~$ wp cron event list | head
+-------------------+---------------------+---------------------+------------+
| hook | next_run_gmt | next_run_relative | recurrence |
+-------------------+---------------------+---------------------+------------+
| wc_cleanup_sessions | 2025-12-27 11:05:00 | in 2 minutes | hourly |
| woocommerce_scheduled_sales | 2025-12-27 12:00:00 | in 57 minutes | hourly |
+-------------------+---------------------+---------------------+------------+
cr0x@server:~$ wp cron test
Success: WP-Cron spawning is working as expected.
Qué significa: WP-Cron puede ejecutarse. Si no puede, tareas demoradas (como finalización de captura en algunos setups) pueden tardar.
Decisión: Si WP-Cron falla, configura un cron real para golpear wp-cron.php y asegura que las salidas HTTP estén permitidas.
Tres microhistorias corporativas desde el frente
Microhistoria 1: La caída causada por una suposición errónea
La empresa migró a un nuevo balanceador. TLS terminaba en el borde, el tráfico al origen era HTTP y todos asintieron como si eso fuera normal. La tienda parecía bien. Las páginas de producto cargaban. Add-to-cart funcionaba. Así que el equipo asumió que la migración estaba bien.
Sin embargo, el checkout comenzó a fallar intermitentemente. No para todos, ni siempre. El síntoma era un spinner y luego un error genérico. La consola del navegador no mostraba nada obvio. Los tickets de soporte llegaban con capturas que todas parecían iguales: “Algo salió mal.” Clásico.
Finalmente alguien notó un patrón: clientes usando métodos de pago guardados fallaban con más frecuencia. Esa fue la pista. Los métodos guardados dependen más de sesiones y nonces, y esos estaban ligados a cookies. Las cookies, a su vez, se establecían de forma inconsistente porque WordPress no podía detectar HTTPS de forma fiable en el origen.
La suposición errónea fue simple: “Si el sitio carga en HTTPS, WordPress sabe que es HTTPS.” Detrás de proxies, eso no está garantizado. La solución fue igualmente simple: corregir headers forward, configurar el origen para confiar en ellos y alinear URLs canónicas. El checkout se estabilizó al instante, como por arte de magia. No fue magia. Fue matemáticas más HTTP.
Microhistoria 2: La optimización que salió mal
Una iniciativa de rendimiento tenía un objetivo noble: mejorar Core Web Vitals. El equipo habilitó optimización agresiva de JS: minificar, combinar, defer, delay y “remover no usados”. La homepage se hizo más rápida. Las gráficas quedaron geniales. Hubo mensajes de celebración en el chat.
Luego las conversiones bajaron. No se desplomaron, pero lo suficiente para activar la fase “¿nos lo imaginamos?”. Los reportes de soporte mencionaban fallos en pagos, pero no de forma consistente. Algunos clientes completaban pedidos; otros no podían cargar la entrada de tarjeta o el desafío 3DS. Los logs tenían timeouts ocasionales de pasarela pero nada concluyente.
El culpable fue el optimizador retrasando scripts que consideró “no críticos”, incluidos campos de pago alojados. En algunos dispositivos, el cliente hacía clic en Place order antes de que el elemento de pago se inicializara, así que la petición de checkout se hizo sin token de pago. WooCommerce hizo lo que pudo: creó un pedido y luego falló el pago.
La solución no fue abandonar el trabajo de rendimiento. Fue dejar de tratar el checkout como una página de marketing. Excluyeron checkout, cart y account de combinar/delay de JS y añadieron monitorización para errores de tokenización. El rendimiento siguió bien donde importaba y el checkout dejó de perder ingresos en silencio.
Microhistoria 3: La práctica aburrida pero correcta que salvó el día
Otro equipo ejecutaba “simulacros de incendio” del checkout semanalmente. No dramáticos. Simplemente hacían una transacción real por cada método de pago en un entorno staging que reproducía la configuración de producción: reglas CDN/WAF, headers, todo. También verificaban entrega de webhooks y transiciones de estado del pedido.
Esta práctica parecía aburrida. No producía dashboards brillantes ni aplausos. Pero creó memoria muscular: dónde están los logs, cómo se ve lo “normal” y qué dashboards de proveedores importan durante un incidente.
Un viernes, una actualización de reglas gestionadas del WAF empezó a bloquear POSTs con ciertos patrones de dirección. El primer síntoma fue un pequeño aumento en fallos de checkout. Como el equipo tenía una línea base y sabía dónde mirar, detectaron bloques 403 en minutos, añadieron una excepción dirigida al endpoint de checkout y siguieron con su día.
La gracia salvadora no fue heroísmo. Fue el hábito poco glamuroso de probar todo el flujo de pago, incluidos los webhooks, como parte de operaciones—no como pánico anual.
Errores comunes: síntoma → causa raíz → solución
1) Spinner eterno tras “Place order”
Síntoma: el botón de checkout gira; no se crea pedido; la red muestra ?wc-ajax=checkout pendiente o fallando.
Causa raíz: error JavaScript, endpoint AJAX bloqueado o cola/timeouts de PHP-FPM.
Solución: Revisa Consola DevTools y luego logs de acceso/errores del servidor. Excluye checkout de la optimización JS. Aumenta capacidad de PHP-FPM solo tras identificar peticiones lentas.
2) “Nonce verification failed” / sesión perdida
Síntoma: fallos esporádicos; a menudo tras tiempo en la página; a veces solo en móvil.
Causa raíz: cachear una página con nonces; cookies bloqueadas; mismatch HTTP/HTTPS; múltiples dominios.
Solución: desactiva caché en checkout/cart/account, aplica un único hostname HTTPS, confirma cookies seguras y siteurl/home consistentes.
3) Bucle de redirección entre HTTP y HTTPS
Síntoma: el navegador dice demasiadas redirecciones; curl muestra 301/302 repetidos.
Causa raíz: ajustes de URL de WordPress en desacuerdo con terminación de proxy; reglas de redirección conflictivas en Nginx/Apache, WordPress y CDN.
Solución: elige una capa canónica de redirección (preferir borde o servidor web), configura correctamente las URLs de WordPress, pasa X-Forwarded-Proto y elimina redirecciones redundantes.
4) Pedidos creados pero atascados en “pendiente de pago”
Síntoma: el cliente afirma que fue cobrado; dashboard de la pasarela muestra éxito; WooCommerce queda en pendiente.
Causa raíz: webhook no entregado o rechazado (firma mismatch, 403 por WAF, timeout), o sitio incapaz de procesar callbacks asíncronos.
Solución: verifica alcance del endpoint de webhook, allowlist de IPs/UA de la pasarela si aplica, confirma secretos y asegúrate de que el servidor no esté caducando al procesar webhooks.
5) El método de pago no aparece en checkout
Síntoma: sección de pasarela en blanco; consola muestra scripts bloqueados.
Causa raíz: mixed content, CSP demasiado estricta, plugin tipo ad-blocker o minify que rompe el orden de scripts.
Solución: elimina assets HTTP; ajusta CSP para permitir dominios de la pasarela; excluye scripts de pasarela de minify/delay; prueba en perfil de navegador limpio.
6) Checkout funciona para admins pero no para clientes
Síntoma: tú puedes ordenar; los clientes no.
Causa raíz: variaciones de caché por cookie; comportamiento por rol; reglas de seguridad que eximen usuarios logueados; problemas de checkout para invitados.
Solución: prueba como usuario desconectado; desactiva caché para checkout; asegúrate de que guest checkout esté habilitado si es la intención; revisa reglas WAF aplicadas a usuarios anónimos.
7) “Hubo un problema procesando tu pedido” aleatorio bajo carga
Síntoma: fallos intermitentes, a menudo durante campañas.
Causa raíz: pm.max_children de PHP-FPM alcanzado, contención en BD, timeouts de APIs externas o rate limiting en la pasarela.
Solución: observa saturación de workers y consultas lentas; añade colas/reintentos donde proceda; reduce llamadas externas sincrónicas durante el checkout.
8) El carrito se vacía al ir al checkout
Síntoma: los ítems desaparecen; “sesión expirada”.
Causa raíz: cookies que no persisten (SameSite/secure), caché o mismatch de dominio entre www/apex.
Solución: estandariza el hostname, asegura HTTPS en todas partes, no caches cart/checkout y valida atributos de cookies.
Listas de verificación / plan paso a paso
Paso a paso: estabilizar el checkout en los próximos 30–60 minutos
- Congelar cambios: no actualizaciones de plugins, no editar reglas CDN, no desplegar temas hasta que el checkout funcione.
- Reproducir con evidencia: Consola DevTools + captura HAR de red, mensaje de error exacto y timestamp.
- Revisar logs de acceso/errores alrededor del timestamp para
?wc-ajax=checkouty endpoints de webhook. - Revisar logs de WooCommerce para la pasarela y errores fatales.
- Desactivar caché de página completa para checkout/cart/account en cada capa: plugin de caché, caché de servidor y CDN.
- Desactivar temporalmente optimización JS en checkout: combine/minify/defer/delay.
- Confirmar canonicalización HTTPS: un hostname, sin bucles de redirección, cookies seguras.
- Validar configuración de la pasarela: claves API, secreto de webhook, endpoint alcanzable, entorno correcto.
- Reducir radio de afectación: si un método de pago falla, desactívalo temporalmente y dirige clientes a una alternativa que funcione mientras arreglas la causa raíz.
- Anunciar estado: stakeholders internos necesitan una señal clara “checkout afectado”, no rumores.
Paso a paso: encontrar la causa raíz sin romper más cosas
- Baselina el sistema: CPU, memoria, disco, saturación PHP-FPM, threads BD, tasas de error.
- Diff del último cambio: actualización de plugin, cambio de tema, upgrade de PHP, actualización de regla WAF, política CDN.
- Aislar conflictos:
- Prueba con tema por defecto en staging.
- Desactiva plugins no esenciales en staging.
- Vuelve a habilitar por lotes hasta que vuelva a romper.
- Validar webhooks end-to-end: el evento llega, la firma valida, el estado del pedido cambia y se envía el email.
- Escribir una nota postmortem: qué falló, por qué no se detectó y qué monitorizar la próxima vez.
Checklist operativo: hacer que futuros fallos de checkout sean menos emocionantes
- Alertar sobre picos en
4xx/5xxpara?wc-ajax=checkouty endpoints de webhook. - Rastrear tasa de éxito de pagos por método (Stripe, PayPal, etc.), no solo pedidos totales.
- Excluir checkout/cart/account de caché y de herramientas de “auto-optimización” por política.
- Mantener un entorno staging con las mismas políticas CDN/WAF que producción.
- Ejecutar una transacción sintética semanal que incluya confirmación por webhook.
FAQ
¿Por qué el checkout de WooCommerce funciona para mí pero no para los clientes?
Los administradores suelen eludir caché y algunas reglas de seguridad, y puedes tener cookies/sesiones diferentes. Prueba desconectado en incógnito. Si solo fallan invitados, sospecha caché, ámbito de cookies o reglas WAF que afectan tráfico anónimo.
¿Cuál es la forma más rápida de saber si es un conflicto de plugin?
Error en la consola del navegador más una ruptura súbita tras una actualización es una señal fuerte. En staging, cambia a un tema por defecto y desactiva plugins no esenciales. Si el checkout vuelve, ya tienes la respuesta.
¿Realmente necesito HTTPS en todas partes o solo en el checkout?
Prácticamente: en todas partes. Los esquemas mezclados causan problemas de cookies y sesiones, y los navegadores modernos penalizan el HTTPS parcial. Además, las pasarelas esperan orígenes seguros para muchos flujos.
¿Por qué quedan pedidos en “pendiente de pago” aunque Stripe diga pagado?
Porque “pagado” en la UI de la pasarela no es lo mismo que “pedido marcado como pagado” en WooCommerce. Los webhooks reconcilian el estado final. Si la entrega de webhooks está bloqueada o la validación de firma falla, WooCommerce no actualizará el pedido.
¿Pueden los plugins de caché romper el checkout aunque digan que lo excluyen?
Sí. Las exclusiones pueden ser incompletas, sobreescritas por reglas CDN o rotas por normalización de query-string. Verifica con cabeceras (cache hit/miss) y asegúrate de que el CDN no esté cacheando respuestas personalizadas.
¿Por qué el checkout falla solo en Safari móvil?
La prevención de rastreo y manejo de cookies de Safari pueden romper campos de pago embebidos de terceros o redirecciones cross-site si SameSite y atributos secure no son correctos. También, un delay agresivo de scripts puede competir con la inicialización del elemento de pago en dispositivos más lentos.
¿Debo aumentar el límite de memoria de PHP para arreglar checkout?
Sólo si tienes evidencia de agotamiento de memoria (errores fatales, OOM kills). Aumentar memoria puede enmascarar plugins ineficientes y empeorar la densidad de procesos. Arregla primero el path de código lento/fallido.
¿Cómo sé si mi WAF está bloqueando el checkout?
Busca respuestas 403 en ?wc-ajax=checkout y reglas coincidentes en logs del WAF o del servidor web. Si ves hits de ModSecurity/reglas gestionadas sobre campos del checkout POST, necesitas una excepción dirigida.
¿Cuál es la causa más común del “estaba bien ayer”?
Un cambio que no conectaste con checkout: auto-update de un plugin, actualización de regla gestionada del CDN/WAF o una función de “optimización” activada. El checkout es sensible; trata los cambios como sospechosos hasta verificarlos.
¿Es seguro desactivar webhooks como solución temporal?
No. Los webhooks son cómo sincronizas el estado de pagos de forma fiable. Si los webhooks fallan, arregla alcance/firmas/timeouts. Desactivarlos solo traslada el trabajo a la reconciliación manual, que fallará a escala.
Conclusión: qué hacer después (y qué dejar de hacer)
Aquí tienes el conjunto de movimientos prácticos siguientes:
- Obtén evidencia: un intento de checkout fallido con timestamp, entrada de red DevTools para
?wc-ajax=checkouty los logs relevantes de WooCommerce/PHP. - Elige el carril: SSL/cookies, pasarela/webhooks, plugin/tema, caché/WAF o límites del servidor. No depures cinco carriles a la vez.
- Estabiliza primero: excluye checkout de caché/optimización, revierte cambios recientes y mantén al menos un método de pago funcionando si es posible.
- Arregla la causa raíz: corrige HTTPS/headers de proxy, repara la configuración de la pasarela, doma el WAF o elimina el plugin conflictivo.
- Hazlo aburrido: añade monitorización para fallos AJAX de checkout y salud de webhooks, y ejecuta transacciones sintéticas semanales.
Deja de alternar plugins al azar como si intentaras abrir una caja con combinación. La fiabilidad del checkout no es superstición. Es un problema de observabilidad: sigue la petición, lee los logs y haz un cambio a la vez.