Как скрыть неоплаченные курсы LearnPress из каталога до оплаты

На учебных сайтах часто возникает одна и та же проблема: курс уже добавлен в каталог, но доступ к нему должен открываться только после успешной оплаты. Если карточка курса видна всем, пользователь может перейти на страницу, увидеть программу и цену, но при этом не понимать, почему курс уже отображается в каталоге до покупки. Это особенно заметно, когда LearnPress работает вместе с WooCommerce и курс продаётся как товар.

Ниже — рабочий сценарий: скрываем неоплаченные курсы из архивов и списков, но не ломаем страницу самого курса, если она нужна для предпросмотра или SEO. Решение строится на стандартных возможностях WordPress и WooCommerce, без выдуманных хуков и без привязки к несуществующим API.

Когда это действительно нужно

Сценарий полезен, если у вас есть каталог курсов, витрина с подборками или главная страница с блоком последних курсов. Пользователь не должен видеть в списке то, что ещё не купил, особенно если курс создаётся как товар WooCommerce и доступ открывается после смены статуса заказа.

Типичные признаки проблемы:

  • курс виден в архиве, хотя заказ ещё в статусе pending или on-hold;
  • в каталоге показываются курсы, которые уже добавлены в корзину, но не оплачены;
  • на сайте есть смешанный каталог: часть курсов бесплатные, часть платные, и нужно скрывать только платные до оплаты;
  • после оплаты курс должен появляться в списке автоматически, без ручной правки.

Диагностика: где именно появляется лишний курс

Перед правкой кода важно понять, на каком уровне курс выводится. В WordPress это может быть архив записи, блок на главной, виджет, шорткод или кастомный запрос темы. Если вы начнёте фильтровать не тот запрос, курс продолжит появляться в другом месте.

Проверьте, как курс связан с оплатой

В LearnPress курс может быть бесплатным, а может быть связан с товаром WooCommerce. Для платного сценария обычно важно, чтобы у пользователя был завершённый заказ, а не просто запись в корзине. Если у вас уже есть логика выдачи доступа после оплаты, не дублируйте её отдельной самописной проверкой на уровне шаблона курса.

Проверьте, где формируется список курсов

Если курсы выводятся через стандартный архив, можно отфильтровать основной запрос. Если список собирается через WP_Query в шаблоне или в блоке темы, потребуется отдельная правка этого запроса. Для начала удобно временно включить логирование или посмотреть SQL-запрос через отладочные инструменты, чтобы убедиться, что фильтр действительно влияет на нужный источник данных.

Решение: скрываем неоплаченные курсы через фильтрацию запроса

Самый практичный вариант — пометить платные курсы и исключать их из публичного каталога, если у пользователя нет доступа. Для этого лучше не опираться только на статус товара WooCommerce, а проверять, открыт ли курс для текущего пользователя. В LearnPress для этого есть стандартная логика доступа к курсу.

Ниже пример для functions.php дочерней темы или небольшого кастомного плагина. Он показывает общий подход: если курс платный и доступ к нему не открыт, запись не попадает в архивные списки.

add_action( 'pre_get_posts', function( $query ) {
	if ( is_admin() || ! $query->is_main_query() ) {
		return;
	}

	if ( ! $query->is_post_type_archive( 'lp_course' ) && ! $query->is_tax() ) {
		return;
	}

	$post_type = $query->get( 'post_type' );
	if ( $post_type && $post_type !== 'lp_course' ) {
		return;
	}

	$meta_query = (array) $query->get( 'meta_query' );

	$meta_query[] = array(
		'relation' => 'OR',
		array(
			'key'     => '_lp_price',
			'compare' => 'NOT EXISTS',
		),
		array(
			'key'     => '_lp_price',
			'value'   => '0',
			'compare' => '=',
		),
	);

	$query->set( 'meta_query', $meta_query );
} );

Этот пример подходит только как отправная точка. В разных версиях LearnPress и в разных схемах продажи курс может хранить цену не в том же мета-поле, а логика доступа может быть завязана на связанный товар WooCommerce. Поэтому надёжнее использовать не только цену, а фактическую проверку доступа. Если у вас курс продаётся через WooCommerce, лучше отталкиваться от статуса заказа и прав доступа пользователя.

Более безопасный вариант: скрывать только те курсы, к которым нет доступа

Если задача не в том, чтобы прятать все платные курсы, а именно в том, чтобы не показывать неоплаченные, логика должна учитывать текущего пользователя. Пример ниже показывает общий принцип: в архиве остаются бесплатные курсы и курсы, к которым у пользователя уже есть доступ.

add_filter( 'the_posts', function( $posts, $query ) {
	if ( is_admin() || ! $query->is_main_query() ) {
		return $posts;
	}

	if ( ! $query->is_post_type_archive( 'lp_course' ) ) {
		return $posts;
	}

	$filtered = array();

	foreach ( $posts as $post ) {
		if ( $post->post_type !== 'lp_course' ) {
			$filtered[] = $post;
			continue;
		}

		$course_id = $post->ID;

		if ( function_exists( 'learn_press_user_has_enrolled_course' ) && is_user_logged_in() ) {
			$user_id = get_current_user_id();
			if ( learn_press_user_has_enrolled_course( $user_id, $course_id ) ) {
				$filtered[] = $post;
				continue;
			}
		}

		$price = get_post_meta( $course_id, '_lp_price', true );
		if ( empty( $price ) || (string) $price === '0' ) {
			$filtered[] = $post;
		}
	}

	return $filtered;
}, 10, 2 );

Здесь есть важная оговорка: функция learn_press_user_has_enrolled_course() может быть доступна не во всех конфигурациях и версиях. Перед использованием проверьте, что она реально существует в вашей установке. Если нет — используйте штатные методы LearnPress, которые уже есть в проекте, либо проверяйте доступ через ту логику, которая у вас отвечает за выдачу курса после оплаты.

Если курс продаётся через WooCommerce

Когда LearnPress связан с WooCommerce, лучше не пытаться угадывать доступ по одному только статусу заказа в каталоге. У пользователя может быть несколько заказов, возврат, повторная покупка или ручная выдача доступа. Поэтому скрытие лучше строить на факте доступа к курсу, а не на косвенных признаках.

Если вам нужно именно отфильтровать товары/курсы в каталоге WooCommerce, можно использовать стандартный фильтр woocommerce_product_query. Это полезно, когда курс отображается как товар, а не как отдельная страница курса.

add_action( 'woocommerce_product_query', function( $q ) {
	if ( is_admin() ) {
		return;
	}

	$meta_query = $q->get( 'meta_query' );
	if ( ! is_array( $meta_query ) ) {
		$meta_query = array();
	}

	$meta_query[] = array(
		'relation' => 'OR',
		array(
			'key'     => '_lp_price',
			'compare' => 'NOT EXISTS',
		),
		array(
			'key'     => '_lp_price',
			'value'   => '0',
			'compare' => '=',
		),
	);

	$q->set( 'meta_query', $meta_query );
} );

Этот вариант уместен только если вы точно понимаете, что каталог WooCommerce и каталог LearnPress у вас совпадают по смыслу. Если нет, можно случайно скрыть обычные товары магазина. В смешанных проектах лучше ограничивать фильтр только страницами курсов и только архивами LearnPress.

Проверка результата после внедрения

После добавления кода проверьте не только внешний вид каталога, но и реальные сценарии пользователя. Ошибка здесь часто проявляется не сразу: на главной курсы скрылись, а в поиске, архиве рубрик или в блоке «популярные курсы» они всё ещё видны.

  • Откройте каталог курсов в режиме гостя и убедитесь, что неоплаченные курсы не отображаются.
  • Авторизуйтесь под пользователем без доступа и проверьте ту же страницу.
  • Откройте страницу уже оплаченного курса — она должна быть доступна, если доступ выдан.
  • Проверьте поиск по сайту и архивы таксономий, если они используются.
  • После оплаты оформите тестовый заказ и убедитесь, что курс появляется в каталоге только в том случае, если это предусмотрено вашей логикой.

Если используете кэш, очистите его после правки. Иначе вы можете принять старую версию страницы за результат работы кода.

Частые ошибки и как их исправить

Фильтр применили только к главному запросу

Это частая ситуация: на странице архива всё выглядит правильно, но блоки темы или шорткоды продолжают показывать курс. Причина в том, что они используют отдельный WP_Query. В таком случае нужно править именно тот запрос, который формирует список, либо добавить аналогичную фильтрацию в шаблон блока.

Скрыли не только неоплаченные, но и бесплатные курсы

Такое происходит, если проверка цены построена слишком грубо. Например, поле цены хранится в нестандартном формате, а условие считает пустое значение платным курсом. Перед запуском на продакшене проверьте несколько типов курсов: бесплатный, платный, уже купленный, курс с ручной выдачей доступа.

Использовали мета-поле, которое не совпадает с вашей версией LearnPress

Не стоит копировать код без проверки структуры данных. В LearnPress и связанных расширениях названия мета-полей могут отличаться. Если поле не то, фильтр будет работать непредсказуемо. Сначала посмотрите, какие метаданные реально записаны у курса, и только потом фиксируйте условие.

Спрятали курс в каталоге, но он всё ещё доступен по прямой ссылке

Это не ошибка, если вы сознательно оставили страницу курса открытой для предпросмотра. Но если курс должен быть полностью закрыт до оплаты, нужно отдельно ограничить доступ к самой странице, а не только к архиву. Это уже другая задача: скрытие из каталога не равно защите контента.

Что лучше: код, плагин или настройка темы

ПодходКогда подходитМинус
Код в дочерней темеНужна точечная логика и контроль над запросамиЗависит от темы и требует аккуратного обновления
Небольшой кастомный плагинЛогика должна переживать смену темыНужно один раз правильно оформить плагин
Настройки темы/конструктораКаталог собирается без кода и без сложной логикиОбычно не хватает гибкости для проверки доступа

Практические советы по безопасности и производительности

Не добавляйте тяжёлые проверки в каждый запрос без необходимости. Если каталог большой, лишние обращения к метаданным и проверкам доступа могут замедлить страницу. Лучше ограничить фильтр только архивами курсов и только теми страницами, где это действительно нужно.

Если логика доступа сложная, вынесите её в отдельный мини-плагин. Так вы не потеряете решение при смене темы и не будете искать код в functions.php через полгода. Для отладки используйте error_log() только временно и не оставляйте его в продакшене.

Если у вас есть кэш страниц, обязательно проверьте поведение для гостя и для авторизованного пользователя. В смешанных каталогах кэш может показать один и тот же HTML всем, и тогда скрытие курсов будет работать только формально.

Если вам нужно не только скрытие, но и более аккуратная выдача контента, иногда удобнее разделить задачи: каталог фильтровать кодом, а оформление карточек и уведомления вынести в отдельные блоки. Для интерфейсных подсказок можно использовать, например, WPRemark, если нужен заметный блок с предупреждением о доступе, но саму логику скрытия всё равно лучше держать в коде.

Проверяйте решение на тестовом пользователе с чистой историей заказов. Это самый надёжный способ убедиться, что курс действительно скрывается до оплаты и появляется после неё только там, где вы этого ожидаете.

Добавь в закладки и поделись с друзьями:

⭐⭐⭐⭐⭐
Как добавить виджет новостей в LearnPress: пошаговое руководство с примерами кода
13.12.2025
Как решить проблему несинхронизации статусов заказов WooCommerce и LearnPress при использовании различных платежных систем
27.06.2026
Решение проблемы несинхронизированных статусов заказов WooCommerce и LearnPress
25.04.2026
Как автоматизировать обновление курсов в LearnPress: практическое руководство
26.11.2025
Как настроить автозаполнение поля оплаты WooCommerce для LearnPress
27.05.2026
×
до 3225₽

WPShop - честная партнерка!

Зарабатывай с каждой продажи

Подключиться ⋙