woocommerce – How to make the product content inaccessible except for certain roles without any template script changes, with the exception of the plug-in?

I create a plugin to hide certain categories only for certain roles.

add_action (& # 39; woocommerce_product_query & # 39 ;, & gt; wcpp_hide_product & # 39;);
function wcpp_hide_product ($ q) {
// if main request, ignore
if (! $ q-> is_main_query ()) return;
// jika admin, skip
if (is_admin ()) return;
// if the user is not logged in or has no roles PRIVATE_MEMBER hide product
$ current_user = wp_get_current_user ();
if (! isset ($ current_user-> roles) ||! in_array (& # 39; PRIVATE_MEMBER & # 39 ;, $ current_user-> roles)) {
$ tax_query = (array) $ q-> get (& # 39; tax_query & # 39;);
$ tax_query[] = table (
& # 39; taxonomy & # 39; => & # 39; product_cat & # 39;
& # 39; field & # 39; => & # 39; id,
& # 39; terms & # 39; => & # 39; PRIVATE_CATEGORY & # 39;
& # 39; operator & # 39; => & # 39; NOT IN & # 39;
)
$ q-> set (& # 39; tax_query & # 39 ;, $ tax_query);
}

}

This code will hide the product of the search. But if another user can still access the product detail page from the direct link.

here is my code to hide the product page that does not work.

add_action (& # 39; woocommerce_before_single_product & # 39;; & gt; wcpp_hide_product_detail & # 39;);
// add_action (& # 39; pre_get_posts & # 39 ;, & gt; wcpp_hide_product_detail & nbsp;);

function wcpp_hide_product_detail () {
if (is_admin ()) return;
global $ post;
$ terms = get_the_terms ($ post-> ID, & # 39; product_cat & # 39;);
if (empty ($ terms)) returns;
$ is_private = false;
foreach ($ terms like $ term) {
if (& # 39; PRIVATE_CATEGORY & # 39; == $ term-> term_id) {
$ is_private = true;
Pause;
}
}
if (! $ is_private) return;
$ current_user = wp_get_current_user ();
if (! isset ($ current_user-> roles) ||! in_array (& # 39; PRIVATE_MEMBER & # 39 ;, $ current_user-> roles)) {
// need to do something here, but not work
// maybe redirect
// wp_redirect (home_url ());
/ * or as a set 404
global $ wp_query;
$ wp_query-> set_404 ();
status_header (404);
* /
}
}

if I put redirect, script will say Unable to edit header information - headers already sent by ...

I think it will work if I can change the product query to a negative ID number that will trigger the product page not found. But I do not know the name of the hook of action to do it.

Thank you for any help.