Emitir Open Badges a partir do WordPress (Sem um Plugin Pesado)

Emite Open Badges a partir de qualquer plugin de curso/LMS do WordPress (LearnDash, LifterLMS, Tutor) chamando a API da Badges Ninja na conclusão — sem instalar um plugin de emblemas separado.

Nacho Coll Por Atualizado 13 min de leitura
Emite Open Badges a partir de qualquer plugin de curso/LMS do WordPress (LearnDash, LifterLMS, Tutor) chamando a API da Badges Ninja na conclusão — sem instalar um plugin de emblemas separado.

Se giras cursos no WordPress, já conheces a rotina: alguém termina um módulo no LearnDash, LifterLMS ou Tutor LMS, o LMS marca-o como “concluído” e depois… nada verificável acontece. Talvez envies um PDF por email. Talvez instales um plugin de emblemas dedicado ao WordPress que acrescenta as suas próprias tabelas de base de dados, a sua própria biblioteca de imagens e a sua própria ideia do que é um “emblema” — uma ideia que ninguém fora do teu site consegue verificar.

Há um caminho mais simples. Os plugins de cursos do WordPress já disparam um action hook no momento em que alguém conclui um curso. Podes apanhar esse hook e fazer uma única chamada HTTP à API da Badges Ninja. Sem plugin extra, sem alojamento extra, sem formato de emblema proprietário — apenas uma credencial Open Badge v2.0 compatível com os standards, que o destinatário pode verificar em qualquer lado, adicionar ao LinkedIn e descarregar como PDF.

Este artigo percorre o padrão uma vez e depois mostra o hook específico para o LearnDash, LifterLMS e Tutor LMS.

Porque não usar simplesmente um plugin de emblemas do WordPress?

Os plugins de emblemas dedicados ao WordPress resolvem um problema mais estreito do que parece. São bons a apresentar uma imagem numa página “as minhas conquistas” dentro do teu site. Costumam ser muito mais fracos nas coisas que realmente importam para uma credencial:

  • Portabilidade. Um emblema que só existe como shortcode no teu site WordPress não é uma credencial — é uma decoração. No momento em que o destinatário muda de emprego, muda de caixa de correio, ou o teu site fica indisponível, a “prova” vai com ele.
  • Verificação. Qualquer pessoa deve conseguir confirmar que uma credencial é real sem iniciar sessão na tua administração do WordPress. O Open Badge v2.0 dá-te, por design, um URL de verificação público e JSON-LD legível por máquina.
  • Superfície de manutenção. Cada plugin do WordPress é mais uma coisa a corrigir, mais uma tabela de base de dados a salvaguardar, mais um ponto de falha durante uma atualização do core.

Chamar uma API externa na conclusão do curso contorna estes três pontos. O WordPress continua responsável pelo que faz bem — entrega de conteúdo e acompanhamento do progresso — e a credencial em si vive num sistema construído especificamente para a emitir, verificar e partilhar.

O padrão: um hook, uma chamada HTTP

Todos os principais plugins de LMS do WordPress disparam uma ação quando um utilizador conclui um curso. A integração é sempre a mesma em três passos:

  1. Liga-te à ação de conclusão do teu LMS específico.
  2. Procura (ou cria) a identidade do destinatário — normalmente apenas o email e o nome de exibição da conta WordPress.
  3. Chama POST /awards na API da Badges Ninja com wp_remote_post(), passando o ID do emblema e os dados do destinatário.

Vais precisar primeiro de uma chave de API. Gera uma a partir do teu painel em API Keys — consulta o guia de chaves de API para o percurso completo. As chaves são mostradas uma única vez na criação, por isso guarda-a em wp-config.php como constante em vez de na base de dados:

define( 'BADGES_NINJA_API_KEY', 'bws_your_32_hex_key_here' );
define( 'BADGES_NINJA_BADGE_ID', 'your-badge-id' );

Newly created key revealed once

Mantém esta chave no servidor. Tudo neste guia corre em PHP no teu servidor WordPress — wp_remote_post() é uma chamada do lado do servidor, e o wp-config.php nunca é enviado ao browser. É exatamente isso que queres: a chave viaja de servidor a servidor via HTTPS, e nenhum visitante do site consegue lê-la. Nunca movas esta chamada para JavaScript do lado do cliente — um fetch() a partir do browser com a chave incluída exporia-a a qualquer pessoa através das ferramentas de programador do seu browser. Dois hábitos que vale a pena manter: assegura-te de que o teu alojamento serve o wp-config.php como PHP executado (nunca como texto simples, e não deixes uma cópia wp-config.php.bak na raiz do site), e cria uma chave de API dedicada para esta integração, para a poderes revogar de forma independente se algum dia for comprometida — sem quebrar as tuas outras ferramentas.

Uma função auxiliar partilhada mantém cada hook de LMS simples:

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 );
}

O email do destinatário é submetido a hash SHA-256 no servidor assim que chega à Badges Ninja, e a resposta devolvida inclui o URL de verificação do prémio — útil se quiseres mostrar um link “ver a tua credencial” logo ali no WordPress depois de a chamada ter sucesso.

LearnDash

O LearnDash dispara learndash_course_completed com um único argumento array contendo user e 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 );
} );

Guardar o ID do emblema como meta do curso (em vez de uma única constante global BADGES_NINJA_BADGE_ID) é a decisão certa assim que tiveres mais do que um curso — cada curso corresponde ao seu próprio design de emblema.

LifterLMS

O LifterLMS dispara lifterlms_course_completed com $user_id e $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

O Tutor LMS dispara tutor_course_complete_after com o ID do curso, e vais buscar o utilizador atual à sessão:

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 );
} );

Se a tua configuração do Tutor LMS disparar a conclusão a partir de um processo em segundo plano (por exemplo, via correção automática de um quiz), substitui get_current_user_id() pelo ID de utilizador que o teu hook específico fornece — verifica o payload com error_log( print_r( func_get_args(), true ) ) uma vez durante a configuração para confirmar os argumentos.

Desenhar o emblema antes de ligar o hook

Antes de qualquer uma destas ações disparar, precisas de um emblema para atribuir. O designer visual trata disto sem precisares de um designer na equipa — mais de 80 modelos de forma, cores personalizadas, biblioteca de ícones e envio de logótipo, tudo com alinhamento automático à grelha. Se és novo no designer, Designing Your First Verifiable Digital Certificate percorre a escolha de um modelo e o lançamento de algo que os destinatários vão realmente querer partilhar.

Depois de o emblema existir, obtém o seu ID a partir do painel (ou via GET /badges) e coloca-o no campo personalizado do teu curso.

O que o destinatário realmente recebe

É esta a parte que um plugin de emblemas do WordPress feito por medida não consegue replicar. Quando POST /awards tem sucesso, o destinatário recebe:

  • Um URL de verificação único que qualquer pessoa pode abrir — sem precisar de sessão iniciada no WordPress — mostrando que a credencial é real, quem a emitiu e quando.
  • Um código QR e um certificado em PDF A4, gerados automaticamente.
  • Um botão Adicionar ao Perfil do LinkedIn, se o teu emissor tiver um ID de organização do LinkedIn definido — consulta Add a LinkedIn “Add to Profile” Button para a configuração única.
  • Acesso ao seu próprio portal do destinatário em badges.ninja/me, através de início de sessão por magic link — sem palavra-passe a esquecer, sem conta separada a gerir no teu site WordPress.
  • JSON-LD Open Badge v2.0 compatível com os standards, para que qualquer verificador com suporte a OB (não só a Badges Ninja) o consiga validar.

Nada disto é trabalho do WordPress construir, e nada disto deveria viver apenas dentro da tua base de dados do WordPress.

Lidar com falhas de forma controlada

Os hooks de conclusão de curso disparam durante um pedido de página ao vivo, e uma chamada de API lenta ou falhada não deve bloquear o ecrã de “parabéns” do formando. Dois ajustes práticos assim que passares de uma prova de conceito:

Não deixes que um prémio falhado bloqueie a interface. A função auxiliar acima já regista e devolve false em vez de lançar uma exceção — o LearnDash, LifterLMS e Tutor continuam todos o seu fluxo normal de conclusão, independentemente do que badges_ninja_issue_award() devolva.

Coloca em fila em vez de chamar inline, à escala. Se estiveres a processar grandes coortes através de um único curso de uma vez (uma data de início de coorte, o desbloqueio de uma repetição de webinar), dispara um evento agendado do WP-Cron a partir do hook em vez de chamares a API de forma síncrona:

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 );

Isto distribui a carga e evita que um único pedido lento se torne um problema para o formando. Para lotes genuinamente grandes (uma coorte inteira já concluída, migrada de uma só vez), salta por completo o caminho do hook do WordPress e usa bulk awards via CSV — carrega uma vez, e a plataforma pagina a emissão do lado do servidor, com pausa/retoma incorporadas.

Uma nota sobre idempotência

Se o teu LMS alguma vez disparar o hook de conclusão mais do que uma vez para o mesmo utilizador (um caso limite comum no LearnDash com repetições de quiz, ou uma re-sincronização do Tutor LMS), não queres atribuir o mesmo emblema duas vezes. A proteção mais simples é uma flag de user-meta do WordPress que defines logo após uma chamada bem-sucedida:

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 );
}

Se estiveres a emitir a volume real e quiseres isto tratado de forma centralizada em vez de por site, consulta webhook-driven event handling ou o API quickstart completo para os formatos de pedido/resposta em todos os endpoints.

Quando este padrão ultrapassa um único curso

Os exemplos acima assumem um emblema por curso. Assim que estiveres a lidar com credenciais empilháveis — um emblema de percurso que se desbloqueia depois de vários emblemas de curso, ou um trabalho final que depende de múltiplos pré-requisitos — tira o mapeamento “que emblema, para que conclusão” da meta do post e coloca-o numa pequena tabela de consulta, ou conduz o processo a partir de uma automação apoiada em folha de cálculo (Airtable ou Google Sheets) que dispara a mesma chamada POST /awards. A chamada à API em si não muda; muda apenas o que decide quando a fazer.

Resolução de problemas da integração

Um punhado de problemas explica a maior parte do atrito que as pessoas encontram ao ligar isto pela primeira vez:

O hook nunca dispara. Confirma que estás a ligar-te à ação certa para a versão real do teu plugin — o LearnDash, em particular, mudou os nomes dos hooks de conclusão entre versões principais (instalações mais antigas por vezes ainda dependem de learndash_course_completed a disparar a partir de um caminho de código diferente do das mais recentes). Adiciona um error_log() temporário no topo do teu callback e observa wp-content/debug.log enquanto concluis um curso de teste como um utilizador descartável.

wp_remote_post() atinge o tempo limite. Os ambientes de alojamento partilhado por vezes bloqueiam HTTPS de saída para hosts desconhecidos, ou limitam os tempos de espera de pedido abaixo dos 15 segundos definidos acima. Se as chamadas atingirem o tempo limite de forma consistente, confirma junto do teu alojamento se as ligações de saída para api.badges.ninja são permitidas, e confirma se a tua extensão PHP curl tem certificados CA atuais — um pacote de certificados desatualizado é uma falha silenciosa comum em stacks de alojamento partilhado mais antigas.

400 Bad Request. Falta um campo obrigatório no payload, ou a forma está errada. POST /awards espera badgeId, um objeto recipient aninhado com name e email, e um timestamp issuedOn — o auxiliar acima envia exatamente isso. O tropeço mais comum é o nome do destinatário: tem de ter pelo menos 5 caracteres, por isso um nome de exibição do WordPress como “Jo” é rejeitado. Recorre ao nome completo da conta (ou à parte local do email) quando o nome de exibição for demasiado curto.

401 Unauthorized. A constante da chave de API não está a carregar, ou foi regenerada depois de a constante ter sido definida. As chaves são mostradas uma única vez na criação e não podem voltar a ser obtidas — se perderes o rasto de qual chave está ativa, apaga a antiga e emite uma nova em vez de arriscar.

Emblema emitido, mas com o design errado. Isto é quase sempre um valor de post-meta badges_ninja_badge_id desatualizado, deixado de um teste. Como o mapeamento vive no post do curso, é fácil esquecer que o alteraste depois de duplicares um curso para uma nova coorte — vale a pena uma verificação rápida sempre que clonas um curso.

Prémios duplicados para a mesma pessoa. Abordado acima com a proteção de user-meta, mas vale a pena repetir: os hooks de conclusão do LMS não têm garantia de disparar exatamente uma vez. Trata “já atribuído” como um caso normal a verificar, não como um caso limite a ignorar.

Perguntas frequentes

Isto funciona com pacotes de curso do WooCommerce? Sim — se uma compra de pacote deve despoletar um emblema independentemente da conclusão do curso (por exemplo, um emblema de “membro certificado” para quem comprar um pacote específico), liga-te a woocommerce_order_status_completed em vez de uma ação de conclusão do LMS, e obtém o ID do emblema a partir da meta do produto em vez de um curso.

Os destinatários podem obter o emblema sem uma conta WordPress? Sim, e é esse o objetivo. O prémio está ligado ao endereço de email deles na Badges Ninja, não a um registo de utilizador do WordPress. Se o teu site permitir checkout como convidado ou conclusão anónima de quiz, garante apenas que estás a capturar um endereço de email real para passar a badges_ninja_issue_award().

E se eu quiser rever os prémios antes de serem enviados? Salta a chamada direta a partir do hook de conclusão. Em vez disso, escreve o prémio pendente (utilizador, curso, timestamp) numa tabela personalizada ou tipo de post, e emite-o a partir de uma tarefa agendada ou de um botão de administração “Aprovar & Atribuir” que chama a mesma função auxiliar a pedido.

Preciso de um emblema separado por curso, ou pode um emblema cobrir vários? Ambos funcionam. Alguns emissores criam um emblema por curso para granularidade; outros criam um único emblema de “conclusão de programa” e só o atribuem quando todos os cursos obrigatórios estiverem concluídos, verificando a conclusão de pré-requisitos dentro do hook antes de chamar a API.


Pronto para emitir a tua primeira credencial verificável? Começa gratuitamente em badges.ninja — designer visual, página de verificação pública, certificado em PDF, saída Open Badge v2.0. Sem necessidade de cartão de crédito.

Nacho Coll

Sobre o autor

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.

Voltar ao Blog

Artigos Relacionados