refund_total / revenue. The first canary on a quality issue or a deploy regression, pair with top_refunded_products.
At a glance
The dollar share of revenue refunded back to customers in the period. Sums refund objects (separate child posts) divided by gross revenue.
| What it counts | SUM(refund.total) / SUM(order.total) x 100 for orders in the window where the order status is completed, processing, or refunded. Refund totals come from /wp-json/wc/v3/orders/{id}/refunds (separate refund posts). |
| REST API endpoint | GET /wp-json/wc/v3/orders/{id}/refunds for refund objects, plus the orders endpoint for the denominator. Refund object field used: total (negative-signed string decimal). |
| VAT / tax treatment | Currency-agnostic ratio. Numerator and denominator are both tax-inclusive total figures, so VAT cancels out in the ratio. |
| Status filter | Numerator covers refunds attached to any order regardless of status. Denominator covers completed + processing + refunded orders so that fully-refunded orders are still in the denominator (they generated revenue, just got refunded). |
| Shipping | Refunds may or may not include shipping back depending on merchant policy. WC stores both line-item and shipping refunds in the same refund object; this card sums all refunded amount. |
| Discounts | Already reflected in order.total. Refunds reduce the realised cash figure regardless of discount path. |
| Refunds (this is the metric) | Counts both full and partial refunds. WC creates a refund post per refund event; merchants sometimes issue 2-3 partial refunds against a single order, each adds to the numerator. |
| Cancelled / failed orders | Excluded from both numerator and denominator. Cancelled orders never got paid; failed orders never authorised. |
| Currency | Multi-currency stores: ratio is currency-agnostic (rates dimensionless), but if currencies differ between numerator (refund) and denominator (order), the ratio loses meaning. Filter by currency for accurate per-currency rates. |
| Channels / sources | Not filtered. Web, POS, marketplace, and B2B refunds all contribute. |
| Self-hosted vs managed-Woo | Same definition. On self-hosted Woo, refund-object polling can lag if the host is slow, refund spikes may show 1-2 days late. |
| Time window | 30D vsP |
| Alert trigger | >5% rate, or +25% vsP, driven by sentiment_key: refund_rate |
| Roles | owner, operations, finance |
Calculation
Calculated automatically from your WooCommerce data. See the At a glance summary above for what the metric tracks and the worked example below for a typical reading.Worked example
A US homewares brand on managed-Woo with a heavy ceramics catalog and known fragility-in-transit issues. Period: 12 Mar 26 to 11 Apr 26 (30D), versus prior 30D.| Refund event type | Count | Sum of refund.total | Note |
|---|---|---|---|
Full refunds (status flipped to refunded) | 64 | $9,420 | Damaged in transit, full money back |
| Partial refunds (replacement of one broken item in a multi-item order) | 218 | $14,860 | Order stays completed, refund posts attached |
| Multi-step refunds (2-3 refund posts per order) | 41 orders, 96 posts | $5,200 | Customer disputes that escalated |
| Total refund value (numerator) | 378 refund posts | $29,480 |
completed + processing + refunded): $384,200.
- WC’s separate refund objects matter for the count. A single order with 3 partial refunds shows as 3 refund posts in the API. Some merchant-side reports plugins double-count these and report 7-8% rates against this card’s 7.67%, that is plugin-counting noise, not real disagreement.
- The fragility pattern hides in partials. Most ceramics breaks generate a partial refund (replace one mug from a 4-pack), not a full refund. Refund-rate cards that count only fully-refunded orders (Shopify-style
displayFinancialStatus = REFUNDED) would miss this entirely. The Woo rate captures it because it sums refund-object totals. - Self-hosted server slowness can hide a refund spike. The merchant’s previous host had an 8-hour outage on 25 Mar 26, the indexer missed 14 refund events that day. They appeared in the index 9 hours later, on the wrong calendar day in the WP timezone. For a 30D window this is invisible; for daily granularity it can shift the spike date by a day. Managed-Woo (this brand’s current host) does not show the pattern.
- Refund-object accounting confuses the merchant. The brand owner expects 29,480 (refunds) = 384,200 and Net sales = $354,720; they often misread Gross as the “true” number. This card reads in dollar % to make the leakage visible.
- vsP is the diagnostic. A +3.47 ppt rise is the signal Vortex IQ catches. Drilling into Top Refunded Products typically shows 1-2 SKUs (a particular fragile mug, a sale-priced overstock) accounting for 40-60% of the spike.
Sibling cards merchants should reference together
| Card | Why pair it with Refund Rate |
|---|---|
| WC Refund Value Trend | The dollar-amount view. Catches when refund value rises but rate stays flat (denominator growing too). |
| WC Top Refunded Products | Shows which SKUs drive refunds. 80% from 20% of SKUs is the usual pattern. |
| WC Total Revenue | Gross of refunds. Pair to see headline + leakage together. |
| WC Failed Order Rate | Cousin metric. Failures happen pre-payment; refunds happen post-payment. Different fixes. |
| BC Refund Rate | Same separate-refund-object pattern as Woo. Useful peer for agencies. |
| Shopify Refund Rate | SaaS-platform peer. Definition differs slightly (status-driven vs object-sum). |
| Stripe Refund Rate | Cross-check against payment processor for stores using WC Stripe Gateway. |
Reconciling against the vendor’s own dashboard
Where to look in WooCommerce Admin: WP Admin → WooCommerce → Reports → Orders → Refunds shows the refund total and count by date. Divide by Sales by date gross sales to compute the rate. WC 4.0+ also exposes Analytics → Revenue with a Refunds column. Other WP Admin views that look like the same number but are not:- Stats overview widget: today only, in WP-site timezone.
- Orders → Filter “Refunded”: shows orders where the parent post status is
refunded. Misses partial refunds (the parent stayscompleted).
| Reason | Direction of divergence |
|---|---|
Time-zone. WC reports run on WP-site timezone (wp_timezone_string()); Vortex IQ on UTC. Refunds processed near midnight on boundary days fall on different sides. | +/- 1 day at the boundary |
| Self-hosted server uptime. Indexer cannot poll during host outage; refund events appear late. | Ours temporarily lower; self-resolves |
Plugin-version compatibility. Some refund-automation plugins bypass the standard refund post type and store custom meta. The Vortex IQ engine reads shop_order_refund posts only. | Either; investigate per-merchant |
| Refund-object aggregation. WC counts each refund post separately. Some merchant-side plugins collapse multiple refunds into one event. | Plugins lower than this card |
Currency plugin behaviour. Multi-currency stores: refund posts inherit order currency. If order is converted via FX plugin and refund is in original currency, the ratio breaks. Filter by currency. | Material for international stores |
| Card | Expected relationship | What causes legitimate divergence |
|---|---|---|
stripe.stripe_refund_rate | Stripe rate ~= WC rate for Stripe-only Woo stores | Stripe sees only Stripe-routed refunds. PayPal refunds, BACS reversals, gift-card returns, manual cash refunds are invisible to Stripe. |
paypal.pp_refund_value | Subset, PayPal refunds only | PayPal-checkout subset by definition. |
Known limitations / merchant FAQs
Hosted-vs-self-hosted, does it affect the rate? The number is the same. Self-hosted Woo on flaky hosting may show daily-granularity refund spikes a day late. Status-filter selection, why this set? Numerator: all refund posts attached to orders in the window. Denominator:completed + processing + refunded orders, fully refunded orders are still revenue that got refunded, so they belong in the denominator. Excluding them would understate the rate.
Refund-object accounting, what should I expect to see?
Each refund event is a separate WC post. A multi-step refund (e.g. customer returns 1 item, then returns another 2 weeks later) is two refund posts. Both contribute to the numerator. The parent order’s status flips to refunded only when refunded amount = order total.
Plugin-induced data shape variance, how is this card protected?
The Vortex IQ Woo engine reads only the canonical shop_order_refund post type. Plugins that store refund-like activity in custom meta without creating refund posts (rare but exists) are invisible. If you spot a refund in WC Admin that does not appear here, contact support.
Multi-currency configuration, how does it affect the rate?
Currency-agnostic ratio. But if your store sells in mixed currencies, ensure refund posts inherit their parent order’s currency, multi-currency plugins occasionally store the refund in shop base currency while the order is in customer currency, which makes the ratio meaningless. Filter by currency for accuracy.
Why does WooCommerce and Stripe disagree?
Stripe sees only WC Stripe Gateway refunds. PayPal refunds, manual cash refunds, gift-card returns, and BACS reversals are invisible to Stripe. Stripe rate <= Woo rate, always. A Stripe rate that matches Woo exactly suggests near-100% Stripe payment mix.
Today is jumpy, why?
Refund-rate is a ratio of small samples on a daily view. A single 5,000-revenue day spikes the rate to 10% on its own. Use rolling 7D / 30D for stable reads.
Sync-lag from self-hosted server slowness, how do I detect it?
If a refund spike appears 1-2 days late and your host page reported an outage, that is the cause. Self-resolves at the next clean poll. Persistent lag (multi-day) suggests REST API key issues or IP block, contact support.
My WP Admin shows a different rate, debug playbook:
- Match date range exactly.
- Match status filter for the denominator (
completed + processing + refunded). - WC’s Refunds report counts refund posts; verify your plugin is not collapsing them.
- Multi-currency: filter both views to a single currency.
- If gap > 2% after all four checks, contact support.