Skip to content

GDPR i zaštita podataka

Zaštita podataka po dizajnu (Privacy-by-Design)

qr3.app je od samog početka izgrađen u skladu s GDPR-om:

  • Pseudonimizacija IP adresa — IP adrese se u Edge Workeru odmah pretvaraju u HMAC-SHA-256 UTC-dnevne pseudonime. Izvorni IP nikada se ne pohranjuje; pseudonim ostaje osobni podatak.
  • Smanjenje količine podataka — Prikupljaju se samo podaci nužni za rad.

Privacy-API

Sažetak podataka (čl. 15. GDPR-a)

GET /v1/account/privacy — Vraća pregled svih pohranjenih podataka.

Terminal window
curl https://qr3.app/v1/account/privacy \
-H "Authorization: Bearer qr3_sk_..."

Response:

{
"data": {
"workspace_id": "ws_xxx",
"organization_id": "org_xxx",
"account": {
"email": "[email protected]",
"name": "Alex Example",
"locale": "en",
"created_at": "2026-01-15T09:00:00.000Z"
},
"stored_data": {
"qr_codes": 42,
"scan_records": 15000,
"dpp_scan_records": 3200,
"dashboard_activity_records": 0,
"dashboard_document_activity_records": 36,
"api_operation_records": 180,
"webhook_delivery_records": 640,
"api_keys": 2,
"webhooks": 1,
"dpp_passports": 12,
"dpp_documents": 30,
"files": 4,
"gs1_identifiers": 2,
"gs1_verification_requests": 1,
"code_comments": 5,
"audit_log_entries": 0,
"dpp_import_jobs": 1,
"workspace_members": 3,
"scan_quota_alerts": 1
},
"gdpr_rights": {
"access": "GET /v1/account/privacy",
"export": "GET /v1/account/export",
"erasure": "DELETE /v1/account",
"consents": "GET /v1/account/privacy/consents"
},
"data_processing": {
"ip_storage": "Purpose-separated HMAC-SHA-256 UTC-day IP pseudonym — pseudonymous personal data",
"scan_retention": "90 days by default",
"usage_retention": "90 UTC days",
"webhook_delivery_retention": "90 UTC days",
"pii_in_export": true,
"erasure": "DELETE /v1/account erases the person's account: organizations they own with all workspaces and data (unless another org admin takes them over), their memberships in other organizations, their login and their Stripe customer. A running subscription ends immediately, without refund.",
"backup_retention": "Erased rows stay restorable from the database backups (Cloudflare D1 Time Travel) for up to 30 days, then they are gone.",
"billing_records": "Invoices stay with Stripe for the statutory retention period (7 years, § 132 BAO)."
}
},
"meta": { "request_id": "req_xxx" }
}

stored_data broji pohranjene zapise. scan_records su skeniranja QR kodova, a dpp_scan_records upiti prema putovnicama proizvoda putem javnog DPP resolvera. Oba sadrže IP pseudonim.

dashboard_activity_records i dashboard_document_activity_records broje dane u kojima je član otvorio nadzornu ploču (dvije tablice), api_operation_records dnevna brojila zahtjeva po API ključu, operaciji i ishodu, a webhook_delivery_records pokušaje dostave webhookova, uključujući pokušaje obrisanih webhookova. Nijedan od njih ne pohranjuje IP adresu ni User-Agent; zapisnik o dostavi pohranjuje payload događaja i prvi kilobajt (1 KB) odgovora primatelja. Sva četiri brišu se nakon 90 UTC dana (usage_retention, webhook_delivery_retention).

account navodi osobu iza zahtjeva — na nadzornoj ploči prijavljenu osobu, inače osobu koja je izradila ključ — s e-poštom, imenom, jezikom i datumom izrade; za Publishable Key vrijednost je null. Ostala brojila obuhvaćaju svaku drugu tablicu s podacima korisničkog računa: putovnice proizvoda, DPP dokumente, datoteke, GS1 identifikatore, komentare, zapise revizije, pokretanja uvoza, članove radnog prostora i obavijesti o kvoti organizacije (scan_quota_alerts). Meko izbrisani retci se broje jer su još pohranjeni. erasure, backup_retention i billing_records opisuju što DELETE /v1/account briše, a što još neko vrijeme ostaje; pii_in_export je true jer izvoz navodi osobu (e-pošta, ime) — IP pseudonimi nikad ne napuštaju bazu podataka.


Izvoz podataka (čl. 20. GDPR-a — prenosivost podataka)

GET /v1/account/export — Preuzima sve podatke kao JSON datoteku.

Terminal window
curl https://qr3.app/v1/account/export \
-H "Authorization: Bearer qr3_sk_..." \
-o my-data.json

Izvoz sadrži:

  • Sve QR kodove (uključujući obrisane)
  • Agregiranu statistiku skeniranja (bez sirovih IP hasheva)
  • Agregirane DPP upite po putovnici proizvoda (dpp_scan_aggregates, također bez IP hasheva)
  • Statistiku korištenja kao agregate (dashboard_activity_aggregates, api_operation_aggregates): aktivni dani i broj aktivnih korisnika po kanalu nadzorne ploče, brojila zahtjeva po operaciji i ishodu — bez ID-jeva korisnika i ključeva
  • Pokušaje dostave webhookova po webhooku i vrsti događaja (webhook_delivery_aggregates), bez payloada i odgovora
  • Osobu iza zahtjeva (account), organizaciju (organization; adresa za naplatu i obavijesti samo za administratore organizacije) i radni prostor (workspace)
  • Putovnice proizvoda, metapodatke DPP dokumenata i datoteka (ne same datoteke), GS1 identifikatore, zahtjeve za njihovu potvrdu i pokretanja uvoza (dpp_passports, dpp_documents, files, gs1_identifiers, gs1_verification_requests, dpp_import_jobs)
  • Komentare bez polja autora (code_comments), zapise revizije kao agregate (audit_log_aggregates) i članove po ulozi (member_aggregates)
  • API ključeve bez ID-a i hasha (api_keys), webhookove bez URL-a i tajne (webhooks) te obavijesti o kvoti organizacije (scan_quota_alerts)
  • Vremenske oznake stvaranja i izmjene

Publishable Keys (qr3_pk_) dobivaju 403: datoteka sadrži odredišne adrese i bilješke svih kodova.


Brisanje računa (čl. 17. GDPR-a — pravo na zaborav)

DELETE /v1/account — Briše korisnički račun osobe iza zahtjeva, odmah i nepovratno: na nadzornoj ploči prijavljenu osobu, inače osobu koja je izradila ključ. Zahtjev za brisanje e-poštom dovodi do istog brisanja: operater briše prijavu kod Clerka, a qr3 zatim briše korisnički račun.

Terminal window
curl -X DELETE https://qr3.app/v1/account \
-H "Authorization: Bearer qr3_sk_..."

Što se događa:

  1. Organizacije koje pripadaju osobi brišu se sa svim radnim prostorima, kodovima, putovnicama proizvoda, datotekama, statistikama i zapisnicima, uključujući njihove datoteke u pohrani. Ako takva organizacija ima drugog aktivnog administratora organizacije, umjesto toga je preuzima onaj s najdužim stažem — zajedno s podacima i pretplatom.
  2. Pretplata: aktivna pretplata završava odmah, bez razmjernog povrata novca i bez završne fakture; zatim se briše kupac u Stripeu. Fakture ostaju kod Stripea tijekom zakonskog roka čuvanja (7 godina, § 132 BAO).
  3. Timovi: članstva osobe u drugim organizacijama završavaju; ti timovi zadržavaju svoje podatke. Komentari osobe više ne navode autora, njezini zapisi revizije gube IP pseudonim i user agent, a API ključevi koje je ondje izradila opozivaju se.
  4. Osoba: prijava (Clerk) i korisnički zapis brišu se.

Odgovor je 200 sa status: "completed" i brojilima organizations_deleted, organizations_transferred, memberships_removed i login_deleted. Kod vrlo velikih organizacija jedan zahtjev nije dovoljan: odgovor je tada 202 sa status: "in_progress", a brisanje se samo dovršava u roku od nekoliko sati.

Potreban je Secret ili Test Key s pravom pisanja ili sesija na nadzornoj ploči (bilo koja uloga, jer je riječ o vlastitom korisničkom računu). Publishable Keys i ključevi samo za čitanje dobivaju 403, ključ bez osobe iza njega (created_by = 'system') dobiva 409. 503 znači da implementaciji nedostaje tajna potrebna za brisanje; ništa nije izbrisano.


Upravljanje privolama

GET /v1/account/privacy/consents — Dohvaćanje trenutnih privola.

POST /v1/account/privacy/consents — Ažuriranje privola.

Terminal window
# Dohvaćanje trenutačnih privola
curl https://qr3.app/v1/account/privacy/consents \
-H "Authorization: Bearer qr3_sk_..."
# Isključivanje marketinških e-poruka
curl -X POST https://qr3.app/v1/account/privacy/consents \
-H "Authorization: Bearer qr3_sk_..." \
-H "Content-Type: application/json" \
-d '{ "marketing_emails": false, "analytics": true, "product_updates": true }'

Polja privole:

PoljeZadanoOpis
marketing_emailsfalseNewsletter i promotivne e-poruke
analyticstrueAgregirana statistika korištenja
product_updatestrueNovosti o proizvodima i zapisi o promjenama (Changelogs)

Za promjenu privola potrebna su prava pisanja: uloga viewer i Publishable Keys dobivaju 403.


Tehnička implementacija GDPR-a

Pseudonimizacija IP adresa (čl. 25. GDPR-a)

HTTP zahtjev → Cloudflare Edge Worker
↓
Zaglavlje CF-Connecting-IP → HMAC-SHA-256(IP_HASH_SALT, svrha, UTC dan, IP)
↓
ip_hash (dnevni pseudonim) → baza podataka D1
↓
Izvorni IP nikada se ne pohranjuje

IP_HASH_SALT je dugotrajna glavna tajna; svrha i UTC dan automatski izvode odvojene dnevne pseudonime. Pseudonimi različitih UTC dana nisu izravno jednaki, ali vlasnik tajne može ponovno izračunati poznatog kandidata IP adrese za određeni dan. Ne koristi se dnevna sol u KV-u ni ručna dnevna rotacija.

Zadržavanje podataka (automatizirano)

PaketZadržavanje
Svi paketi90 dana

Statistika korištenja (aktivnost na nadzornoj ploči, brojila korištenja API-ja) i zapisnici o dostavi webhookova također se čuvaju 90 UTC dana: tekući UTC dan i 89 prethodnih dana. Isti dnevni cron zadatak briše starije retke (purgeOldDashboardActivity, purgeOldApiOperationUsage, purgeOldWebhookDeliveries).

DPA s podizvršiteljima obrade

Cloudflare, Inc. obrađuje podatke kao izvršitelj obrade. DPA je dostupan na cloudflare.com/cloudflare-customer-dpa.

Cloudflare obrađuje podatke uz naznaku lokacije u regiji EU-a (između ostalog Frankfurt); ugovorno zajamčena nadležnost EU-a trenutačno nije ugovorena. Budući da Cloudflare, Inc. ima sjedište u SAD-u, kao osnova za prijenos primjenjuju se standardne ugovorne klauzule (SCC) prema čl. 46. GDPR-a.


Kontakt