Skip to content

GDPR и защита на данните

Поверителност по дизайн

qr3.app е създаден от самото начало в съответствие с GDPR:

  • Псевдонимизиране на IP адреси — IP адресите се преобразуват незабавно в HMAC-SHA-256 UTC-дневни псевдоними в Edge Worker. Оригиналният IP адрес никога не се съхранява; псевдонимът остава лични данни.
  • Минимизиране на данните — Събират се само данните, които са абсолютно необходими за работата на услугата.

API за поверителност

Обобщение на данните (Чл. 15 от GDPR)

GET /v1/account/privacy — Връща преглед на всички съхранени данни.

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 отчита съхранените записи. scan_records са сканирания на QR кодове, а dpp_scan_records — заявки към продуктови паспорти през публичния DPP Resolver. И двете съдържат IP псевдонима.

dashboard_activity_records и dashboard_document_activity_records отчитат дните, в които член е отварял таблото (две таблици), api_operation_records — ежедневните броячи на заявките по API ключ, операция и резултат, а webhook_delivery_records — опитите за доставка на уебхукове, включително тези на изтрити уебхукове. Нито една от тях не съхранява IP адрес или User-Agent; дневникът за доставка обаче съхранява payload на събитието и първия килобайт (1 KB) от отговора на получателя. И четирите се изтриват след 90 UTC дни (usage_retention, webhook_delivery_retention).

account посочва лицето зад заявката — в таблото влезлото лице, иначе лицето, което е създало ключа — с имейл, име, език и дата на създаване; при Publishable Key стойността е null. Останалите броячи обхващат всяка друга таблица с данни на профила: продуктови паспорти, DPP документи, файлове, GS1 идентификатори, коментари, записи в одитния дневник, изпълнения на импорт, членове на работното пространство и известията за квота на организацията (scan_quota_alerts). Меко изтритите редове се броят, защото все още се съхраняват. erasure, backup_retention и billing_records описват какво изтрива DELETE /v1/account и какво остава за известно време; pii_in_export е true, защото експортът посочва лицето (имейл, име) — IP псевдонимите никога не напускат базата данни.


Експорт на данни (Чл. 20 от GDPR — Преносимост на данните)

GET /v1/account/export — Изтегля всички данни като JSON файл.

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

Експортът съдържа:

  • Всички QR кодове (включително изтритите)
  • Агрегирани статистики за сканиранията (без сурови IP хешове)
  • Агрегирани заявки към DPP за всеки продуктов паспорт (dpp_scan_aggregates, също без IP хешове)
  • Статистика на използването като агрегати (dashboard_activity_aggregates, api_operation_aggregates): активни дни и брой активни потребители по канал на таблото, броячи на заявките по операция и резултат — без идентификатори на потребители и ключове
  • Опити за доставка на уебхукове по уебхук и тип събитие (webhook_delivery_aggregates), без payload и отговор
  • Лицето зад заявката (account), организацията (organization; адрес за фактуриране и известия само за администратори на организацията) и работното пространство (workspace)
  • Продуктови паспорти, метаданни на DPP документи и файлове (не самите файлове), GS1 идентификатори, заявки за тяхното потвърждение и изпълнения на импорт (dpp_passports, dpp_documents, files, gs1_identifiers, gs1_verification_requests, dpp_import_jobs)
  • Коментари без полета за автор (code_comments), записи в одитния дневник като агрегати (audit_log_aggregates) и членове по роля (member_aggregates)
  • API ключове без ID и хеш (api_keys), уебхукове без URL и тайна (webhooks) и известията за квота на организацията (scan_quota_alerts)
  • Времеви клейма за създаване и промяна

Publishable Keys (qr3_pk_) получават 403: файлът съдържа целевите адреси и бележките на всички кодове.


Изтриване на акаунт (Чл. 17 от GDPR — Правото да бъдеш забравен)

DELETE /v1/account — Изтрива профила на лицето зад заявката незабавно и необратимо: в таблото влезлото лице, иначе лицето, което е създало ключа. Искане за изтриване по имейл води до същото изтриване: операторът изтрива входа в Clerk, след което qr3 изтрива профила.

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

Какво се случва:

  1. Организациите, които принадлежат на лицето, се изтриват с всички работни пространства, кодове, продуктови паспорти, файлове, статистики и дневници, включително файловете им в хранилището. Ако такава организация има друг активен администратор на организацията, вместо това я поема този с най-дълъг стаж — заедно с данните и абонамента.
  2. Абонамент: текущ абонамент приключва незабавно, без пропорционално възстановяване и без финална фактура; след това клиентът в Stripe се изтрива. Фактурите остават в Stripe за законовия срок на съхранение (7 години, § 132 BAO).
  3. Екипи: членството на лицето в чужди организации приключва; тези екипи запазват данните си. Коментарите на лицето вече не посочват автор, записите му в одитния дневник губят IP псевдонима и user agent, а API ключовете, които е създало там, се отменят.
  4. Лицето: входът (Clerk) и потребителският запис се изтриват.

Отговорът е 200 със status: "completed" и броячите organizations_deleted, organizations_transferred, memberships_removed и login_deleted. При много големи организации една заявка не стига: тогава отговорът е 202 със status: "in_progress" и изтриването се довършва автоматично в рамките на часове.

Изисква Secret или Test Key с право на запис или сесия в таблото (всяка роля, защото профилът е собствен). Publishable Keys и ключовете само за четене получават 403, ключ без лице зад него (created_by = 'system') получава 409. 503 означава, че на внедряването липсва тайна, нужна за изтриването; нищо не е изтрито.


Управление на съгласията

GET /v1/account/privacy/consents — Извличане на текущите съгласия.

POST /v1/account/privacy/consents — Актуализиране на съгласията.

Terminal window
# Извличане на текущите съгласия
curl https://qr3.app/v1/account/privacy/consents \
-H "Authorization: Bearer qr3_sk_..."
# Деактивиране на маркетинговите имейли
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 }'

Полета за съгласие:

ПолеПо подразбиранеОписание
marketing_emailsfalseБюлетини и рекламни имейли
analyticstrueАгрегирани статистики за използването
product_updatestrueНовини за продукти и списъци с промени (changelogs)

Промяната на съгласията изисква права за запис: ролята viewer и Publishable Keys получават 403.


Техническо внедряване на GDPR

Псевдонимизиране на IP адреси (Чл. 25 от GDPR)

HTTP заявка → Cloudflare Edge Worker
↓
CF-Connecting-IP заглавка → HMAC-SHA-256(IP_HASH_SALT, цел, UTC ден, IP)
↓
ip_hash (дневен псевдоним) → D1 база данни
↓
Оригиналният IP адрес никога не се съхранява

IP_HASH_SALT е дълготрайна главна тайна; целта и UTC денят автоматично извеждат отделни дневни псевдоними. Псевдонимите от различни UTC дни не са пряко равни, но притежател на тайната може да изчисли отново известен кандидат за IP адрес за конкретен ден. Не се използват KV дневни соли или ръчна ежедневна ротация.

Задържане на данни (автоматизирано)

ПланЗадържане
Всички планове90 дни

Статистиката на използването (активност в таблото, броячи за използване на API) и дневниците за доставка на уебхукове също се съхраняват 90 UTC дни: текущият UTC ден плюс предходните 89 дни. Същата ежедневна cron задача изтрива по-старите редове (purgeOldDashboardActivity, purgeOldApiOperationUsage, purgeOldWebhookDeliveries).

DPA с подизпълнители

Cloudflare, Inc. обработва данни в качеството си на обработващ лични данни (data processor). Споразумението за обработка на данни (DPA) е достъпно на cloudflare.com/cloudflare-customer-dpa.

Cloudflare обработва данни с указание за местоположение в региона на ЕС (включително Франкфурт); към момента не е резервирана договорно гарантирана юрисдикция в ЕС. Тъй като Cloudflare, Inc. е базирана в САЩ, като основание за предаването се прилагат Стандартните договорни клаузи (SCCs) съгласно чл. 46 от GDPR.


Контакт