Docs/Tutorial
// tutorial
Privacy policy generata con GPT
Stripe non ti approva un account senza termini e privacy policy raggiungibili. Scriverli da zero è mezza giornata persa; farseli buttare giù da un modello e poi rivederli sono venti minuti. In ZeroToShip il prompt è già scritto: sta come commento in cima a app/privacy-policy/page.tsx e app/tos/page.tsx.
1. Il prompt è già nel file
Apri app/privacy-policy/page.tsx: prima dell'import del componente trovi questo blocco. È un prompt completo, con i dati di ZeroToShip dentro. Il tuo lavoro è sostituire quei dati con i tuoi.
// You are an excellent lawyer.
// I need your help to write a simple privacy policy for my website. Here is some context:
// - Website: https://zerotoship.com
// - Name: ZeroToShip
// - Description: A JavaScript code boilerplate to help entrepreneurs launch their startups faster
// - User data collected: name, email and payment information
// - Non-personal data collection: web cookies
// - Purpose of Data Collection: Order processing
// - Data sharing: we do not share the data with any other parties
// - Children's Privacy: we do not collect any data from children
// - Updates to the Privacy Policy: users will be updated by email
// - Contact information: alberto@zerotoship.com
// Please write a simple privacy policy for my site. Add the current date. Do not add or explain your reasoning. Answer:Le due righe finali sono la parte che la gente dimentica: add the current date e do not add or explain your reasoning. Senza, ti ritrovi mezza risposta di preamboli da incollare a mano.
2. Prendi i fatti dal tuo config.ts
Il prompt vale quanto valgono i dati che gli dai. Quasi tutti stanno già in config.ts, non inventarli:
appName: "ZeroToShip",
domainName: "zerotoship.com", // niente https://, niente slash finale
resend: {
supportEmail: "...", // l'indirizzo di contatto della policy
},
stripe: {
plans: [ /* Starter 199, All-In 249 — pagamento una tantum */ ],
},3. Elenca i fornitori che usi davvero
Questa è la parte che il prompt di default lascia vaga e che invece va resa esplicita, perché sono i soggetti a cui passano dati. In un progetto ZeroToShip non toccato sono questi:
- Supabase — autenticazione (magic link e login Google) e database. Ci finiscono email, id utente e la riga di profilo con lo stato di accesso.
- Stripe — checkout e abbonamenti. I dati della carta non passano mai dal tuo server: l'utente li inserisce su Stripe. Tu ti tieni un
customer_ide unprice_id. - Resend — email transazionali, e in molti setup anche SMTP per i magic link di Supabase.
- Analytics —
app/layout.tsxcarica uno script di analytics in versione cookieless. Anche se non piazza cookie, dichiaralo lo stesso. - Crisp — presente in
config.ts, ma conid: ""è spento. Citalo solo se lo accendi.
Se hai tolto o sostituito uno di questi, togli o sostituisci anche la riga corrispondente nel prompt. Una policy che nomina un fornitore che non usi è peggio di una policy generica.
4. Scrivi il prompt e mandalo
Metti insieme i due pezzi e incollalo nel modello che preferisci — ChatGPT, Claude, quello che hai aperto. Una versione con i fornitori dentro:
Sei un avvocato. Scrivimi una privacy policy semplice e leggibile per il mio sito.
Contesto:
- Sito: https://iltuodominio.com
- Nome: IlTuoProdotto
- Descrizione: [una riga su cosa fa]
- Dati personali raccolti: nome, email, dati di fatturazione
- Dati non personali: cookie tecnici e analytics
- Finalità: creazione account, gestione ordini, supporto
- Fornitori terzi che trattano i dati:
- Supabase (autenticazione e database)
- Stripe (pagamenti; i dati della carta non transitano dai nostri server)
- Resend (email transazionali)
- [analytics, se lo usi]
- Condivisione dati: nessuna vendita o cessione a terzi oltre ai fornitori sopra
- Minori: non raccogliamo dati da minori di 13 anni
- Diritti dell'utente: accesso, rettifica e cancellazione scrivendo al contatto
- Aggiornamenti: comunicati via email
- Contatto: ciao@iltuodominio.com
Scrivila in italiano, con la data di oggi. Non aggiungere spiegazioni o commenti tuoi.Se i tuoi utenti stanno in Europa, aggiungi una riga per chiedere i punti su base giuridica del trattamento, tempi di conservazione e diritti GDPR. Sono i tre paragrafi che un modello salta se non glieli chiedi.
5. Incolla il risultato nella pagina
Il testo va dentro il <pre> di app/privacy-policy/page.tsx, che è già impostato con whitespace-pre-wrap: i tuoi a capo restano, il testo si adatta alla larghezza.
<pre
className="leading-relaxed whitespace-pre-wrap font-sans text-base"
style={{ color: "var(--ink)" }}
>
{`Ultimo aggiornamento: 24 agosto 2026
Grazie per aver visitato IlTuoProdotto...`}
</pre>Due trappole. La prima: il testo sta dentro un template literal, quindi se la risposta contiene un backtick o la sequenza dollaro-graffa il build si rompe — toglili o mettici davanti un backslash. La seconda: aggiorna la riga della data, perché è la prima cosa che guarda chi legge una policy.
Poi ripeti lo stesso giro con app/tos/page.tsx: ha il suo prompt in cima, con in più le voci su proprietà del codice, rimborsi e legge applicabile. Il Footer già linka entrambe le pagine nella colonna LEGALE, quindi non devi toccare altro.
6. Generarla via codice, se proprio vuoi
Nel repo c'è libs/gpt.ts, un helper minimale che chiama l'API di OpenAI con axios. Non c'è nessun SDK: fa un POST diretto a https://api.openai.com/v1/chat/completions, logga il prompt e la risposta in console insieme ai token consumati, e restituisce il contenuto del primo messaggio. Se la chiamata fallisce non lancia: logga l'errore e ritorna null.
export const sendOpenAi = async (
messages: any[],
userId: number,
max = 100,
temp = 1
) => {
const url = 'https://api.openai.com/v1/chat/completions';
const body = JSON.stringify({
model: 'gpt-4',
messages,
max_tokens: max,
temperature: temp,
user: userId,
});
const options = {
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
'Content-Type': 'application/json',
},
};
try {
const res = await axios.post(url, body, options);
return res.data.choices[0].message.content;
} catch (e) {
console.error('GPT Error: ' + e?.response?.status, e?.response?.data);
return null;
}
};import { sendOpenAi } from "@/libs/gpt";
const testo = await sendOpenAi(
[{ role: "user", content: prompt }],
1, // userId — finisce nel campo "user" della richiesta a OpenAI
3000, // max_tokens: il default è 100, troppo poco per una policy
0.4 // temperature bassa: vuoi testo prevedibile, non creativo
);
if (!testo) {
// sendOpenAi ritorna null in caso di errore, non lancia
}Tre avvertenze pratiche. Il max di default è 100 token: se non lo alzi ti torna una policy troncata a metà frase. La chiave OPENAI_API_KEY non è in .env.example, devi aggiungerla tu (vedi Configurare le variabili). E sendOpenAi va chiamata solo lato server — route handler o server component — altrimenti la chiave finisce nel bundle del browser.
Detto questo: per due pagine legali che scrivi una volta sola, il copia-incolla del passo 5 è più veloce e non ti costa token. libs/gpt.ts è lì per quando l'AI serve dentro il prodotto, non per generarti il footer.
7. Fallo leggere a un umano
Questa parte non è opzionale. Un testo legale generato da un modello è una bozza: può citare leggi sbagliate, dimenticare obblighi che valgono nel tuo paese o affermare cose che non corrispondono a come tratti davvero i dati. Prima di pubblicare rileggi riga per riga e verifica che ogni frase descriva quello che il tuo codice fa realmente, poi fallo controllare a un avvocato — a maggior ragione se vendi in Europa o tratti dati sensibili.
Questa guida spiega un flusso di lavoro tecnico, non è consulenza legale. La responsabilità di quello che pubblichi sul tuo dominio resta tua.