Выдача Open Badges из WordPress (без тяжёлого плагина)
Выдавайте Open Badges из любого плагина курсов/LMS для WordPress (LearnDash, LifterLMS, Tutor), вызывая API Badges Ninja при завершении курса — без отдельного плагина для значков.
Nacho founded Badges Ninja to make issuing verifiable digital credentials as simple as a single API call — Open Badge v2.0 badges and certificates, minted, hosted, and verifiable without standing up your own issuer infrastructure.
Если вы ведёте курсы на WordPress, вам знаком этот сценарий: кто-то заканчивает модуль в LearnDash, LifterLMS или Tutor LMS, LMS отмечает это как «завершено», а дальше… ничего проверяемого не происходит. Может быть, вы отправляете PDF по почте. Может быть, устанавливаете отдельный плагин значков для WordPress, который добавляет собственные таблицы в базу данных, собственную библиотеку изображений и собственное представление о том, что такое «значок» — то, которое никто за пределами вашего сайта проверить не может.
Есть более простой путь. Плагины курсов WordPress уже вызывают action-хук в момент, когда кто-то завершает курс. Вы можете перехватить этот хук и сделать один HTTP-запрос к API Badges Ninja. Без дополнительного плагина, без дополнительного хостинга, без проприетарного формата значков — просто соответствующая стандарту credential Open Badge v2.0, которую получатель может верифицировать где угодно, добавить в LinkedIn и скачать в виде PDF.
В этой статье сначала разбирается общий паттерн, а затем показывается конкретный хук для LearnDash, LifterLMS и Tutor LMS.
Почему бы просто не использовать плагин значков для WordPress?
Специализированные плагины значков для WordPress решают более узкую задачу, чем кажется на первый взгляд. Они хорошо справляются с отображением изображения на странице «мои достижения» внутри вашего сайта. Но в том, что действительно важно для credential, они, как правило, гораздо слабее:
- Портативность. Значок, который существует только как шорткод на вашем сайте WordPress, — не credential, а украшение. В тот момент, когда получатель меняет работу, меняет почтовый ящик или ваш сайт падает, «доказательство» пропадает вместе с ним.
- Верификация. Любой человек должен иметь возможность проверить, что credential настоящая, не входя в админку вашего WordPress. Open Badge v2.0 по своей природе даёт публичный URL для верификации и машиночитаемый JSON-LD.
- Поверхность обслуживания. Каждый плагин WordPress — это ещё одна вещь, которую нужно патчить, ещё одна таблица базы данных, которую нужно резервировать, ещё одна точка отказа при обновлении ядра.
Вызов внешнего API при завершении курса обходит все три проблемы. WordPress остаётся ответственным за то, в чём он хорош — доставку контента и отслеживание прогресса, — а сама credential живёт в системе, созданной специально для того, чтобы её выдавать, верифицировать и делиться ею.
Паттерн: один хук, один HTTP-запрос
Каждый крупный LMS-плагин для WordPress вызывает action, когда пользователь завершает курс. Интеграция всегда состоит из одних и тех же трёх шагов:
- Подключиться к action завершения для вашей конкретной LMS.
- Найти (или создать) личность получателя — обычно это просто email и отображаемое имя его аккаунта WordPress.
- Вызвать
POST /awardsв API Badges Ninja черезwp_remote_post(), передав ID значка и данные получателя.
Сначала вам понадобится API-ключ. Сгенерируйте его в своей панели управления в разделе API Keys — полное пошаговое руководство смотрите в гайде по API-ключам. Ключи показываются только один раз при создании, поэтому храните его в wp-config.php как константу, а не в базе данных:
define( 'BADGES_NINJA_API_KEY', 'bws_your_32_hex_key_here' );
define( 'BADGES_NINJA_BADGE_ID', 'your-badge-id' );

Держите этот ключ на стороне сервера. Всё в этом руководстве выполняется на PHP на вашем сервере WordPress — wp_remote_post() это серверный вызов, а wp-config.php никогда не отправляется в браузер. Это именно то, что нужно: ключ путешествует от сервера к серверу по HTTPS, и ни один посетитель сайта не может его прочитать. Никогда не переносите этот вызов в клиентский JavaScript — fetch() из браузера со встроенным ключом раскрыл бы его любому через инструменты разработчика браузера. Стоит соблюдать две привычки: убедитесь, что ваш хостинг отдаёт wp-config.php как исполняемый PHP (никогда как обычный текст, и не оставляйте копию wp-config.php.bak в корне сайта), и создайте отдельный API-ключ специально для этой интеграции, чтобы можно было отозвать его независимо в случае компрометации — не ломая остальные ваши инструменты.
Общая вспомогательная функция делает хук каждой LMS минимальным:
function badges_ninja_issue_award( $recipient_email, $recipient_name, $badge_id ) {
$response = wp_remote_post( 'https://api.badges.ninja/awards', array(
'headers' => array(
'X-Api-Key' => BADGES_NINJA_API_KEY,
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( array(
'badgeId' => $badge_id,
'recipient' => array(
'name' => $recipient_name, // must be at least 5 characters
'email' => $recipient_email,
),
'issuedOn' => round( microtime( true ) * 1000 ), // epoch milliseconds
) ),
'timeout' => 15,
) );
if ( is_wp_error( $response ) ) {
error_log( 'Badges Ninja award failed: ' . $response->get_error_message() );
return false;
}
$code = wp_remote_retrieve_response_code( $response );
if ( $code >= 400 ) {
error_log( 'Badges Ninja award failed with HTTP ' . $code . ': ' . wp_remote_retrieve_body( $response ) );
return false;
}
return json_decode( wp_remote_retrieve_body( $response ), true );
}
Email получателя хешируется на сервере алгоритмом SHA-256 сразу после того, как попадает в Badges Ninja, а возвращаемые данные содержат URL верификации награды — удобно, если вы хотите показать ссылку «посмотреть вашу credential» прямо в WordPress после успешного вызова.
LearnDash
LearnDash вызывает learndash_course_completed с одним аргументом-массивом, содержащим user и course:
add_action( 'learndash_course_completed', function( $data ) {
$user = $data['user'];
$course = $data['course'];
// Map LearnDash course ID → your Badges Ninja badge ID.
// A simple approach: store the badge ID in a course custom field.
$badge_id = get_post_meta( $course->ID, 'badges_ninja_badge_id', true );
if ( ! $badge_id ) {
return; // no badge configured for this course
}
badges_ninja_issue_award( $user->user_email, $user->display_name, $badge_id );
} );
Хранить ID значка как метаданные курса (а не в единой глобальной константе BADGES_NINJA_BADGE_ID) — правильное решение, как только у вас появляется больше одного курса: каждый курс соответствует своему собственному дизайну значка.
LifterLMS
LifterLMS вызывает lifterlms_course_completed с $user_id и $course_id:
add_action( 'lifterlms_course_completed', function( $user_id, $course_id ) {
$user = get_userdata( $user_id );
$badge_id = get_post_meta( $course_id, 'badges_ninja_badge_id', true );
if ( ! $user || ! $badge_id ) {
return;
}
badges_ninja_issue_award( $user->user_email, $user->display_name, $badge_id );
}, 10, 2 );
Tutor LMS
Tutor LMS вызывает tutor_course_complete_after с ID курса, а текущего пользователя вы получаете из сессии:
add_action( 'tutor_course_complete_after', function( $course_id ) {
$user_id = get_current_user_id();
$user = get_userdata( $user_id );
$badge_id = get_post_meta( $course_id, 'badges_ninja_badge_id', true );
if ( ! $user || ! $badge_id ) {
return;
}
badges_ninja_issue_award( $user->user_email, $user->display_name, $badge_id );
} );
Если ваша настройка Tutor LMS вызывает завершение из фонового процесса (например, через автоматическую проверку теста), замените get_current_user_id() на ID пользователя, который предоставляет ваш конкретный хук — один раз проверьте payload с помощью error_log( print_r( func_get_args(), true ) ) во время настройки, чтобы убедиться в аргументах.
Проектирование значка до подключения хука
Прежде чем что-либо из этого сработает, вам нужен значок для выдачи. Визуальный редактор закрывает эту задачу без необходимости держать в штате дизайнера — более 80 шаблонов форм, настраиваемые цвета, библиотека иконок и загрузка логотипа, всё с привязкой к сетке. Если вы новичок в редакторе, статья Создаём ваш первый верифицируемый цифровой сертификат проведёт вас через выбор шаблона и создание чего-то, чем получатели действительно захотят поделиться.
Когда значок создан, возьмите его ID из панели управления (или через GET /badges) и вставьте в кастомное поле вашего курса.
Что на самом деле получает получатель
Это та часть, которую самодельный плагин значков для WordPress не способен воспроизвести. Когда POST /awards выполняется успешно, получатель получает:
- Уникальный URL верификации, который может открыть кто угодно — без входа в WordPress — показывающий, что credential настоящая, кто её выдал и когда.
- QR-код и PDF-сертификат формата A4, создаваемые автоматически.
- Кнопку «Добавить в профиль LinkedIn», если у вашего эмитента настроен ID организации LinkedIn — см. Добавление кнопки LinkedIn «Добавить в профиль» для одноразовой настройки.
- Доступ к собственному порталу получателя на
badges.ninja/meчерез вход по magic-ссылке — без пароля, который можно забыть, без отдельного аккаунта, которым нужно управлять на вашем сайте WordPress. - Соответствующий стандарту Open Badge v2.0 JSON-LD, так что любой верификатор, поддерживающий OB (не только Badges Ninja), может его проверить.
Ничего из этого не входит в задачи WordPress, и ничто из этого не должно жить исключительно в вашей базе данных WordPress.
Изящная обработка сбоев
Хуки завершения курса срабатывают во время живого запроса страницы, и медленный или неудачный вызов API не должен блокировать экран «поздравляем» для учащегося. Две практические доработки, когда вы выходите за рамки proof of concept:
Не позволяйте неудачной выдаче награды блокировать интерфейс. Функция-помощник выше уже логирует ошибку и возвращает false вместо того, чтобы выбрасывать исключение — LearnDash, LifterLMS и Tutor продолжают свой обычный процесс завершения независимо от того, что вернёт badges_ninja_issue_award().
В масштабе — ставьте в очередь, а не вызывайте напрямую. Если вы одновременно проводите большие когорты через один курс (дата старта когорты, открытие записи вебинара), вызывайте из хука запланированное событие WP-Cron вместо синхронного вызова API:
add_action( 'learndash_course_completed', function( $data ) {
wp_schedule_single_event( time() + 30, 'badges_ninja_deferred_award', array(
$data['user']->user_email,
$data['user']->display_name,
get_post_meta( $data['course']->ID, 'badges_ninja_badge_id', true ),
) );
} );
add_action( 'badges_ninja_deferred_award', 'badges_ninja_issue_award', 10, 3 );
Это распределяет нагрузку и не даёт одному медленному запросу стать проблемой учащегося. Для по-настоящему больших пакетов (целая прошлая когорта, мигрируемая за один раз) полностью пропустите путь с хуком WordPress и используйте массовую выдачу через CSV — загрузите один раз, а платформа выполнит постраничную выдачу на сервере со встроенной паузой и возобновлением.
Замечание об идемпотентности
Если ваша LMS когда-либо вызывает хук завершения более одного раза для одного и того же пользователя (частый пограничный случай LearnDash при повторном прохождении теста, или повторная синхронизация Tutor LMS), вы не захотите выдать один и тот же значок дважды. Самая простая защита — флаг в user-meta WordPress, который вы устанавливаете сразу после успешного вызова:
if ( get_user_meta( $user_id, 'badges_ninja_awarded_' . $course_id, true ) ) {
return; // already issued
}
$result = badges_ninja_issue_award( $user->user_email, $user->display_name, $badge_id );
if ( $result ) {
update_user_meta( $user_id, 'badges_ninja_awarded_' . $course_id, true );
}
Если вы выдаёте награды в реальном объёме и хотите обрабатывать это централизованно, а не для каждого сайта отдельно, посмотрите на обработку событий на основе вебхуков или полный быстрый старт по API для форматов запросов и ответов по всем эндпоинтам.
Когда этот паттерн перерастает один курс
Приведённые выше примеры предполагают один значок на курс. Как только вы начинаете использовать составные credentials — значок трека, который открывается после нескольких значков курсов, или итоговый значок, зависящий от нескольких предварительных условий, — перенесите сопоставление «какой значок для какого завершения» из метаданных записи в небольшую таблицу соответствий или управляйте им из автоматизации на основе таблиц (Airtable или Google Sheets), которая вызывает тот же POST /awards. Сам вызов API не меняется — меняется только то, что решает, когда его выполнять.
Устранение неполадок интеграции
Небольшое число проблем объясняет большую часть трудностей, с которыми люди сталкиваются при первой настройке этого решения:
Хук никогда не срабатывает. Убедитесь, что вы подключаетесь к правильному action для фактической версии вашего плагина — в частности, LearnDash менял названия хуков завершения в разных крупных релизах (более старые установки иногда всё ещё полагаются на то, что learndash_course_completed срабатывает из другого участка кода, чем в новых версиях). Добавьте временный error_log() в начало вашего callback и наблюдайте за wp-content/debug.log, проходя тестовый курс под одноразовым тестовым пользователем.
wp_remote_post() завершается по таймауту. Среды с общим хостингом иногда блокируют исходящий HTTPS к незнакомым хостам или ограничивают таймауты запросов ниже установленных выше 15 секунд. Если вызовы стабильно завершаются по таймауту, уточните у хостинг-провайдера, разрешены ли исходящие соединения к api.badges.ninja, и убедитесь, что ваше расширение PHP curl имеет актуальные сертификаты CA — устаревший набор сертификатов — распространённая тихая проблема на старых стеках общего хостинга.
400 Bad Request. В payload отсутствует обязательное поле или он имеет неправильную форму. POST /awards ожидает badgeId, вложенный объект recipient с name и email, а также временную метку issuedOn — функция-помощник выше отправляет именно это. Самая частая заминка — имя получателя: оно должно содержать не менее 5 символов, поэтому отображаемое имя WordPress вроде «Jo» отклоняется. Если отображаемое имя слишком короткое, используйте полное имя аккаунта (или локальную часть email) в качестве запасного варианта.
401 Unauthorized. Константа API-ключа не загружается, либо ключ был перевыпущен после того, как константа была установлена. Ключи показываются только один раз при создании и не могут быть получены повторно — если вы потеряли из виду, какой ключ действующий, удалите устаревший и выпустите новый вместо того, чтобы гадать.
Значок выдан, но с неправильным дизайном. Это почти всегда устаревшее значение метаданных записи badges_ninja_badge_id, оставшееся с тестирования. Поскольку сопоставление хранится в записи курса, легко забыть, что вы изменили его после дублирования курса для новой когорты — стоит проверять это каждый раз, когда вы клонируете курс.
Дублирующиеся награды для одного и того же человека. Уже рассмотрено выше с защитой через user-meta, но стоит повторить: хуки завершения LMS не гарантированно срабатывают ровно один раз. Относитесь к «уже выдано» как к нормальному случаю, который нужно проверять, а не как к граничному случаю, который можно игнорировать.
Часто задаваемые вопросы
Работает ли это с наборами курсов WooCommerce? Да — если покупка набора должна запускать выдачу значка независимо от завершения курса (например, значок «сертифицированный участник» для каждого, кто покупает определённый набор), подключитесь к woocommerce_order_status_completed вместо action завершения LMS и ищите ID значка в метаданных товара, а не курса.
Могут ли получатели получить значок без аккаунта WordPress? Да, и в этом весь смысл. Награда привязана к их email-адресу в Badges Ninja, а не к записи пользователя WordPress. Если ваш сайт разрешает оформление заказа гостем или анонимное прохождение теста, просто убедитесь, что вы фиксируете настоящий email-адрес для передачи в badges_ninja_issue_award().
Что если я хочу проверять награды перед отправкой? Не вызывайте запрос напрямую из хука завершения. Вместо этого запишите ожидающую награду (пользователь, курс, временная метка) в кастомную таблицу или тип записи и выдайте её из запланированной задачи или через кнопку админа «Одобрить и выдать», которая по требованию вызывает ту же функцию-помощник.
Нужен ли отдельный значок для каждого курса, или один может охватывать несколько? Работает и так, и так. Одни эмитенты создают по значку на курс ради детализации; другие создают единый значок «программа завершена» и выдают его только после завершения всех обязательных курсов, проверяя выполнение предварительных условий внутри хука перед вызовом API.
Готовы выдать вашу первую верифицируемую credential? Начните бесплатно на badges.ninja — визуальный редактор, публичная страница верификации, PDF-сертификат, вывод в формате Open Badge v2.0. Без банковской карты.
Как создана эта статья
Некоторые статьи в этом блоге готовятся с помощью ИИ-ассистента, а затем проверяются, сверяются с фактами и редактируются командой Badges Ninja перед публикацией. Каждый пример кода и цена проверены на актуальном продукте. Подробнее о нашем редакционном процессе и работе с ИИ читайте на странице редакционного процесса .

Об авторе
Nacho Coll
Founder & Engineer at Badges Ninja
Nacho founded Badges Ninja to make issuing verifiable digital credentials as simple as a single API call — Open Badge v2.0 badges and certificates, minted, hosted, and verifiable without standing up your own issuer infrastructure. Writes about the Open Badges spec, credential verification, and running a credentialing platform serverless on AWS, from the operator side of the wire.
