DEV Community

Isabel Smith
Isabel Smith

Posted on

How We Fixed 87% Checkout Abandonment on a WooCommerce Booking Site

In January 2026, we noticed something ugly: 87% of users who started checkout never completed a purchase. Not "abandoned cart" — abandoned checkout. They filled in their name, email, selected dates, entered passenger counts, and then… nothing. Zero clicks on the payment button.

This is the story of how we diagnosed it, what we found, and the fixes that brought completion back to a normal range. If you run any WooCommerce site with date pickers, payment iframes, or caching plugins, some of this might save you weeks of debugging.

The Symptoms

We use Microsoft Clarity for session recordings. When we actually watched users going through checkout, the pattern was immediately clear:

  • User selects a tour, picks a date, adds to cart
  • User fills in personal info (name, email, phone) — no issues
  • User reaches the payment section (Mercado Pago iframe)
  • User clicks the "Pagar" (Pay) button
  • Nothing happens
  • User clicks again. And again. Rage clicks. Leaves.

Clarity's rage click heatmap looked like a crime scene — the pay button was getting hammered by frustrated users who had already committed to buying.

The Investigation: Four Hypotheses

We opened a bug ticket with four possible root causes. Here's what we checked:

Hypothesis 1: WP Rocket Delay JS breaking the payment iframe

WP Rocket's "Delay JavaScript Execution" feature defers all JS until user interaction. The idea is great for Core Web Vitals — but it means scripts don't run until a scroll, click, or keypress. The Mercado Pago SDK needs to initialize before the user clicks pay.

We tested by adding Mercado Pago's SDK to WP Rocket's exclusion list:

// wp-rocket delay JS exclusions

// Settings → WP Rocket → File Optimization → Delay JS

// Exclude Mercado Pago SDK

/sdk\.mercadopago\.com/

/v1\/checkout/

/mercadopago/

// Also exclude WooCommerce checkout scripts

/wc-checkout/

/checkout\.min\.js/

Result: Partial improvement. The payment iframe now loaded on time for most users, but some sessions still showed the dead-click behavior.

Hypothesis 2: WooTours date picker conflict

WooTours (v3.3.2 — critically outdated, current is v3.6.5) injects its own jQuery UI datepicker. When WP Rocket delays jQuery, the datepicker initializes late. This cascades: if the date selection doesn't register properly, the cart data is incomplete, and Mercado Pago's checkout form never receives the order total.

We confirmed this by checking the browser console on affected sessions:

// Console error on affected sessions:

Uncaught TypeError: $(...).datepicker is not a function

    at wootours-frontend.min.js:1:4328

// jQuery UI wasn't loaded yet when WooTours tried to init

The fix: exclude jQuery UI from delay as well, and load WooTours scripts with an explicit dependency chain.

// functions.php — force correct load order

add_action('wp_enqueue_scripts', function() {

    if (is_checkout() || is_product()) {

        wp_enqueue_script('jquery-ui-datepicker');

        wp_script_add_data('wootours-frontend', 'group', 1);

    }

}, 20);

Hypothesis 3: Cloudflare Rocket Loader double-deferring

Cloudflare has its own JS deferral mechanism called Rocket Loader. If both WP Rocket's Delay JS and Cloudflare Rocket Loader are active, scripts get deferred twice — they load so late that the payment iframe misses its initialization window entirely.

We had Rocket Loader enabled. Turning it off while keeping WP Rocket's delay (with exclusions) resolved the double-deferral issue.

⚠️ Lesson learned: If you use WP Rocket + Cloudflare, disable Cloudflare Rocket Loader. They do the same thing, and having both active creates race conditions with third-party payment SDKs. WP Rocket gives you more granular control over exclusions anyway.

Hypothesis 4: Consent mode blocking the SDK

We use Complianz for GDPR/cookie consent. Complianz's "consent mode" integration can block third-party scripts until the user accepts cookies. Mercado Pago's SDK was being categorized as "statistics" instead of "functional", meaning users who hadn't accepted cookies couldn't pay.

// Complianz → Integrations → Script Center

// Mercado Pago was incorrectly categorized

// Before: Category = "statistics" (blocked until consent)

// After:  Category = "functional" (always allowed)

// Payment processing is functional, not tracking

The Combined Fix

No single hypothesis was the full answer. The actual bug was a combination of all four, creating a cascade failure:

  1. Cloudflare Rocket Loader + WP Rocket Delay JS double-deferred all scripts
  2. jQuery UI loaded after WooTours tried to initialize, breaking the date picker
  3. Broken date picker meant incomplete cart data
  4. Mercado Pago SDK was also blocked by Complianz consent mode
  5. Even when the SDK loaded, it received incomplete order data and silently failed
  6. The "Pay" button looked clickable but did nothing

The fix was four changes deployed together:

Change

What

Disable Cloudflare Rocket Loader

Cloudflare dashboard → Speed → Optimization → Rocket Loader OFF

WP Rocket JS exclusions

Exclude Mercado Pago SDK, jQuery UI, WooCommerce checkout, and WooTours scripts from Delay JS

WooTours dependency chain

wp_enqueue_script with explicit dependency on jquery-ui-datepicker

Complianz recategorization

Move Mercado Pago from "statistics" to "functional" in Script Center

Results

We deployed the combined fix to our staging environment first (back.calafate.tours), tested across Chrome, Safari, Firefox, and mobile, then pushed to production.

Metric

Before

After

Checkout completion rate

~13%

~58%

Rage clicks on pay button

340/week

12/week

LCP (Largest Contentful Paint)

1.8s

2.1s (+0.3s)

TBT (Total Blocking Time)

120ms

180ms (+60ms)

Checkout completion went from 13% to 58%. The trade-off was a small hit to Core Web Vitals (LCP +0.3s, TBT +60ms) because we're loading more scripts eagerly now. Acceptable — a slightly slower page that actually converts is infinitely better than a fast page where nobody can pay.

What We'd Do Differently

If we were starting this site from scratch, here's what we'd change:

Ditch WooTours entirely. The plugin is outdated (we're on v3.3.2, three major versions behind), the jQuery UI dependency is a liability, and the date picker is the root of too many cascading failures. We've scoped a custom replacement — roughly 2,450 lines of vanilla JS with a modern date picker component and direct WooCommerce REST API integration. No jQuery dependency.

Use a headless checkout for payments. The Mercado Pago iframe-within-WooCommerce-checkout architecture is fragile. A headless approach where the checkout is a standalone React/Next.js component calling WooCommerce's REST API would eliminate the script-loading race conditions entirely.

Separate the blog. We actually already did this — our blog runs on Next.js/Vercel at /blog/, routed via a Cloudflare Worker. This keeps the WordPress install lighter and avoids plugin bloat on the content side. If you run a content-heavy WooCommerce site, consider this architecture.

// Simplified Cloudflare Worker for blog routing

export default {

  async fetch(request) {

    const url = new URL(request.url);

    // Route /blog/* to Next.js on Vercel

    if (url.pathname.startsWith('/blog') ||

        url.pathname.startsWith('/en/blog') ||

        url.pathname.startsWith('/pt/blog')) {

      const blogUrl = new URL(url.pathname, 'https://blog.calafate.tours');

      const newRequest = new Request(blogUrl, {

        headers: { ...Object.fromEntries(request.headers),

          'Host': 'blog.calafate.tours' }

      });

      return fetch(newRequest);

    }

    // Everything else → WordPress origin

    return fetch(request);

  }

};

The Broader Lesson: Performance Tools Can Break Functionality

The irony of this whole bug is that every tool involved — WP Rocket, Cloudflare Rocket Loader, Complianz — was doing exactly what it was designed to do. Defer scripts for performance. Block tracking for privacy. Each one is a good tool used correctly.

The problem is the interaction between them. No single tool knew about the others. WP Rocket didn't know Cloudflare was also deferring. Complianz didn't know Mercado Pago was a payment processor, not a tracker. And WooTours didn't know its jQuery dependency would load 3 seconds after it needed it.

If you're running a WooCommerce site with multiple optimization and compliance layers, here's my checklist:

  1. Pick ONE JS deferral method — WP Rocket OR Cloudflare Rocket Loader, never both
  2. Exclude all payment SDKs from deferral — Stripe, PayPal, Mercado Pago, whatever. Payment scripts are mission-critical
  3. Audit your consent tool's script categorization — payment processing is "functional", not "statistics" or "marketing"
  4. Test checkout on a fresh browser with no cookies — this simulates a first-time visitor who hasn't granted consent yet
  5. Use session recordings (Clarity, Hotjar) on your checkout page — analytics will tell you the what, recordings tell you the why

Stack Summary

For reference, here's our full production stack:

Layer

Tool

CMS

WordPress 6.x

E-commerce

WooCommerce

Booking

WooTours (v3.3.2, migration planned)

Translation

TranslatePress (ES base, /en/, /pt/)

Caching

WP Rocket

CDN / SSL

Cloudflare (Full Strict)

Payment

Mercado Pago

Consent

Complianz

Analytics

GA4 via GTM + Microsoft Clarity

Blog

Next.js on Vercel, routed via CF Worker

SEO

Yoast SEO + custom FAQPage JSON-LD

The site is calafate.tours if you want to see the live implementation. We're a small team running a tour booking operation in Patagonia — not a dev shop — so this was very much a "learn by breaking things" process.

— — —

If you've dealt with similar WP Rocket + payment gateway conflicts, I'd love to hear how you solved it. And if you're building booking systems on WooCommerce: update your plugins, test your checkout on a clean browser, and for the love of all that is holy, watch your session recordings.

📌 TL;DR: WP Rocket Delay JS + Cloudflare Rocket Loader + Complianz consent mode were triple-blocking our Mercado Pago payment iframe. Fix: disable Rocket Loader, exclude payment + jQuery UI from WP Rocket delay, recategorize payment SDK as "functional" in Complianz. Checkout completion went from 13% to 58%.

Top comments (0)