Afficher le passeport dans votre page
Afficher le passeport d'une marque dans votre propre page sans rien perdre : le passeport, sa preuve et sa signature en un appel, les scans et les lectures NFC de vos visiteurs comptés pour eux, le QR imprimé qui mène à votre page, et la revendication et le transfert dans une fenêtre hébergée qui revient chez vous. Droits passport:read, scans:write et nfc:verify.
Sur cette page
- Ce qu'il vous faut
- Les six points d'entrée
- Le passeport, sa preuve et sa signature en un appel
- Compter chaque scan pour le visiteur qui l'a fait
- Une seule vérification NFC par lecture
- Le QR imprimé mène à votre page
- La revendication dans une fenêtre hébergée
- Le transfert dans une fenêtre hébergée
- Plafonds
- Erreurs
- Voir aussi
Cette page s'adresse au partenaire qui affiche les passeports d'une marque dans sa propre page, avec son propre habillage : un revendeur pour ses marques clientes, une agence pour la marque qu'elle sert. En la quittant, vous saurez lire un passeport complet en un appel, faire compter chaque scan pour le visiteur qui l'a fait, vérifier une puce NFC une seule fois, recevoir les scans du QR imprimé sur votre page, et faire revendiquer un produit sans jamais demander le mot de passe SealTrust de votre visiteur.
La page hébergée par SealTrust reste l'option par défaut. Rien de ce qui suit ne change pour une marque qui ne l'a pas demandé.
#Ce qu'il vous faut
- Une clef d'API qui porte les droits dont vous avez besoin :
passport:readpour lire,scans:writepour enregistrer les scans,nfc:verifypour les puces NFC. Vous la créez dans la console, Paramètres puis Développeurs. - Une offre qui comprend l'accès API.
- Pour un revendeur, une clef revendeur : elle agit pour votre marque et pour chacune des marques clientes de votre contrat.
- Votre serveur. Tous ces appels partent de votre serveur, jamais du navigateur de votre visiteur : votre clef ne doit jamais atteindre une page.
#Les six points d'entrée
Toutes les adresses existent aussi avec le préfixe /v1, qui est la forme
recommandée.
| Méthode et chemin | Ce qu'il fait | Droit |
|---|---|---|
GET /partner/passports/{identifier} | le passeport d'un exemplaire, sa preuve et sa signature | passport:read |
GET /partner/passports/01/{gtin} | le passeport d'un modèle et sa preuve | passport:read |
GET /partner/passports/01/{gtin}/10/{lot} | le passeport d'un lot et sa preuve | passport:read |
POST /partner/scans | enregistre le scan d'un de vos visiteurs | scans:write |
POST /partner/nfc/verify | vérifie une lecture de puce pour un de vos visiteurs | nfc:verify |
GET /partner/nfc/receipts/{receipt} | lit le résultat d'une lecture que notre page a vérifiée | nfc:verify |
Chaque point d'entrée ne répond que pour les marques que votre clef peut servir. Un produit d'une autre marque reçoit le même 404 qu'un identifiant qui n'existe pas.
#Le passeport, sa preuve et sa signature en un appel
{identifier} est le numéro de série imprimé sur le produit, son empreinte de
puce ou son numéro de jeton.
curl https://api.sealtrust.io/v1/partner/passports/K4QNAFCHDETK \
-H "Authorization: Bearer st_live_0000000000000000000000000000000000000000000000"{
"level": "unit",
"serial": "K4QNAFCHDETK",
"passport": { "data": {}, "data_hash": "…", "gs1_digital_link": "…" },
"proof": { "data_hash": "…", "anchor": {}, "vc": {} },
"credential": { "format": "dc+sd-jwt", "issuer": "did:web:…", "sd_jwt_vc": "…" }
}passportest exactement la réponse publique deGET /passport/{identifier}au niveau d'accèspublic.proofest exactement celle deGET /passport/{identifier}/proof.credentialest exactement celle deGET /passport/{identifier}/vc, ounulltant que la marque n'a pas émis d'attestation signée pour ce passeport.
Votre page montre donc ce que la page hébergée montre à un visiteur anonyme,
ni plus ni moins. Pour un modèle ou un lot, credential vaut toujours null,
et level vaut model ou lot.
#Compter chaque scan pour le visiteur qui l'a fait
Vos appels partent de votre serveur, donc de votre adresse. Sans autre information, tous vos visiteurs auraient votre adresse : deux personnes qui scannent le même produit dans la même demi-heure compteraient pour un seul scan, et chaque scan serait situé chez votre hébergeur.
POST /partner/scans et POST /partner/nfc/verify demandent donc quatre
en-têtes qui nomment le visiteur.
| En-tête | Ce qu'il contient |
|---|---|
X-SealTrust-Visitor-IP | l'adresse de votre visiteur, telle que votre serveur l'a vue |
X-SealTrust-Visitor-UA | l'agent de son navigateur, facultatif |
X-SealTrust-Visitor-Timestamp | l'heure de la signature, en secondes depuis 1970 |
X-SealTrust-Visitor-Signature | v1= suivi de la signature, en hexadécimal |
La signature est un HMAC-SHA256. Sa clef est l'empreinte SHA-256 de votre
clef d'API, écrite en hexadécimal minuscule. Le message signé est fait de six
lignes séparées par un saut de ligne : v1, l'heure, la méthode en
majuscules, le chemin appelé sans sa partie après ?, l'adresse du visiteur,
et son agent (une ligne vide s'il n'y en a pas).
import hashlib, hmac, time, requests
API_KEY = "st_live_0000000000000000000000000000000000000000000000"
path = "/v1/partner/scans"
ip, ua = "203.0.113.77", "Mozilla/5.0"
ts = str(int(time.time()))
key = hashlib.sha256(API_KEY.encode()).hexdigest().encode()
message = "\n".join(["v1", ts, "POST", path, ip, ua]).encode()
signature = hmac.new(key, message, hashlib.sha256).hexdigest()
requests.post(
"https://api.sealtrust.io" + path,
json={"serial": "K4QNAFCHDETK"},
headers={
"Authorization": f"Bearer {API_KEY}",
"X-SealTrust-Visitor-IP": ip,
"X-SealTrust-Visitor-UA": ua,
"X-SealTrust-Visitor-Timestamp": ts,
"X-SealTrust-Visitor-Signature": f"v1={signature}",
},
timeout=10,
)import { createHash, createHmac } from "node:crypto";
const apiKey = "st_live_0000000000000000000000000000000000000000000000";
const path = "/v1/partner/scans";
const ip = "203.0.113.77";
const ua = "Mozilla/5.0";
const ts = String(Math.floor(Date.now() / 1000));
const key = createHash("sha256").update(apiKey).digest("hex");
const signature = createHmac("sha256", key)
.update(["v1", ts, "POST", path, ip, ua].join("\n"))
.digest("hex");
await fetch("https://api.sealtrust.io" + path, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
"X-SealTrust-Visitor-IP": ip,
"X-SealTrust-Visitor-UA": ua,
"X-SealTrust-Visitor-Timestamp": ts,
"X-SealTrust-Visitor-Signature": `v1=${signature}`,
},
body: JSON.stringify({ serial: "K4QNAFCHDETK" }),
});Une signature vaut pour une méthode, un chemin et cinq minutes. Signez chaque appel au moment de l'envoyer.
Le corps de POST /partner/scans nomme le produit par serial ou par
uid_hash, jamais les deux. Il accepte aussi latitude et longitude quand
l'appareil du visiteur les a données. Nous ne gardons de l'adresse qu'une ville
et un pays, et nous enregistrons une adresse anonymisée.
Un même visiteur qui revient sur le même produit dans la demi-heure compte pour
un seul scan. La réponse vaut alors {"ok": true, "deduped": true}.
#Une seule vérification NFC par lecture
Une puce NFC produit, à chaque lecture, une adresse à usage unique. La vérifier deux fois fait répondre la seconde vérification comme une copie de l'adresse, ce qui s'affiche comme un possible clone d'un produit authentique. Deux cas se présentent.
La lecture ouvre notre page. C'est le cas des puces déjà en circulation :
leur adresse mène à SealTrust. Quand la marque affiche ses passeports chez
vous, notre page vérifie la lecture une fois, puis envoie le visiteur sur votre
page avec level=nfc, serial et receipt. Votre serveur lit le résultat
avec ce reçu :
curl https://api.sealtrust.io/v1/partner/nfc/receipts/Zr3Kx0vQ8a1bW9c2dE7fG4hJ6kL5mN0p \
-H "Authorization: Bearer st_live_0000000000000000000000000000000000000000000000"Ne renvoyez jamais cette lecture à POST /partner/nfc/verify : elle a déjà été
vérifiée. Un reçu se lit pendant dix minutes, autant de fois que nécessaire.
Votre propre lecteur a lu la puce. Envoyez e, c et, pour une puce
scellée, t, tels que la puce les a écrits, avec les quatre en-têtes du
visiteur, à POST /partner/nfc/verify. Si votre appel échoue en route,
renvoyez la même lecture avec la même clef : pendant dix minutes, la réponse
redonne le premier résultat avec repeat: true, au lieu de le prendre pour
une copie. Si votre premier appel est encore en cours quand la relance
arrive, la relance attend son résultat quelques secondes, ou répond 503
NFC_READ_IN_PROGRESS avec un en-tête Retry-After : renvoyez-la après ce
délai, elle redonnera le premier résultat.
Les deux réponses portent les mêmes champs : verified, serial, uid_hash,
ctr, brand_code, brand_id, product_name, token_id, declared_stolen,
tamper_status, seal_intact, tamper_policy, scan_area et verified_at.
brand_code est le code public de la marque. brand_id, son numéro interne,
est déprécié : lisez brand_code.
Pour le passeport lui-même, appelez ensuite
GET /partner/passports/{serial}.
#Le QR imprimé mène à votre page
Dans la console, Paramètres puis Développeurs, le bloc « Passeport affiché dans la page d'un partenaire » déclare vos adresses web et la page qui affiche un passeport. Une case envoie ensuite les scans du QR imprimé vers cette page. Un revendeur le règle une fois sur sa propre marque, et le réglage vaut pour toutes les marques clientes de son contrat, sauf celles qui ont le leur.
Les liens imprimés /p/{serial}, /01/{gtin}/21/{serial}, /01/{gtin} et
/01/{gtin}/10/{lot} répondent alors 302 vers votre page, avec ces
paramètres :
| Paramètre | Valeur |
|---|---|
level | unit, model ou lot |
serial | le numéro de série, pour un exemplaire |
gtin et lot | pour un modèle ou un lot |
lang | fr ou en, la langue du lecteur |
Deux demandes gardent notre passeport : une machine qui demande le passeport
lui-même (?linkType=dpp), et un exemplaire retiré. Le scan d'un exemplaire
est enregistré au moment de la redirection, pour le visiteur. Si votre page
l'enregistre aussi avec POST /partner/scans pour le même visiteur, il ne
compte qu'une fois. Les robots qui suivent un lien pour en afficher l'aperçu
(WhatsApp, Slack, LinkedIn, moteurs de recherche) sont redirigés, mais ne
comptent pas comme un scan.
Une marque cliente dont l'offre ne comprend pas l'accès API voit quand même, dans ce bloc, que ses scans partent vers la page de son revendeur. Elle peut les garder sur la page hébergée : le bouton « Garder les scans du QR sur la page hébergée » ne demande pas l'accès API.
#La revendication dans une fenêtre hébergée
Revendiquer un produit demande un compte SealTrust. Ne demandez jamais le mot de passe de votre visiteur : ouvrez notre page de revendication, dans une nouvelle fenêtre ou en y envoyant le visiteur.
https://sealtrust.io/fr/claim?serial=K4QNAFCHDETK&return=https%3A%2F%2Fvotre-site.example%2FpasseportUn visiteur qui n'a pas encore de compte le crée depuis cette page. Le numéro
de série et return le suivent jusqu'au courriel de confirmation, puis jusqu'à
la connexion : il revient sur l'écran de revendication de ce produit, sans
rien ressaisir. Les liens « Se connecter » et « S'inscrire » du haut de la page
les portent aussi dès l'affichage, avant même la fin du chargement : un
visiteur sur un réseau mobile lent qui clique tout de suite ne perd rien.
return est la page où revenir. Elle doit se trouver sur l'une des adresses
web déclarées dans la console pour cette marque, ou sur le domaine vérifié qui
sert ses pages publiques, sinon aucun bouton de retour n'apparaît. Quand vous venez de lire la puce, ajoutez la lecture après un #,
par exemple #e=…&c=…, pour que le visiteur n'ait pas à la relire : la
lecture ne quitte pas son navigateur.
Notre page ne revendique jamais seule. Elle ne prend la lecture passée après
le # que si return est accepté pour ce produit, puis affiche un bouton
« Revendiquer ce produit » : la revendication part au clic du visiteur, pas
avant. Sans return accepté, la lecture est ignorée.
Cette lecture sert pendant cinq minutes après sa vérification. Passé ce délai, ou si le visiteur met plus longtemps à se connecter, il relit la puce sur notre page. C'est la même règle que sur la page hébergée : une lecture de puce ne se rejoue pas au-delà de quelques minutes.
Une fois la revendication faite, le bouton « Retour vers votre-site.example » ramène le visiteur :
- si vous avez ouvert la fenêtre avec
window.open, votre page reçoit un message{ "type": "passport-claim", "outcome": "claimed", "serial": "K4QNAFCHDETK" }adressé à l'origine exacte de la pagereturn, et la fenêtre se ferme. Ouvrez donc la fenêtre depuis une page de cette même origine ; - sinon, le visiteur revient sur
return, avecclaim_outcome=claimedajouté à l'adresse.
outcome prend les valeurs claimed, submitted, already_owned,
expired, not_activated, window_closed, code_not_issued, unavailable
ou failed.
#Le transfert dans une fenêtre hébergée
Le propriétaire transfère son produit de la même façon, sur notre page de transfert, avec les mêmes paramètres :
https://sealtrust.io/fr/transfer?serial=K4QNAFCHDETK&return=https%3A%2F%2Fvotre-site.example%2FpasseportLe produit nommé par serial est présélectionné s'il appartient au visiteur
connecté. Un visiteur qui n'est pas connecté passe d'abord par la page de
connexion, puis revient sur cette même adresse, serial et return compris.
return suit la même règle que pour la revendication. Deux boutons
ramènent le visiteur : « Retour vers votre-site.example » une fois le transfert
fait, et « Retour vers votre-site.example sans transférer » avant.
- si vous avez ouvert la fenêtre avec
window.open, votre page reçoit un message{ "type": "passport-transfer", "outcome": "transferred", "serial": "K4QNAFCHDETK" }adressé à l'origine exacte de la pagereturn, et la fenêtre se ferme ; - sinon, le visiteur revient sur
return, avectransfer_outcome=transferredajouté à l'adresse.
outcome vaut transferred (transfert fait), pending_acceptance (le
destinataire doit encore accepter) ou cancelled (le visiteur est revenu sans
transférer). Le retour et le rachat se font depuis l'espace du propriétaire sur
la page hébergée.
#Plafonds
Le plafond de débit se compte par clef et par marque, jamais par adresse : tous les visiteurs que votre serveur représente ne se partagent pas un seul compteur. Il est décrit dans la vue d'ensemble. Ces six points d'entrée ne consomment pas le quota quotidien de la clef.
#Erreurs
| Code | Condition | Que faire |
|---|---|---|
| 400 | Les en-têtes du visiteur manquent. | Envoyez les quatre en-têtes décrits plus haut. |
| 401 | Clef absente ou inconnue, signature fausse, ou heure à plus de cinq minutes de la nôtre. | Signez chaque appel au moment de l'envoi, avec le chemin exact appelé. |
| 403 | Le droit manque, ou l'offre ne comprend pas l'accès API. | Créez une clef qui porte ce droit. |
| 403 | SDM_MAC_MISMATCH : la signature de la puce ne se vérifie pas. | La lecture n'est pas celle d'une puce authentique. |
| 404 | Identifiant inconnu, passeport non publié, reçu expiré, ou produit d'une marque que votre clef ne sert pas. Pour une puce, la marque est vérifiée avant tout contrôle de rejeu : une puce d'une autre marque rend toujours 404, jamais 409, et sa lecture n'est pas consommée. | Vérifiez l'identifiant et la clef. |
| 409 | Cette lecture de puce a déjà été vérifiée ailleurs. | Si notre page l'a vérifiée, lisez son reçu. |
| 422 | Corps invalide, ou adresse de visiteur qui n'est pas publique : adresse locale, ou adresse d'un réseau privé (10.x, 172.16 à 172.31, 192.168.x, 100.64 à 100.127, IPv6 commençant par fc ou fd). | Envoyez l'adresse publique du visiteur. Derrière un répartiteur de charge, lisez-la dans l'en-tête que celui-ci ajoute, pas dans l'adresse de la connexion. |
| 429 | Plafond de débit atteint. | Attendez le délai indiqué par Retry-After. |
| 503 | NFC_READ_IN_PROGRESS : votre appel précédent vérifie encore la même lecture. | Renvoyez la même lecture après le délai indiqué par Retry-After. |
#Voir aussi
- API partenaire, vue d'ensemble, clefs, droits et plafonds.
- Remplir le catalogue par l'API, la clef revendeur.
- Clés d'API et webhooks, le réglage dans la console.
Cette page vous a-t-elle été utile ?
Votre réponse ouvre un courriel pré-rempli dans votre messagerie, à destination de contact@sealtrust.io. Vous le relisez avant de l'envoyer.