PaidRadar › Stripe webhook not received in WooCommerce

Stripe says the payment succeeded. WooCommerce never heard about it.

The charge is in your Stripe dashboard. The order in WooCommerce still says pending payment. That gap is almost always a webhook that was sent but never processed. Below: how to read Stripe's delivery log properly, the five causes that explain nearly every case, how to fix each — and the part most guides skip, namely that repairing delivery does nothing for the orders that already broke.

First: find out whether Stripe actually tried

Do not start by changing settings. Start by reading what Stripe recorded, because that single screen tells you whether the problem is on Stripe's side, in transit, or inside WordPress.

  1. Open the Stripe Dashboard and go to the webhooks section under Developers. Make sure you are in the same mode (live or test) as the payment you are investigating — this is the single most common source of confusion.
  2. Open the endpoint that points at your shop and look at the list of recent event deliveries.
  3. Find the event for the payment in question (typically a payment_intent.succeeded or charge.succeeded, depending on how the gateway plugin is configured).
  4. Read the HTTP status code Stripe got back. That code is the diagnosis.

If there is no endpoint at all, or the event is not listed under any endpoint, Stripe never had anywhere to send it. If the event is listed with a failure, the next table tells you what the code means in a WooCommerce context.

What the response code is telling you

What you see in Stripe Most likely cause How to confirm Fix
403 / 406 / “blocked” A firewall, WAF or security plugin is rejecting the POST before WordPress ever runs it. Check the block log of your WAF (host-level, Cloudflare, or a security plugin) for a request to the webhook path at that timestamp. Allow the webhook path, and allow Stripe's published webhook source IP ranges. Do not disable the firewall wholesale.
301 / 302 The endpoint URL is not the final URL — usually http:// instead of https://, or a www/non-www mismatch. Request the endpoint URL yourself and see whether it redirects. Store the final canonical URL in Stripe. A redirect is not followed here, so it counts as a failed delivery.
404 Stale endpoint. Common after a domain change, a migration, or when permalinks/rewrites changed. Compare the URL in Stripe against the webhook URL the gateway plugin displays in its own settings screen. Replace the URL in Stripe with the one the plugin shows. Copy it, do not retype it.
500 / 502 / 504, or a timeout PHP fatal error or the request exceeding the execution/proxy time limit — often caused by slow hooks running on order completion. Look in the PHP error log and the WooCommerce logs for entries at the same timestamp. Fix the fatal, or move slow work off the webhook request. Raise the PHP/proxy timeout only as a stopgap.
200 / 204, but nothing happened in the order A cache or optimisation layer answered on WordPress's behalf, so the gateway plugin never ran. The response body is empty or a cached page, and there is no matching entry in the gateway plugin's log. Exclude the webhook path from page cache, CDN cache and any “optimise POST requests” feature.
400 with a signature error The signing secret in WooCommerce does not match this endpoint, or live keys are paired with a test-mode endpoint. The gateway plugin logs a signature verification failure at that timestamp. Copy the signing secret belonging to this endpoint into the plugin, and check that key mode and endpoint mode match.
Endpoint marked disabled by Stripe Deliveries failed for long enough that Stripe stopped trying. The endpoint shows as disabled and Stripe usually notified the account email. Fix the underlying cause first, then re-enable. Re-enabling alone gets you the same failures.

Status codes, retry behaviour and IP ranges are Stripe's, documented by Stripe and subject to change — verify current details in their documentation rather than trusting any third-party summary, including this one.

Two cases the delivery log will not explain

Silent

A staging or cloned site is receiving the events

Site clones keep the gateway configuration. If a staging copy still holds a live endpoint, or the live endpoint points at a URL that now resolves to staging, Stripe reports a clean 200 while the production shop learns nothing. Check which host actually answers the endpoint URL, not just that something answers with 200.

Silent

Test mode looked fine, live mode was never wired up

Endpoints, keys and signing secrets exist separately per mode. A checkout that worked perfectly in test can go live with no live endpoint at all — and there is nothing to see in the live delivery log, because there is no endpoint to look at. Always verify the live-mode endpoint separately after go-live.

Confirming the fix actually works

  1. Send a test event to the endpoint from the Stripe dashboard and confirm a 200 with a real response, not a cached page.
  2. Check the gateway plugin's own log for a corresponding entry. Stripe reporting 200 only proves that something answered; the plugin log proves WooCommerce processed it.
  3. Place one small real order end to end and watch the order move out of pending on its own.
  4. Only then go back and deal with the orders that broke while it was down.

Step 2 matters more than it looks. A large share of “webhooks are working now” reports are really a cache or a WAF returning a friendly status code while the plugin never sees a thing.

Fixing delivery does not heal the events you already missed

Webhook delivery is forward-looking. Once the endpoint works, new payments complete normally — and every order that got stuck while it was broken stays exactly where it is. Nothing in WooCommerce goes back and re-checks them.

You have three ways to deal with that backlog. If the events are still inside Stripe's retention window, replaying them from the dashboard is the best possible option: your existing gateway plugin processes them exactly as designed. If they are older than that, you check each order against the gateway by hand. If there are too many for that to be realistic, you automate the same check.

That last case is what PaidRadar is for: it re-queries the gateway API read-only for orders sitting in pending, on-hold or failed, and completes only the ones the gateway confirms as captured — through WooCommerce's own payment_complete() flow, with an audit trail per order. It is a backstop for a properly configured webhook, not a substitute for one. The full side-by-side is in the comparison of every recovery option.

When you should not install anything

Questions

Stripe shows a 200 response, but the WooCommerce order is still pending. Why?

Because something other than the gateway plugin answered. Page caches, CDNs and some optimisation plugins reply to the webhook URL themselves, which produces a perfectly healthy-looking 200 or 204 in Stripe while WordPress never processes anything. Confirm against the gateway plugin's own log: if there is no entry there for that timestamp, the request never reached it. Exclude the webhook path from every cache layer.

Can I replay webhook events that Stripe already sent?

Yes, within the window Stripe retains events for. You resend the delivery from the dashboard and your site processes it as if it had arrived the first time, which is the cleanest possible repair because your gateway plugin does all the work. Outside that window the event is gone and you have to verify the order against the payment record instead.

Why did Stripe disable my endpoint?

Stripe stops delivering to endpoints that keep failing over an extended period and notifies the account email. Re-enabling it without fixing the cause simply restarts the same failures. Fix the block, the timeout or the URL first, verify with a test event, then re-enable.

Do I need to allowlist Stripe's IP addresses in my firewall?

If a WAF or security plugin is rejecting the requests, yes — Stripe publishes the source ranges it sends webhooks from. Prefer allowing the specific webhook path for those ranges over disabling protection generally, and re-check the list periodically since published ranges change.

My webhooks work again. Will the old pending orders resolve themselves?

No. WooCommerce does not re-examine past orders. Every order that got stuck stays stuck until someone or something verifies it against the gateway and completes it. Replay the events if they are still available; otherwise check them against the payment record before changing any status.

Is a missing webhook the only reason an order stays pending?

No, and this matters before you change anything. Customers abandon checkout after the order object is created, cards get declined, and some payment methods legitimately stay pending for a while. A pending order is a candidate for investigation, never proof that money arrived.

See how PaidRadar works Order paid but not completed?