RODO i ochrona danych
Privacy by Design
qr3.app został zaprojektowany od podstaw z myślą o zgodności z RODO:
- Pseudonimizacja IP — adresy IP są natychmiast konwertowane w Edge Worker na pseudonimy HMAC-SHA-256 dnia UTC. Oryginalny adres IP nigdy nie jest przechowywany; pseudonim pozostaje daną osobową.
- Minimalizacja danych — gromadzone są wyłącznie dane niezbędne do działania usługi.
Privacy-API
Podsumowanie danych (art. 15 RODO)
GET /v1/account/privacy — Zwraca podsumowanie wszystkich przechowywanych danych.
curl https://qr3.app/v1/account/privacy \ -H "Authorization: Bearer qr3_sk_..."Response:
{ "data": { "workspace_id": "ws_xxx", "organization_id": "org_xxx", "account": { "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 zlicza zapisane wpisy. scan_records to skanowania kodów QR, dpp_scan_records to zapytania do paszportów produktów przez publiczny resolver DPP. Oba zawierają pseudonim IP.
dashboard_activity_records i dashboard_document_activity_records zliczają dni, w których członek otworzył dashboard (dwie tabele), api_operation_records dzienne liczniki żądań według klucza API, operacji i wyniku, a webhook_delivery_records próby dostarczenia webhooków, w tym próby usuniętych webhooków. Żadna z nich nie przechowuje adresu IP ani User-Agent; log dostarczeń przechowuje jednak payload zdarzenia i pierwszy kilobajt (1 KB) odpowiedzi odbiorcy. Wszystkie cztery są usuwane po 90 dniach UTC (usage_retention, webhook_delivery_retention).
account wskazuje osobę stojącą za żądaniem — w dashboardzie zalogowaną osobę, w przeciwnym razie osobę, która utworzyła klucz — z adresem e-mail, imieniem i nazwiskiem, językiem i datą utworzenia; w przypadku Publishable Key wartość wynosi null. Pozostałe liczniki obejmują każdą dalszą tabelę z danymi konta: paszporty produktów, dokumenty DPP, pliki, identyfikatory GS1, komentarze, wpisy audytu, uruchomienia importu, członków workspace i powiadomienia o limicie organizacji (scan_quota_alerts). Wiersze usunięte logicznie są liczone, ponieważ nadal są przechowywane. erasure, backup_retention i billing_records opisują, co usuwa DELETE /v1/account, a co pozostaje przez pewien czas; pii_in_export ma wartość true, ponieważ eksport wskazuje osobę (e-mail, imię i nazwisko) — pseudonimy IP nigdy nie opuszczają bazy danych.
Eksport danych (art. 20 RODO — przenoszenie danych)
GET /v1/account/export — Pobiera wszystkie dane jako plik JSON.
curl https://qr3.app/v1/account/export \ -H "Authorization: Bearer qr3_sk_..." \ -o my-data.jsonEksport zawiera:
- Wszystkie kody QR (w tym usunięte)
- Zagregowane statystyki skanowania (brak surowych skrótów IP)
- Zagregowane zapytania DPP dla każdego paszportu produktu (
dpp_scan_aggregates, również bez skrótów IP) - Statystyki użycia jako agregaty (
dashboard_activity_aggregates,api_operation_aggregates): aktywne dni i liczba aktywnych użytkowników według kanału dashboardu, liczniki żądań według operacji i wyniku — bez identyfikatorów użytkowników i kluczy - Próby dostarczenia webhooków według webhooka i typu zdarzenia (
webhook_delivery_aggregates), bez payloadu i odpowiedzi - Osobę stojącą za żądaniem (
account), organizację (organization; adres rozliczeniowy i do powiadomień tylko dla administratorów organizacji) i workspace (workspace) - Paszporty produktów, metadane dokumentów DPP i plików (nie same pliki), identyfikatory GS1, wnioski o ich weryfikację i uruchomienia importu (
dpp_passports,dpp_documents,files,gs1_identifiers,gs1_verification_requests,dpp_import_jobs) - Komentarze bez pól autora (
code_comments), wpisy audytu jako agregaty (audit_log_aggregates) i członków według roli (member_aggregates) - Klucze API bez ID i skrótu (
api_keys), webhooki bez URL i sekretu (webhooks) oraz powiadomienia o limicie organizacji (scan_quota_alerts) - Znaczniki czasu utworzenia i modyfikacji
Publishable Keys (qr3_pk_) otrzymują 403: plik zawiera adresy docelowe i notatki wszystkich kodów.
Usuwanie konta (art. 17 RODO — prawo do bycia zapomnianym)
DELETE /v1/account — Usuwa konto osoby stojącej za żądaniem, natychmiast i nieodwracalnie: w dashboardzie zalogowanej osoby, w przeciwnym razie osoby, która utworzyła klucz. Wniosek o usunięcie wysłany e-mailem prowadzi do tego samego usunięcia: operator usuwa logowanie w Clerk, a następnie qr3 usuwa konto.
curl -X DELETE https://qr3.app/v1/account \ -H "Authorization: Bearer qr3_sk_..."Co się dzieje:
- Organizacje należące do osoby są usuwane wraz ze wszystkimi workspace, kodami, paszportami produktów, plikami, statystykami i logami, łącznie z ich plikami w magazynie. Jeśli taka organizacja ma innego aktywnego administratora organizacji, zamiast tego przejmuje ją ten z najdłuższym stażem — wraz z danymi i subskrypcją.
- Subskrypcja: trwająca subskrypcja kończy się natychmiast, bez proporcjonalnego zwrotu środków i bez faktury końcowej; następnie usuwany jest klient w Stripe. Faktury pozostają w Stripe przez ustawowy okres przechowywania (7 lat, § 132 BAO).
- Zespoły: członkostwa osoby w innych organizacjach kończą się; te zespoły zachowują swoje dane. Komentarze osoby nie wskazują już autora, jej wpisy audytu tracą pseudonim IP i user agent, a klucze API, które tam utworzyła, zostają unieważnione.
- Osoba: logowanie (Clerk) i rekord użytkownika zostają usunięte.
Odpowiedź to 200 ze status: "completed" i licznikami organizations_deleted, organizations_transferred, memberships_removed i login_deleted. W przypadku bardzo dużych organizacji jedno żądanie nie wystarcza: odpowiedź to wtedy 202 ze status: "in_progress", a usuwanie kończy się automatycznie w ciągu kilku godzin.
Wymaga Secret Key lub Test Key z prawem zapisu albo sesji w dashboardzie (dowolna rola, ponieważ chodzi o własne konto). Publishable Keys i klucze tylko do odczytu otrzymują 403, klucz bez osoby za nim (created_by = 'system') otrzymuje 409. 503 oznacza, że wdrożeniu brakuje sekretu potrzebnego do usunięcia; nic nie zostało usunięte.
Zarządzanie zgodami
GET /v1/account/privacy/consents — Pobieranie aktualnych zgód.
POST /v1/account/privacy/consents — Aktualizacja zgód.
# Pobranie bieżących zgódcurl https://qr3.app/v1/account/privacy/consents \ -H "Authorization: Bearer qr3_sk_..."
# Wyłączenie e-maili marketingowychcurl -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 }'Pola zgód:
| Pole | Domyślnie | Opis |
|---|---|---|
marketing_emails | false | Newsletter i e-maile marketingowe |
analytics | true | Zagregowane statystyki użytkowania |
product_updates | true | Nowości produktowe i dzienniki zmian (changelogi) |
Zmiana zgód wymaga uprawnień do zapisu: rola viewer i Publishable Keys otrzymują 403.
Techniczna implementacja RODO
Pseudonimizacja IP (art. 25 RODO)
Żądanie HTTP → Cloudflare Edge Worker ↓Nagłówek CF-Connecting-IP → HMAC-SHA-256(IP_HASH_SALT, cel, dzień UTC, IP) ↓ip_hash (dzienny pseudonim) → baza danych D1 ↓Oryginalny adres IP nigdy nie jest przechowywanyIP_HASH_SALT jest długotrwałym głównym sekretem; cel i dzień UTC automatycznie wyprowadzają odrębne dzienne pseudonimy. Pseudonimy z różnych dni UTC nie są bezpośrednio takie same, lecz posiadacz sekretu może ponownie obliczyć znanego kandydata IP dla konkretnego dnia. Nie stosuje się dziennej soli w KV ani ręcznej codziennej rotacji.
Retencja danych (automatyczna)
| Plan | Retencja |
|---|---|
| Wszystkie plany | 90 dni |
Statystyki użycia (aktywność w dashboardzie, liczniki użycia API) i logi dostarczeń webhooków są również przechowywane przez 90 dni UTC: bieżący dzień UTC i 89 poprzednich dni. To samo codzienne zadanie cron usuwa starsze wiersze (purgeOldDashboardActivity, purgeOldApiOperationUsage, purgeOldWebhookDeliveries).
Umowa DPA z podprocesorami
Cloudflare, Inc. przetwarza dane jako podmiot przetwarzający. Umowa DPA jest dostępna pod adresem cloudflare.com/cloudflare-customer-dpa.
Cloudflare przetwarza dane ze wskazaniem lokalizacji na region UE (m.in. Frankfurt); umownie zagwarantowana jurysdykcja UE nie jest obecnie wykupiona. Ponieważ spółka Cloudflare, Inc. ma siedzibę w USA, podstawą transferu są Standardowe Klauzule Umowne (SCC) zgodnie z art. 46 RODO.
Kontakt
- Inspektor Ochrony Danych: [email protected]
- Umowa powierzenia przetwarzania danych (DPA): [email protected] (na żądanie)
- Polityka prywatności: qr3.app/de/legal/datenschutz