Changing a price by filtering woocommerce_get_price alters it everywhere at once, including the catalogue and the order confirmation email — and it fires often enough to be a performance problem. For a cart-dependent price, the cart is the right place.
add_action( 'woocommerce_before_calculate_totals', function ( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
foreach ( $cart->get_cart() as $item ) {
if ( $item['quantity'] >= 10 ) {
$item['data']->set_price( $item['data']->get_price() * 0.9 );
}
}
} );
The is_admin() guard is required or the hook also runs while an order is being edited in wp-admin and re-discounts an already-discounted line. Because the cart is recalculated on every change, the callback runs frequently — keep it free of queries. Anything the customer should see before adding to cart needs a separate display filter as well; this hook only affects totals.