WooCommerce Refund Button Disabled on HPOS Order Screen

WooCommerce refund button greyed out on HPOS order edit screen after 10.8? Here is why the capability check fails and the action filter fix that works again.
WooCommerce Refund Button Disabled on HPOS Order Screen
WooCommerceHPOSWordPress
June 4, 20266 min read1028 words

The Problem

A client store updated to WooCommerce 10.8 last weekend. By Tuesday morning two of their refunds team members were pinging support: the Refund button on the order edit screen was greyed out and the hover tooltip said nothing. Clicking it did nothing. Opening the refund panel via the keyboard shortcut still rendered the modal, but the Refund manually and Refund via Stripe buttons inside the modal were also disabled.

The shop owner could refund just fine. So could the two admins on the standard WordPress administrator role. The two staff members affected were both on a custom Shop Manager Plus role that the previous developer had cloned from shop_manager and given a couple of extra capabilities for the editorial team. That role had refunded thousands of orders without issue under 10.7. After the 10.8 update the button was dead.

The store had been running on HPOS for six months. WooCommerce status report showed everything green. No errors in wc-logs, nothing in the PHP error log, nothing in the browser console.

Why It Happens

WooCommerce 10.8 introduced a stricter capability check on the HPOS order edit screen. Before 10.8 the refund UI in the legacy and HPOS admin both rendered if the current user could edit_shop_orders. That is broad enough to cover most custom roles cloned from shop_manager. In 10.8 the HPOS order screen now wraps the refund control in a check that calls current_user_can('refund_shop_orders', $order_id) via a new helper in Automattic\WooCommerce\Internal\Admin\Orders\Edit.

refund_shop_orders is a fresh capability. It is registered on the administrator and shop_manager roles when WooCommerce updates, but only on roles that the updater can recognise. A role cloned from shop_manager before 10.8, using add_role() in a custom plugin or a role editor like Members or User Role Editor, does not get the new capability mapped. The updater iterates shop_manager and administrator by name, not by inheritance.

That is why every custom role that was working fine under 10.7 silently loses the refund button on the first cache flush after the 10.8 update. The legacy post screen still works for any role that has edit_shop_orders because the legacy admin path was left on the old check for backwards compatibility, but HPOS is now stricter.

The WooCommerce 10.8 capabilities documentation lists the new capability under the order management section but does not call out the migration risk for custom roles. If you look at the diff in core, the WC_Install::create_roles() method adds the cap, but WC_Install::maybe_add_capabilities() only touches the two built-in roles.

The Fix

Two options. Pick one based on how stable your custom roles are.

Option A: Grant the new capability to your custom role permanently. This is the right fix for stores with a fixed set of admin roles. Drop this into a custom plugin or your theme's functions.php. It runs once and the role definition is updated in the database:

add_action('init', function () {
    $role = get_role('shop_manager_plus');
    if (! $role) {
        return;
    }
    if (! $role->has_cap('refund_shop_orders')) {
        $role->add_cap('refund_shop_orders');
    }
}, 20);

After it runs once the capability is stored on the role and persists across WooCommerce updates. Comment the action out afterwards or leave it in. It is idempotent once the role has the cap.

If you use a role editor plugin like Members, you can tick the refund_shop_orders checkbox on the role and skip the code path entirely. Confirm the capability is there by querying wp_options for the wp_user_roles option and looking for your role key.

Option B: Map the check at runtime with user_has_cap. Use this if your role hierarchy is more dynamic. For example, you assign refund rights via a meta flag on the user rather than a static role. Filter the capability map so any user that already has edit_shop_orders inherits refund_shop_orders:

add_filter('user_has_cap', function ($allcaps, $caps, $args, $user) {
    if (! in_array('refund_shop_orders', $caps, true)) {
        return $allcaps;
    }
    if (! empty($allcaps['edit_shop_orders'])) {
        $allcaps['refund_shop_orders'] = true;
    }
    return $allcaps;
}, 10, 4);

This mirrors how shop_manager is treated by core but applies to any role with edit_shop_orders. The filter runs on every capability check, so keep the body short and avoid any database calls.

Verify the fix on the order screen. Log in as the affected user, open an order that has a paid payment, and confirm the Refund button is no longer greyed out. Then run a small refund through Stripe or PayPal and check the order notes for the refund record. If the button is enabled but the gateway refund fails, the issue has moved to your gateway credentials — that is a different problem.

The other thing worth checking: if you use the WooCommerce REST API with an application password issued to one of the affected users, the new check also applies to POST /orders/{id}/refunds. The endpoint returns 401 woocommerce_rest_cannot_create instead of refunding. Both fixes above cover the REST path because they update the underlying capability map.

The Lesson

WooCommerce 10.8 split edit_shop_orders and refund_shop_orders into separate capabilities and only auto-grants the new one to the two built-in roles. Any cloned role loses the refund button silently. Grant the cap on your custom role at init, or filter user_has_cap so the check inherits from edit_shop_orders. The legacy order screen still works either way, which makes the bug easy to miss until the refunds team complains.

If your store update silently broke a custom admin role and you want it audited end to end, that is the kind of HPOS upgrade work I bill for. See my services. For a related capability gotcha on the same admin screen, read WooCommerce HPOS bulk status update failing silently.

Custom WooCommerce role silently broken after an update? Get it patched.

Back to blogStart a project