Premium Themes

Handcrafted Beautiful Bootstrap HTML Templates for your Business Website.

WooCommerce Order Stuck on Pending Payment (But the Customer Paid): Causes and Fixes

By Alex Carter | | 9 min read

A customer emails you: I paid yesterday. Where’s my order?

You open WooCommerce and find their order sitting on Pending payment. Or worse, it says Cancelled, with a note that reads Unpaid order cancelled – time limit reached. Meanwhile, your Stripe or PayPal dashboard clearly shows the money arrived.

This is one of the most common, and most expensive, silent failures in WooCommerce. Nothing crashed. No error was shown. The store simply never found out that the payment succeeded.

This guide explains why it happens, how to confirm it, how to fix the orders you already have, and how to stop it from happening again.

Why WooCommerce thinks a paid order is unpaid

Flow diagram: shopper places order, WooCommerce creates a Pending payment order, the gateway takes the money, but the webhook back to WooCommerce is blocked, so WooCommerce still thinks the order is unpaid while the gateway shows it paid.

A WooCommerce sale isn’t one event. It’s a handoff between separate systems:

  1. The shopper’s browser submits checkout.
  2. WooCommerce creates the order in **Pending payment**.
  3. The payment gateway (Stripe, PayPal, Square, Mollie, Razorpay…) takes the money.
  4. The gateway tells WooCommerce the payment succeeded, either by redirecting the shopper back to your site or by sending a **webhook** (a server-to-server notification).
  5. WooCommerce moves the order to **Processing** and sends the confirmation email.

If step 4 fails, WooCommerce has no way of knowing that step 3 happened. Your store and your gateway now disagree, and WooCommerce doesn’t check.

The 6 most common causes

1. The webhook was blocked before it reached WooCommerce

This is the number-one cause. Something between the gateway and WordPress rejected the request:

  • Cloudflare Bot Fight Mode or a WAF rule challenging the gateway’s server
  • ModSecurity, Imunify360 or another host-level firewall rule
  • A security plugin that blocks or restricts the REST API or unknown POST requests
  • A redirect (HTTP to HTTPS, www to non-www) on the webhook URL. Webhooks generally don’t follow redirects
  • Maintenance mode returning a 503
  • Password protection on the site (common on staging copies that were pushed live)

2. The webhook secret is wrong

Each Stripe webhook endpoint has its own signing secret (it starts with ‘whsec_’). If you cloned the site, migrated hosts, restored a backup, or recreated the endpoint, the secret saved in WooCommerce may no longer match. The webhook arrives, the signature check fails, and the plugin rightly refuses to trust it, so the order never moves.

3. The shopper closed the tab before returning

With off-site or redirect payment methods (PayPal, iDEAL, some wallets and bank methods), the shopper pays on the gateway’s page and is then sent back to your store. If they close the tab, lose signal, or their session cookie doesn’t survive the trip, the “return” leg never happens. If the webhook is also broken, nothing else will tell WooCommerce.

4. The webhook arrived but PHP crashed while processing it

The gateway’s log shows the delivery as successful, yet the order didn’t change. A fatal error in a plugin hooked into the order status change, or a timeout, can kill the request halfway through. Check ‘WooCommerce → Status → Logs’ for fatal errors around the payment time.

5. The site URL is wrong

If the ‘home’ or ‘siteurl’ options still point at an old or staging domain, the webhook URL registered with your gateway may be pointing somewhere that no longer exists.

6. Background processing is stalled

Some gateway plugins hand part of their webhook processing to WooCommerce’s background queue, Action Scheduler, to avoid race conditions. If WP-Cron or Action Scheduler isn’t running, that queued work sits there and the order never advances. (We cover this in detail in WooCommerce order emails not sending.)

Why paid orders get cancelled: the “Hold stock” timer

Timeline: order created at 0 minutes, customer pays at 1 minute, webhook blocked, stock held, and at 60 minutes WooCommerce cancels the order with the note 'Unpaid order cancelled - time limit reached'. The Hold stock setting is under WooCommerce, Settings, Products, Inventory.

This catches out a lot of store owners.

Go to WooCommerce → Settings → Products → Inventory and look at Hold stock (minutes). The default is 60. When stock management is turned on, WooCommerce reserves stock for unpaid orders for that many minutes. When the timer runs out, it cancels any order still on Pending payment and adds the note *”Unpaid order cancelled – time limit reached.”*

That’s sensible for genuinely abandoned checkouts. But when the webhook is broken, a paid order looks exactly like an abandoned one. An hour later, WooCommerce cancels it and releases the stock. The customer has paid for an order your store now considers dead, and it won’t appear in your Processing list to be shipped.

If you use slow payment methods (bank transfers, some buy-now-pay-later providers), consider raising this value. Leaving the field blank turns off automatic cancellation entirely.

How to confirm a stuck order was actually paid

Before you change anything, verify the money. Never mark an order paid based on a customer’s word alone.

  1. Open the order and look in the Order notes and custom fields for a gateway reference: a Stripe Payment Intent ID (‘pi_…’) or charge ID (‘ch_…’), a PayPal transaction ID, and so on. Many gateways write this reference to the order before the payment is confirmed.
  2. Search for that reference in your gateway dashboard. Confirm the status is Succeeded / Completed, and that the amount and currency match the order.
  3. Check the gateway’s webhook log. In Stripe, open the Webhooks section of the Dashboard (under Developers / Workbench) and inspect the endpoint’s recent deliveries. A failed delivery shows the HTTP status your site returned, which tells you where to look:
    • 403: a firewall or security plugin blocked it
    • 301 / 302: a redirect on the webhook URL
    • 400 / 401: usually a signature or secret problem
    • 500: PHP crashed; check the error logs
    • 503: maintenance mode or the server was overloaded
  4. Check WooCommerce logs under ‘WooCommerce → Status → Logs’, filtering by your gateway’s log source.

How to fix the orders you already have

Once you’ve confirmed the payment succeeded:

  • Resend the event from the gateway (Stripe lets you resend a webhook event from the event’s page). If the underlying problem is fixed, WooCommerce will process it normally and update the order itself. This is the cleanest option because the gateway plugin records everything as it would have originally.
  • Or update the order manually. Change the status to Processing and add a private note with the transaction ID and how you verified it. For a cancelled order, check stock levels afterwards, because cancelling released the stock and restoring the order may not reduce it again.
  • Send the customer their confirmation. From the order’s Order actions dropdown, choose Email invoice / order details to customer, and add a short apology. Customers who pay and hear nothing often open a dispute.

How to fix the root cause

  • Test the webhook URL. For the official WooCommerce Stripe plugin, the endpoint is shown in ‘WooCommerce → Settings → Payments → Stripe’ and usually looks like ‘https://yourstore.com/?wc-api=wc_stripe’. An unsigned test request should reach the plugin and be rejected with a 400, not blocked with a 403.
  • Allow gateway traffic through your firewall. Create Cloudflare/WAF rules that skip challenges for the webhook path, and check your security plugin’s REST API and firewall settings.
  • Re-enter the signing secret after any migration, clone or restore. For PayPal Payments, use the plugin’s option to resubscribe webhooks.
  • Make sure background jobs run. Check ‘WooCommerce → Status → Scheduled Actions’ for a pile of past-due actions.
  • Send a test event from the gateway (or the Stripe CLI) and confirm it returns a 200 and the order updates.

Catching this automatically instead of from angry emails

The painful part of this problem is not fixing it. It’s *noticing*. A broken webhook can quietly affect dozens of orders before a customer complains, and by then many of them have been auto-cancelled.

We built Ordafide Order Integrity for WooCommerce (free on WordPress.org) to scan your recent orders for exactly this pattern:

  • Stuck payment: orders left on Pending payment or Failed even though the gateway wrote a payment reference on them.
  • Paid but cancelled: orders with a confirmed payment date sitting in Cancelled or Failed, with nothing refunded.

Each finding is sorted by the money at risk and comes with the evidence and what to check in your gateway before you touch anything. If WooCommerce itself recorded a confirmed payment date on an order, Ordafide can restore it to Processing with one click. That change is written to an activity log and can be undone. It never captures, refunds or voids payments, and it sends no data to any third party.

For stores that need certainty rather than inference, Ordafide Pro adds read-only gateway reconciliation with Stripe, PayPal, Square, Mollie and Razorpay. It compares what your gateway actually collected against what WooCommerce recorded, including payments that never produced an order at all, and can alert you on Slack, SMS, email or webhook.

Quick checklist

  • [ ] Verify the payment in the gateway before changing any order
  • [ ] Check webhook delivery logs and note the HTTP status code
  • [ ] Fix the firewall, redirect, secret or URL that caused it
  • [ ] Confirm Action Scheduler isn’t backed up
  • [ ] Review Hold stock (minutes) for your payment methods
  • [ ] Restore affected orders and resend confirmation emails
  • [ ] Set up ongoing monitoring so you hear about it before customers do

Frequently asked questions

Why does my WooCommerce order say Pending payment when the customer paid?
Because WooCommerce never received confirmation from the payment gateway. The most common reason is a blocked or failed webhook, often caused by a firewall, security plugin, redirect or wrong webhook secret.

Why did WooCommerce cancel a paid order?
The *Hold stock (minutes)* setting cancels orders that stay on Pending payment past the time limit (60 minutes by default). If the payment confirmation never arrived, a paid order looks unpaid and gets cancelled.

Is it safe to manually mark a Pending order as Processing?
Yes, but only after confirming in your gateway dashboard that the payment succeeded for the right amount. Add a private note recording the transaction ID.

How do I stop it happening again?
Fix the webhook path, keep background jobs running, and monitor for paid orders that don’t advance. A free tool like Ordafide flags these orders automatically.

—

*Have a stuck-order story we didn’t cover? Ask on the Ordafide support forum.

We have updated our Privacy Policy and Cookies policy