Emiterea de Open Badges din WordPress (fără un plugin greoi)

Emite Open Badges din orice plugin de curs/LMS WordPress (LearnDash, LifterLMS, Tutor) apelând API-ul Badges Ninja la finalizare — fără un plugin separat de insigne de instalat.

Nacho Coll De Actualizat 13 min de citit
Emite Open Badges din orice plugin de curs/LMS WordPress (LearnDash, LifterLMS, Tutor) apelând API-ul Badges Ninja la finalizare — fără un plugin separat de insigne de instalat.

Dacă rulezi cursuri pe WordPress, deja cunoști rutina: cineva termină un modul în LearnDash, LifterLMS sau Tutor LMS, LMS-ul îl marchează „finalizat”, iar apoi… nu se întâmplă nimic verificabil. Poate trimiți un PDF pe email. Poate instalezi un plugin dedicat de insigne WordPress care adaugă propriile tabele în baza de date, propria bibliotecă de imagini și propria idee despre ce înseamnă o „insignă” — una pe care nimeni din afara site-ului tău nu o poate verifica.

Există o cale mai simplă. Pluginurile de curs WordPress declanșează deja un action hook în momentul în care cineva finalizează un curs. Poți prinde acel hook și faci un singur apel HTTP către API-ul Badges Ninja. Fără plugin suplimentar, fără găzduire suplimentară, fără format de insignă proprietar — doar o credențială Open Badge v2.0 conformă cu standardele, pe care destinatarul tău o poate verifica oriunde, o poate adăuga pe LinkedIn și o poate descărca ca PDF.

Acest articol prezintă tiparul o singură dată, apoi arată hook-ul specific pentru LearnDash, LifterLMS și Tutor LMS.

De ce să nu folosești pur și simplu un plugin de insigne WordPress?

Pluginurile dedicate de insigne WordPress rezolvă o problemă mai restrânsă decât par să rezolve. Se pricep la randarea unei imagini pe o pagină „realizările mele” din interiorul site-ului tău. De obicei sunt mult mai slabe la lucrurile care contează cu adevărat pentru o credențială:

  • Portabilitatea. O insignă care există doar ca un shortcode pe site-ul tău WordPress nu este o credențială — este o decorație. În momentul în care destinatarul își schimbă locul de muncă, își schimbă inbox-ul sau site-ul tău cade, „dovada” dispare odată cu el.
  • Verificarea. Oricine ar trebui să poată verifica faptul că o credențială este reală fără să se autentifice în panoul de administrare WordPress. Open Badge v2.0 îți oferă, prin design, o adresă URL publică de verificare și JSON-LD care poate fi citit automat.
  • Suprafața de întreținere. Fiecare plugin WordPress este încă un lucru de actualizat, încă un tabel din baza de date de salvat, încă un punct de eșec în timpul unei actualizări de bază.

Apelarea unui API extern la finalizarea cursului evită toate cele trei. WordPress rămâne responsabil pentru ceea ce știe să facă bine — livrarea conținutului și urmărirea progresului — iar credențiala în sine trăiește într-un sistem construit special pentru a o emite, verifica și partaja.

Tiparul: un hook, un apel HTTP

Fiecare plugin LMS major pentru WordPress declanșează o acțiune atunci când un utilizator finalizează un curs. Integrarea este mereu formată din aceiași trei pași:

  1. Conectează-te la acțiunea de finalizare specifică LMS-ului tău.
  2. Caută (sau creează) identitatea destinatarului — de obicei doar emailul și numele afișat al contului WordPress.
  3. Apelează POST /awards pe API-ul Badges Ninja folosind wp_remote_post(), transmițând ID-ul insignei și detaliile destinatarului.

Mai întâi ai nevoie de o cheie API. Generează una din dashboard-ul tău, la API Keys — vezi ghidul cheilor API pentru parcursul complet. Cheile sunt afișate o singură dată la creare, așa că stocheaz-o în wp-config.php ca o constantă, nu în baza de date:

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

Newly created key revealed once

Păstrează această cheie pe server. Tot ce este în acest ghid rulează în PHP pe serverul tău WordPress — wp_remote_post() este un apel efectuat pe server, iar wp-config.php nu este trimis niciodată către browser. Exact asta îți dorești: cheia circulă server-la-server prin HTTPS, iar niciun vizitator al site-ului nu o poate citi. Niciodată nu muta acest apel în JavaScript pe partea de client — un fetch() din browser cu cheia inclusă direct ar expune-o oricui, prin instrumentele pentru dezvoltatori ale browserului. Două obiceiuri merită păstrate: asigură-te că gazda ta servește wp-config.php ca PHP executat (niciodată ca text simplu, și nu lăsa o copie wp-config.php.bak în webroot) și creează o cheie API dedicată pentru această integrare, ca s-o poți revoca independent dacă vreodată este compromisă — fără să-ți strici celelalte unelte.

O funcție helper comună menține fiecare hook LMS suplu:

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

Emailul destinatarului este hashuit SHA-256 pe server odată ce ajunge la Badges Ninja, iar răspunsul returnat include adresa URL de verificare a premiului — util dacă vrei să afișezi un link „vezi credențiala ta” chiar acolo, în WordPress, după ce apelul reușește.

LearnDash

LearnDash declanșează learndash_course_completed cu un singur argument de tip array, conținând user și 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 );
} );

Stocarea ID-ului insignei ca metadată a cursului (mai degrabă decât o singură constantă globală BADGES_NINJA_BADGE_ID) este decizia corectă odată ce ai mai mult de un curs — fiecare curs se mapează la propriul design de insignă.

LifterLMS

LifterLMS declanșează lifterlms_course_completed cu $user_id și $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 declanșează tutor_course_complete_after cu ID-ul cursului, iar tu preiei utilizatorul curent din sesiune:

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

Dacă instalarea ta de Tutor LMS declanșează finalizarea dintr-un proces de fundal (de exemplu prin auto-corectarea unui quiz), înlocuiește get_current_user_id() cu ID-ul de utilizator furnizat de hook-ul tău specific — verifică payload-ul cu error_log( print_r( func_get_args(), true ) ) o dată, în timpul configurării, pentru a confirma argumentele.

Proiectarea insignei înainte de a conecta hook-ul

Înainte ca oricare dintre acestea să se declanșeze, ai nevoie de o insignă de acordat. Designerul vizual acoperă acest lucru fără să fie nevoie de un designer în echipă — peste 80 de șabloane de forme, culori personalizate, bibliotecă de iconițe și încărcare de logo, toate cu aliniere automată la grilă. Dacă ești nou în designer, Designing Your First Verifiable Digital Certificate te ghidează prin alegerea unui șablon și livrarea a ceva ce destinatarii chiar vor vrea să distribuie.

Odată ce insignă există, ia-i ID-ul din dashboard (sau prin GET /badges) și pune-l în câmpul personalizat al cursului tău.

Ce primește destinatarul, de fapt

Aici este partea pe care un plugin de insigne WordPress creat în casă nu o poate replica. Când POST /awards reușește, destinatarul primește:

  • O adresă URL de verificare unică, pe care oricine o poate deschide — fără autentificare WordPress necesară — arătând că credențiala este reală, cine a emis-o și când.
  • Un cod QR și un certificat PDF A4, generate automat.
  • Un buton Add to LinkedIn Profile, dacă emitentul tău are setat un ID de organizație LinkedIn — vezi Add a LinkedIn “Add to Profile” Button pentru configurarea unică.
  • Acces la propriul portal al destinatarului, la badges.ninja/me, prin autentificare cu magic-link — nicio parolă de uitat, niciun cont separat de administrat pe site-ul tău WordPress.
  • JSON-LD Open Badge v2.0 conform standardelor, astfel încât orice verificator care înțelege OB (nu doar Badges Ninja) îl poate valida.

Niciunul dintre acestea nu este treaba WordPress-ului să le construiască, și niciunul nu ar trebui să existe doar în baza ta de date WordPress.

Gestionarea grațioasă a eșecurilor

Hook-urile de finalizare a cursului se declanșează în timpul unei cereri de pagină live, iar un apel API lent sau eșuat nu ar trebui să blocheze ecranul de „felicitări” al cursantului. Două ajustări practice odată ce depășești stadiul de proof of concept:

Nu lăsa un premiu eșuat să blocheze interfața. Funcția helper de mai sus deja loghează și returnează false în loc să arunce o excepție — LearnDash, LifterLMS și Tutor își continuă cu toții fluxul normal de finalizare, indiferent de ce returnează badges_ninja_issue_award().

Pune-l în coadă în loc să apelezi inline, la scară. Dacă rulezi cohorte mari printr-un singur curs deodată (o dată de start a unei cohorte, deblocarea unei reluări de webinar), declanșează un eveniment programat WP-Cron din hook, în loc să apelezi API-ul sincron:

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

Acest lucru distribuie sarcina și evită ca o singură cerere lentă să devină problema cursantului. Pentru loturi cu adevărat mari (o întreagă cohortă trecută, migrată dintr-o dată), sari complet peste calea hook-ului WordPress și folosește emiterea în bloc din CSV — încarci o dată, iar platforma paginează emiterea pe server, cu pauză/reluare integrate.

O notă despre idempotență

Dacă LMS-ul tău declanșează vreodată hook-ul de finalizare de mai multe ori pentru același utilizator (un caz limită comun în LearnDash, cu reluări de quiz, sau o resincronizare Tutor LMS), nu vrei să acorzi aceeași insignă de două ori. Cea mai simplă protecție este un flag de user-meta WordPress pe care îl setezi imediat după un apel reușit:

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

Dacă emiți la volum real și vrei ca acest lucru să fie gestionat centralizat, nu per site, uită-te la gestionarea evenimentelor prin webhook sau la ghidul complet de pornire rapidă al API-ului pentru formele cererilor/răspunsurilor pe fiecare endpoint.

Când acest tipar depășește un singur curs

Exemplele de mai sus presupun o insignă per curs. Odată ce rulezi credențiale stackabile — o insignă de traseu care se deblochează după mai multe insigne de curs, sau o insignă capstone care depinde de mai multe premise — mută maparea „ce insignă, pentru ce finalizare” din post meta într-un mic tabel de lookup, sau condu-o dintr-o automatizare bazată pe foaie de calcul (Airtable sau Google Sheets) care declanșează același apel POST /awards. Apelul API în sine nu se schimbă; se schimbă doar ce decide când să-l facă.

Depanarea integrării

Un număr limitat de probleme reprezintă cea mai mare parte a frecărilor pe care oamenii le întâlnesc când conectează asta pentru prima dată:

Hook-ul nu se declanșează niciodată. Confirmă că te conectezi la acțiunea corectă pentru versiunea efectivă a plugin-ului tău — LearnDash, în special, și-a schimbat numele hook-urilor de finalizare de-a lungul versiunilor majore (instalările mai vechi uneori încă se bazează pe learndash_course_completed declanșat dintr-o cale de cod diferită față de cele mai noi). Adaugă temporar un error_log() la începutul callback-ului tău și urmărește wp-content/debug.log în timp ce finalizezi un curs de test ca utilizator de unică folosință.

wp_remote_post() expiră timpul de așteptare. Mediile de găzduire partajată blochează uneori conexiunile HTTPS de ieșire către gazde necunoscute sau limitează timpul de expirare al cererilor sub cele 15 secunde setate mai sus. Dacă apelurile expiră constant, verifică la gazda ta dacă sunt permise conexiunile de ieșire către api.badges.ninja și confirmă că extensia PHP curl are certificate CA actuale — un pachet de certificate învechit este un eșec silențios comun pe stive de găzduire partajată mai vechi.

400 Bad Request. Payload-ul lipsește un câmp obligatoriu sau are forma greșită. POST /awards așteaptă badgeId, un obiect recipient imbricat cu name și email, și un timestamp issuedOn — helper-ul de mai sus trimite exact asta. Cea mai frecventă capcană este numele destinatarului: trebuie să aibă cel puțin 5 caractere, așa că un nume afișat WordPress precum „Jo” este respins. Revino la numele complet al contului (sau la partea locală a emailului) când numele afișat este prea scurt.

401 Unauthorized. Constanta cheii API nu se încarcă sau a fost regenerată după ce constanta a fost setată. Cheile sunt afișate o singură dată la creare și nu pot fi recuperate din nou — dacă ai pierdut urma cheii active, șterge-o pe cea veche și emite una nouă, în loc să ghicești.

Insignă emisă, dar cu designul greșit de insignă. Aproape întotdeauna este o valoare veche a metadatei badges_ninja_badge_id, rămasă din testare. Pentru că maparea trăiește pe postarea cursului, este ușor să uiți că ai schimbat-o după ce ai duplicat un curs pentru o nouă cohortă — merită o verificare rapidă de fiecare dată când clonezi un curs.

Premii duplicate pentru aceeași persoană. Acoperit mai sus cu protecția prin user-meta, dar merită reafirmat: hook-urile de finalizare LMS nu sunt garantate să se declanșeze exact o singură dată. Tratează „deja acordat” ca un caz normal de verificat, nu ca un caz limită de ignorat.

Întrebări frecvente

Funcționează cu pachetele de cursuri WooCommerce? Da — dacă o achiziție de pachet ar trebui să declanșeze o insignă independent de finalizarea cursului (de exemplu o insignă „membru certificat” pentru oricine cumpără un anumit pachet), conectează-te la woocommerce_order_status_completed în loc de o acțiune de finalizare LMS, și preia ID-ul insignei din metadata produsului, nu din curs.

Pot destinatarii să primească insignă fără un cont WordPress? Da, și acesta este chiar scopul. Premiul este legat de adresa lor de email în Badges Ninja, nu de o înregistrare de utilizator WordPress. Dacă site-ul tău permite checkout ca invitat sau finalizare anonimă a unui quiz, asigură-te doar că prinzi o adresă de email reală, pentru a o transmite către badges_ninja_issue_award().

Ce fac dacă vreau să revizuiesc premiile înainte să fie trimise? Sari peste declanșarea apelului direct din hook-ul de finalizare. În schimb, scrie premiul în așteptare (utilizator, curs, timestamp) într-un tabel personalizat sau tip de postare, și emite-l dintr-o sarcină programată sau dintr-un buton de administrare „Approve & Award” care apelează aceeași funcție helper la cerere.

Am nevoie de o insignă separată per curs, sau una poate acoperi mai multe? Ambele funcționează. Unii emitenți creează câte o insignă per curs, pentru granularitate; alții creează o singură insignă „finalizare program” și o acordă doar o dată ce fiecare curs necesar este finalizat, verificând finalizarea prealabilă în interiorul hook-ului înainte de a apela API-ul.


Gata să emiți prima ta credențială verificabilă? Începe gratuit pe badges.ninja — designer vizual, pagină publică de verificare, certificat PDF, ieșire Open Badge v2.0. Nu este necesar niciun card de credit.

Nacho Coll

Despre 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.

Înapoi la Blog

Articole similare