126a74cddb
- Clarify that STRIPE_SECRET_KEY is required from step 2 - Document --profile dev as the recommended local startup command - Replace manual stripe listen instructions with docker-compose service - Add Stripe test cards and payment verification checklist - Fix docker compose restart → --force-recreate in webhook setup steps - Add troubleshooting entries for PENDING orders and .env not reloading
467 lines
13 KiB
Markdown
467 lines
13 KiB
Markdown
# E-commerce Platform
|
||
|
||
Piattaforma e-commerce containerizzata, avviabile con un singolo comando.
|
||
|
||
**Stack:** Next.js 14 · PostgreSQL 16 · Prisma · Stripe · Caddy · Docker Compose
|
||
|
||
---
|
||
|
||
## Avvio in locale (test su localhost)
|
||
|
||
### Prerequisiti
|
||
|
||
- [Docker Desktop](https://docs.docker.com/get-docker/) installato e avviato
|
||
- Porta **80** libera (nessun altro web server in esecuzione)
|
||
- Account [Stripe](https://stripe.com) gratuito (necessario per i pagamenti)
|
||
|
||
### 1. Clona il repository
|
||
|
||
```bash
|
||
git clone <url-repository>
|
||
cd ecommerce-platform
|
||
```
|
||
|
||
### 2. Crea il file `.env`
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
```
|
||
|
||
Apri `.env` e inserisci la tua chiave Stripe test (gratuita, dalla [Stripe Dashboard → API keys](https://dashboard.stripe.com/test/apikeys)):
|
||
|
||
```env
|
||
STRIPE_SECRET_KEY=sk_test_la_tua_chiave
|
||
```
|
||
|
||
Il resto dei valori è già preconfigurato per localhost e non va modificato.
|
||
|
||
### 3. Avvia la piattaforma
|
||
|
||
**Senza pagamenti** (admin, catalogo, email):
|
||
|
||
```bash
|
||
docker compose up -d
|
||
```
|
||
|
||
**Con pagamenti e webhook Stripe attivi** (consigliato):
|
||
|
||
```bash
|
||
docker compose --profile dev up -d
|
||
```
|
||
|
||
Il primo avvio richiede **5–10 minuti**: Docker scarica le immagini, installa le dipendenze npm, compila Next.js, esegue le migrazioni e crea l'utente admin.
|
||
|
||
Segui il progresso con:
|
||
|
||
```bash
|
||
docker compose logs -f app
|
||
```
|
||
|
||
La piattaforma è pronta quando vedi:
|
||
|
||
```
|
||
✓ Ready in 91ms
|
||
```
|
||
|
||
### 4. Apri nel browser
|
||
|
||
| URL | Cosa trovi |
|
||
|-----|------------|
|
||
| http://localhost | Sito pubblico (vetrina) |
|
||
| http://localhost/admin | Dashboard amministratore |
|
||
| http://localhost:8025 | Mailpit — cattura le email di test |
|
||
|
||
### 5. Primo accesso admin
|
||
|
||
Vai su **http://localhost/admin** e accedi con:
|
||
|
||
```
|
||
Email: admin@example.com
|
||
Password: Admin1234!test
|
||
```
|
||
|
||
Al primo accesso il sistema ti obbliga a cambiare la password.
|
||
|
||
**Requisiti password:** minimo 12 caratteri, almeno una maiuscola, una minuscola, un numero e un simbolo.
|
||
|
||
### 6. Configura il negozio (ordine consigliato)
|
||
|
||
1. **Product Types** → crea un tipo di prodotto (es. "Prodotto") con gli attributi che vuoi
|
||
2. **Categories** → crea le categorie
|
||
3. **Products** → crea un prodotto, assegna tipo e categoria, impostalo su **Published**
|
||
4. Apri http://localhost → il prodotto appare in homepage
|
||
|
||
### 7. Setup webhook Stripe (una tantum per macchina)
|
||
|
||
Il profilo `dev` include un container `stripe-cli` che riceve i webhook da Stripe e li inoltra all'app. Senza di esso, dopo il pagamento l'ordine resta in `PENDING` e nessuna email viene inviata.
|
||
|
||
**Al primo avvio con `--profile dev`, recupera il webhook secret dai log:**
|
||
|
||
```bash
|
||
docker compose logs stripe-cli | grep "webhook signing secret"
|
||
```
|
||
|
||
Copia il valore `whsec_...` nel `.env`:
|
||
|
||
```env
|
||
STRIPE_WEBHOOK_SECRET=whsec_abc123...
|
||
```
|
||
|
||
Poi ricrea il container app per applicare la variabile (`restart` non ricarica il `.env`):
|
||
|
||
```bash
|
||
docker compose up -d --force-recreate app
|
||
```
|
||
|
||
Da questo momento `docker compose --profile dev up -d` avvia tutto inclusi i webhook — non serve altro.
|
||
|
||
### 8. Testa i pagamenti
|
||
|
||
**Carte di test Stripe:**
|
||
|
||
| Carta | Risultato |
|
||
|-------|-----------|
|
||
| `4242 4242 4242 4242` | Pagamento riuscito |
|
||
| `4000 0000 0000 0002` | Carta rifiutata |
|
||
| `4000 0025 0000 3155` | Richiede autenticazione 3D Secure |
|
||
|
||
Scadenza: qualsiasi data futura · CVC: qualsiasi 3 cifre
|
||
|
||
**Verifica che tutto funzioni dopo un pagamento:**
|
||
|
||
```bash
|
||
docker compose logs stripe-cli --tail=20
|
||
# deve mostrare [200] per checkout.session.completed
|
||
```
|
||
|
||
1. L'ordine nel pannello admin passa da `PENDING` a `PAID`
|
||
2. L'email di conferma appare su http://localhost:8025
|
||
|
||
### 9. Ferma la piattaforma
|
||
|
||
```bash
|
||
docker compose --profile dev down # ferma tutto (con stripe-cli), i dati restano
|
||
docker compose down -v # ferma e cancella anche il database
|
||
```
|
||
|
||
---
|
||
|
||
## Differenze tra localhost e produzione
|
||
|
||
Quattro cose cambiano quando si passa da localhost a un dominio reale.
|
||
|
||
### 1 — Dominio e HTTPS
|
||
|
||
Modificare **una sola riga** nel `Caddyfile`:
|
||
|
||
```
|
||
# localhost
|
||
localhost { ... }
|
||
|
||
# produzione
|
||
tuodominio.com { ... }
|
||
```
|
||
|
||
Caddy ottiene e rinnova automaticamente il certificato HTTPS tramite Let's Encrypt. Non serve nessuna configurazione SSL manuale.
|
||
|
||
### 2 — Variabili d'ambiente
|
||
|
||
Cinque variabili da aggiornare nel `.env`:
|
||
|
||
| Variabile | Localhost | Produzione |
|
||
|-----------|-----------|------------|
|
||
| `APP_URL` | `http://localhost` | `https://tuodominio.com` |
|
||
| `AUTH_SECRET` | qualsiasi stringa | `openssl rand -hex 32` |
|
||
| `STRIPE_SECRET_KEY` | `sk_test_...` | `sk_live_...` |
|
||
| `STRIPE_WEBHOOK_SECRET` | dal container stripe-cli | dal dashboard Stripe |
|
||
| `SMTP_*` | Mailpit locale (porta 8025) | provider reale (Resend, Mailgun, SendGrid…) |
|
||
|
||
### 3 — Server
|
||
|
||
Serve un VPS Linux con Docker installato (DigitalOcean, Hetzner, OVH, ecc.) e il record DNS del dominio puntato all'IP del server. Il comando di avvio è identico: `docker compose up -d` (senza `--profile dev`).
|
||
|
||
### 4 — Backup
|
||
|
||
In locale i dati si perdono con `docker compose down -v`. In produzione è necessario un backup automatico del database (vedi sezione [Backup database](#backup-database)) e conservare il volume `uploads` che contiene le immagini dei prodotti.
|
||
|
||
---
|
||
|
||
## Deploy in produzione
|
||
|
||
### Prerequisiti
|
||
|
||
- Server Linux con Docker e Docker Compose v2 (qualsiasi VPS o cloud VM)
|
||
- Dominio con record **A** puntato all'IP del server
|
||
- Porte **80** e **443** aperte nel firewall del server
|
||
- Account [Stripe](https://stripe.com) (per i pagamenti reali)
|
||
- Account SMTP (per le email: Mailgun, Resend, SendGrid, ecc.)
|
||
|
||
### 1. Clona il repository sul server
|
||
|
||
```bash
|
||
git clone <url-repository>
|
||
cd ecommerce-platform
|
||
```
|
||
|
||
### 2. Crea e configura `.env`
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
nano .env
|
||
```
|
||
|
||
Modifica questi valori:
|
||
|
||
```env
|
||
# URL pubblico del sito — con https://
|
||
APP_URL=https://tuodominio.com
|
||
|
||
# Genera un segreto casuale: openssl rand -hex 32
|
||
AUTH_SECRET=incolla_qui_il_risultato_di_openssl
|
||
|
||
# Credenziali del primo admin — cambia subito dopo il primo accesso
|
||
INITIAL_ADMIN_EMAIL=admin@tuodominio.com
|
||
INITIAL_ADMIN_PASSWORD=UnaPasswordSicura1!
|
||
|
||
# Stripe — usa le chiavi live (non sk_test_)
|
||
STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
|
||
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxx
|
||
|
||
# SMTP reale per le email
|
||
SMTP_HOST=smtp.tuoprovider.com
|
||
SMTP_PORT=587
|
||
SMTP_USER=user@tuodominio.com
|
||
SMTP_PASSWORD=la_tua_password_smtp
|
||
SMTP_FROM=noreply@tuodominio.com
|
||
```
|
||
|
||
> `DATABASE_URL` non va modificato: usa già il nome del container Docker corretto.
|
||
|
||
### 3. Genera `AUTH_SECRET`
|
||
|
||
```bash
|
||
openssl rand -hex 32
|
||
```
|
||
|
||
Copia l'output nel campo `AUTH_SECRET` nel file `.env`.
|
||
|
||
### 4. Configura il dominio nel Caddyfile
|
||
|
||
Apri `Caddyfile` e sostituisci `localhost` con il tuo dominio:
|
||
|
||
```
|
||
tuodominio.com {
|
||
handle /uploads/* {
|
||
root * /srv
|
||
file_server
|
||
}
|
||
encode gzip zstd
|
||
reverse_proxy app:3000
|
||
}
|
||
```
|
||
|
||
### 5. Avvia
|
||
|
||
```bash
|
||
docker compose up -d
|
||
```
|
||
|
||
Attendi che l'app sia pronta:
|
||
|
||
```bash
|
||
docker compose logs -f app
|
||
# attendi: ✓ Ready in ...ms
|
||
```
|
||
|
||
Il sito è live su `https://tuodominio.com`.
|
||
|
||
### 6. Configura il webhook Stripe
|
||
|
||
Nel [dashboard Stripe → Webhooks](https://dashboard.stripe.com/webhooks), aggiungi un endpoint:
|
||
|
||
```
|
||
URL endpoint: https://tuodominio.com/api/webhooks/stripe
|
||
|
||
Eventi da ascoltare:
|
||
- checkout.session.completed
|
||
- payment_intent.succeeded
|
||
- payment_intent.payment_failed
|
||
```
|
||
|
||
Copia il **Signing secret** (`whsec_...`) nel `.env` come `STRIPE_WEBHOOK_SECRET`, poi ricrea il container per applicarlo:
|
||
|
||
```bash
|
||
docker compose up -d --force-recreate app
|
||
```
|
||
|
||
### 7. Primo accesso e configurazione negozio
|
||
|
||
1. Vai su `https://tuodominio.com/admin`
|
||
2. Accedi con le credenziali del `.env`
|
||
3. Cambia la password quando richiesto
|
||
4. Configura il negozio in questo ordine:
|
||
- **Settings** → nome negozio, logo, colori
|
||
- **Product Types** → tipi di prodotto con attributi personalizzati
|
||
- **Categories** → categorie prodotto
|
||
- **Products** → aggiungi i tuoi prodotti
|
||
- **Admin Users** → aggiungi altri amministratori se necessario
|
||
|
||
---
|
||
|
||
## Porte di rete
|
||
|
||
### Porte da aprire nel firewall del server (produzione)
|
||
|
||
| Porta | Protocollo | Servizio | Perché è necessaria |
|
||
|-------|-----------|---------|---------------------|
|
||
| **80** | TCP | Caddy (HTTP) | Redirect automatico HTTP → HTTPS e rinnovo certificati Let's Encrypt (ACME challenge) |
|
||
| **443** | TCP | Caddy (HTTPS) | Traffico web principale — sito pubblico e pannello admin |
|
||
|
||
> Solo queste due porte devono essere aperte verso l'esterno. Tutto il resto è interno a Docker.
|
||
|
||
### Porte interne (rete Docker — non esporre al pubblico)
|
||
|
||
| Porta | Servizio | Note |
|
||
|-------|---------|------|
|
||
| **3000** | Next.js (app) | Accessibile solo da Caddy tramite `reverse_proxy app:3000` |
|
||
| **5432** | PostgreSQL (db) | Accessibile solo dall'app tramite `DATABASE_URL` |
|
||
| **1025** | Mailpit SMTP | Accessibile solo dall'app per l'invio email |
|
||
|
||
### Porta solo per sviluppo locale (non aprire in produzione)
|
||
|
||
| Porta | Servizio | Note |
|
||
|-------|---------|------|
|
||
| **8025** | Mailpit UI | Interfaccia web per ispezionare le email di test — non deve mai essere esposta in produzione |
|
||
|
||
### Riepilogo comandi firewall (UFW — Ubuntu/Debian)
|
||
|
||
```bash
|
||
sudo ufw allow 80/tcp
|
||
sudo ufw allow 443/tcp
|
||
sudo ufw deny 8025/tcp
|
||
sudo ufw enable
|
||
sudo ufw status
|
||
```
|
||
|
||
---
|
||
|
||
## Aggiornamenti
|
||
|
||
```bash
|
||
git pull
|
||
docker compose build app
|
||
docker compose up -d
|
||
```
|
||
|
||
Le migrazioni del database vengono applicate automaticamente all'avvio.
|
||
|
||
---
|
||
|
||
## Backup database
|
||
|
||
**Dump manuale:**
|
||
|
||
```bash
|
||
docker compose exec db pg_dump -U ecommerce ecommerce > backup_$(date +%Y%m%d_%H%M%S).sql
|
||
```
|
||
|
||
**Ripristino:**
|
||
|
||
```bash
|
||
cat backup_XXXXXXXX_XXXXXX.sql | docker compose exec -T db psql -U ecommerce ecommerce
|
||
```
|
||
|
||
**Backup automatico giornaliero** (aggiungi al crontab del server con `crontab -e`):
|
||
|
||
```bash
|
||
0 3 * * * cd /percorso/ecommerce-platform && docker compose exec -T db pg_dump -U ecommerce ecommerce | gzip > /backups/ecommerce_$(date +\%Y\%m\%d).sql.gz
|
||
```
|
||
|
||
---
|
||
|
||
## Problemi comuni
|
||
|
||
**Il build impiega molto tempo al primo avvio**
|
||
È normale. npm install + compilazione Next.js richiedono 5–10 minuti la prima volta. I build successivi sono molto più veloci grazie alla cache Docker.
|
||
|
||
**http://localhost non risponde (502 Bad Gateway)**
|
||
L'app è ancora in fase di avvio. Aspetta qualche secondo e ricarica. Controlla lo stato con:
|
||
```bash
|
||
docker compose logs -f app
|
||
```
|
||
|
||
**Errore "port 80 already in use"**
|
||
Un altro servizio usa la porta 80 (Apache, Nginx, ecc.). Fermalo o cambia la porta nel `docker-compose.yml`.
|
||
|
||
**L'ordine resta in PENDING dopo il pagamento**
|
||
Il container `stripe-cli` non è in esecuzione o `STRIPE_WEBHOOK_SECRET` non è configurato. Verifica:
|
||
```bash
|
||
docker compose --profile dev ps # stripe-cli deve essere "running"
|
||
docker compose logs stripe-cli --tail=10
|
||
```
|
||
Se il `whsec_...` manca nel `.env`, segui lo step 7 del setup locale.
|
||
|
||
**Le variabili del `.env` non vengono applicate dopo la modifica**
|
||
`docker compose restart` non ricarica il `.env`. Usa sempre:
|
||
```bash
|
||
docker compose up -d --force-recreate app
|
||
```
|
||
|
||
**Dimentico la password admin**
|
||
Resetta direttamente nel database:
|
||
```bash
|
||
# genera hash bcrypt per la nuova password
|
||
docker compose exec app node -e "
|
||
const bcrypt = require('bcryptjs');
|
||
bcrypt.hash('NuovaPassword1!', 12).then(h => console.log(h));
|
||
"
|
||
# aggiorna nel db
|
||
docker compose exec db psql -U ecommerce ecommerce -c \
|
||
"UPDATE \"User\" SET \"passwordHash\"='HASH_QUI', \"mustChangePassword\"=true WHERE email='admin@example.com';"
|
||
```
|
||
|
||
---
|
||
|
||
## Struttura del progetto
|
||
|
||
```
|
||
ecommerce-platform/
|
||
├── docker-compose.yml Orchestrazione: db, app, caddy, mailpit, stripe-cli (profilo dev)
|
||
├── Caddyfile Reverse proxy — modifica qui il dominio
|
||
├── .env Variabili d'ambiente (non committare)
|
||
├── .env.example Template da copiare
|
||
└── app/
|
||
├── Dockerfile Build multi-stage Next.js
|
||
├── entrypoint.sh Sequenza avvio: migrazioni → admin → server
|
||
├── prisma/
|
||
│ └── schema.prisma Schema database completo
|
||
├── scripts/
|
||
│ └── bootstrap-admin.ts Crea il primo OWNER se non esiste
|
||
└── src/
|
||
├── app/ Pagine Next.js (App Router)
|
||
│ ├── admin/ Dashboard admin (protetta per ruolo)
|
||
│ ├── api/ API routes (auth, prodotti, ordini, webhook)
|
||
│ └── (storefront) Homepage, catalogo, carrello, checkout
|
||
├── components/ Componenti React riutilizzabili
|
||
└── lib/ Auth, Prisma client, Stripe, email
|
||
```
|
||
|
||
---
|
||
|
||
## Variabili d'ambiente
|
||
|
||
| Variabile | Descrizione | Locale | Produzione |
|
||
|-----------|-------------|:------:|:----------:|
|
||
| `APP_URL` | URL pubblico del sito | `http://localhost` | `https://tuodominio.com` |
|
||
| `DATABASE_URL` | Connessione PostgreSQL | invariato | invariato |
|
||
| `AUTH_SECRET` | Segreto sessioni (min 32 char) | qualsiasi | `openssl rand -hex 32` |
|
||
| `INITIAL_ADMIN_EMAIL` | Email primo admin | qualsiasi | la tua email |
|
||
| `INITIAL_ADMIN_PASSWORD` | Password primo admin | qualsiasi | sicura |
|
||
| `STRIPE_SECRET_KEY` | Chiave Stripe | `sk_test_...` | `sk_live_...` |
|
||
| `STRIPE_WEBHOOK_SECRET` | Segreto webhook Stripe | dal container stripe-cli | dal dashboard Stripe |
|
||
| `SMTP_HOST` | Server SMTP | `mailpit` | provider reale |
|
||
| `SMTP_PORT` | Porta SMTP | `1025` | `587` |
|
||
| `SMTP_USER` | Utente SMTP | vuoto | obbligatorio |
|
||
| `SMTP_PASSWORD` | Password SMTP | vuoto | obbligatorio |
|
||
| `SMTP_FROM` | Mittente email | qualsiasi | `noreply@tuodominio.com` |
|