ฝังการตรวจสอบตราสัญลักษณ์ดิจิทัลบนเว็บไซต์ของคุณ (ไม่ใช้ iframe ไม่ใช้ SDK)

เพิ่มสคริปต์ JavaScript เล็กๆ (หรือการ fetch ฝั่งเซิร์ฟเวอร์) ลงในเว็บไซต์ของคุณเพื่อตรวจสอบ Open Badge ใดก็ได้ผ่านเอ็นด์พอยต์ JSON-LD สาธารณะ ไม่ใช้ iframe ไม่มีตัวติดตามจากบุคคลที่สาม

Nacho Coll โดย อัปเดตล่าสุด 4 นาทีอ่าน
เพิ่มสคริปต์ JavaScript เล็กๆ (หรือการ fetch ฝั่งเซิร์ฟเวอร์) ลงในเว็บไซต์ของคุณเพื่อตรวจสอบ Open Badge ใดก็ได้ผ่านเอ็นด์พอยต์ JSON-LD สาธารณะ ไม่ใช้ iframe ไม่มีตัวติดตามจากบุคคลที่สาม

หากคุณออกเอกสารรับรอง ไม่ช้าก็เร็วจะมีคนถามว่า “แล้วจะรู้ได้ยังไงว่าตราสัญลักษณ์นี้ของจริง” คำตอบที่ซื่อตรงต้องไม่ใช่ “เชื่อไฟล์ PNG เถอะ” Open Badge v2.0 แก้ปัญหานี้ด้วยเอ็นด์พอยต์ตรวจสอบสาธารณะที่เครื่องอ่านได้ และการใส่วิดเจ็ต “Verified” บนเว็บไซต์ของคุณเองใช้โค้ดราวสิบบรรทัดเท่านั้น ไม่ต้องใช้ iframe ไม่ต้องใช้สคริปต์ฝังจากบุคคลที่สาม และไม่ต้องใช้ SDK

บทความนี้แสดงวิธีเรียกเอ็นด์พอยต์และแสดงผลลัพธ์อย่างละเอียด ทั้งด้วย JavaScript ล้วนและฝั่งเซิร์ฟเวอร์ด้วย Node และ Python นอกจากนี้ยังอธิบายว่า “การตรวจสอบ” ตรวจอะไรจริงๆ เอ็นด์พอยต์นี้อยู่ตรงไหนเมื่อเทียบกับหน้าสำหรับคนที่ badges.ninja มอบให้ผู้รับอยู่แล้ว และข้อผิดพลาดที่ทำให้วิดเจ็ตตรวจสอบดูเหมือนเสร็จสมบูรณ์ทั้งที่ไม่ได้พิสูจน์อะไรเลย

”ตรวจสอบแล้ว” หมายความว่าอย่างไรกันแน่

assertion ของ Open Badge v2.0 คือเอกสาร JSON-LD ที่มีโครงสร้างตายตัว ได้แก่ตัว assertion เอง (ใครเป็นผู้รับ เมื่อไร) การอ้างอิงถึง BadgeClass (ตราสัญลักษณ์นี้คืออะไร ใครกำหนดเกณฑ์) และการอ้างอิงถึงโปรไฟล์ผู้ออก (ใครเป็นผู้รับรอง) เมื่อคุณ “ตรวจสอบ” ตราสัญลักษณ์ คุณกำลังตรวจสามอย่างแยกกัน:

  1. assertion มีอยู่จริงและเปิดอ่านได้หรือไม่ ID ของรางวัลเป็นของจริง ไม่ได้เดามาหรือถูกแก้ไข
  2. ถูกเพิกถอนแล้วหรือยัง ผู้ออกสามารถเพิกถอนตราสัญลักษณ์ภายหลังได้ เช่น เอกสารที่ออกผิดพลาด การละเมิดนโยบาย หรือใบรับรองที่หมดอายุ และตัวตรวจสอบที่ถูกต้องต้องแสดงสถานะนั้น ไม่ใช่แค่ยืนยันว่ามีระเบียนอยู่
  3. ห่วงโซ่ทั้งหมดสอดคล้องกันหรือไม่ การอ้างอิงตราสัญลักษณ์และผู้ออกใน 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 ครอบคลุมการยืนยันตัวตนและการเรียกสร้างรางวัลที่เป็นต้นทางของเอาต์พุตจากเอ็นด์พอยต์นี้

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.

กลับไปที่บล็อก

บทความที่เกี่ยวข้อง