Ενσωματώστε την επαλήθευση ψηφιακών σημάτων στον ιστότοπό σας (χωρίς iframe, χωρίς SDK)

Προσθέστε ένα μικρό απόσπασμα JavaScript (ή ένα server-side fetch) στον ιστότοπό σας που επαληθεύει οποιοδήποτε Open Badge μέσω ενός δημόσιου endpoint JSON-LD. Χωρίς iframe, χωρίς tracker τρίτων.

Nacho Coll Από Ενημερώθηκε 10 λεπτά ανάγνωσης
Προσθέστε ένα μικρό απόσπασμα JavaScript (ή ένα server-side fetch) στον ιστότοπό σας που επαληθεύει οποιοδήποτε Open Badge μέσω ενός δημόσιου endpoint JSON-LD. Χωρίς iframe, χωρίς tracker τρίτων.

Αν εκδίδετε διαπιστευτήρια, αργά ή γρήγορα κάποιος θα ρωτήσει: «πώς ξέρω ότι αυτό το ψηφιακό σήμα είναι αληθινό;» Η ειλικρινής απάντηση δεν πρέπει ποτέ να είναι «εμπιστευτείτε το PNG». Το Open Badge v2.0 λύνει το πρόβλημα με ένα δημόσιο, αναγνώσιμο από μηχανές endpoint επαλήθευσης, και η προσθήκη ενός widget «Επαληθευμένο» στον δικό σας ιστότοπο θέλει περίπου δέκα γραμμές κώδικα — όχι iframe, όχι script ενσωμάτωσης τρίτου, όχι SDK.

Αυτό το άρθρο δείχνει ακριβώς πώς να καλέσετε το endpoint και να αποδώσετε το αποτέλεσμα, σε vanilla JavaScript και στον server με Node και Python. Καλύπτει επίσης τι ελέγχει πραγματικά η «επαλήθευση», πού ταιριάζει το endpoint δίπλα στις σελίδες για ανθρώπους που το badges.ninja δίνει ήδη στους αποδέκτες, και τα λάθη που κάνουν ένα widget επαλήθευσης να φαίνεται έτοιμο ενώ στην πράξη δεν αποδεικνύει τίποτα.

Τι σημαίνει πραγματικά «επαληθευμένο»

Μια δήλωση (assertion) Open Badge v2.0 είναι ένα έγγραφο JSON-LD με σταθερή δομή: η ίδια η δήλωση (ποιος το έλαβε, πότε), μια αναφορά στο BadgeClass (τι είναι, ποιος ορίζει τα κριτήρια) και μια αναφορά στο προφίλ του εκδότη (ποιος το εγγυάται). Όταν «επαληθεύετε» ένα ψηφιακό σήμα, στην ουσία ελέγχετε τρία ξεχωριστά πράγματα:

  1. Υπάρχει η δήλωση και επιλύεται. Το αναγνωριστικό της απονομής είναι πραγματικό, όχι μαντεμένο ή παραποιημένο.
  2. Έχει ανακληθεί. Ένας εκδότης μπορεί να ανακαλέσει ένα ψηφιακό σήμα εκ των υστέρων — διαπιστευτήριο που εκδόθηκε κατά λάθος, παραβίαση πολιτικής, πιστοποίηση που έληξε — και ένας σωστός επαληθευτής πρέπει να το επισημαίνει, όχι απλώς να επιβεβαιώνει ότι η εγγραφή υπάρχει.
  3. Στέκει η αλυσίδα. Οι αναφορές της δήλωσης προς το badge και τον εκδότη επιλύονται πράγματι σε υπαρκτές, αντίστοιχες εγγραφές, όχι σε ορφανά ή ασυμβίβαστα αναγνωριστικά.

Ένα widget επαλήθευσης που απλώς καλεί ένα URL και ελέγχει για κατάσταση 200 χάνει εντελώς το σημείο 2. Μια ανακληθείσα δήλωση εξακολουθεί να επιλύεται κανονικά — απλώς φέρει μια σημαία ανάκλησης στο payload. Αν το widget σας ελέγχει μόνο «πέτυχε το αίτημα;», ένα ανακληθέν διαπιστευτήριο θα εμφανιστεί ως επαληθευμένο, κάτι χειρότερο από το να μην υπάρχει καθόλου widget.

Η δημόσια σελίδα για ανθρώπους έναντι του endpoint του API

Κάθε απονομή που εκδίδεται στο https://badges.ninja αποκτά ένα δημόσιο URL που οι αποδέκτες μπορούν να μοιραστούν απευθείας, χωρίς εγγραφή για να το δει κανείς:

Public credential page

Αυτή η σελίδα είναι αυτή που οι περισσότεροι αποδέκτες θα συνδέσουν από το LinkedIn, ένα βιογραφικό ή την υπογραφή του email τους — είναι φτιαγμένη για ανθρώπους, δείχνει την εικόνα του ψηφιακού σήματος, τον εκδότη και την ημερομηνία έκδοσης, και δεν χρειάζεται τίποτα από εσάς για να λειτουργήσει. Το endpoint του API παρακάτω επιστρέφει την ίδια υποκείμενη δήλωση ως δομημένο JSON-LD, που είναι αυτό που χρειάζεστε όταν φτιάχνετε το δικό σας UI αντί να παραπέμπετε απλώς στο δικό μας.

Το endpoint

GET https://api.badges.ninja/certify-badge/award/{guid}

Δεν απαιτείται header πιστοποίησης — είναι σκόπιμα δημόσιο, επειδή όλο το νόημα των Open Badges είναι ότι οποιοσδήποτε (εργοδότης, recruiter, επαγγελματικό σώμα αδειοδότησης) μπορεί να επαληθεύσει ένα διαπιστευτήριο χωρίς λογαριασμό. Οι πλήρεις λεπτομέρειες του συμβολαίου, μαζί με τα ακριβή ονόματα πεδίων της επιστρεφόμενης δήλωσης, βρίσκονται στα έγγραφα επαλήθευσης.

Περάστε το GUID της απονομής, το οποίο θα έχετε είτε από τα δικά σας αρχεία έκδοσης είτε αντλημένο από έναν σύνδεσμο badges.ninja/awards/<guid> που σας μοιράστηκε ένας αποδέκτης.

Vanilla JS: fetch και απόδοση

Αυτό είναι ολόκληρο το widget. Χωρίς build step, χωρίς πακέτο 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 απονομής. Αυτή είναι ολόκληρη η ενσωμάτωση στην πλευρά του client: κανένα όριο iframe για να παλέψετε με το CSS, και κανένα script τρίτου που φέρνει τα δικά του analytics ή tracking pixels μαζί με τον έλεγχο επαλήθευσης.

Server-side: Node

Αν προτιμάτε να επαληθεύετε στο backend σας — ας πούμε, πριν εμφανίσετε ένα σήμα «Επαληθευμένος συνεργάτης» σε μια σελίδα προφίλ που ελέγχετε εσείς, ή πριν δεχτείτε ένα διαπιστευτήριο ως απόδειξη σε μια ροή onboarding — το 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 };
}

Server-side: 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 ως «δεν βρέθηκε» αντί να πετούν εξαίρεση. Ένα χαλασμένο ή μαντεμένο αναγνωριστικό απονομής είναι αναμενόμενη είσοδος για ένα δημόσιο endpoint επαλήθευσης, όχι εξαιρετική περίπτωση, οπότε χειριστείτε το ως κανονικό κλάδο και όχι ως σφάλμα που πιάνεται.

Στυλ του αποτελέσματος αντί για ενσωμάτωση του UI κάποιου άλλου

Επειδή το παραπάνω widget είναι απλώς μια κλήση fetch μέσα στο δικό σας DOM, ελέγχετε κάθε pixel του. Ένα συνηθισμένο μοτίβο είναι τρεις οπτικές καταστάσεις που προκύπτουν κατευθείαν από την απάντηση:

  • Επαληθευμένο — ένα πράσινο τσεκ μαζί με «Επαληθευμένο» και την ημερομηνία έκδοσης, με στυλ που ταιριάζει στα components badge/tag του ιστότοπού σας.
  • Ανακληθέν — μια σβησμένη ή κόκκινη κατάσταση που δείχνει τον λόγο ανάκλησης αντί να τον κρύβει. Και οι αποδέκτες και τρίτοι ωφελούνται από ένα ειλικρινές «γιατί», όχι από μια σιωπηλή αποτυχία.
  • Δεν βρέθηκε — αντιμετωπίστε το οπτικά όπως το «ανακληθέν» (μην αποδώσετε ψεύτικη κατάσταση επιτυχίας), αλλά με διαφορετικό κείμενο, αφού ένα λάθος αναγνωριστικό και μια σκόπιμη ανάκληση είναι διαφορετικές καταστάσεις για όποιον διαβάζει τη σελίδα.

Τίποτα από αυτά δεν απαιτεί βιβλιοθήκη components. Ένα switch σε verified / revoked / not_found και τρεις κλάσεις CSS τα καλύπτουν.

Caching, για να μη βομβαρδίζετε το endpoint

Το endpoint επαλήθευσης είναι δημόσιο και χωρίς πιστοποίηση, που σημαίνει επίσης ότι δεν υπάρχει κόστος credits ανά αίτημα — αλλά παραμένει ένας γύρος δικτύου σε κάθε φόρτωση σελίδας αν το καλείτε από τον client χωρίς caching. Για μια σελίδα που δείχνει το ίδιο ψηφιακό σήμα σε κάθε επισκέπτη (ένα σήμα «Επαληθευμένος συνεργάτης» στο footer, για παράδειγμα), αποθηκεύστε το αποτέλεσμα στον server με σύντομο TTL — πέντε έως δεκαπέντε λεπτά αρκούν, αφού οι ανακλήσεις δεν είναι σήμα που αλλάζει γρήγορα — αντί να το ξανακαλείτε σε κάθε αίτημα:

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

Για ένα widget ανά επισκέπτη (ένας αποδέκτης που ενσωματώνει το δικό του διαπιστευτήριο σε προσωπικό ιστότοπο), παραλείψτε το cache και καλέστε το φρέσκο σε κάθε φόρτωση σελίδας — το επιπλέον αίτημα είναι αμελητέο σε τέτοια κλίμακα.

Τι δίνει ήδη το badges.ninja στους αποδέκτες

Πριν φτιάξετε μια πλήρως προσαρμοσμένη ενσωμάτωση, αξίζει να ξέρετε τι υπάρχει ήδη έτοιμο. Η δημόσια σελίδα κάθε απονομής περιλαμβάνει έτοιμα snippets κοινοποίησης και ενσωμάτωσης που οι αποδέκτες μπορούν να αντιγράψουν απευθείας, χωρίς κώδικα από μέρους τους:

Public share page with social buttons + embed snippets

Αν η περίπτωσή σας είναι «αφήστε έναν αποδέκτη να δείξει ότι το ψηφιακό σήμα του είναι αληθινό», ο σύνδεσμος προς τη σελίδα του badges.ninja/awards/<guid> είναι συχνά απλούστερος από το να φτιάξετε ένα προσαρμοσμένο widget — είναι η σελίδα για ανθρώπους που είδαμε νωρίτερα και χειρίζεται ήδη τη διάκριση επαληθευμένου/ανακληθέντος για εσάς. Φτιάξτε το προσαρμοσμένο widget αυτού του άρθρου όταν χρειάζεστε την κατάσταση επαλήθευσης αποδοσμένη μέσα στο UI του δικού σας προϊόντος — έναν κατάλογο συνεργατών, ένα εσωτερικό dashboard ελέγχου, ένα εργαλείο πρόσληψης που ελέγχει τις δηλωμένες πιστοποιήσεις ενός υποψηφίου — όπου η παραπομπή σε άλλον ιστότοπο δεν είναι η σωστή εμπειρία χρήστη.

Γιατί όχι iframe ή widget προμηθευτή;

Ορισμένες πλατφόρμες σας δίνουν ένα snippet όπως <iframe src="https://vendor.example.com/verify-widget"> για την ενσωμάτωση της κατάστασης επαλήθευσης. Λειτουργεί, αλλά έχει πραγματικό κόστος: δεν μπορείτε να αλλάξετε το στυλ του περιεχομένου ώστε να ταιριάζει στον ιστότοπό σας (είστε δεμένοι με όποιο CSS έστειλε ο προμηθευτής), εισάγει ένα cross-origin έγγραφο που η Content-Security-Policy σας πρέπει να επιτρέψει ρητά, και κάθε φόρτωση σελίδας κάνει αίτημα στον φιλοξενητή του iframe ενός τρίτου, είτε ενδιαφέρει τον επισκέπτη η κατάσταση επαλήθευσης είτε όχι. Ένα widget με δύο κλήσεις fetch αποφεύγει και τα τρία — είναι το δικό σας DOM, το δικό σας στυλ, και τρέχει μόνο όταν αποφασίσετε εσείς να το καλέσετε.

Διασχίζοντας ολόκληρη την αλυσίδα επαλήθευσης

Αν φτιάχνετε κάτι πιο εμπεριστατωμένο από ένα widget κατάστασης — ένα εργαλείο ελέγχου που ελέγχει μια παρτίδα διαπιστευτηρίων, ας πούμε, ή μια αναφορά συμμόρφωσης — διασχίστε ολόκληρη την αλυσίδα αντί να σταματήσετε στη δήλωση. Η δήλωση αναφέρεται σε ένα URL badge (το BadgeClass: όνομα, περιγραφή, κριτήρια) και το BadgeClass με τη σειρά του αναφέρεται σε ένα URL issuer (το προφίλ του οργανισμού). Ένας αυστηρός επαληθευτής επιλύει και τα τρία και επιβεβαιώνει ότι είναι εσωτερικά συνεπή — το ψηφιακό σήμα ανήκει πράγματι στον εκδότη που δηλώνει, η δήλωση αναφέρεται πράγματι στο badge που δηλώνει — και όχι απλώς ότι η δήλωση ανώτατου επιπέδου επιστρέφει 200. Αυτό είναι περισσότερο βάθος από όσο χρειάζεται ένα widget κατάστασης για ένα μόνο διαπιστευτήριο, αλλά αξίζει να γνωρίζετε ότι η δομή υπάρχει αν ένας συνεργάτης ή ελεγκτής ρωτήσει ποτέ «πόσο βαθιά πάει πραγματικά η επαλήθευσή σας;»

Συνηθισμένα λάθη που πρέπει να αποφύγετε

  • Έλεγχος μόνο της κατάστασης HTTP. Όπως εξηγήθηκε παραπάνω, μια ανακληθείσα δήλωση εξακολουθεί να επιλύεται. Διαβάζετε πάντα το assertion.revoked, μην συμπεραίνετε επαλήθευση μόνο από τον κωδικό κατάστασης.
  • Σκληροκωδικοποίηση του αναγνωριστικού απονομής σε κοινόχρηστο component. Αν το widget προορίζεται να επαναχρησιμοποιείται σε σελίδες πολλών αποδεκτών, δεχτείτε το αναγνωριστικό απονομής ως παράμετρο (data attribute, query string, prop), όχι ως literal ενσωματωμένο στο script.
  • Σιωπηλή κατάποση σφαλμάτων δικτύου. Ένα try/catch που δεν καταγράφει τίποτα και δείχνει το ίδιο κείμενο «δεν βρέθηκε» με ένα πραγματικά ανύπαρκτο ψηφιακό σήμα κάνει το debugging επίπονο αργότερα. Ξεχωρίστε το «απέτυχε η κλήση δικτύου» από το «το API είπε όχι» στους κλάδους σφάλματος, τουλάχιστον στα logs.
  • Επανεπαλήθευση σε κάθε render σε client-side εφαρμογή. Αν το widget ζει μέσα σε component React ή Vue που κάνει συχνά re-render, επαληθεύστε μία φορά ανά mount (ή μία φορά ανά παράθυρο TTL του cache, σύμφωνα με την ενότητα για το caching), όχι σε κάθε re-render.

Κοστίζει credits, και λειτουργεί στο δωρεάν πλάνο;

Όχι. Το endpoint επαλήθευσης βρίσκεται σκόπιμα εκτός του συστήματος credits που διέπει την έκδοση — ο έλεγχος του αν ένα ψηφιακό σήμα είναι έγκυρο δεν κοστίζει τίποτα στον εκδότη, και δεν απαιτεί από τον επισκέπτη που το καλεί να έχει λογαριασμό, κλειδί API ή οποιοδήποτε πλάνο. Αυτό ισχύει είτε το ψηφιακό σήμα εκδόθηκε σε πλάνο Free, Starter ή Pro: η επαλήθευση είναι ιδιότητα του ίδιου του διαπιστευτηρίου, όχι λειτουργία που ελέγχεται από τη συνδρομή του εκδότη. Γι’ αυτό μπορείτε επίσης να βάλετε με ασφάλεια αυτό το widget σε δημόσια σελίδα με απρόβλεπτη κίνηση (σελίδα καριέρας, κατάλογο συνεργατών) χωρίς να ανησυχείτε για όρια ρυθμού που συνδέονται με τον λογαριασμό σας — καλείτε ένα δημόσιο endpoint μόνο για ανάγνωση, όχι μια διαδρομή API με πιστοποίηση.

Ένα πράγμα που αξίζει να ειπωθεί ρητά: το JSON της δήλωσης που επιστρέφει το endpoint δεν περιλαμβάνει την ακατέργαστη διεύθυνση email του αποδέκτη. Το email του αποδέκτη αποθηκεύεται κατακερματισμένο (hashed) στο backend ακριβώς για να μην μπορεί μια δημόσια απάντηση επαλήθευσης να χρησιμοποιηθεί για συλλογή προσωπικών στοιχείων επικοινωνίας — όσα παίρνετε αρκούν για να επιβεβαιώσετε το διαπιστευτήριο (badge, εκδότης, ημερομηνίες, κατάσταση ανάκλησης) χωρίς να μετατρέπεται το endpoint σε εργαλείο παραγωγής leads για οποιονδήποτε διατρέχει αναγνωριστικά απονομών.

Έτοιμοι να εκδώσετε το πρώτο σας επαληθεύσιμο διαπιστευτήριο;

Ξεκινήστε δωρεάν στο badges.ninja — οπτικός σχεδιαστής, δημόσια σελίδα επαλήθευσης, πιστοποιητικό PDF, έξοδος Open Badge v2.0. Δεν απαιτείται πιστωτική κάρτα.

Για την πλήρη σχολιασμένη δομή κάθε πεδίου μιας δήλωσης, δείτε την αναφορά πεδίων JSON-LD. Και αν εκδίδετε διαπιστευτήρια προγραμματιστικά αντί απλώς να τα επαληθεύετε, το API quickstart καλύπτει την πιστοποίηση και την κλήση δημιουργίας απονομής από την οποία προέρχεται η έξοδος αυτού του endpoint.

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.

Πίσω στο Blog

Σχετικά άρθρα