Web3Forms
Il modulo "contattaci" per qualsiasi sito, senza programmare un server
Incasso mensile: 43.000 USD (fonte indicata nella scheda)
Cosa fa
Chi ha un sito semplice (fatto con HTML, Webflow, Framer…) spesso non sa dove far arrivare i messaggi del modulo contatti. Questo servizio dà un indirizzo a cui collegare il modulo: i messaggi arrivano per email, con filtro antispam.
Come guadagna
Ha entrate ricorrenti ogni mese, verificate su TrustMRR. Prezzi e piani esatti sono indicati sul sito del progetto.
Rendilo tuo
- Pensato per le attività italiane: messaggi anche su WhatsApp o Telegram, non solo per email.
- Aggiungi le richieste di preventivo con allegati foto, utili per artigiani e officine.
- Dashboard in italiano con tutte le richieste ricevute e lo stato (da richiamare, fatto).
Le frasi da scrivere all'AI
1. Base del sito e grafica
Crea un'app web in italiano chiamata "Modulino": dà a chiunque abbia un sito un indirizzo a cui collegare il modulo "contattaci": i messaggi arrivano per email e si leggono in un pannello. Usa React, Vite, TypeScript e Tailwind CSS.
Pagine da creare:
1. Pagina iniziale con il codice di esempio da copiare, i vantaggi e i prezzi
2. Registrazione e accesso
3. Pannello con i moduli creati
4. Pagina di un modulo: codice da incollare, impostazioni e messaggi ricevuti
5. Documentazione semplice: come collegarlo a WordPress, Webflow, HTML
Stile: pulito e tecnico ma amichevole: sfondo bianco, testo #111827, accento viola #6D28D9, blocchi di codice su fondo scuro, carattere "Inter" e "JetBrains Mono" per il codice. Nessuna immagine generica di stock.
Tutto deve funzionare perfettamente da telefono. Tutti i testi in italiano, chiari e senza parole tecniche.
2. Database, account e sicurezza
Collega il progetto a Supabase e attiva la registrazione con email e password.
Crea queste tabelle:
- "moduli": nome, email a cui inoltrare, chiave pubblica del modulo, collegati all'utente
- "messaggi": i dati inviati da ogni modulo, con data e un riferimento anonimo al mittente per bloccare gli abusi
Regole di sicurezza obbligatorie:
- ogni utente vede solo i suoi moduli e i loro messaggi
- i messaggi li scrive solo la Edge Function pubblica, dopo i controlli antispam
Usa esattamente il codice SQL che trovi nei "Dettagli tecnici" di questa scheda: eseguilo nel SQL Editor di Supabase prima di continuare, poi collega le pagine alle tabelle.
3. L'indirizzo che riceve i moduli
Crea la Edge Function pubblica "invia" (codice nei Dettagli tecnici) che riceve i moduli da qualsiasi sito:
1. legge la chiave del modulo e controlla che esista;
2. scarta i messaggi con il campo nascosto antispam compilato (i robot lo riempiono, le persone no);
3. limita a 10 messaggi all'ora per mittente;
4. salva il messaggio nella tabella "messaggi";
5. invia un'email al proprietario con tutti i campi, tramite Resend;
6. reindirizza il visitatore a una pagina di ringraziamento (quella del modulo o una predefinita).
Nella pagina di ogni modulo mostra il codice HTML pronto da copiare, con la chiave già inserita e il pulsante "Copia".
4. Pannello dei messaggi
Nella pagina di un modulo mostra i messaggi ricevuti, dal più recente, con ricerca, segno "letto/da leggere" e pulsante per esportare tutto in CSV. Impostazioni del modulo: email di destinazione (anche più di una), oggetto dell'email, pagina di ringraziamento e domini autorizzati a usare il modulo.
Mostra in alto il contatore dei messaggi del mese rispetto al limite del piano.
5. Pagamenti con Stripe
Aggiungi un abbonamento a 5 € al mese. Gratis l'utente ha: 100 messaggi al mese e un solo modulo. Con il piano Pro sblocca: messaggi e moduli illimitati, allegati, notifiche su Telegram e nessun marchio nell'email.
A pagamento riuscito il webhook porta l'utente al piano Pro; se l'abbonamento viene disdetto o non rinnovato lo riporta al piano gratuito.
Per la sessione di pagamento e il webhook segui la guida "Pagamenti con Stripe" di Fattibile.
6. Controlli specifici di questo progetto
Oltre ai controlli della guida "Controllo finale", verifica in particolare:
1. Antispam: invia 20 messaggi di seguito dallo stesso indirizzo e un messaggio con il campo nascosto compilato: devono essere bloccati.
Correggi quello che non va e scrivimi cosa hai cambiato.
Dettagli tecnici
Tabelle del database
Moduli e messaggi ricevuti. I messaggi li scrive solo la Edge Function.
create table moduli (
id uuid primary key default gen_random_uuid(),
utente_id uuid not null references auth.users(id) on delete cascade,
chiave uuid not null unique default gen_random_uuid(),
nome text not null,
email_destinazione text not null,
pagina_grazie text,
creato_il timestamptz not null default now()
);
create table messaggi (
id uuid primary key default gen_random_uuid(),
modulo_id uuid not null references moduli(id) on delete cascade,
dati jsonb not null,
mittente_hash text not null, -- IP cifrato, mai salvato in chiaro
letto boolean not null default false,
creato_il timestamptz not null default now()
);
alter table moduli enable row level security;
create policy "le mie righe" on moduli
for all using (auth.uid() = utente_id) with check (auth.uid() = utente_id);
alter table messaggi enable row level security;
create policy "messaggi dei miei moduli" on messaggi
for select using (exists (
select 1 from moduli m where m.id = modulo_id and m.utente_id = auth.uid()
));La Edge Function che riceve i moduli
Riceve il modulo da qualsiasi sito, blocca robot e abusi, salva il messaggio e manda l'email. Va pubblicata con --no-verify-jwt perché è pubblica.
// supabase/functions/invia/index.ts
import { createClient } from "npm:@supabase/supabase-js";
const db = createClient(Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!);
async function hash(testo: string) {
const dati = new TextEncoder().encode(testo + Deno.env.get("SALE_SEGRETO"));
const h = await crypto.subtle.digest("SHA-256", dati);
return [...new Uint8Array(h)].map((b) => b.toString(16).padStart(2, "0")).join("");
}
Deno.serve(async (req) => {
const form = await req.formData();
const chiave = String(form.get("chiave") ?? "");
if (form.get("_sito_web")) return new Response("ok"); // campo nascosto: è un robot
const { data: modulo } = await db.from("moduli").select("*").eq("chiave", chiave).single();
if (!modulo) return new Response("Modulo non trovato", { status: 404 });
const mittente = await hash(req.headers.get("x-forwarded-for") ?? "sconosciuto");
const unOraFa = new Date(Date.now() - 3_600_000).toISOString();
const { count } = await db.from("messaggi").select("id", { count: "exact", head: true })
.eq("mittente_hash", mittente).gte("creato_il", unOraFa);
if ((count ?? 0) >= 10) return new Response("Troppi messaggi, riprova più tardi", { status: 429 });
const dati = Object.fromEntries([...form.entries()].filter(([k]) => !k.startsWith("_") && k !== "chiave"));
await db.from("messaggi").insert({ modulo_id: modulo.id, dati, mittente_hash: mittente });
await fetch("https://api.resend.com/emails", {
method: "POST",
headers: { Authorization: `Bearer ${Deno.env.get("RESEND_API_KEY")}`, "Content-Type": "application/json" },
body: JSON.stringify({
from: "Modulino <avvisi@tuodominio.it>",
to: modulo.email_destinazione,
subject: `Nuovo messaggio da ${modulo.nome}`,
text: Object.entries(dati).map(([k, v]) => `${k}: ${v}`).join("\n"),
}),
});
return Response.redirect(modulo.pagina_grazie ?? "https://tuodominio.it/grazie", 303);
});Il codice da incollare nel sito
Il modulo che il cliente copia nel suo sito. Il campo nascosto "_sito_web" serve a riconoscere i robot.
<form action="https://TUO-PROGETTO.supabase.co/functions/v1/invia" method="POST">
<input type="hidden" name="chiave" value="LA-CHIAVE-DEL-MODULO">
<!-- Trappola per robot: le persone non la vedono -->
<input type="text" name="_sito_web" style="display:none" tabindex="-1" autocomplete="off">
<label>Nome <input name="nome" required></label>
<label>Email <input name="email" type="email" required></label>
<label>Messaggio <textarea name="messaggio" required></textarea></label>
<button type="submit">Invia</button>
</form>
Pezzi comuni a tutti i progetti
Glossario
- JSONB
- Un tipo di colonna che contiene dati strutturati flessibili, come un piccolo documento.
- CSV
- Un file di testo con i dati separati da virgole, che si apre con Excel.