Déploiement
Vercel (recommandé), variables d'environnement, 18 crons QStash, configuration domaine.
Voie A — Assistant guidé (recommandé pour les premiers déploiements)
Si tu n'as jamais déployé un SaaS Next.js sur Vercel + Supabase tout seul, ouvre heartco.fr/setup et suis l'assistant en 5 étapes : création des comptes, copie des clés avec validation live, génération des secrets internes, bouton Deploy pré-formé, création du compte admin. ~30 minutes, aucun terminal requis.
La suite de ce guide couvre la Voie B — déploiement à la main pour les devs qui préfèrent contrôler chaque étape.
Vercel (recommandé)
Étapes de déploiement
-
Créer un projet Vercel et connecter le repository Git
-
Configurer le Framework Preset : Next.js (auto-détecté)
-
Build Command (automatique) :
pnpm build -
Node.js Version : 22.x
-
Configurer les variables d'environnement (voir section suivante)
-
Déployer — Vercel lance automatiquement le build à chaque push
Build settings
Le build nécessite 7 GB de RAM (configuré dans package.json) :
{
"build": "NODE_OPTIONS=--max-old-space-size=7168 next build"
}Prisma sur Vercel
Le binary target rhel-openssl-3.0.x est nécessaire pour l'environnement Vercel :
generator client {
provider = "prisma-client-js"
output = "../generated/prisma"
binaryTargets = ["native", "rhel-openssl-3.0.x"]
}
npx prisma generateest automatiquement exécuté lors dupostinstall.
Variables d'environnement (production)
Obligatoires
| Variable | Description | Exemple |
|---|---|---|
DATABASE_URL | PostgreSQL (pgBouncer) | postgresql://...?pgbouncer=true |
DIRECT_URL | PostgreSQL (direct, migrations) | postgresql://... |
AUTH_SECRET | Secret NextAuth (≥32 chars) | openssl rand -base64 32 |
NEXTAUTH_URL | URL de l'app | https://app.votre-domaine.fr |
NEXT_PUBLIC_APP_URL | URL publique | https://app.votre-domaine.fr |
STRIPE_SECRET_KEY | Clé secrète Stripe | sk_live_xxxx |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Clé publique Stripe | pk_live_xxxx |
STRIPE_WEBHOOK_SECRET | Secret webhook Stripe | whsec_xxxx |
RESEND_API_KEY | Clé API Resend | re_xxxx |
UPSTASH_REDIS_REST_URL | URL Redis (rate limiting) | https://xxxx.upstash.io |
UPSTASH_REDIS_REST_TOKEN | Token Redis | AXxxxx |
TOKEN_ENCRYPTION_KEY | AES-256-GCM (64 hex chars) | openssl rand -hex 32 |
CRON_SECRET | Secret des crons (≥32 chars) | openssl rand -base64 32 |
Stripe Price IDs
Chaque plan/module nécessite ses Price IDs Stripe (monthly + annual) :
# Plans
STRIPE_PRICE_STARTER="price_xxxx"
STRIPE_PRICE_STARTER_ANNUAL="price_xxxx"
STRIPE_PRICE_CORE="price_xxxx"
STRIPE_PRICE_CORE_ANNUAL="price_xxxx"
STRIPE_PRICE_BUSINESS="price_xxxx"
STRIPE_PRICE_BUSINESS_ANNUAL="price_xxxx"
STRIPE_PRICE_ENTERPRISE="price_xxxx"
STRIPE_PRICE_ENTERPRISE_ANNUAL="price_xxxx"
# Utilisateur supplémentaire
STRIPE_PRICE_EXTRA_USER="price_xxxx"
STRIPE_PRICE_EXTRA_USER_ANNUAL="price_xxxx"
# Modules (16 × 2 = 32 variables)
STRIPE_PRICE_MODULE_DEVIS="price_xxxx"
STRIPE_PRICE_MODULE_DEVIS_ANNUAL="price_xxxx"
# ... (voir .env.example pour la liste complète)Optionnelles (mais recommandées)
| Variable | Description |
|---|---|
MISTRAL_API_KEY | Copilote IA (Mistral) |
PUSHER_APP_ID / PUSHER_SECRET / NEXT_PUBLIC_PUSHER_KEY | Temps réel |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | OAuth Google |
MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRET | OAuth Microsoft |
SUPABASE_URL / SUPABASE_SERVICE_KEY | Storage Supabase |
WEBHOOK_SECRET | HMAC webhooks sortants |
ADMIN_CHALLENGE_SECRET | 2FA panel admin |
Crons (tâches planifiées)
HeartCo utilise 18 crons QStash définis dans apps/web/src/lib/qstash-schedules.ts et enregistrés via POST /api/admin/setup-crons (idempotent — appeler une fois après le premier déploiement).
Quotidiens (14 crons)
| Cron | Schedule | Description |
|---|---|---|
/api/cron/check-trial | 09:00 | Expiration des trials + notifs J-1 |
/api/cron/trial-expiry | 06:00 | Downgrade auto FREE à expiration trial |
/api/cron/trial-reminder | 08:00 | Email rappel trial J-3 et J-1 |
/api/cron/demo-reset | 03:00 | Reset données de démo chaque nuit |
/api/cron/expire-quotes | 00:00 | Expiration automatique des devis |
/api/cron/recurring-invoices | 07:00 | Génération des factures récurrentes |
/api/cron/calendar-reminders | 06:00 | Rappels événements calendrier J+1 |
/api/cron/appointment-reminders | 08:00 | Rappels rendez-vous clients |
/api/cron/inactivity-checker | 10:00 | Détection inactivité + séquences re-engagement |
/api/dunning/enroll | 11:00 | Auto-enroll factures en retard dans dunning |
/api/dunning/execute | 14:00 | Exécution des étapes dunning en cours |
/api/dunning/winback | 09:00 | Campagnes win-back factures recouvrées |
/api/recouvrement/cron | 01:00 | Transitions factures SENT/PARTIALLY_PAID → OVERDUE |
/api/crons/signatures-expiry | 02:00 | Expiration signatures PENDING > 7 jours |
Jours ouvrés (1 cron)
| Cron | Schedule | Description |
|---|---|---|
/api/relances/cron | Lun-Ven 09:00 | Envoi des relances factures (jours ouvrés) |
Hebdomadaires (2 crons)
| Cron | Schedule | Description |
|---|---|---|
/api/cron/privilege-audit | Lundi 02:00 | Audit hebdo comptes dormants |
/api/crons/deals-decay | Lundi 04:00 | Décroissance probabilité deals inactifs > 30j |
Mensuel (1 cron)
| Cron | Schedule | Description |
|---|---|---|
/api/cron/reset-usage | 1er du mois 00:00 | Reset mensuel compteurs freemium |
Timeouts (configurés dans apps/web/vercel.json)
| Durée | Routes |
|---|---|
| 30s (défaut Vercel) | invoice send, webhooks Stripe/Resend |
| 60s | relances, demo-reset, dunning, recurring-invoices, privilege-audit |
Configuration domaine
Architecture recommandée
votre-domaine.fr → Landing page / marketing
app.votre-domaine.fr → Dashboard (application SaaS)
Redirections automatiques
HeartCo gère automatiquement dans le middleware :
| Domaine source | Destination |
|---|---|
heartco.ch / www.heartco.ch | 301 → heartco.fr |
heartco.com | 301 → app.heartco.fr |
app.heartco.fr/dashboard/* | Autorisé (application) |
heartco.fr/dashboard | 301 → app.heartco.fr/dashboard |
CORS (app mobile)
Les origines autorisées pour l'app mobile Expo :
// Production
"heartco.fr", "heartco.com", "app.heartco.fr"
// Développement
"localhost", "192.168.*", "10.*", "172.*"Méthodes : GET, POST, DELETE, OPTIONS Headers : Content-Type, Authorization, x-trpc-source
Checklist déploiement
- Toutes les variables obligatoires sont configurées
- Les Price IDs Stripe correspondent à l'environnement (live, pas test)
-
NEXTAUTH_URLpointe vers l'URL de production -
NEXT_PUBLIC_APP_URLest identique - Le webhook Stripe est configuré pour l'URL de production
- Le DNS pointe vers Vercel
- SSL est activé (automatique avec Vercel)
- Les crons fonctionnent (vérifier les logs Vercel)
- Le seed de production est exécuté (si nécessaire)
- Le domaine email (Resend) est vérifié