What you are seeing
A broken store shows up as a checkout error, money taken with no order, orders stuck in one status, or carts that empty. The message and the order status tell you which layer failed.
| Message or sign | What it usually means |
There was an error processing your order. Please check for any charges in your payment method and review your order history before placing the order again. | The classic checkout’s script got a reply it could not read: a PHP error, a firewall 403, or a 500 page instead of data. |
Sorry, your session has expired. Return to shop | The checkout security token failed and the cart was empty: the session cookie was lost, or a cached page served an old token. |
We were unable to process your order, please try again. | The token check failed while the cart still had items, usually a cached checkout page. |
| A decline or error from the payment method under the card fields | The gateway answered. Its dashboard has the reason. |
| Card fields or wallet buttons missing | The gateway’s script is blocked, the page is not secure, the currency is unsupported, or the domain is not registered for wallets. |
Paid in the gateway dashboard, but Pending payment or On hold in WooCommerce | The gateway’s confirmation (a webhook or the return redirect) never reached the site. |
Orders in Draft | Normal for the block checkout. WooCommerce deletes drafts older than a day. |
| The cart empties between pages | The session cookie is not kept, nearly always caching. |
| Dozens or hundreds of failed or pending orders for small amounts, often from guest customers with mismatched names and addresses | A card testing attack: bots checking stolen card numbers through your checkout. See card testing and fake failed orders. |
The block checkout talks to the Store API under /wp-json/wc/store/v1/; the classic checkout posts to ?wc-ajax=checkout. A firewall rule blocking one breaks only that checkout.
Confirm it in two minutes
1. Check the checkout page is not cached.
curl -sI https://example.com/checkout/ | grep -iE '^(HTTP|location|cache-control|age|x-cache|cf-cache-status|x-litespeed-cache)'
Run it in your own computer’s terminal (on Windows, type curl.exe). Replace example.com with your domain and /checkout/ with your checkout page’s path if you changed it. WooCommerce sends no-cache headers on cart, checkout and account pages. Good: no-cache or no-store and no HIT. A 302 to the cart is normal, because curl has an empty cart. Bad: HIT or an age above 0, meaning visitors share a stored copy with stale tokens.
2. Test the Store API.
curl -s -o /dev/null -w '%{http_code} %{content_type}\n' https://example.com/wp-json/wc/store/v1/cart
Run it on your own computer. -w prints the status code and content type. Good: 200 application/json. Bad: 403 (a security plugin or firewall blocks the REST API), 404 (rewrite rules broken), or text/html (an error page instead of data). The block checkout and mini cart depend on it.
3. Read the WooCommerce fatal error log.
ls -lt wp-content/uploads/wc-logs/ | head
tail -n 30 wp-content/uploads/wc-logs/LOG_FILE
Run these in an SSH terminal inside the site’s folder (the one with wp-config.php). -lt lists the newest log files first. LOG_FILE is a name from that list; names start with the source, such as fatal-errors-, followed by the date and a random suffix. Good: no fatal-errors file for the day of the failures. Bad: a PHP Fatal error naming a file in wp-content/plugins/. An empty folder may mean logs are kept in the database; read them under WooCommerce, Status, Logs. Gateway plugins log only when logging is on in their settings.
4. List recent stuck orders.
wp wc shop_order list --user=ADMIN_USER --status=pending --fields=id,date_created,payment_method_title,total
Run it in the same SSH terminal. ADMIN_USER is an administrator’s username; WooCommerce’s commands run through its REST API and need a user with permission. Swap pending for on-hold or failed. Good: a few scattered abandoned orders. Bad: every recent order stuck, with the same payment method, from a clear start time.
5. Check scheduled actions are running.
wp db query "SELECT status, COUNT(*) FROM $(wp db prefix)actionscheduler_actions WHERE scheduled_date_gmt < UTC_TIMESTAMP() GROUP BY status"
Run it in the same terminal. $(wp db prefix) fills in your table prefix. Good: pending near zero. Bad: hundreds of overdue pending actions, meaning background jobs such as order emails and renewals are not running, usually because WP-Cron is not.
6. Check the gateway’s side. Open your gateway’s status page (Stripe’s is status.stripe.com; search for your gateway’s name and "status" for others). Then open its dashboard and look at failed payments and webhook delivery attempts for the same window.
Most likely causes, in order
- A payment gateway outage. Tell: only one payment method fails, its status page shows an incident, and its dashboard shows API errors for that window. Fix: offer a second payment method until it clears, and do not change plugins during someone else’s outage.
- Webhooks not reaching the site. Tell: payments succeed in the gateway dashboard while orders stay pending or on hold, and the webhook log shows 403, 404 or 5xx responses, or signature errors after keys were changed. Fix: let the webhook URL from your gateway plugin’s settings through the firewall, and update the signing secret after changing keys or domain. Stripe retries failed live webhooks for up to three days, so recent payments catch up.
- A cached cart, checkout or account page. Tell: check 1 shows
HIT, customers see session expired messages, or one customer sees another’s cart. Fix: exclude those pages from every cache layer, as the cart field guide shows.
- An update broke the store. Tell: a fatal error in check 3 timed just after an update, or outdated template overrides listed in the System Status report. Fix: follow the WooCommerce update field guide.
- An SSL lapse on checkout. Tell: an expired certificate or mixed content warnings on checkout, and card fields or wallet buttons vanish. Browsers only allow the Payment Request API, which wallet buttons use, on secure pages. Fix: renew the certificate (see the SSL field guide) and fix
http:// files.
- A currency or tax plugin conflict. Tell: a gateway vanishes when a currency switcher picks a currency it does not support, totals change between cart and checkout, or a tax plugin’s live lookup fails. Fix: test checkout in each currency, check the gateway’s supported currencies, and read the tax plugin’s log.
- Wallet buttons after a domain change. Tell: Apple Pay disappeared after moving domains or launching from staging. Apple Pay needs each domain registered with the gateway. Fix: register the new domain in the gateway plugin or dashboard.
What it usually is not
- The customer’s card, when every order fails. One decline is the bank. A cluster across different cards is the store or the gateway.
- Fixed by clearing the customer’s cookies. That helps one person and leaves the cached page in place for everyone else.
- Broken WooCommerce files. If
wp plugin verify-checksums woocommerce passes, reinstalling will not help.
- A server that needs more power. A checkout waiting on a slow shipping, tax or gateway API stays slow on a bigger server.
What to send your host or the relevant provider
- To your host: the failing URLs, the times in UTC, your public IP, and the output of checks 1 and 2.
- A request to check firewall logs for blocks on
/wp-json/wc/store/, ?wc-ajax=checkout and your gateway’s webhook URL.
- The fatal error lines from check 3.
- To your payment gateway: order numbers, the gateway’s payment IDs, amounts and times. Never send card numbers; the payment IDs are enough.
- The webhook delivery attempts and responses for those payments, from the gateway dashboard.
- Whether keys, the domain or the certificate changed recently.