De fleste teams behandler et digitalt produktpas som en side: du opretter det, printer QR-koden og glemmer det. Dermed lader du det mest brugbare signal ligge. Hver gang nogen scanner QR-koden på et produkt, er det en hændelse — en rigtig person, i et rigtigt land, der holder en rigtig enhed, på et rigtigt tidspunkt. Kobl de hændelser ind i din stack, og passet holder op med at være en statisk side og bliver frontenden på en automatiseringspipeline. Denne guide viser, hvordan du abonnerer på qr.scanned, verificerer hver payload og forvandler scanninger og opdateringer til rigtige workflows med qr3 SDK'et.
Hvorfor et DPP er en hændelseskilde, ikke en statisk side
Værdien af et pas er ikke den side, en forbruger ser én gang. Det er strømmen af interaktioner omkring det: scanninger i felten, dataændringer fra dit ERP, livscyklusovergange. Hver enkelt er noget, dine systemer kan reagere på i realtid.
Webhooks vender den sædvanlige polling-model på hovedet. I stedet for at spørge "er der sket noget?" med jævne mellemrum, ringer qr3 til dig i samme øjeblik, det sker. De hændelsestyper, platformen udsender, omfatter qr.scanned, plus qr.created, qr.updated og qr.deleted til livscyklusændringer. Den, de fleste teams udnytter for lidt, er qr.scanned: den udløses, når en forbruger, tekniker eller toldofficer rent faktisk scanner et produkt ude i den virkelige verden.
En qr.scanned-payload bærer den kontekst, du har brug for for at handle — herunder landet for scanningen og det dpp/code id, der identificerer, hvilken enhed der blev scannet. Det er nok til at drive analyse, genopfyldning og tilbagekaldelseslogik uden et menneske i loopet.
Sådan abonnerer du på qr.scanned
Peg et webhook-endpoint mod din tjeneste, og håndtér hændelsen. qr3 SDK'et leveres med en verificering, så du ikke skal parse rå bodies i hånden:
import express from "express";
import { verifyWebhook } from "@qr3/sdk";
const app = express();
const secret = process.env.QR3_WEBHOOK_SECRET!;
// Use the raw body so the signature matches the exact bytes qr3 signed.
app.post("/webhooks/qr3", express.raw({ type: "application/json" }), (req, res) => {
const event = verifyWebhook(req.body, req.headers["qr3-signature"], secret);
if (event.type === "qr.scanned") {
// event payload includes fields like the scan country and the dpp/code id
handleScan(event);
}
res.sendStatus(200);
});
Handleren er bevidst tynd: verificér, forgren på event.type, kvittér hurtigt med et 200. Lad det tunge arbejde (analyse-skrivninger, ERP-kald) foregå asynkront, så et langsomt downstream-system aldrig blokerer kvitteringen.
Verificér signaturer (verifyWebhook, HMAC-SHA256) — gør altid dette
Et webhook-endpoint er en offentlig URL. Alle, der finder den, kan POST'e til den. Hvis du stoler på bodyen uden at tjekke, hvem der sendte den, kan en angriber forfalske "scanninger", udløse falske genbestillinger eller affyre falske tilbagekaldelsesmarkeringer. Verificér altid signaturen, før du handler på en payload.
qr3 signerer hver webhook med HMAC-SHA256 over request-bodyen ved hjælp af din endpoint-hemmelighed. Signaturen ankommer i qr3-signature-request-headeren. verifyWebhook(body, signature, secret) genberegner HMAC'en og sammenligner den; hvis den ikke matcher, kaster den en fejl, og du afviser requesten:
import { verifyWebhook } from "@qr3/sdk";
app.post("/webhooks/qr3", express.raw({ type: "application/json" }), (req, res) => {
try {
const event = verifyWebhook(req.body, req.headers["qr3-signature"], secret);
process(event);
res.sendStatus(200);
} catch {
// signature mismatch → not from qr3 (or body was altered in transit)
res.sendStatus(401);
}
});
Tre regler, der holder dette ærligt:
- Verificér mod de rå bytes. Re-serialisering af JSON kan omarrangere nøgler og ændre whitespace, hvilket bryder HMAC'en. Fang den rå body (ovenfor,
express.raw). - Hold hemmeligheden hemmelig. Den lever i dit miljø, aldrig i klientkode eller et repo.
- Fejl lukket. Ingen gyldig signatur →
401, ingen sideeffekter. "Behandl alligevel" aldrig ved et mismatch.
Mønstre: analyse / genbestilling / tilbagekaldelse
Når du først stoler på hændelsen, dækker en håndfuld mønstre det meste af, hvad teams ønsker:
function handleScan(event: { type: string; data: { country?: string; dpp_id?: string } }) {
// 1) Analytics — where and how often are products scanned?
metrics.increment("dpp.scan", { country: event.data.country });
// 2) Re-order — a scan can signal consumption or field activity
if (event.data.country) maybeReplenish(event.data.dpp_id, event.data.country);
// 3) Recall flag — scans of a flagged unit alert your team
if (isRecalled(event.data.dpp_id)) alertRecall(event.data.dpp_id, event.data.country);
}
- Analyse: aggregér scanninger efter land og enhed for at se engagement i den virkelige verden — hvilke markeder der faktisk scanner, og hvilke SKU'er der ser mest interaktion efter salget.
- Genbestilling / genopfyldning: en byge af scanninger i en region kan fodre efterspørgselssignaler eller udløse genopfyldnings-workflows i dit ERP.
- Tilbagekaldelse / sikkerhed: hvis en enhed er under tilbagekaldelse, er en scanning en chance for at nå den, der holder den — advar dit team, eller vis en meddelelse på selve passet.
Ingen af disse kræver polling eller et natligt batch-job. De sker i samme øjeblik, produktet scannes.
Hold data aktuelle via client.dpp.update
At reagere på scanninger er halvdelen af loopet; den anden halvdel er at holde selve passet korrekt. Forordninger som ESPR (EU 2024/1781) og batteriforordningen (EU 2023/1542) forventer, at pasdata afspejler virkeligheden over produktets levetid — genberegnet CO2-aftryk, opdaterede reparationsinstruktioner, nåede mål for genanvendt indhold.
Driv de opdateringer fra det system, der fører registret. Når en værdi ændres i dit ERP, skub den til passet:
import { QR3 } from "@qr3/sdk";
const client = new QR3({ apiKey: process.env.QR3_API_KEY! });
// GTIN and serial are immutable; data fields are updatable.
await client.dpp.update(dppId, {
battery_data: { carbon_footprint_kg: 58, recycled_content_pct: 16 },
});
Fordi QR-koden indkoder en stabil resolver-URL (https://qr3.app/dpp/{gtin}/{serial}, tilføj ?format=jsonld for JSON-LD), genprinter du aldrig en etiket for at ændre data. Identiteten forbliver fast; indholdet bag den forbliver aktuelt. Kombinér dette med qr.updated, og du kan udsende en notifikation, hver gang et pas ændrer sig — så loopet mellem dit ERP, passet og enhver, der ser med downstream, lukkes.
En hændelsestabel: hændelse → hvad der skal automatiseres
| Hændelse | Udløses, når | Hvad der skal automatiseres |
|---|---|---|
qr.scanned |
En produkt-QR scannes i felten | Analyse efter land, genopfyldningssignaler, tilbagekaldelsesalarmer |
qr.created |
Et nyt pas oprettes | Indeksér det, synkronisér til PIM/ERP, underret katalogteamet |
qr.updated |
Pasdata ændres | Cach den offentlige side igen, udsend ændringsnotifikationer |
qr.deleted |
Et pas fjernes | Tombstone interne registreringer, tilbagekald downstream-referencer |
Start med qr.scanned til engagement og feltsignaler; tilføj livscyklushændelserne, efterhånden som du synkroniserer pas ind i dine bredere systemer.
FAQ
Skal jeg verificere signaturen, hvis min endpoint-URL er hemmelig?
Ja. En URL er ikke en hemmelighed — den lækker i logfiler, proxyer og browserhistorik. HMAC-verificering med verifyWebhook er det eneste, der beviser, at en payload faktisk kom fra qr3.
Hvad sker der, hvis mit endpoint er nede, når en hændelse udløses?
Kvittér hurtigt med 200, når du har verificeret, og udfør langsomt arbejde asynkront, så forbigående downstream-problemer aldrig stopper svaret. Hold din egen idempotens på dpp/code id, så en gentaget levering ikke tælles dobbelt.
Kan jeg opdatere GTIN eller serienummer via client.dpp.update? Nej — GTIN og serienummer er uforanderlige; de er produktets stabile identitet. Kun datafelterne kan opdateres. Det er netop den uforanderlighed, der lader den printede QR-kode forblive gyldig for altid.
Kilder
Kom i gang gratis og kobl din første DPP-webhook op: app.qr3.app/sign-up