Cấp Open Badge Từ WordPress (Không Cần Plugin Nặng Nề)
Cấp Open Badge từ bất kỳ plugin khóa học/LMS nào của WordPress (LearnDash, LifterLMS, Tutor) bằng cách gọi API Badges Ninja khi hoàn thành khóa học — không cần cài thêm plugin huy hiệu riêng.
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.
Nếu bạn chạy khóa học trên WordPress, hẳn bạn đã quá quen với kịch bản này: ai đó hoàn thành một module trong LearnDash, LifterLMS, hoặc Tutor LMS, LMS đánh dấu họ “complete”, rồi… không có gì có thể xác minh được xảy ra cả. Có thể bạn gửi email một file PDF. Có thể bạn cài một plugin huy hiệu kỹ thuật số chuyên dụng cho WordPress, nó thêm vào bảng cơ sở dữ liệu riêng, thư viện hình ảnh riêng, và khái niệm riêng về “huy hiệu” là gì — một khái niệm mà không ai bên ngoài trang web của bạn có thể xác minh được.
Có một cách đơn giản hơn. Các plugin khóa học WordPress vốn đã kích hoạt một action hook ngay khi ai đó hoàn thành khóa học. Bạn có thể bắt hook đó và thực hiện một lệnh gọi HTTP đến API Badges Ninja. Không cần plugin bổ sung, không cần hosting bổ sung, không cần định dạng huy hiệu độc quyền — chỉ là một chứng chỉ Open Badge v2.0 tuân thủ chuẩn mà người nhận có thể xác minh ở bất cứ đâu, thêm vào LinkedIn, và tải xuống dưới dạng PDF.
Bài viết này trình bày mẫu tích hợp đó một lần, sau đó chỉ ra hook cụ thể cho LearnDash, LifterLMS, và Tutor LMS.
Tại sao không nên chỉ dùng một plugin huy hiệu WordPress?
Các plugin huy hiệu WordPress chuyên dụng thực chất giải quyết một vấn đề hẹp hơn nhiều so với vẻ ngoài của chúng. Chúng làm tốt việc hiển thị một hình ảnh trên trang “my achievements” bên trong trang web của bạn. Nhưng chúng thường yếu hơn nhiều ở những điều thực sự quan trọng đối với một chứng chỉ:
- Khả năng di chuyển. Một huy hiệu chỉ tồn tại dưới dạng shortcode trên trang WordPress của bạn không phải là chứng chỉ — đó chỉ là trang trí. Ngay khi người nhận đổi việc, đổi hộp thư, hoặc trang web của bạn ngừng hoạt động, “bằng chứng” cũng biến mất theo.
- Xác minh. Bất kỳ ai cũng nên có thể kiểm tra xem một chứng chỉ có thật hay không mà không cần đăng nhập vào trang quản trị WordPress của bạn. Open Badge v2.0 được thiết kế để cung cấp cho bạn một URL xác minh công khai và dữ liệu JSON-LD mà máy có thể đọc được.
- Bề mặt bảo trì. Mỗi plugin WordPress là thêm một thứ cần vá lỗi, thêm một bảng cơ sở dữ liệu cần sao lưu, thêm một điểm có thể gây lỗi mỗi khi cập nhật lõi.
Gọi một API bên ngoài khi hoàn thành khóa học giúp tránh cả ba vấn đề trên. WordPress vẫn chịu trách nhiệm cho phần việc nó làm tốt — phân phối nội dung và theo dõi tiến độ — còn bản thân chứng chỉ thì sống trong một hệ thống được xây dựng chuyên biệt để cấp, xác minh và chia sẻ nó.
Mẫu tích hợp: một hook, một lệnh gọi HTTP
Mọi plugin LMS lớn của WordPress đều kích hoạt một action khi người dùng hoàn thành khóa học. Việc tích hợp luôn gồm ba bước giống nhau:
- Gắn hook vào action hoàn thành dành riêng cho LMS của bạn.
- Tra cứu (hoặc tạo) danh tính người nhận — thường chỉ cần email và tên hiển thị tài khoản WordPress của họ.
- Gọi
POST /awardstrên API Badges Ninja bằngwp_remote_post(), truyền vào ID huy hiệu và thông tin người nhận.
Trước tiên bạn cần một API key. Tạo key từ dashboard của bạn, trong mục API Keys — xem hướng dẫn API keys để biết đầy đủ các bước. Key chỉ được hiển thị một lần khi tạo, vì vậy hãy lưu nó dưới dạng hằng số trong wp-config.php thay vì trong cơ sở dữ liệu:
define( 'BADGES_NINJA_API_KEY', 'bws_your_32_hex_key_here' );
define( 'BADGES_NINJA_BADGE_ID', 'your-badge-id' );

Hãy giữ key này ở phía server. Mọi thứ trong hướng dẫn này chạy bằng PHP trên server WordPress của bạn — wp_remote_post() là một lệnh gọi phía server, và wp-config.php không bao giờ được gửi tới trình duyệt. Đó chính xác là điều bạn muốn: key di chuyển từ server tới server qua HTTPS, và không khách truy cập nào của trang web có thể đọc được nó. Không bao giờ chuyển lệnh gọi này sang JavaScript phía client — một fetch() từ trình duyệt với key được nhúng thẳng vào sẽ lộ key đó cho bất kỳ ai qua công cụ dev tools của trình duyệt họ. Có hai thói quen đáng duy trì: đảm bảo host của bạn phục vụ wp-config.php dưới dạng PHP được thực thi (không bao giờ ở dạng plaintext, và đừng để lại bản sao wp-config.php.bak trong webroot), và tạo một API key riêng cho tích hợp này để bạn có thể thu hồi nó độc lập nếu nó từng bị lộ — mà không làm hỏng các công cụ khác của bạn.
Một hàm hỗ trợ dùng chung giúp mỗi hook LMS luôn gọn nhẹ:
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 của người nhận được băm bằng SHA-256 ở phía server ngay khi tới Badges Ninja, và payload trả về bao gồm URL xác minh của award — rất hữu ích nếu bạn muốn hiển thị ngay một liên kết “view your credential” trong WordPress sau khi lệnh gọi thành công.
LearnDash
LearnDash kích hoạt learndash_course_completed với một đối số mảng duy nhất chứa user và 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 );
} );
Việc lưu ID huy hiệu dưới dạng meta của khóa học (thay vì một hằng số toàn cục duy nhất BADGES_NINJA_BADGE_ID) là lựa chọn đúng đắn ngay khi bạn có nhiều hơn một khóa học — mỗi khóa học ánh xạ tới thiết kế huy hiệu riêng của nó.
LifterLMS
LifterLMS kích hoạt lifterlms_course_completed với $user_id và $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 kích hoạt tutor_course_complete_after với ID khóa học, và bạn lấy người dùng hiện tại từ phiên làm việc:
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 );
} );
Nếu thiết lập Tutor LMS của bạn kích hoạt việc hoàn thành từ một tiến trình nền (ví dụ qua chấm điểm tự động cho bài kiểm tra), hãy thay get_current_user_id() bằng ID người dùng mà hook cụ thể của bạn cung cấp — kiểm tra payload bằng error_log( print_r( func_get_args(), true ) ) một lần trong quá trình thiết lập để xác nhận các đối số.
Thiết kế huy hiệu trước khi gắn hook
Trước khi bất kỳ điều gì ở trên được kích hoạt, bạn cần có một huy hiệu để trao. Trình thiết kế trực quan xử lý việc này mà không cần thuê nhà thiết kế — hơn 80 mẫu hình dạng, màu sắc tùy chỉnh, thư viện biểu tượng, và tải logo lên, tất cả đều tự động căn theo lưới (snap-to-grid). Nếu bạn chưa quen với trình thiết kế, Designing Your First Verifiable Digital Certificate sẽ hướng dẫn bạn chọn một mẫu và tạo ra thứ mà người nhận thực sự muốn chia sẻ.
Khi huy hiệu đã tồn tại, hãy lấy ID của nó từ dashboard (hoặc qua GET /badges) và đưa vào custom field của khóa học.
Người nhận thực sự nhận được gì
Đây là phần mà một plugin huy hiệu WordPress tự chế không thể sao chép được. Khi POST /awards thành công, người nhận sẽ có được:
- Một URL xác minh duy nhất mà bất kỳ ai cũng có thể mở — không cần đăng nhập WordPress — cho thấy chứng chỉ là thật, ai đã cấp nó, và cấp khi nào.
- Một mã QR và chứng chỉ PDF khổ A4, được tạo tự động.
- Nút Add to LinkedIn Profile, nếu tổ chức cấp của bạn đã thiết lập LinkedIn organization ID — xem Add a LinkedIn “Add to Profile” Button để biết cách thiết lập một lần.
- Quyền truy cập vào cổng thông tin người nhận của riêng họ tại
badges.ninja/me, thông qua đăng nhập bằng magic-link — không cần nhớ mật khẩu, không cần quản lý một tài khoản riêng trên trang WordPress của bạn. - Open Badge v2.0 JSON-LD tuân thủ chuẩn, để bất kỳ công cụ xác minh nào hỗ trợ OB (không chỉ riêng Badges Ninja) đều có thể xác thực nó.
Không điều nào trong số đó là việc mà WordPress cần phải xây dựng, và không điều nào trong số đó nên chỉ tồn tại bên trong cơ sở dữ liệu WordPress của bạn.
Xử lý lỗi một cách hợp lý
Các hook hoàn thành khóa học được kích hoạt trong một request trang trực tiếp, và một lệnh gọi API chậm hoặc thất bại không nên chặn màn hình “congratulations” của người học. Hai điều chỉnh thực tế khi bạn vượt qua giai đoạn thử nghiệm khái niệm:
Đừng để một award thất bại chặn giao diện. Hàm hỗ trợ ở trên đã ghi log và trả về false thay vì ném lỗi — LearnDash, LifterLMS, và Tutor đều tiếp tục luồng hoàn thành bình thường của chúng bất kể badges_ninja_issue_award() trả về gì.
Đưa vào hàng đợi thay vì gọi trực tiếp, khi ở quy mô lớn. Nếu bạn đang chạy các nhóm học viên lớn qua một khóa học cùng lúc (một ngày bắt đầu của nhóm học viên, một lần mở khóa bản ghi hình webinar), hãy kích hoạt một sự kiện WP-Cron được lên lịch từ hook thay vì gọi API một cách đồng bộ:
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 );
Cách này giúp phân tán tải và ngăn một request chậm duy nhất trở thành vấn đề của người học. Với các đợt thực sự lớn (toàn bộ một nhóm học viên cũ được di chuyển cùng một lúc), hãy bỏ qua hoàn toàn con đường hook WordPress và dùng bulk awards via CSV — chỉ cần tải lên một lần, và nền tảng sẽ tự phân trang việc cấp phát ở phía server, có sẵn tính năng tạm dừng/tiếp tục.
Một lưu ý về tính bất biến khi lặp lại (idempotency)
Nếu LMS của bạn từng kích hoạt hook hoàn thành nhiều hơn một lần cho cùng một người dùng (một trường hợp biên phổ biến của LearnDash khi làm lại bài kiểm tra, hoặc khi Tutor LMS đồng bộ lại), bạn sẽ không muốn trao cùng một huy hiệu hai lần. Cách bảo vệ đơn giản nhất là một cờ user-meta của WordPress mà bạn đặt ngay sau một lệnh gọi thành công:
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 );
}
Nếu bạn cấp phát với khối lượng thực sự lớn và muốn việc này được xử lý tập trung thay vì theo từng trang web, hãy xem webhook-driven event handling hoặc API quickstart đầy đủ để biết cấu trúc request/response của mọi endpoint.
Khi mẫu tích hợp này vượt ra ngoài một khóa học đơn lẻ
Các ví dụ trên giả định mỗi khóa học có một huy hiệu. Khi bạn bắt đầu triển khai các chứng chỉ có thể xếp chồng — một huy hiệu lộ trình được mở khóa sau nhiều huy hiệu khóa học, hoặc một huy hiệu tổng kết phụ thuộc vào nhiều điều kiện tiên quyết — hãy chuyển ánh xạ “huy hiệu nào, cho lần hoàn thành nào” ra khỏi post meta và vào một bảng tra cứu nhỏ, hoặc điều khiển nó từ một quy trình tự động dựa trên bảng tính (Airtable hoặc Google Sheets) kích hoạt cùng một lệnh gọi POST /awards. Bản thân lệnh gọi API không thay đổi; chỉ có phần quyết định khi nào gọi nó là thay đổi.
Xử lý sự cố tích hợp
Một số ít vấn đề thường gây ra phần lớn khó khăn khi mọi người thiết lập tích hợp này lần đầu:
Hook không bao giờ được kích hoạt. Hãy xác nhận bạn đang gắn hook vào đúng action cho phiên bản thực tế của plugin — đặc biệt là LearnDash đã thay đổi tên completion-hook qua các bản phát hành lớn (các bản cài đặt cũ đôi khi vẫn dựa vào learndash_course_completed được kích hoạt từ một luồng code khác so với các bản mới). Thêm tạm một error_log() ở đầu callback của bạn và theo dõi wp-content/debug.log trong khi bạn hoàn thành một khóa học thử nghiệm bằng một tài khoản dùng một lần.
wp_remote_post() bị timeout. Một số môi trường hosting dùng chung đôi khi chặn kết nối HTTPS ra ngoài tới các host không quen thuộc, hoặc giới hạn timeout của request thấp hơn 15 giây đã đặt ở trên. Nếu các lệnh gọi liên tục bị timeout, hãy kiểm tra với nhà cung cấp hosting của bạn xem kết nối ra ngoài tới api.badges.ninja có được cho phép hay không, và xác nhận extension curl trong PHP của bạn có chứng chỉ CA hiện hành — một bộ chứng chỉ lỗi thời là lỗi âm thầm phổ biến trên các stack shared-hosting cũ.
400 Bad Request. Payload thiếu một trường bắt buộc hoặc có cấu trúc sai. POST /awards yêu cầu badgeId, một object recipient lồng nhau với name và email, cùng một timestamp issuedOn — hàm hỗ trợ ở trên gửi chính xác như vậy. Lỗi phổ biến nhất là trường name của người nhận: nó phải có ít nhất 5 ký tự, vì vậy một tên hiển thị WordPress như “Jo” sẽ bị từ chối. Hãy dùng tên đầy đủ của tài khoản (hoặc phần trước @ của email) làm phương án dự phòng khi tên hiển thị quá ngắn.
401 Unauthorized. Hằng số API key không được nạp, hoặc key đã được tạo lại sau khi hằng số được thiết lập. Key chỉ hiển thị một lần khi tạo và không thể lấy lại — nếu bạn không còn nhớ key nào đang hoạt động, hãy xóa key cũ và cấp một key mới thay vì đoán mò.
Huy hiệu được cấp, nhưng sai thiết kế huy hiệu. Điều này gần như luôn là do giá trị post-meta badges_ninja_badge_id đã cũ còn sót lại từ quá trình thử nghiệm. Vì ánh xạ này nằm trên post của khóa học, rất dễ quên rằng bạn đã thay đổi nó sau khi sao chép một khóa học cho một nhóm học viên mới — nên kiểm tra nhanh mỗi khi bạn nhân bản một khóa học.
Award trùng lặp cho cùng một người. Đã được đề cập ở trên với cơ chế bảo vệ user-meta, nhưng đáng nhắc lại: các hook hoàn thành LMS không được đảm bảo chỉ kích hoạt đúng một lần. Hãy coi “đã được cấp rồi” là một trường hợp bình thường cần kiểm tra, chứ không phải trường hợp biên có thể bỏ qua.
Câu hỏi thường gặp
Điều này có hoạt động với các gói khóa học WooCommerce không? Có — nếu việc mua một gói nên kích hoạt một huy hiệu độc lập với việc hoàn thành khóa học (ví dụ một huy hiệu “certified member” cho bất kỳ ai mua một gói cụ thể), hãy gắn hook woocommerce_order_status_completed thay vì một action hoàn thành LMS, và tra cứu ID huy hiệu từ meta của sản phẩm thay vì từ khóa học.
Người nhận có thể nhận huy hiệu mà không cần tài khoản WordPress không? Có, và đó chính là mục đích. Award được gắn với địa chỉ email của họ trong Badges Ninja, chứ không phải với bản ghi người dùng WordPress. Nếu trang web của bạn cho phép thanh toán như khách hoặc hoàn thành bài kiểm tra ẩn danh, chỉ cần đảm bảo bạn thu thập một địa chỉ email thật để truyền vào badges_ninja_issue_award().
Nếu tôi muốn xem xét lại các award trước khi chúng được gửi đi thì sao? Đừng kích hoạt lệnh gọi trực tiếp từ hook hoàn thành. Thay vào đó, hãy ghi award đang chờ xử lý (user, course, timestamp) vào một bảng tùy chỉnh hoặc post type, rồi cấp nó từ một tác vụ được lên lịch hoặc một nút admin “Approve & Award” gọi cùng hàm hỗ trợ đó theo yêu cầu.
Tôi có cần một huy hiệu riêng cho mỗi khóa học, hay một huy hiệu có thể bao quát nhiều khóa học? Cả hai cách đều được. Một số tổ chức cấp tạo một huy hiệu cho mỗi khóa học để có độ chi tiết cao; những tổ chức khác tạo một huy hiệu “program completion” duy nhất và chỉ trao nó khi mọi khóa học bắt buộc đã hoàn thành, kiểm tra việc hoàn thành các điều kiện tiên quyết bên trong hook trước khi gọi API.
Sẵn sàng cấp chứng chỉ có thể xác minh đầu tiên của bạn chưa? Bắt đầu miễn phí tại badges.ninja — trình thiết kế trực quan, trang xác minh công khai, chứng chỉ PDF, đầu ra Open Badge v2.0. Không cần thẻ tín dụng.
Bài viết này được thực hiện như thế nào
Một số bài viết trên blog này được soạn với sự hỗ trợ của trợ lý AI, sau đó được đội ngũ Badges Ninja xem xét, kiểm chứng và biên tập trước khi xuất bản. Mọi đoạn code và mức giá đều được đối chiếu với sản phẩm thực tế. Tìm hiểu thêm về quy trình biên tập và AI của chúng tôi tại trang quy trình biên tập .

Về tác giả
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.
