Emettere Open Badge da WordPress (senza un plugin pesante)

Emetti Open Badge da qualsiasi plugin di corsi/LMS per WordPress (LearnDash, LifterLMS, Tutor) chiamando l'API di Badges Ninja al completamento — senza installare un plugin per badge separato.

Nacho Coll Di Aggiornato 13 min di lettura
Emetti Open Badge da qualsiasi plugin di corsi/LMS per WordPress (LearnDash, LifterLMS, Tutor) chiamando l'API di Badges Ninja al completamento — senza installare un plugin per badge separato.

Se gestisci corsi su WordPress, conosci già la routine: qualcuno completa un modulo in LearnDash, LifterLMS o Tutor LMS, l’LMS lo segna come “completato” e poi… non succede nulla di verificabile. Magari invii un PDF via email. Magari installi un plugin per badge dedicato per WordPress che aggiunge le proprie tabelle nel database, la propria libreria di immagini e una propria idea di cosa sia un “badge” — una che nessuno al di fuori del tuo sito può verificare.

C’è un percorso più semplice. I plugin per corsi di WordPress attivano già un action hook nel momento in cui qualcuno completa un corso. Puoi intercettare quell’hook ed effettuare un’unica chiamata HTTP all’API di Badges Ninja. Nessun plugin extra, nessun hosting aggiuntivo, nessun formato di badge proprietario — solo una credenziale Open Badge v2.0 conforme agli standard che il destinatario può verificare ovunque, aggiungere a LinkedIn e scaricare in PDF.

Questo articolo illustra il pattern una volta, poi mostra l’hook specifico per LearnDash, LifterLMS e Tutor LMS.

Perché non usare semplicemente un plugin per badge di WordPress?

I plugin per badge dedicati a WordPress risolvono un problema più ristretto di quanto sembri. Sono bravi a mostrare un’immagine in una pagina “i miei traguardi” all’interno del tuo sito. Sono di solito molto più deboli sulle cose che contano davvero per una credenziale:

  • Portabilità. Un badge che esiste solo come shortcode sul tuo sito WordPress non è una credenziale — è una decorazione. Nel momento in cui il destinatario cambia lavoro, cambia casella di posta o il tuo sito va giù, la “prova” scompare con esso.
  • Verifica. Chiunque dovrebbe poter controllare che una credenziale sia reale senza accedere all’area admin di WordPress. Open Badge v2.0 offre per design un URL di verifica pubblico e JSON-LD leggibile dalle macchine.
  • Superficie di manutenzione. Ogni plugin di WordPress è una cosa in più da aggiornare, una tabella del database in più da salvare, un punto di rottura in più durante un aggiornamento del core.

Chiamare un’API esterna al completamento del corso evita tutti e tre questi problemi. WordPress resta responsabile di ciò in cui è bravo — distribuzione dei contenuti e monitoraggio dei progressi — mentre la credenziale vive in un sistema costruito appositamente per emetterla, verificarla e condividerla.

Il pattern: un hook, una chiamata HTTP

Ogni plugin LMS importante per WordPress attiva un’azione quando un utente completa un corso. L’integrazione è sempre composta dagli stessi tre passaggi:

  1. Agganciati all’azione di completamento del tuo LMS specifico.
  2. Recupera (o crea) l’identità del destinatario — di solito solo l’email dell’account WordPress e il nome visualizzato.
  3. Chiama POST /awards sull’API di Badges Ninja con wp_remote_post(), passando l’ID del badge e i dettagli del destinatario.

Prima di tutto ti servirà una chiave API. Generane una dalla tua dashboard, sotto API Keys — consulta la guida alle chiavi API per la procedura completa. Le chiavi vengono mostrate una sola volta alla creazione, quindi salvala in wp-config.php come costante invece che nel database:

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

Chiave appena creata mostrata una sola volta

Mantieni questa chiave lato server. Tutto ciò che è descritto in questa guida viene eseguito in PHP sul tuo server WordPress — wp_remote_post() è una chiamata lato server, e wp-config.php non viene mai inviato al browser. È esattamente ciò che vuoi: la chiave viaggia da server a server su HTTPS e nessun visitatore del sito può leggerla. Non spostare mai questa chiamata in JavaScript lato client — una fetch() dal browser con la chiave incorporata la esporrebbe a chiunque tramite gli strumenti per sviluppatori del browser. Due abitudini da mantenere: assicurati che il tuo host serva wp-config.php come PHP eseguito (mai come testo semplice, e non lasciare una copia wp-config.php.bak nella webroot), e crea una chiave API dedicata per questa integrazione, così puoi revocarla in modo indipendente se venisse mai compromessa — senza interrompere gli altri tuoi strumenti.

Una funzione helper condivisa mantiene snello ogni hook 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 );
}

L’email del destinatario viene sottoposta a hashing SHA-256 lato server non appena arriva a Badges Ninja, e il payload di risposta include l’URL di verifica del riconoscimento — utile se vuoi mostrare direttamente in WordPress un link “visualizza la tua credenziale” dopo il successo della chiamata.

LearnDash

LearnDash attiva learndash_course_completed con un singolo argomento array contenente 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 );
} );

Salvare l’ID del badge come meta del corso (invece di un’unica costante globale BADGES_NINJA_BADGE_ID) è la scelta giusta non appena hai più di un corso — ogni corso corrisponde al proprio design del badge.

LifterLMS

LifterLMS attiva lifterlms_course_completed con $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

Tutor LMS attiva tutor_course_complete_after con l’ID del corso, e recuperi l’utente corrente dalla sessione:

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 la tua configurazione di Tutor LMS attiva il completamento da un processo in background (ad esempio tramite la correzione automatica di un quiz), sostituisci get_current_user_id() con l’ID utente fornito dal tuo hook specifico — controlla il payload con error_log( print_r( func_get_args(), true ) ) una volta durante la configurazione per confermare gli argomenti.

Progettare il badge prima di collegare l’hook

Prima che tutto questo si attivi, ti serve un badge da assegnare. Il designer visuale copre questa esigenza senza bisogno di un designer nel team — oltre 80 template di forme, colori personalizzati, libreria di icone e caricamento del logo, tutto con allineamento automatico alla griglia. Se non hai mai usato il designer, Progettare il tuo primo certificato digitale verificabile ti guida nella scelta di un template e nella creazione di qualcosa che i destinatari vorranno davvero condividere.

Una volta creato il badge, recupera il suo ID dalla dashboard (oppure tramite GET /badges) e inseriscilo nel campo personalizzato del tuo corso.

Cosa riceve davvero il destinatario

Questa è la parte che un plugin per badge fatto in casa per WordPress non può replicare. Quando POST /awards ha successo, il destinatario riceve:

  • Un URL di verifica univoco che chiunque può aprire — senza bisogno di accesso a WordPress — che mostra che la credenziale è reale, chi l’ha emessa e quando.
  • Un codice QR e un certificato PDF in formato A4, generati automaticamente.
  • Un pulsante Aggiungi al profilo LinkedIn, se il tuo emittente ha impostato un ID organizzazione LinkedIn — consulta Aggiungere un pulsante LinkedIn “Aggiungi al profilo” per la configurazione una tantum.
  • L’accesso al proprio portale del destinatario su badges.ninja/me, tramite accesso con magic link — nessuna password da dimenticare, nessun account separato da gestire sul tuo sito WordPress.
  • Open Badge v2.0 JSON-LD conforme agli standard, così qualsiasi verificatore compatibile con OB (non solo Badges Ninja) può validarlo.

Niente di tutto ciò spetta a WordPress costruirlo, e niente di tutto ciò dovrebbe vivere solo dentro il tuo database WordPress.

Gestire i fallimenti con eleganza

Gli hook di completamento corso si attivano durante una richiesta di pagina live, e una chiamata API lenta o fallita non dovrebbe bloccare la schermata di “congratulazioni” dello studente. Due accorgimenti pratici una volta superata la fase di proof of concept:

Non lasciare che un riconoscimento fallito blocchi l’interfaccia. La funzione helper sopra registra già l’errore e restituisce false invece di generare un’eccezione — LearnDash, LifterLMS e Tutor continuano tutti il loro normale flusso di completamento indipendentemente da ciò che restituisce badges_ninja_issue_award().

Mettila in coda invece di chiamarla in linea, su larga scala. Se stai facendo passare grandi coorti attraverso un unico corso contemporaneamente (una data di inizio coorte, lo sblocco della replica di un webinar), attiva un evento pianificato WP-Cron dall’hook invece di chiamare l’API in modo sincrono:

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

Questo distribuisce il carico ed evita che una singola richiesta lenta diventi un problema per lo studente. Per lotti realmente grandi (un’intera coorte passata, migrata in un’unica volta), salta del tutto il percorso dell’hook di WordPress e usa i riconoscimenti in blocco via CSV — carica una volta sola, e la piattaforma pagina l’emissione lato server con pausa/ripresa integrate.

Una nota sull’idempotenza

Se il tuo LMS dovesse mai attivare il suo hook di completamento più di una volta per lo stesso utente (un caso limite comune di LearnDash con i tentativi ripetuti di un quiz, o una nuova sincronizzazione di Tutor LMS), non vuoi assegnare lo stesso badge due volte. La protezione più semplice è un flag user-meta di WordPress che imposti subito dopo una chiamata riuscita:

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 stai emettendo a volumi reali e vuoi che questo venga gestito centralmente invece che per singolo sito, dai un’occhiata alla gestione degli eventi tramite webhook oppure alla guida rapida all’API completa per le forme di richiesta/risposta di ogni endpoint.

Quando questo pattern supera un singolo corso

Gli esempi sopra presuppongono un badge per corso. Quando gestisci credenziali impilabili — un badge di percorso che si sblocca dopo diversi badge di corso, o un badge finale che dipende da più prerequisiti — sposta la mappatura “quale badge, per quale completamento” fuori dal post meta e in una piccola tabella di lookup, oppure guidala tramite un’automazione basata su un foglio di calcolo (Airtable o Google Sheets) che attiva la stessa chiamata POST /awards. La chiamata API in sé non cambia; cambia solo ciò che decide quando effettuarla.

Risoluzione dei problemi dell’integrazione

Una manciata di problemi rappresenta la maggior parte degli attriti che le persone incontrano quando configurano tutto questo per la prima volta:

L’hook non si attiva mai. Verifica di esserti agganciato all’azione corretta per la versione effettiva del tuo plugin — LearnDash in particolare ha cambiato i nomi degli hook di completamento tra le release principali (le installazioni più vecchie a volte si affidano ancora a learndash_course_completed attivato da un percorso di codice diverso rispetto a quelle più recenti). Aggiungi un error_log() temporaneo all’inizio della tua callback e osserva wp-content/debug.log mentre completi un corso di prova con un utente usa e getta.

wp_remote_post() va in timeout. Gli ambienti di hosting condiviso a volte bloccano l’HTTPS in uscita verso host sconosciuti, o limitano i timeout delle richieste sotto i 15 secondi impostati sopra. Se le chiamate vanno costantemente in timeout, verifica con il tuo host se le connessioni in uscita verso api.badges.ninja sono consentite, e conferma che la tua estensione PHP curl abbia certificati CA aggiornati — un bundle di certificati obsoleto è un errore silenzioso comune sugli stack di hosting condiviso più datati.

400 Bad Request. Al payload manca un campo obbligatorio oppure ha la forma sbagliata. POST /awards si aspetta badgeId, un oggetto recipient annidato con name ed email, e un timestamp issuedOn — l’helper sopra invia esattamente questo. L’inciampo più comune è il nome del destinatario: deve essere di almeno 5 caratteri, quindi un nome visualizzato di WordPress come “Jo” viene rifiutato. Ripiega sul nome completo dell’account (o sulla parte locale dell’email) quando il nome visualizzato è troppo corto.

401 Unauthorized. La costante della chiave API non viene caricata, oppure è stata rigenerata dopo che la costante era stata impostata. Le chiavi vengono mostrate una sola volta alla creazione e non possono essere recuperate di nuovo — se hai perso traccia di quale chiave sia attiva, elimina quella obsoleta ed emettine una nuova invece di tirare a indovinare.

Badge emesso, ma con il design sbagliato. Questo è quasi sempre un valore post-meta badges_ninja_badge_id obsoleto rimasto dai test. Poiché la mappatura vive sul post del corso, è facile dimenticare di averla cambiata dopo aver duplicato un corso per una nuova coorte — vale la pena un controllo rapido ogni volta che cloni un corso.

Riconoscimenti duplicati per la stessa persona. Trattato sopra con la protezione user-meta, ma vale la pena ribadirlo: non è garantito che gli hook di completamento dell’LMS si attivino esattamente una volta. Tratta “già assegnato” come un caso normale da controllare, non come un caso limite da ignorare.

Domande frequenti

Funziona con i pacchetti di corsi WooCommerce? Sì — se l’acquisto di un pacchetto dovrebbe attivare un badge indipendentemente dal completamento del corso (ad esempio un badge “membro certificato” per chiunque acquisti un pacchetto specifico), aggancia woocommerce_order_status_completed invece di un’azione di completamento LMS, e recupera l’ID del badge dai meta del prodotto invece che da un corso.

I destinatari possono ricevere il badge senza un account WordPress? Sì, ed è proprio questo il punto. Il riconoscimento è legato al loro indirizzo email in Badges Ninja, non a un record utente di WordPress. Se il tuo sito consente il checkout come ospite o il completamento anonimo di un quiz, assicurati semplicemente di catturare un indirizzo email reale da passare a badges_ninja_issue_award().

Cosa succede se voglio revisionare i riconoscimenti prima che vengano inviati? Evita di attivare la chiamata direttamente dall’hook di completamento. Scrivi invece il riconoscimento in sospeso (utente, corso, timestamp) in una tabella personalizzata o in un tipo di post, ed emettilo tramite un’attività pianificata o un pulsante admin “Approva e assegna” che richiama la stessa funzione helper su richiesta.

Serve un badge separato per ogni corso, oppure un badge può coprirne diversi? Funzionano entrambi gli approcci. Alcuni emittenti creano un badge per corso per avere maggiore granularità; altri creano un unico badge “completamento del programma” e lo assegnano solo quando tutti i corsi richiesti sono stati completati, verificando il completamento dei prerequisiti all’interno dell’hook prima di chiamare l’API.


Pronto a emettere la tua prima credenziale verificabile? Inizia gratis su badges.ninja — designer visuale, pagina di verifica pubblica, certificato PDF, output Open Badge v2.0. Nessuna carta di credito richiesta.

Nacho Coll

Informazioni sull'autore

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.

Torna al Blog

Articoli correlati