Refunds you issue in the Stripe or PayPal dashboard—not through WooCommerce admin—only reach your WooCommerce order if the gateway’s webhook fires, reaches your server, passes signature verification, and is processed by the WooCommerce Stripe extension without error. When any step in that chain breaks, the refund completes at the gateway, the customer gets their money back, and the WooCommerce order stays marked “completed” forever. GA4 never records the refund. Google Ads never corrects the conversion. Meta CAPI never adjusts attribution. The average ecommerce return rate is 17.9% (NRF, 2024)—enough volume that a single misconfiguration can hide thousands of dollars of refunded revenue in plain sight.
The Gateway-Only Refund Gap GA4 Cannot See
This failure mode is structurally different from the refund-tracking problem most store owners have read about. If you issue a refund inside WooCommerce admin, the woocommerce_order_refunded hook fires locally, the order status changes, and every downstream tool—GA4, Facebook CAPI, Google Ads—can pick up the signal. That path is broken in its own ways (we covered it in refunds issued inside WooCommerce break GA4 in one way), but at least WooCommerce knows the refund happened.
The dashboard path does not use that hook at all. WooCommerce has no local event to fire. Everything depends on an inbound webhook from Stripe, and webhooks are a dependency chain—every link has to hold.
If one link breaks, the refund becomes a silent entry in Stripe’s ledger and nothing else.
The Webhook Dependency Chain Most Store Owners Never See
Here is what actually has to happen for a dashboard refund to reach WooCommerce:
- Stripe fires the
charge.refundedevent the moment the refund completes at the gateway. - Stripe delivers it to your configured webhook endpoint—the URL you (or the WooCommerce Stripe extension setup wizard) registered inside Stripe’s Developers panel.
- Your server accepts the POST request and responds within Stripe’s timeout window.
- The endpoint verifies the Stripe signature header against your webhook signing secret.
- The WooCommerce Stripe extension processes the payload, locates the order, updates its status to “refunded,” and returns 2xx.
If any of those five steps fails, Stripe treats the delivery as failed and retries. Per Stripe’s webhook documentation, retries continue for up to three days with exponential back-off. After that, the event is no longer delivered automatically. It can only be resent manually from the Stripe dashboard within 15 days of the original event. Miss that window and the refund is permanently invisible to WooCommerce.
Four Ways the Chain Breaks (And Why You Often Do Not Notice)
The breakage modes are boringly specific:
- The webhook was never configured. Many stores migrated from older Stripe integrations, or swapped hosting, and the webhook URL was never re-registered. The plugin works fine for checkout (which does not need webhooks). Refunds silently fall off.
- Signature verification fails after a secret rotation. You rotated your Stripe webhook signing secret or restored an old WordPress backup. Stripe sends the event, your server receives it, the signature does not match, the endpoint returns 401, and Stripe logs a failure.
- The endpoint returns 5xx under load. PHP timeouts, memory limits, a slow plugin in the request stack, or a scheduled WP-Cron job eating resources at the wrong minute. Stripe records the failure and retries. If the same condition keeps recurring for three days, the retries expire.
- A known extension bug blocks the status update. Documented in WooCommerce Stripe Extension GitHub issue #2497: on Stripe API version 2022-11-15, the extension returned 2xx to Stripe (so no retries fired) but failed to expand the refund list internally. The webhook looked successful on both sides. The order status never changed.
The common pattern: your Stripe dashboard shows “webhook succeeded” or the three-day retry window quietly closes, and you have no alarm on your side that anything went wrong.
As Putler’s reconciliation analysis noted in 2026, processing refunds directly in Stripe or PayPal without going through WooCommerce leaves the store with no record that the refund happened.
What Gets Poisoned Downstream
When WooCommerce does not know about the refund, three things happen automatically:
GA4 lifetime revenue stays inflated. Your purchase event fired at checkout. No refund event ever fires, because WooCommerce never emitted one. GA4’s revenue, ROAS, and LTV calculations all include money the customer no longer has.
Google Ads Smart Bidding keeps optimising toward refunded customers. The conversion value is never adjusted via the Offline Conversion Adjustments API. Smart Bidding’s training signal now contains examples of “good” conversions that were actually full refunds—and the algorithm cheerfully finds more customers like them.
Meta CAPI keeps the purchase in its attribution model. Advantage+ audiences and lookalikes are built from a purchase set that includes the refund. You pay to acquire more of the same profile.
This is not a theoretical concern. 67% of businesses cannot trust their data, per Precisely and Drexel University’s 2025 Data Integrity Trends Report, and revenue reconciliation gaps between commerce platforms and payment gateways are one of the most common specific triggers they cite.
The Three-Step Diagnostic You Can Run Today
You do not need a developer to find out whether this is happening to your store. You need about fifteen minutes and access to the Stripe dashboard.
Step 1: Open Stripe’s failed webhook log
In Stripe Dashboard: Developers → Webhooks → your endpoint → Failed. Filter to the last 90 days. Any charge.refunded event in that list is a refund that never reached WooCommerce automatically.
Step 2: Cross-check completed orders against Stripe refunds
Export your WooCommerce orders marked “completed” for the same window. In Stripe, run the Refunds report for the same dates. Any Stripe refund whose payment_intent maps back to a WooCommerce order still marked “completed” is a confirmed gateway-only refund.
Step 3: Identify what is still recoverable
Events inside Stripe’s 15-day manual resend window can be triggered again: find the event, click Resend, and watch whether your WooCommerce Stripe extension updates the order this time. Anything older than 15 days has to be resolved by marking the WooCommerce order as refunded manually, then letting your normal refund tracking fire downstream.
This is the same diagnostic pattern we used in order edits have the same no-correction-event problem: when WooCommerce never learned about a change, no downstream system can self-correct.
The Permanent Fix: Capture Gateway Events Independently
Fixing the current backlog is bookkeeping. Preventing the next one requires an architecture that does not treat the WooCommerce Stripe extension webhook handler as the single source of truth for refunds.
Transmute Engine™ is a first-party Node.js server that runs on your subdomain (for example, data.yourstore.com). Alongside the inPIPE WordPress plugin—which captures events from WooCommerce hooks—the Transmute Engine server can also listen to gateway webhooks directly, so a Stripe charge.refunded event reaches your tracking pipeline whether or not the WooCommerce Stripe extension processes it correctly. Refunds flow through to GA4, Google Ads, and Meta CAPI on a parallel path that stays healthy even when the plugin path does not.
Key Takeaways
- Dashboard refunds use a different mechanism than admin refunds. They depend entirely on Stripe’s
charge.refundedwebhook, not thewoocommerce_order_refundedhook. - The webhook chain has five failure points. Missing configuration, signature mismatch, 5xx responses, retry exhaustion, and known extension bugs can all leave the order marked “completed.”
- Stripe only retries for three days. Events are manually resendable for 15 days from the original event, then permanently lost to automatic delivery.
- The downstream damage is automatic. GA4 revenue, Google Ads Smart Bidding, and Meta Advantage+ all inherit WooCommerce’s view and optimise toward customers who return their orders.
- You can diagnose today. Stripe Dashboard → Developers → Webhooks → Failed tab → filter 90 days → cross-check against WooCommerce completed orders.
Frequently Asked Questions
Because refunds issued in the Stripe dashboard do not change WooCommerce order status directly. The status only updates if Stripe’s charge.refunded webhook is received, signature-verified, and processed by the WooCommerce Stripe extension without error. If the webhook is missing, misconfigured, or hitting a bug, the order stays “completed” even though the money has left your account.
Only if webhooks are correctly configured. Refunds initiated inside WooCommerce admin update the order directly via the woocommerce_order_refunded hook. Refunds initiated in the Stripe dashboard depend on the charge.refunded webhook firing, reaching your server, authenticating, and being processed successfully. Any break in that chain leaves the order unchanged.
Partially. Stripe keeps failed webhook events available for manual resend for 15 days from the original event. After that, the automatic delivery window is closed and you have to reconcile manually by cross-checking the Stripe payouts report against WooCommerce orders, then marking those orders as refunded in WooCommerce admin.
One common cause is gateway-only refunds that never updated WooCommerce. Stripe already subtracted the refund from your payout, but WooCommerce still counts the original order as completed revenue. GA4 and ad platforms inherit the WooCommerce view, so they also show inflated numbers. The gap between payout and WooCommerce total is often the missing refund signal.
The charge.refunded event tells the WooCommerce Stripe extension that a refund was processed at the gateway. On success, the extension marks the order as refunded and triggers downstream events. On failure—signature mismatch, 5xx response, timeout, or a bug in the extension—Stripe retries with exponential back-off for three days, then stops. If retries expire, the order stays unchanged and the only way to force a resync is manual resend from the Stripe dashboard within the 15-day recovery window.
Run the three-step diagnostic first. Then decide whether the next refund you issue in Stripe’s dashboard should depend on a plugin webhook handler staying healthy forever—or reach your tracking layer on its own path. See how Transmute Engine captures gateway events directly.
