PaidRadar › Stripe webhook not received in WooCommerce
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.
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.
payment_intent.succeeded or
charge.succeeded, depending on how the gateway plugin is configured).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 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.