A broken checkout is the most expensive bug a store can have, and the one with the least information: the customer sees a spinner, gives up, and never tells you. So the first rule is to reproduce it yourself, as a customer, in a private window, on your phone. Then run these eight checks in order. Each has a test that takes about a minute, and the first three cover most stores.
Step 01 A plugin conflict after an update
The most common cause, especially in the week after a WooCommerce or theme update. Test: open the browser console (F12) on the checkout page and click Place Order. A red JavaScript error, usually mentioning a plugin’s file, means one script broke the checkout’s own script. Deactivate the plugin named, or, if none is named, switch to the Storefront theme for one test order. If checkout works on Storefront, the theme is overriding a WooCommerce template that changed.
Plain English WooCommerce ships new checkout code a few times a year. Any plugin or theme that customised the old checkout can break the new one. Look at what updated last.
Step 02 Caching on the checkout page
Cart, checkout, and My Account must never be served from a page cache. If they are, customers see someone else’s cart, the nonce is stale, and Place Order fails with “Sorry, your session has expired”. Test: load /checkout/ logged out and read the response headers. Anything like x-cache: HIT or cf-cache-status: HIT means it’s cached. Exclude /cart/, /checkout/, /my-account/ and the wc-ajax endpoint in your caching plugin and at the CDN.
Stop here, and place a test order after every future update.
Step 03. Now we look at what the customer sees.
Step 03 “No shipping methods available”
The customer can’t pay because there’s nothing to pick. Test: WooCommerce → Settings → Shipping. Is there a zone that covers the customer’s country, and does that zone have at least one method enabled? The classic: a “Rest of the World” zone with no methods, or a product with no weight when the shipping plugin needs one. Also check the product’s shipping class matches a rate in the zone.
Step 04 The payment gateway
“Payment declined” or “There was an error processing your payment” with nothing else. Test: WooCommerce → Status → Logs, pick the gateway’s log (stripe, paypal, etc.), and read the last entry. It nearly always names the cause: an expired API key, a webhook secret that changed, a currency the account doesn’t accept, or “test mode” still on after launch. Check the gateway’s own dashboard for the failed attempt too.
Step 05 The button spins and never finishes
The order actually gets created (check Orders → Pending payment) but the customer never sees the thank-you page. This is a JavaScript failure after submission or a server timeout. Test: Network tab, filter for wc-ajax=checkout. A 500 or 504 on that request means PHP crashed or timed out during order creation: an email plugin that fails to send, an inventory sync that calls a slow ERP, or a memory limit. The site’s debug.log or the host’s PHP error log has the exact line.
Step 06 Emails and the timeout that follows
Closely related. On Place Order, WooCommerce sends two or three emails. If your host’s mail function hangs (blocked SMTP port, a dead SMTP plugin), the whole checkout request waits for it, then dies. Test: WooCommerce → Status → Tools → send a test email, or check the SMTP plugin’s log. The fix is a transactional email service (Resend, Postmark, SES) with the SMTP plugin configured properly, and it’s cheap insurance regardless.
Step 07 Required fields and address validation
The button works, the customer fills everything in, and a red notice says a field is required that isn’t on the page. Usually a checkout-field plugin or theme customisation removed the field’s HTML but not the validation, or a block-checkout customisation collides with the classic one. Test: deactivate checkout-field plugins one at a time; if it clears, rebuild the customisation the supported way.
Step 08 Redirect loops and HTTPS
Checkout bounces to the cart, or the browser reports “too many redirects”. Almost always a mismatch between the site URL (http vs https) in Settings → General, a force-SSL rule at the host or CDN, and WooCommerce’s “Force secure checkout” setting. Test: make sure Site Address and WordPress Address both start with https://, and turn off any plugin-level SSL forcing if the host already does it.
After the fix Place a real test order with a real card (refund it). “It loads” is not “it works”. Then write down what broke and what update caused it; the next update will need that note.
If you’ve walked all eight and it’s still broken, it’s something specific to your store: a custom plugin, a payment provider’s quirk, or two extensions fighting. That’s the point where I’d want to see the logs, and it’s the store rescue I do most weeks, same day, because every hour a checkout is down has a number attached.