Base legale (ANALISI-REQUISITI-LEGALI.md §5): il considerando 37 della Dir. (UE)
2023/2673 dice che il professionista *can* offrire il recesso su parte del
contratto, non che *deve*. Un pulsante a livello di ordine e' conforme. Ma il form
non lo diceva, mentre le automazioni agivano in modo totale.
- statementTemplate: 'relativo all'INTERO ordine #X' (la dichiarazione ora
corrisponde a cio' che l'app fa davvero).
- SCOPE_HINT sotto la dichiarazione: il recesso riguarda l'intero ordine, per il
parziale contattare il negozio (canale modulo tipo/email, sempre valido).
- Admin: banner quando autoCancelUnfulfilled e' attivo -> annulla e rimborsa
l'INTERO ordine, irreversibile; con ordini multi-articolo meglio tenerlo spento.
- PLAN: A6-ter (recesso parziale) come attivita' OPZIONALE con toggle per-shop
partialWithdrawalEnabled, default OFF. Include la nota che l'idempotenza
(shop, orderId) di fa8ee9c andra' riportata a (shop, orderId, articoli).
- ANALISI-REQUISITI-LEGALI.md §5: punto chiarito + panorama concorrenti (Revize
fa il parziale, Rescindly no, Shopify non ha pulsante nativo, LegalBlink non e'
un concorrente ma un generatore di documenti).
269 lines
21 KiB
Markdown
269 lines
21 KiB
Markdown
# Pizeta Recesso — App Shopify per la funzione di recesso
|
||
### Piano di sviluppo multi-agente
|
||
|
||
> **Obiettivo:** App Shopify riutilizzabile che rende qualsiasi negozio conforme alla funzione
|
||
> elettronica di recesso ("pulsante di recesso") obbligatoria — **Art. 54-bis Codice del Consumo**
|
||
> (D.Lgs 209/2025), recepimento Dir. (UE) 2023/2673. In vigore per contratti conclusi **dal 19
|
||
> giugno 2026**. I merchant sono esposti *da adesso*.
|
||
>
|
||
> **Base legale verificata:** vedi `ANALISI-REQUISITI-LEGALI.md` (fonti primarie/secondarie).
|
||
> Le voci ⚠ restano da confermare su EUR-Lex prima del go-live.
|
||
|
||
---
|
||
|
||
## 0. Decisioni bloccate
|
||
|
||
| Tema | Decisione | Nota |
|
||
|---|---|---|
|
||
| Distribuzione | **Custom distribution ora (2-3 store live) → Public molto più avanti** | Custom = no review, ok per pochi store. Account separati → 1 registrazione custom per store. Codice riusato per il futuro public |
|
||
| Backend | **Template Shopify Remix (Node) + Polaris** | Strada ufficiale Shopify |
|
||
| Hosting | **Fly.io** | Riuso setup esistente |
|
||
| Database | **Postgres + Prisma** | Fly Postgres (default) o Supabase (ho le creds) — decido in Fase 0 |
|
||
| Email (ricevuta durevole) | **Stub in MVP**, provider (Resend/Postmark) da decidere prima del go-live | Ricevuta con timestamp |
|
||
| Store di test | **SOLO `pcrt-reso-test`** (usa e getta) | ⚠ MAI pizeta-pharma-2 o store da trasferire: `shopify app dev`/install app DISABILITA il transfer in modo irreversibile |
|
||
| Consegna storefront | **Theme App Extension + App Proxy** | installazione senza toccare il tema, guest-capable |
|
||
|
||
**La cartella tema (`pizeta-pharma/`) NON è l'app** — è un tema Liquid, solo target di test.
|
||
L'app è questo repo separato.
|
||
|
||
---
|
||
|
||
## 0-bis. ⚠️ VINCOLO CRITICO — trasferimento store (IRREVERSIBILE)
|
||
|
||
**Il landmine è STRETTO: solo le DRAFT app / `shopify app dev` disabilitano il transfer.**
|
||
Lanciare `shopify app dev` (draft app) contro un development store — o abilitare developer preview —
|
||
**disabilita il trasferimento in modo permanente e irreversibile** (CLI issue #3946). Uno store
|
||
transfer-disabled non si trasferisce più: andrebbe ricostruito da zero.
|
||
|
||
**Cosa NON disabilita il transfer** (verificato):
|
||
- **Custom distribution via install link** (la nostra app recesso finita): si installa come una
|
||
normale app, **non** applica "Transfer Disabled". Installabile anche su store del cliente (via link
|
||
store-specifico, scadenza 7 giorni; ownership non richiesta).
|
||
- **Legacy/admin custom app** (es. "Import Negozio"): non disabilita il transfer.
|
||
|
||
**Regole operative:**
|
||
1. Sviluppo/test (**`shopify app dev` = draft**) **solo su `pcrt-reso-test`** (usa e getta). ← unica vera protezione.
|
||
2. **Mai** `shopify app dev` / draft contro **pizeta-pharma-2** o store destinati al cliente.
|
||
3. **App finita (custom link):** installabile su pizeta + altri store, **prima o dopo** il transfer,
|
||
senza rompere niente. Per zero-rischi assoluti, installa **dopo** il transfer (banale, via link).
|
||
4. ⚠ Nuance da confermare empiricamente: install custom su un client-transfer store ancora "in your
|
||
org" (pre-transfer) — fonti in conflitto. Se vuoi installare pre-transfer, testa prima.
|
||
|
||
---
|
||
|
||
## 0-ter. Strategia distribuzione & isolamento ambienti
|
||
|
||
**Principio:** *una build public-grade, distribuita Custom ora.* Non due sviluppi.
|
||
|
||
Vincolo di piattaforma (verificato): una app **Public — anche unlisted — richiede review Shopify** per
|
||
installarsi su store **live**. Solo **Custom** installa su live senza review. Quindi:
|
||
- **ORA:** distribuzione **Custom** (unico canale per il live senza review) su 2-3 store clienti.
|
||
- **DOMANI:** **nuova registrazione Public** + review, stesso codice. La distribuzione non è convertibile,
|
||
ma non si converte: si aggiunge una seconda registrazione.
|
||
|
||
**Codice public-grade dal giorno 1** (così il public futuro = re-registrazione, non rebuild):
|
||
1. Template Shopify CLI/Remix standard — OAuth multi-tenant, session token (no scorciatoia single-store).
|
||
2. Dati **multi-tenant per-shop** dal giorno 1, anche con soli 2-3 negozi.
|
||
3. **Webhook GDPR obbligatori** subito.
|
||
4. **Billing API dietro flag** (`isPublic`): off in custom, on in public. Unico ramo di codice divergente.
|
||
5. Performance/sicurezza a livello review **man mano**, non retrofit.
|
||
|
||
**Isolamento ambienti (anti-rottura) — creato separato dal giorno 1 in A0:**
|
||
```
|
||
pcrt-reso-test → staging (Fly) → recesso-custom (Fly, CLIENTI LIVE)
|
||
sviluppo collaudo stabile — deploy solo testati — DB proprio
|
||
|
||
recesso-public (Fly, DOMANI) → registrazione Public + DB proprio
|
||
stesso codice, deployment SEPARATO → non tocca mai recesso-custom
|
||
```
|
||
Registrazioni Partner distinte + Fly app distinti + DB distinti → il deploy della public **non può**
|
||
impattare gli store live sulla custom (isolamento strutturale, non per disciplina).
|
||
|
||
**Regole anti-rottura sulla custom-live:** test su `pcrt-reso-test` prima; migrazioni DB solo
|
||
**additive/backward-compatible**; theme extension **versionate** (rollback); **API version pinnata**.
|
||
|
||
---
|
||
|
||
## 1. Architettura
|
||
|
||
```
|
||
STOREFRONT (acquirente)
|
||
├─ Theme App Extension (app embed) → pulsante "recedere dal contratto qui"
|
||
└─ App Proxy /apps/recesso → flusso recesso guest-capable sul dominio dello shop
|
||
1. lookup: Order ID + email (guest — buona prassi per "facilmente accessibile")
|
||
2. form: nome, dichiarazione, email (precompilato se loggato)
|
||
3. STEP 2 conferma: "conferma recesso" (funzione dedicata, anti-accidentale)
|
||
4. successo + ricevuta durevole inviata
|
||
|
||
BACKEND (Fly.io)
|
||
├─ App Remix (admin embedded, Polaris)
|
||
├─ Postgres (Prisma): richieste, regole, impostazioni, audit
|
||
├─ Motore compliance: calcolo finestra, esclusioni Art. 59, tipo-prodotto, scadenza
|
||
├─ Ricevuta durevole: email con testo dichiarazione + timestamp di TRASMISSIONE
|
||
├─ Webhook: fulfillment (avvio finestra) + 3 GDPR obbligatori
|
||
└─ Billing (aggiunto in fase App Store)
|
||
```
|
||
|
||
**Perché Theme App Extension + App Proxy:** il pulsante acquirente + form guest devono vivere sul
|
||
dominio del negozio e sopravvivere agli aggiornamenti del tema senza che il merchant tocchi il
|
||
Liquid. App Proxy consente a un URL storefront (`/apps/recesso`) di raggiungere il backend
|
||
restando sul dominio dello shop — necessario per l'accesso guest e per "facile come acquistare".
|
||
|
||
---
|
||
|
||
## 2. Matrice requisiti (il contratto su cui ogni agente costruisce)
|
||
|
||
Corretta e integrata dopo `ANALISI-REQUISITI-LEGALI.md`.
|
||
|
||
| # | Requisito legale | Funzionalità | Criteri di accettazione |
|
||
|---|---|---|---|
|
||
| R1 | Funzione visibile, facilmente accessibile, **continuativamente disponibile** per tutta la finestra | Pulsante extension + pagina proxy sempre attiva, legata al singolo ordine | Label **"recedere dal contratto qui"**; funzione attiva tutta la finestra di quell'ordine |
|
||
| R2 | Accesso facile (guest = buona prassi, non obbligo) | Lookup Order ID + email | Guest completa il recesso con solo n° ordine + email; nessun login forzato |
|
||
| R3 | Raccolta nome, id contratto, mezzo elettronico, dichiarazione | Form recesso | I 4 dati raccolti + persistiti |
|
||
| R4 | Conferma a 2 step, funzione dedicata | Funzione **"conferma recesso"** | Invio impossibile senza 2ª azione esplicita; **no dark pattern** |
|
||
| R5 | Ricevuta su supporto durevole **senza ritardo**, con dichiarazione + **timestamp di trasmissione** | Email transazionale | Email < 1 min; contiene testo dichiarazione + data/ora di trasmissione |
|
||
| R6 | Onere probatorio del professionista | Persistenza DB + **audit log immutabile** | Record immutabile richiesta + timestamp + invio ricevuta |
|
||
| R7 | Funzione **non più onerosa della conclusione** (anti dark pattern) | UX ≤ passaggi del checkout | ≤ passaggi/attriti del checkout; no pop-up dissuasivi, pre-flag, retention nudge |
|
||
| R8 | Termine 14gg, decorrenza per tipo | Motore compliance | Scadenza corretta per beni (consegna/ultimo lotto) / servizi (conclusione) / digitale |
|
||
| R9 | Esclusioni Art. 59 (su misura, deperibili, sigillati igiene) | Regole esclusione (prodotto/collezione/tag) | Item escluso segnalato con motivo; no recesso "cieco" |
|
||
| R10 | Non conformità → 14gg → 12 mesi+14gg (Art. 53) | Warning misconfig + calcolo corretto | Admin avvisa su misconfig; scadenza riflette proroga quando applicabile |
|
||
| R11 | Pulsante **AGGIUNTIVO**, non sostitutivo | Coesistenza modulo tipo Allegato I-B + email | App non disabilita/nasconde gli altri canali |
|
||
| R12 | Obblighi info **Art. 49** (ubicazione funzione) | Testo info recesso + link ubicazione | Info diritto di recesso + posizione funzione mostrate |
|
||
| R13 | Rimborso: spese consegna incluse; reso a carico consumatore se informato; diminuzione valore | Logica rimborso + informativa | Schermata informa su chi paga reso; calcolo include spedizione standard |
|
||
| R14 | Lingua del contratto/offerta | i18n (IT/EN/DE/FR/ES…) | Flusso acquirente + ricevuta localizzati |
|
||
| R15 | Solo **B2C** | Filtro ordini | Ordini B2B esclusi dal flusso obbligatorio |
|
||
| R16 | Webhook GDPR obbligatori Shopify | `customers/data_request`,`customers/redact`,`shop/redact` | Tutti e 3 implementati + HMAC verificato |
|
||
| R17 | Sanzione pulsante non conforme = pratica scorretta Art. 27 (AGCM, fino €10M/4%) | (rischio, non feature) | Documentato; guida merchant alla conformità |
|
||
|
||
---
|
||
|
||
## 3. Modello dati (bozza Prisma)
|
||
|
||
- **Shop** — dominio, accessToken(cifrato), piano, installedAt
|
||
- **Settings** — labelPulsante, brandColors, indirizzoReso, giorniFinestraDefault, overrideMercati, testoInfoRecesso, email (emailSubject/emailIntro/emailNote), + toggle per-shop: notifyEnabled/notifyEmail, tagEnabled, enforceWindow, enforceExclusions, **stateAwareEmail, autoCancelUnfulfilled, returnAtCustomerExpense, returnInstructions**
|
||
- **ExclusionRule** — scope(prodotto|collezione|tag|tutto), targetId, motivo(sumisura|deperibile|igiene), attiva
|
||
- **WithdrawalRequest** — shopId, orderId, orderName, nomeCliente, email, testoDichiarazione, **trasmessoAt (ts)**, locale, canale(guest|account), tipoProdotto, stato, ricevutaInviataAt, scadenzaCalcolata
|
||
- **AuditLog** — shopId, evento, hashPayload, ts (append-only, immutabile)
|
||
- **WebhookEvent** — topic, ricevutoAt, processato (idempotenza)
|
||
|
||
---
|
||
|
||
## 4. Piano multi-agente (agenti SPECIALIZZATI, esecuzione SEQUENZIALE)
|
||
|
||
Preferenza utente: agenti **super-specializzati**, eseguiti **uno alla volta** (parallelo solo dove
|
||
strettamente utile), **minimo consumo di token**. Ogni agente = un brief focalizzato con
|
||
input / deliverable / criteri di uscita espliciti. **Nessun avvio automatico** — parto al tuo ok.
|
||
|
||
### Fase 0 — Fondamenta *(gate: tutto il resto dipende da qui)*
|
||
- **A0 architect** — init app Remix, schema Prisma, config Fly, igiene segreti, collegamento ai 2 dev store, CI. **Uscita:** app installata su entrambi i dev store, `/health` verde su Fly, DB migrato.
|
||
|
||
### Fase 1 — Specifica compliance *(gate: il contratto di accettazione)*
|
||
- **A1 compliance-mapper** — trasforma `ANALISI-REQUISITI-LEGALI.md` + §2 in criteri di accettazione testabili + copy deck (etichette IT esatte, template ricevuta, testo info recesso), risolve i punti ⚠. **Uscita:** file criteri approvato contro cui ogni agente testa.
|
||
|
||
### Fase 2 — MVP core (minimo legalmente conforme) *(sequenziale)*
|
||
- **A2 storefront-extension** — Theme App Extension + App Proxy lookup guest (R1,R2,R7,R11).
|
||
- **A3 backend-flow** — endpoint recesso, conferma 2 step, persistenza + timestamp (R3,R4,R6).
|
||
- **A4 receipt** — email durevole con dichiarazione + timestamp trasmissione (provider stub) (R5).
|
||
- **Uscita:** guest su dev store immacolato completa il recesso → record + ricevuta. **Questo è già conforme.**
|
||
|
||
### Fase 3 — Admin merchant *(sequenziale; A5→A6)*
|
||
- **A5 admin-UI + motore di stile estensibile** — Polaris: dashboard richieste, impostazioni, testo info (R12). **Theming a 3 livelli** (requisito utente: "super estendibile, personalizzabile da template"):
|
||
1. **Token da Settings** (no-code) — colore brand, font, raggio, logo, larghezza → override delle CSS variables del form (base già pronta: il form del design è tutto a token `:root`).
|
||
2. **Eredita tema** — opzione App Proxy in **Liquid** → il form è reso dentro il tema del negozio (look nativo, "template del merchant").
|
||
3. **CSS custom** — escape hatch per personalizzazione totale.
|
||
Merchant sceglie il livello; default = card a token controllata. Può diventare un agente dedicato (theming) se troppo grande per A5.
|
||
- **A6 compliance-engine** — esclusioni Art. 59 + calcolo finestra/scadenza per tipo prodotto + warning misconfig + logica rimborso (R8,R9,R10,R13,R15). **Uscita:** merchant configura esclusioni/finestre; item escluso si comporta secondo R9.
|
||
- **A6-bis operatività per stato ordine** *(confermata call Pizeta 2026-06-16 + `AUDIT-STATI-ORDINE.md`)* — comportamenti aggiuntivi, **tutti toggle per-shop** (app generica: ognuno decide). Dati già raccolti dal lookup: `displayFulfillmentStatus`, `deliveredAt`, `cancelledAt`, `financialStatus`.
|
||
1. **Ricevuta differenziata per stato** (`stateAwareEmail`, default ON): la ricevuta durevole (sempre inviata) include un blocco operativo diverso —
|
||
- *non evaso* → "ordine annullato, procederemo al rimborso";
|
||
- *spedito/consegnato* → istruzioni di reso (indirizzo, spese a carico cliente, prodotto integro, rimborso dopo il rientro).
|
||
2. **Auto-annullamento ordini non evasi** (`autoCancelUnfulfilled`, default OFF — irreversibile, opt-in): al recesso su ordine non evaso → `orderCancel` (refund+restock). Bonus: Shopify emette `orders/cancelled` → si ferma il bot di remarketing del merchant.
|
||
3. **Istruzioni di reso configurabili**: `returnAddress` (già in schema), `returnAtCustomerExpense` (default ON — Art. 57: informa + rende il cliente responsabile delle spese), `returnInstructions` (testo opzionale).
|
||
**Resta MANUALE per scelta Pizeta** (nessuna automazione richiesta): resi parziali + rimborso proporzionale (pannello Shopify), emissione rimborso (Shopify manda la sua mail), verifica data consegna se il corriere non passa l'evento a Shopify.
|
||
**Uscita:** merchant attiva/disattiva ciascun comportamento; il flusso si adatta allo stato dell'ordine.
|
||
- **A6-ter recesso parziale** *(OPZIONALE — non è un obbligo di legge)* — Base legale in `ANALISI-REQUISITI-LEGALI.md` §5: il **considerando 37 Dir. (UE) 2023/2673** dice che il professionista **"can"** offrire il recesso su parte del contratto, non che **deve**. Un pulsante a livello di ordine è conforme; il parziale è **differenziazione di prodotto** (lo fa Revize, non lo fa Rescindly) e copre il caso reale Pizeta (3 confezioni, ne rende 2).
|
||
- Toggle per-shop **`partialWithdrawalEnabled`** (default **OFF** = comportamento attuale, un clic = recesso totale).
|
||
- Passo 2: elenco articoli dell'ordine con quantità; **default tutti selezionati** (nessun attrito per chi vuole il totale).
|
||
- Dichiarazione **generata sugli articoli scelti** (oggi dice "intero ordine": non si può lasciare così se il cliente seleziona un sottoinsieme).
|
||
- `createShopifyReturn`: reso limitato alle **righe/quantità selezionate**.
|
||
- `autoCancelUnfulfilled`: si attiva **solo** se la selezione copre l'intero ordine. Parziale + non evaso → order edit / rimborso parziale, che resta manuale.
|
||
- **Idempotenza da rivedere**: oggi la chiave è `(shop, orderId)` (commit `fa8ee9c`), corretta solo nel mondo total-only. Col parziale deve diventare `(shop, orderId, articoli)`: il cliente può recedere per l'articolo A oggi e per il B domani, entrambi legittimi nella finestra.
|
||
- Dati da aggiungere: `lookupOrder` deve restituire titoli, quantità e ID riga; mappatura `lineItem ↔ fulfillmentLineItem` per il reso; nuova colonna (es. `items Json`) su `WithdrawalRequest`.
|
||
- **Uscita:** merchant può abilitare il parziale; con toggle OFF il comportamento resta identico a oggi.
|
||
|
||
### Fase 4 — Robustezza *(A7→A8 sequenziali, poi A9 audit)*
|
||
- **A7 i18n** — localizzazione IT/EN/DE/FR/ES (R14).
|
||
- **A8 compliance-webhooks** — 3 webhook GDPR + verifica HMAC + idempotenza (R16).
|
||
- **A9 qa-security-auditor** — avversariale: HMAC, abuso lookup guest (enumeration/rate-limit), gestione PII, edge case (fulfillment parziale, beni digitali, calcolo scadenza). **Uscita:** report audit, zero criticità.
|
||
|
||
### Fase 5 — App Store pubblica *(quando custom → public)*
|
||
- **A10 billing** — Shopify Billing API + piani.
|
||
- **A11 app-store-submission** — listing, screenshot, privacy, checklist review. **Uscita:** pacchetto pronto per submission.
|
||
|
||
### Grafo dipendenze
|
||
```
|
||
A0 → A1 → A2 → A3 → A4 → A5 → A6 → A7 → A8 → A9 → A10 → A11
|
||
(sequenziale per default; A7/A8 parallelizzabili solo se serve accelerare)
|
||
```
|
||
|
||
---
|
||
|
||
## 4-ter. Piano consolidato — stato attuale + residuo *(agg. 2026-07-07)*
|
||
|
||
Vista unica: cosa è FATTO e cosa RESTA, con A6-bis (Pizeta) e i gap dell'audit
|
||
integrati. Dettaglio stato/commit nella memoria di progetto + git.
|
||
|
||
### ✅ Fatto (custom-grade, testato su pcrt-reso-test)
|
||
- **A0–A1** fondamenta + spec compliance + copy deck.
|
||
- **A2** Theme App Extension (app block + app embed) + **modal** storefront (iframe, redesign de-AI, popover info).
|
||
- **A3** App Proxy: flusso guest 2-step + persistenza + **timestamp trasmissione** + audit (anti-enumeration).
|
||
- **A4** ricevuta durevole (nodemailer/Mailpit dev) + **dati per-shop dinamici** (nome/URL/link ordine da Shopify) + **template editabile vincolato** (oggetto/intro/nota + anteprima live + ripristino).
|
||
- **A5 (parziale)** admin Polaris: dashboard **Recessi**, **Impostazioni** (email/notifiche/regole), **Esclusioni** (CRUD). Theming: solo token base.
|
||
- **A6 (parziale)** engine finestra + esclusioni Art. 59 (dietro toggle, default OFF).
|
||
- **A8 (stub)** webhook GDPR + HMAC.
|
||
- **EXTRA (oltre il piano originale):** integrazione **Resi Shopify** (`returnCreate` su ordini evasi), **notifica merchant + tag** (toggle), **fix P1** (finestra sulla CONSEGNA, skip ordini annullati/rimborsati), **avviso ordini chiusi**, **audit stati ordine** (`AUDIT-STATI-ORDINE.md`).
|
||
|
||
### ⏳ Residuo (prioritizzato)
|
||
|
||
- **R1 — A6-bis operatività per stato ordine (Pizeta-confirmed)** — 3 toggle per-shop: `stateAwareEmail` (ricevuta differenziata non-evaso/spedito-consegnato), `autoCancelUnfulfilled` (annullo automatico ordini non evasi), `returnAtCustomerExpense`+`returnInstructions`+`returnAddress` (istruzioni reso, Art. 57). Dettaglio: sezione A6-bis.
|
||
- **R2 — A6 residuo copy compliance** — G7: promemoria rimborso 14gg + facoltà di trattenuta (Art. 56) nella notifica al merchant. *(G6 spese-reso assorbito da R1.)*
|
||
- **R2-bis — A6-ter recesso parziale** *(opzionale, toggle `partialWithdrawalEnabled`, default OFF)* — non è compliance ma prodotto: selezione articoli/quantità, dichiarazione sugli articoli scelti, reso limitato alle righe scelte, auto-annullo solo se selezione = intero ordine, idempotenza su `(ordine, articoli)`. Vedi A6-ter e `ANALISI-REQUISITI-LEGALI.md` §5.
|
||
- **R3 — Deploy custom (go-live)** — Fly.io + Postgres prod + URL stabile + tunnel Cloudflare nominato + link install custom sui 2-3 store live. *(Serve per usarlo davvero; occhio deadline transfer pizeta-pharma-2 ~metà luglio 2026.)*
|
||
- **R4 — A9 hardening / QA-security** — rate-limit robusto, retry ricevuta fallita, idempotenza webhook, review avversariale (enumeration lookup, HMAC, PII), Protected Customer Data (approvazione Shopify per leggere `order.email` in prod).
|
||
- **R5 — A7 i18n** — IT/EN/DE/FR/ES.
|
||
- **R6 — A5 residuo theming** — motore 3 livelli completo (token no-code / eredita-tema Liquid / CSS custom).
|
||
- **R7 — Pubblica** — A10 billing + A11 submission App Store (registrazione Partner separata → non impatta le custom live).
|
||
|
||
### Ordine consigliato
|
||
`R1 → R2 → R3 (deploy) → R4 (hardening) → R5/R6 → R7`
|
||
R1/R2 chiudono valore-cliente + compliance copy; R3 mette live; R4 mette in sicurezza prima del traffico reale; R5/R6 rifiniscono; R7 quando si va public.
|
||
|
||
---
|
||
|
||
## 5. Milestone
|
||
|
||
- **M1 (Fase 0–1):** scaffold + contratto compliance. *Nulla di visibile, ma toglie rischio a tutto.*
|
||
- **M2 (Fase 2):** **MVP conforme live su pizeta.** ← primo valore reale, azzera esposizione legale.
|
||
- **M3 (Fase 3–4):** production-grade, configurabile, auditato.
|
||
- **M4 (Fase 5):** submission App Store.
|
||
|
||
---
|
||
|
||
## 6. Rischi / punti di attenzione
|
||
|
||
- **Sovrapposizione nativo Shopify** — Shopify ha regole reso/cancellazione UE; non ricostruire i resi, avvolgerli. Verificare in A1 cosa è nativo vs gap.
|
||
- **Competitor esistenti** (Rescindly, REVER) — differenziare su conformità IT esatta + semplicità + prezzo.
|
||
- **Lookup guest = superficie di abuso** — enumeration ordini; serve rate-limit + match email + nessun leak su miss (A9).
|
||
- **Prova supporto durevole** — ricevuta riproducibile/loggata, non fire-and-forget (A4/A6).
|
||
- **Calcolo scadenza** — consegna vs conclusione, beni digitali, fulfillment parziale; fonte unica di verità nel motore (A6).
|
||
- **PII** — dichiarazione + email = dati personali; cifratura at rest + webhook GDPR non negoziabili (A8).
|
||
- **Base legale ⚠** — confermare numeri articoli UE e inquadramento Art. 27 su fonte primaria (A1).
|
||
|
||
---
|
||
|
||
## 7. Punti aperti (non bloccanti)
|
||
|
||
1. DB: Fly Postgres vs Supabase Postgres — decido in A0.
|
||
2. Provider email — decido prima di M2.
|
||
3. Automazione rimborso — fuori scope MVP; confermare che il tracking manuale basta al lancio.
|