# Protected Customer Data — verdetto e autovalutazione Stato: **2026-07-10**. App: `Legal Return PCRT` (custom distribution) su Fly `recesso-custom`. --- ## 1. Serve l'approvazione di Shopify? **No, non per la distribuzione custom.** Dalla documentazione ufficiale ([shopify.dev/docs/apps/launch/protected-customer-data](https://shopify.dev/docs/apps/launch/protected-customer-data)): | Livello | Public app | **Custom app** | Admin-created custom app | |---|---|---|---| | Level 1 | Requires review | **Always available** | Always available | | Level 2 (nome, indirizzo, email, telefono) | Requires review | **Always available** | Varies by plan | Conseguenze: - **Oggi (custom, store live): nessuna approvazione da attendere.** `order.email` e `order.statusPageUrl` sono accessibili. - **Domani (R7, app pubblica): la review sarà obbligatoria.** Questo documento e' la base della futura richiesta. - ⚠ Il fatto che oggi funzioni su `pcrt-reso-test` **non prova nulla**: sui development store la review non e' richiesta comunque. La prova e' la tabella qui sopra, non il test. **Da fare comunque nel Dev Dashboard:** dichiarare quali dati e campi protetti si usano (App → API access requests → Protected customer data). Testi pronti al §3. **⚠ Attenzione:** `order.statusPageUrl` richiede **Level 2** dal 15 marzo 2024 ([changelog](https://shopify.dev/changelog/level-2-protected-customer-data-requirements-are-now-needed-to-access-the-order-statuspageurl-field)). Lo usiamo solo per il bottone "Vedi il tuo ordine" nella ricevuta al cliente: e' una comodita', non un dato necessario. Per la data minimization della futura app pubblica **valutare di rimuoverlo**. --- ## 2. Dati protetti effettivamente trattati | Dato | Da dove | Perche' e' il minimo necessario | |---|---|---| | `order.email` | Admin API (lookup ordine) | Verifica d'identita' del consumatore **guest** (senza login, come impone l'art. 54-bis) e destinatario della **ricevuta su supporto durevole** (obbligo di legge) | | Nome del consumatore | Inserito dal consumatore nel form | Elemento richiesto dall'art. 54-bis nella dichiarazione | | Testo della dichiarazione | Inserito dal consumatore | E' l'atto giuridico stesso; va conservato come prova | | `order.statusPageUrl` | Admin API | Solo comodita' (link "Vedi il tuo ordine"). **Non necessario** | NON trattiamo: indirizzi, telefono, dati di pagamento, profilazione. Nessuna vendita o condivisione a terzi. Unico sub-processor: il **provider SMTP scelto dal merchant**. --- ## 3. Testi per la dichiarazione nel Dev Dashboard **Protected customer data — motivazione:** > L'app implementa la funzione di recesso obbligatoria ex art. 54-bis del Codice del Consumo > italiano (D.Lgs 209/2025, Dir. UE 2023/2673). Per legge la funzione deve essere utilizzabile > senza login: l'app deve quindi verificare l'identita' del consumatore confrontando il numero > d'ordine con l'email associata all'ordine, e inviare a quell'indirizzo la ricevuta su supporto > durevole con il timestamp di trasmissione. Nessun altro dato del cliente viene letto. **Protected customer field — `email`:** > Necessaria per due obblighi di legge: (1) verificare che chi esercita il recesso sia il > titolare dell'ordine (accesso guest, senza autenticazione); (2) recapitare la ricevuta su > supporto durevole, che l'art. 54-bis impone di inviare senza indebito ritardo. **Protected customer field — `name`:** non richiesto via API (lo inserisce il consumatore). --- ## 4. Requisiti Level 1 / Level 2 — stato reale | # | Requisito | Stato | Evidenza / Gap | |---|---|---|---| | L1 | Minimizzazione dei dati | 🟡 | Ok tranne `statusPageUrl` (non necessario) | | L1 | Informare il merchant su dati e finalita' | 🟡 | `CHECKLIST-COMPLIANCE-MERCHANT.md` c'e'; manca una **privacy policy dell'app** | | L1 | Uso limitato alle finalita' dichiarate | ✅ | Nessun uso secondario | | L1 | Periodi di retention definiti | ❌ | **Non documentati.** `shop/redact` fa purge totale; `customers/redact` pseudonimizza. Manca la policy scritta | | L1 | Cifratura in transito | ✅ | HTTPS ovunque; SMTP con STARTTLS/TLS | | L1 | Cifratura a riposo | ✅ | Volume Fly `pg_data` **ENCRYPTED = true**. Password SMTP per-shop cifrate AES-256-GCM (`crypto.server.ts`) | | L2 | Backup cifrati | ✅ | Snapshot automatici Fly (cifrati perche' il volume lo e') | | L2 | Separazione test / produzione | ✅ | Dev = Postgres in Docker locale; prod = Fly Postgres. DB e app distinti | | L2 | Accesso staff limitato | 🟡 | Un solo titolare. **Ma la password master Fly era in chiaro nel file `Cred Fly`: da cambiare** | | L2 | Password robuste | ❌ | Vedi sopra: cambio password Fly **non confermato** | | L2 | Access log | 🟡 | Log applicativi Fly (effimeri) + `AuditLog` append-only con payload hashati. Nessun log di accesso al DB | | L2 | Incident response policy | ❌ | **Non esiste.** Va scritta | | L2 | Data loss prevention | ❌ | Snapshot con **retention 5 giorni**, nodo singolo, nessuna copia off-site. Vedi §5 | --- ## 5. Due rischi che non sono formalita' ### 5.1 Durabilita' dei record legali Gli snapshot Fly hanno **retention 5 giorni** e vivono nella stessa infrastruttura. Sono *disaster recovery*, non *archivio*. Ma le `WithdrawalRequest` e l'`AuditLog` sono la **prova** del recesso: se il termine si estende a 12 mesi per mancata informativa, o se sorge una controversia, quei record devono esistere ben oltre 5 giorni. Un solo nodo, nessuna copia off-site: se l'app Postgres viene cancellata, la prova sparisce. **Fix:** dump logico periodico verso storage esterno (o Fly Managed Postgres / Supabase, che hanno backup gestiti con retention lunga). Non e' un requisito Shopify — e' il motivo per cui l'app esiste. ### 5.2 Cold start sulla funzione di recesso `min_machines_running = 0`: la macchina si spegne. Una richiesta a freddo ha impiegato **38 secondi**. L'art. 54-bis pretende una funzione **"sempre accessibile"** e **"facilmente utilizzabile"**: 38 secondi di attesa dopo il clic sono un ostacolo, e con ogni probabilita' il consumatore abbandona. **Fix:** `min_machines_running = 1` (una macchina sempre calda). --- ## 6. Azioni, in ordine 1. **Cambiare la password Fly** (era in chiaro). — *utente* 2. **`min_machines_running = 1`** nel `fly.toml`. — *codice, banale* 3. **Backup off-site** dei record legali (dump periodico). — *da progettare* 4. **Privacy policy dell'app** + **retention policy** scritte. — *documenti* 5. **Incident response policy** (chi, cosa, entro quanto). — *documento* 6. Dichiarare dati e campi nel **Dev Dashboard** (testi al §3). — *utente* 7. *(Per R7, app pubblica)* valutare la **rimozione di `statusPageUrl`** per minimizzazione.