ฝังการตรวจสอบตราสัญลักษณ์ดิจิทัลบนเว็บไซต์ของคุณ (ไม่ใช้ iframe ไม่ใช้ SDK)
เพิ่มสคริปต์ JavaScript เล็กๆ (หรือการ fetch ฝั่งเซิร์ฟเวอร์) ลงในเว็บไซต์ของคุณเพื่อตรวจสอบ Open Badge ใดก็ได้ผ่านเอ็นด์พอยต์ JSON-LD สาธารณะ ไม่ใช้ iframe ไม่มีตัวติดตามจากบุคคลที่สาม
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.
หากคุณออกเอกสารรับรอง ไม่ช้าก็เร็วจะมีคนถามว่า “แล้วจะรู้ได้ยังไงว่าตราสัญลักษณ์นี้ของจริง” คำตอบที่ซื่อตรงต้องไม่ใช่ “เชื่อไฟล์ PNG เถอะ” Open Badge v2.0 แก้ปัญหานี้ด้วยเอ็นด์พอยต์ตรวจสอบสาธารณะที่เครื่องอ่านได้ และการใส่วิดเจ็ต “Verified” บนเว็บไซต์ของคุณเองใช้โค้ดราวสิบบรรทัดเท่านั้น ไม่ต้องใช้ iframe ไม่ต้องใช้สคริปต์ฝังจากบุคคลที่สาม และไม่ต้องใช้ SDK
บทความนี้แสดงวิธีเรียกเอ็นด์พอยต์และแสดงผลลัพธ์อย่างละเอียด ทั้งด้วย JavaScript ล้วนและฝั่งเซิร์ฟเวอร์ด้วย Node และ Python นอกจากนี้ยังอธิบายว่า “การตรวจสอบ” ตรวจอะไรจริงๆ เอ็นด์พอยต์นี้อยู่ตรงไหนเมื่อเทียบกับหน้าสำหรับคนที่ badges.ninja มอบให้ผู้รับอยู่แล้ว และข้อผิดพลาดที่ทำให้วิดเจ็ตตรวจสอบดูเหมือนเสร็จสมบูรณ์ทั้งที่ไม่ได้พิสูจน์อะไรเลย
”ตรวจสอบแล้ว” หมายความว่าอย่างไรกันแน่
assertion ของ Open Badge v2.0 คือเอกสาร JSON-LD ที่มีโครงสร้างตายตัว ได้แก่ตัว assertion เอง (ใครเป็นผู้รับ เมื่อไร) การอ้างอิงถึง BadgeClass (ตราสัญลักษณ์นี้คืออะไร ใครกำหนดเกณฑ์) และการอ้างอิงถึงโปรไฟล์ผู้ออก (ใครเป็นผู้รับรอง) เมื่อคุณ “ตรวจสอบ” ตราสัญลักษณ์ คุณกำลังตรวจสามอย่างแยกกัน:
- assertion มีอยู่จริงและเปิดอ่านได้หรือไม่ ID ของรางวัลเป็นของจริง ไม่ได้เดามาหรือถูกแก้ไข
- ถูกเพิกถอนแล้วหรือยัง ผู้ออกสามารถเพิกถอนตราสัญลักษณ์ภายหลังได้ เช่น เอกสารที่ออกผิดพลาด การละเมิดนโยบาย หรือใบรับรองที่หมดอายุ และตัวตรวจสอบที่ถูกต้องต้องแสดงสถานะนั้น ไม่ใช่แค่ยืนยันว่ามีระเบียนอยู่
- ห่วงโซ่ทั้งหมดสอดคล้องกันหรือไม่ การอ้างอิงตราสัญลักษณ์และผู้ออกใน assertion ต้องชี้ไปยังระเบียนจริงที่ตรงกัน ไม่ใช่ ID ที่ไร้ที่มาหรือไม่ตรงกัน
วิดเจ็ตตรวจสอบที่แค่ส่งคำขอไปยัง URL แล้วเช็กสถานะ 200 จะพลาดข้อ 2 ไปทั้งหมด assertion ที่ถูกเพิกถอนยังเปิดอ่านได้ตามปกติ เพียงแต่มีแฟล็กการเพิกถอนอยู่ใน payload หากวิดเจ็ตของคุณเช็กแค่ว่า “คำขอสำเร็จหรือไม่” เอกสารที่ถูกเพิกถอนก็จะแสดงว่าตรวจสอบแล้ว ซึ่งแย่กว่าไม่มีวิดเจ็ตเลย
หน้าสาธารณะสำหรับคนกับเอ็นด์พอยต์ API
ทุกรางวัลที่ออกบน https://badges.ninja จะได้ URL สาธารณะที่ผู้รับแชร์ได้โดยตรง และไม่ต้องสมัครสมาชิกเพื่อเปิดดู:

หน้านี้คือสิ่งที่ผู้รับส่วนใหญ่จะลิงก์จาก LinkedIn เรซูเม่ หรือลายเซ็นอีเมล สร้างมาเพื่อคน แสดงภาพตราสัญลักษณ์ ผู้ออก และวันที่ออก และไม่ต้องให้คุณทำอะไรเลยก็ใช้งานได้ ส่วนเอ็นด์พอยต์ API ด้านล่างคืน assertion ชุดเดียวกันในรูป JSON-LD แบบมีโครงสร้าง ซึ่งเหมาะเมื่อคุณสร้าง UI ของตัวเองแทนที่จะลิงก์ไปยังหน้าของเรา
เอ็นด์พอยต์
GET https://api.badges.ninja/certify-badge/award/{guid}
ไม่ต้องใช้ header สำหรับยืนยันตัวตน เพราะตั้งใจให้เป็นสาธารณะ จุดประสงค์ทั้งหมดของ Open Badges คือให้ทุกคน (นายจ้าง ผู้สรรหาบุคลากร สภาวิชาชีพ) ตรวจสอบเอกสารรับรองได้โดยไม่ต้องมีบัญชี รายละเอียดสัญญา API ฉบับเต็ม รวมถึงชื่อฟิลด์ที่แน่นอนใน assertion ที่ส่งกลับ อยู่ในเอกสารการตรวจสอบ
ส่ง GUID ของรางวัลเข้าไป ซึ่งคุณจะได้มาจากบันทึกการออกของตัวเอง หรือแยกออกมาจากลิงก์ badges.ninja/awards/<guid> ที่ผู้รับแชร์ให้
JS ล้วน: fetch แล้วแสดงผล
นี่คือวิดเจ็ตทั้งหมด ไม่ต้องมีขั้นตอน build ไม่ต้องมีแพ็กเกจ npm วางลงในหน้าใดก็ได้ทันที:
<div id="badge-verify"></div>
<script>
async function renderBadgeVerification(awardId, mountId) {
const el = document.getElementById(mountId);
el.textContent = "Checking badge status";
try {
const res = await fetch(
`https://api.badges.ninja/certify-badge/award/${awardId}`
);
if (!res.ok) {
el.textContent = "Credential not found.";
return;
}
const assertion = await res.json();
// —— revocation check first, a resolvable assertion isn't enough on its own
if (assertion.revoked) {
el.textContent = `Revoked: ${assertion.revocationReason || "no reason given"}`;
return;
}
el.textContent = `Verified, issued ${new Date(assertion.issuedOn).toLocaleDateString()}`;
} catch (err) {
el.textContent = "Verification check failed.";
}
}
renderBadgeVerification("REPLACE_WITH_AWARD_ID", "badge-verify");
</script>
แทนที่ REPLACE_WITH_AWARD_ID ด้วย GUID ของรางวัลจริง เท่านี้คือการผสานรวมฝั่งไคลเอนต์ทั้งหมด ไม่มีขอบเขต iframe ที่ต้องมาต่อสู้กับ CSS และไม่มีสคริปต์ของบุคคลที่สามที่พ่วงระบบวิเคราะห์หรือพิกเซลติดตามของตัวเองมาพร้อมกับการตรวจสอบ
ฝั่งเซิร์ฟเวอร์: Node
หากคุณอยากตรวจสอบบนแบ็กเอนด์ เช่น ก่อนแสดงตรา “Verified Partner” บนหน้าโปรไฟล์ที่คุณควบคุมเอง หรือก่อนยอมรับเอกสารรับรองเป็นหลักฐานในขั้นตอนต้อนรับผู้ใช้ การ fetch ก็เหมือนกันทุกประการ เพียงตัดส่วน DOM ออก:
async function verifyAward(awardId) {
const res = await fetch(
`https://api.badges.ninja/certify-badge/award/${awardId}`
);
if (!res.ok) return { verified: false, reason: "not_found" };
const assertion = await res.json();
if (assertion.revoked) {
return { verified: false, reason: assertion.revocationReason || "revoked" };
}
return { verified: true, issuedOn: assertion.issuedOn, badge: assertion.badge };
}
ฝั่งเซิร์ฟเวอร์: Python
import requests
# —— resolve the assertion, then check revocation before trusting it
def verify_award(award_id):
res = requests.get(f"https://api.badges.ninja/certify-badge/award/{award_id}")
if res.status_code != 200:
return {"verified": False, "reason": "not_found"}
assertion = res.json()
if assertion.get("revoked"):
return {"verified": False, "reason": assertion.get("revocationReason", "revoked")}
return {"verified": True, "issued_on": assertion.get("issuedOn"), "badge": assertion.get("badge")}
ทั้งสองตัวอย่างถือว่าการตอบกลับที่ไม่ใช่ 200 คือ “ไม่พบ” แทนที่จะโยนข้อผิดพลาด ID รางวัลที่ผิดหรือเดามาเป็นอินพุตที่คาดไว้อยู่แล้วสำหรับเอ็นด์พอยต์ตรวจสอบสาธารณะ ไม่ใช่เหตุการณ์ผิดปกติ จึงควรจัดการเป็นเส้นทางปกติ ไม่ใช่ข้อผิดพลาดที่ดักจับ
จัดสไตล์ผลลัพธ์เองแทนการฝัง UI ของคนอื่น
เนื่องจากวิดเจ็ตข้างต้นเป็นเพียงการเรียก fetch ลงใน DOM ของคุณเอง คุณจึงควบคุมได้ทุกพิกเซล รูปแบบที่พบบ่อยคือสามสถานะที่แสดงผลตรงจากการตอบกลับ:
- Verified เครื่องหมายถูกสีเขียวพร้อมคำว่า “Verified” และวันที่ออก จัดสไตล์ให้เข้ากับคอมโพเนนต์ป้ายหรือแท็กของเว็บไซต์คุณ
- Revoked สถานะสีจางหรือสีแดงที่แสดงเหตุผลของการเพิกถอนแทนที่จะซ่อนไว้ ทั้งผู้รับและบุคคลที่สามได้ประโยชน์จาก “เหตุผล” ที่ตรงไปตรงมา ไม่ใช่ความล้มเหลวแบบเงียบๆ
- Not found ให้แสดงผลเหมือนกับ “revoked” (อย่าแสดงสถานะสำเร็จปลอมๆ) แต่ใช้ข้อความต่างกัน เพราะ ID ที่ผิดกับการเพิกถอนโดยตั้งใจเป็นคนละสถานการณ์สำหรับผู้อ่านหน้านั้น
ทั้งหมดนี้ไม่ต้องใช้ไลบรารีคอมโพเนนต์ แค่ switch บน verified / revoked / not_found กับ CSS สามคลาสก็ครอบคลุมแล้ว
แคช เพื่อไม่ให้ยิงเอ็นด์พอยต์รัวเกินไป
เอ็นด์พอยต์ตรวจสอบเป็นสาธารณะและไม่ต้องยืนยันตัวตน นั่นหมายความว่าไม่ต้องกังวลเรื่องค่าเครดิตต่อคำขอ แต่ก็ยังเป็นการเรียกผ่านเครือข่ายทุกครั้งที่โหลดหน้า หากเรียกฝั่งไคลเอนต์โดยไม่แคช สำหรับหน้าที่แสดงตราสัญลักษณ์เดียวกันให้ผู้เข้าชมทุกคน (เช่นตรา “Verified Partner” ที่ส่วนท้ายเว็บ) ให้แคชผลลัพธ์ฝั่งเซิร์ฟเวอร์ด้วย TTL สั้นๆ ห้าถึงสิบห้านาทีก็เพียงพอ เพราะการเพิกถอนไม่ใช่สัญญาณที่เปลี่ยนเร็ว แทนการ fetch ใหม่ทุกคำขอ:
const cache = new Map();
async function verifyAwardCached(awardId, ttlMs = 10 * 60 * 1000) {
const hit = cache.get(awardId);
if (hit && Date.now() - hit.at < ttlMs) return hit.result;
const result = await verifyAward(awardId);
cache.set(awardId, { result, at: Date.now() });
return result;
}
สำหรับวิดเจ็ตรายผู้เข้าชม (ผู้รับที่ฝังเอกสารรับรองของตัวเองบนเว็บไซต์ส่วนตัว) ข้ามแคชไปแล้วเรียกใหม่ตอนโหลดหน้าได้เลย คำขอที่เพิ่มขึ้นแทบไม่มีผลในระดับนั้น
สิ่งที่ badges.ninja มอบให้ผู้รับอยู่แล้ว
ก่อนสร้างการฝังแบบกำหนดเองทั้งหมด ควรรู้ก่อนว่ามีอะไรให้ใช้ได้เลย หน้าสาธารณะของทุกรางวัลมีสนิปเป็ตสำหรับแชร์และฝังที่พร้อมใช้ ผู้รับคัดลอกได้ทันทีโดยไม่ต้องเขียนโค้ด:

หากกรณีใช้งานของคุณคือ “ให้ผู้รับแสดงว่าตราสัญลักษณ์ของตนเป็นของจริง” การลิงก์ไปยังหน้า badges.ninja/awards/<guid> ของผู้รับมักง่ายกว่าการสร้างวิดเจ็ตเอง เพราะเป็นหน้าสำหรับคนที่แสดงไว้ข้างต้น และจัดการความต่างระหว่างตรวจสอบแล้วกับถูกเพิกถอนให้คุณเรียบร้อย ให้สร้างวิดเจ็ตแบบกำหนดเองจากบทความนี้เมื่อคุณต้องการแสดงสถานะการตรวจสอบ ภายใน UI ของผลิตภัณฑ์คุณเอง เช่น ไดเรกทอรีพันธมิตร แดชบอร์ดตรวจสอบภายใน หรือเครื่องมือสรรหาที่เช็กใบรับรองที่ผู้สมัครอ้าง ซึ่งการลิงก์ไปเว็บไซต์อื่นไม่ใช่ UX ที่เหมาะสม
ทำไมไม่ใช้ iframe หรือวิดเจ็ตของผู้ให้บริการ
บางแพลตฟอร์มให้สนิปเป็ตอย่าง <iframe src="https://vendor.example.com/verify-widget"> เพื่อฝังสถานะการตรวจสอบ ใช้ได้ก็จริง แต่มีต้นทุนจริง คือคุณปรับสไตล์เนื้อหาให้เข้ากับเว็บไซต์ไม่ได้ (ต้องใช้ CSS ที่ผู้ให้บริการมาให้) มันนำเอกสารข้ามต้นทางเข้ามาซึ่ง Content-Security-Policy ของคุณต้องอนุญาตอย่างชัดเจน และทุกครั้งที่โหลดหน้าจะมีคำขอไปยังโฮสต์ iframe ของบุคคลที่สามไม่ว่าผู้เข้าชมจะสนใจสถานะการตรวจสอบหรือไม่ วิดเจ็ต fetch แบบสองขั้นตอนหลีกเลี่ยงทั้งสามข้อ เพราะเป็น DOM ของคุณ สไตล์ของคุณ และทำงานเฉพาะเมื่อคุณสั่งเรียก
ไล่ตรวจห่วงโซ่การตรวจสอบทั้งหมด
หากคุณสร้างอะไรที่ละเอียดกว่าวิดเจ็ตแสดงสถานะ เช่นเครื่องมือตรวจสอบที่เช็กเอกสารรับรองเป็นชุด หรือรายงานการปฏิบัติตามข้อกำหนด ให้ไล่ตรวจทั้งห่วงโซ่ อย่าหยุดแค่ assertion โดย assertion อ้างอิง URL ของ badge (BadgeClass: ชื่อ คำอธิบาย เกณฑ์) และ BadgeClass ก็อ้างอิง URL ของ issuer (โปรไฟล์องค์กร) ตัวตรวจสอบที่เข้มงวดจะเปิดอ่านทั้งสามอย่างและยืนยันว่าสอดคล้องกัน คือตราสัญลักษณ์เป็นของผู้ออกที่อ้างจริง และ assertion อ้างอิงตราสัญลักษณ์ที่ระบุจริง ไม่ใช่แค่ว่า assertion ระดับบนสุดตอบ 200 ลึกกว่าที่วิดเจ็ตสถานะของเอกสารเดียวต้องการ แต่ควรรู้ว่าโครงสร้างนี้มีอยู่ หากพันธมิตรหรือผู้ตรวจสอบถามว่า “การตรวจสอบของคุณลึกแค่ไหนกันแน่”
ข้อผิดพลาดที่พบบ่อยและควรหลีกเลี่ยง
- เช็กแค่สถานะ HTTP ดังที่กล่าวไป assertion ที่ถูกเพิกถอนยังเปิดอ่านได้ ให้อ่าน
assertion.revokedเสมอ อย่าสรุปว่าตรวจสอบแล้วจากรหัสสถานะเพียงอย่างเดียว - ฮาร์ดโค้ด ID รางวัลในคอมโพเนนต์ที่ใช้ร่วมกัน หากวิดเจ็ตต้องนำกลับมาใช้ในหน้าของผู้รับจำนวนมาก ให้รับ ID รางวัลเป็นพารามิเตอร์ (data attribute, query string, prop) ไม่ใช่ค่าตายตัวในสคริปต์
- กลืนข้อผิดพลาดเครือข่ายอย่างเงียบๆ
try/catchที่ไม่บันทึกอะไรเลยและแสดงข้อความ “ไม่พบ” เหมือนกับตราสัญลักษณ์ที่ไม่มีอยู่จริง ทำให้ดีบักลำบากภายหลัง ให้แยกระหว่าง “การเรียกเครือข่ายล้มเหลว” กับ “API ตอบว่าไม่” ในเส้นทางข้อผิดพลาด อย่างน้อยในล็อก - ตรวจสอบซ้ำทุกครั้งที่เรนเดอร์ในแอปฝั่งไคลเอนต์ หากวิดเจ็ตอยู่ในคอมโพเนนต์ React หรือ Vue ที่เรนเดอร์ใหม่บ่อย ให้ตรวจสอบครั้งเดียวต่อการ mount (หรือครั้งเดียวต่อช่วง TTL ที่แคช ตามหัวข้อแคชข้างต้น) ไม่ใช่ทุกครั้งที่เรนเดอร์ใหม่
ต้องเสียเครดิตไหม และใช้กับแผนฟรีได้หรือเปล่า
ไม่ เอ็นด์พอยต์ตรวจสอบตั้งใจอยู่นอกระบบเครดิตที่ควบคุมการออก การเช็กว่าตราสัญลักษณ์ใช้ได้หรือไม่ไม่ทำให้ผู้ออกเสียอะไร และผู้เข้าชมที่เรียกใช้ก็ไม่ต้องมีบัญชี คีย์ API หรือแผนใดๆ ทั้งสิ้น ไม่ว่าตราสัญลักษณ์จะออกภายใต้แผน Free, Starter หรือ Pro การตรวจสอบเป็นคุณสมบัติของเอกสารรับรองเอง ไม่ใช่ฟีเจอร์ที่ผูกกับการสมัครสมาชิกของผู้ออก นี่ยังเป็นเหตุผลที่คุณวางวิดเจ็ตนี้บนหน้าสาธารณะที่มีทราฟฟิกคาดเดาไม่ได้ (หน้าสมัครงาน ไดเรกทอรีพันธมิตร) ได้อย่างปลอดภัยโดยไม่ต้องกังวลเรื่องขีดจำกัดอัตราที่ผูกกับบัญชีของคุณ เพราะคุณเรียกเอ็นด์พอยต์สาธารณะแบบอ่านอย่างเดียว ไม่ใช่เส้นทาง API ที่ต้องยืนยันตัวตน
อีกเรื่องที่ควรพูดให้ชัด คือ JSON ของ assertion ที่เอ็นด์พอยต์ส่งกลับไม่มีอีเมลดิบของผู้รับ อีเมลของผู้รับถูกเก็บแบบแฮชบนแบ็กเอนด์ เพื่อไม่ให้คำตอบการตรวจสอบสาธารณะถูกดึงไปเก็บข้อมูลติดต่อส่วนบุคคลได้ สิ่งที่คุณได้กลับมาเพียงพอสำหรับยืนยันเอกสารรับรอง (ตราสัญลักษณ์ ผู้ออก วันที่ สถานะการเพิกถอน) โดยไม่ทำให้เอ็นด์พอยต์กลายเป็นเครื่องมือหาลูกค้าเป้าหมายสำหรับใครก็ตามที่วนลูป ID รางวัล
พร้อมออกเอกสารรับรองที่ตรวจสอบได้ฉบับแรกของคุณแล้วหรือยัง
เริ่มใช้ฟรีที่ badges.ninja มีตัวออกแบบภาพ หน้าตรวจสอบสาธารณะ ใบรับรอง PDF และเอาต์พุต Open Badge v2.0 ไม่ต้องใช้บัตรเครดิต
สำหรับโครงสร้างพร้อมคำอธิบายครบทุกฟิลด์ใน assertion ดูเอกสารอ้างอิงฟิลด์ JSON-LD และหากคุณออกเอกสารรับรองด้วยโปรแกรมแทนที่จะแค่ตรวจสอบ คู่มือเริ่มต้นใช้งาน API ครอบคลุมการยืนยันตัวตนและการเรียกสร้างรางวัลที่เป็นต้นทางของเอาต์พุตจากเอ็นด์พอยต์นี้
บทความนี้จัดทำขึ้นอย่างไร
บางบทความในบล็อกนี้ร่างขึ้นด้วยความช่วยเหลือของผู้ช่วย AI จากนั้นทีมงาน Badges Ninja จะตรวจสอบ ยืนยันข้อเท็จจริง และแก้ไขก่อนเผยแพร่ ตัวอย่างโค้ดและราคาทุกรายการได้รับการตรวจสอบกับผลิตภัณฑ์จริง อ่านเพิ่มเติมเกี่ยวกับกระบวนการด้านบรรณาธิการและ AI ของเราได้ที่ หน้ากระบวนการด้านบรรณาธิการ .

เกี่ยวกับผู้เขียน
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.

