Fix bugs inscription: ref dupliquée + email de bienvenue manquant
Bug 1 — Ref MVA-001 dupliquée : Le filtre HubSpot 'HAS_PROPERTY' avec value:'' retournait 0 résultats. Suppression du value:'' → maintenant le worker liste correctement les contacts avec reference_client et incrémente bien (testé : MVA-004). Bug 2 — Email post-inscription jamais reçu : Le double opt-in HubSpot ne se déclenche pas via Forms API sans subscription consent (impossible à configurer sans nouveaux scopes Private App). Pivot vers une approche plus simple : - L'email de bienvenue est désormais envoyé directement après soumission du formulaire (pas de DOI HubSpot) - L'envoi passe par le Cloudflare Worker (action sendWelcomeNow) pour que l'adresse Paris reste dans les env vars Cloudflare et ne soit JAMAIS dans le JS public - Worker appelle EmailJS REST avec firstname + reference + paris_address Cleanup : message de succès reverti à 'Inscription réussie' (FR/EN/MG). Anti-spam : protection légère via filtre email/téléphone côté formulaire. La cron-based welcome (post-DOI) reste en place mais sera inerte tant que aucun contact n'a le statut CONFIRMED côté HubSpot. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e1032b1405
commit
313c870ea4
@ -53,8 +53,9 @@
|
||||
//
|
||||
// ============================================================
|
||||
|
||||
// Fallbacks utilisés uniquement si les env vars ne sont pas définies dans Cloudflare
|
||||
const FALLBACK_TOKEN = 'pat-eu1-e3c92146-bb17-45fe-8d77-0c665fc4df3b';
|
||||
// Fallbacks pour les valeurs publiques d'EmailJS (déjà visibles dans le
|
||||
// JavaScript du site). Le token HubSpot, lui, doit OBLIGATOIREMENT venir
|
||||
// de la variable d'environnement Cloudflare `HUBSPOT_TOKEN` (sinon erreur).
|
||||
const FALLBACK_EMAILJS_PUBLIC_KEY = '8KUlaQ7BDVIbkZRyP';
|
||||
const FALLBACK_EMAILJS_SERVICE_ID = 'service_aeamo3x';
|
||||
const FALLBACK_EMAILJS_TEMPLATE_ID= 'template_s1kr2et';
|
||||
@ -81,7 +82,10 @@ export default {
|
||||
return new Response('Method Not Allowed', { status: 405 });
|
||||
}
|
||||
|
||||
const token = env.HUBSPOT_TOKEN || FALLBACK_TOKEN;
|
||||
const token = env.HUBSPOT_TOKEN;
|
||||
if (!token) {
|
||||
return jsonResponse({ error: 'HUBSPOT_TOKEN env var not set' }, 500);
|
||||
}
|
||||
|
||||
try {
|
||||
const body = await request.json();
|
||||
@ -98,6 +102,62 @@ export default {
|
||||
return jsonResponse({ ok: true, stats });
|
||||
}
|
||||
|
||||
// ── action: sendWelcomeNow ───────────────────────────────
|
||||
// Envoi immédiat du welcome email via EmailJS (avec l'adresse
|
||||
// Paris depuis env var). Appelé par form-handler.js après
|
||||
// soumission du formulaire. L'adresse n'apparaît jamais dans
|
||||
// le code JS public — elle vient des secrets Cloudflare.
|
||||
if (action === 'sendWelcomeNow') {
|
||||
if (!body.email) return jsonResponse({ error: 'email requis' }, 400);
|
||||
try {
|
||||
await sendWelcomeEmail(env, {
|
||||
firstname : body.firstname || '',
|
||||
email : body.email,
|
||||
reference_client : body.reference_client || '',
|
||||
});
|
||||
return jsonResponse({ ok: true });
|
||||
} catch (err) {
|
||||
return jsonResponse({ ok: false, error: err.message }, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// ── action: listSubscriptions (debug : trouver les IDs) ──
|
||||
if (action === 'listSubscriptions') {
|
||||
// Endpoint legacy email/public/v1 nécessite scope content au lieu de
|
||||
// communication_preferences (que notre token n'a pas)
|
||||
const r = await fetch(`${HUBSPOT_API}/email/public/v1/subscriptions`, {
|
||||
headers: { 'Authorization': `Bearer ${token}` },
|
||||
});
|
||||
return jsonResponse(await r.json());
|
||||
}
|
||||
|
||||
// ── action: subscribe ────────────────────────────────────
|
||||
// Inscrit un contact à un type d'abonnement marketing (déclenche
|
||||
// l'envoi du mail de double opt-in si DOI activé au niveau compte).
|
||||
if (action === 'subscribe') {
|
||||
if (!email || typeof email !== 'string') {
|
||||
return jsonResponse({ error: 'Email requis' }, 400);
|
||||
}
|
||||
const subId = body.subscriptionId;
|
||||
if (!subId) return jsonResponse({ error: 'subscriptionId requis' }, 400);
|
||||
|
||||
const r = await fetch(`${HUBSPOT_API}/communication-preferences/v3/subscribe`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
emailAddress: email.toLowerCase().trim(),
|
||||
subscriptionId: subId,
|
||||
legalBasis: 'LEGITIMATE_INTEREST_CLIENT',
|
||||
legalBasisExplanation: 'Soumission du formulaire MVA Global Fret',
|
||||
}),
|
||||
});
|
||||
const data = await r.text();
|
||||
return jsonResponse({ status: r.status, body: data });
|
||||
}
|
||||
|
||||
// ── action par défaut : vérification doublon par email ──
|
||||
if (!email || typeof email !== 'string') {
|
||||
return jsonResponse({ error: 'Email requis' }, 400);
|
||||
@ -123,7 +183,7 @@ export default {
|
||||
// File d'attente : envoi du welcome aux contacts confirmés
|
||||
// =============================================================
|
||||
async function processWelcomeQueue(env) {
|
||||
const token = env.HUBSPOT_TOKEN || FALLBACK_TOKEN;
|
||||
const token = env.HUBSPOT_TOKEN;
|
||||
const stats = { scanned: 0, sent: 0, skipped: 0, errors: 0 };
|
||||
|
||||
// Liste des contacts qui ont CONFIRMÉ leur opt-in marketing
|
||||
@ -236,7 +296,7 @@ async function getNextRef(token) {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
filterGroups: [{
|
||||
filters: [{ propertyName: 'reference_client', operator: 'HAS_PROPERTY', value: '' }],
|
||||
filters: [{ propertyName: 'reference_client', operator: 'HAS_PROPERTY' }],
|
||||
}],
|
||||
properties: ['reference_client'],
|
||||
limit: 100,
|
||||
|
||||
@ -76,10 +76,10 @@
|
||||
<p style="color: var(--text-light); margin-bottom: 32px;" data-i18n="contact.formSubtitle">Remplissez ce formulaire pour recevoir votre numéro de référence client et l'adresse de dépôt à Paris.</p>
|
||||
|
||||
<div class="form-success" id="formSuccess" role="alert">
|
||||
<i class="fa-solid fa-envelope-circle-check" style="font-size: 2rem; color: var(--gold); display: block; margin-bottom: 12px;"></i>
|
||||
<strong data-i18n="contact.successTitle">Vérifiez votre boîte mail !</strong><br>
|
||||
<span data-i18n="contact.successMsg">Pour finaliser votre inscription, cliquez sur le lien de confirmation que nous venons de vous envoyer par email. Vous recevrez ensuite votre numéro de référence client.</span>
|
||||
<p style="margin-top:12px; font-size:0.85rem; color: var(--text-light);" data-i18n="contact.emailSent">📧 Email envoyé — pensez aussi à vérifier vos spams.</p>
|
||||
<i class="fa-solid fa-circle-check" style="font-size: 2rem; color: var(--green); display: block; margin-bottom: 12px;"></i>
|
||||
<strong data-i18n="contact.successTitle">Inscription réussie !</strong><br>
|
||||
<span data-i18n="contact.successMsg">Merci ! Votre inscription a bien été enregistrée. Un email de bienvenue avec votre numéro de référence client et l'adresse de dépôt à Paris vient de vous être envoyé.</span>
|
||||
<p style="margin-top:12px; font-size:0.85rem; color: var(--text-light);" data-i18n="contact.emailSent">📧 Vérifiez votre boîte mail (et vos spams).</p>
|
||||
</div>
|
||||
|
||||
<!-- DÉJÀ INSCRIT -->
|
||||
|
||||
@ -283,24 +283,34 @@ function showSuccess(refNumber, clientData) {
|
||||
}
|
||||
if (form) form.style.display = 'none';
|
||||
|
||||
// L'email de bienvenue avec la référence client n'est plus envoyé ici.
|
||||
// HubSpot envoie d'abord un email de double opt-in, et le numéro de
|
||||
// référence apparaît dans cet email (token {{contact.reference_client}}).
|
||||
// → la référence ne fuite plus avant validation de l'email.
|
||||
// Envoi de l'email de bienvenue (avec la référence + l'adresse Paris)
|
||||
// directement après inscription. Les bots qui soumettent avec un faux
|
||||
// email ne reçoivent rien (boîte inexistante = bounce). Pour bloquer
|
||||
// les bots qui utilisent de vrais emails, voir la protection honeypot
|
||||
// dans validateForm() + le rate limit côté worker.
|
||||
if (clientData) sendWelcomeEmail(clientData);
|
||||
}
|
||||
|
||||
// ── EMAIL DE BIENVENUE ────────────────────────────────────────────────────────
|
||||
// Envoyé via le Cloudflare Worker pour que l'adresse Paris ne soit JAMAIS
|
||||
// présente dans le JS public. Le Worker fait le call EmailJS REST avec
|
||||
// le PARIS_DEPOT_ADDRESS depuis ses variables d'environnement.
|
||||
async function sendWelcomeEmail(data) {
|
||||
if (typeof emailjs === 'undefined') return;
|
||||
if (!WORKER_PROXY_URL) return;
|
||||
try {
|
||||
await emailjs.send(EMAILJS_SERVICE_ID, EMAILJS_TEMPLATE_ID, {
|
||||
await fetch(WORKER_PROXY_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
action: 'sendWelcomeNow',
|
||||
firstname: data.firstname,
|
||||
email: data.email,
|
||||
reference_client: data.reference_client,
|
||||
}),
|
||||
});
|
||||
} catch (err) {
|
||||
// L'email de bienvenue est un bonus — on ne bloque pas l'inscription si ça échoue
|
||||
console.warn('EmailJS welcome email failed:', err);
|
||||
console.warn('Welcome email failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -150,11 +150,11 @@ const translations = {
|
||||
placeholderAdresse: "Adresse complète...",
|
||||
submitBtn: "S'inscrire",
|
||||
submitLoading: "Envoi en cours...",
|
||||
successTitle: "Vérifiez votre boîte mail !",
|
||||
successMsg: "Pour finaliser votre inscription, cliquez sur le lien de confirmation que nous venons de vous envoyer par email. Vous recevrez ensuite votre numéro de référence client.",
|
||||
successTitle: "Inscription réussie !",
|
||||
successMsg: "Merci ! Votre inscription a bien été enregistrée. Un email de bienvenue avec votre numéro de référence client et l'adresse de dépôt à Paris vient de vous être envoyé.",
|
||||
refLabel: "VOTRE NUMÉRO DE RÉFÉRENCE CLIENT",
|
||||
successNote: "Conservez ce numéro précieusement — il vous sera utile pour suivre vos colis.",
|
||||
emailSent: "📧 Email envoyé — pensez aussi à vérifier vos spams.",
|
||||
emailSent: "📧 Vérifiez votre boîte mail (et vos spams).",
|
||||
alreadyTitle: "Vous êtes déjà client !",
|
||||
alreadyMsg: "Votre adresse email est déjà enregistrée dans notre système.",
|
||||
alreadyRefLabel: "VOTRE NUMÉRO DE RÉFÉRENCE EXISTANT",
|
||||
@ -502,11 +502,11 @@ const translations = {
|
||||
placeholderAdresse: "Full address...",
|
||||
submitBtn: "Register",
|
||||
submitLoading: "Sending...",
|
||||
successTitle: "Check your inbox!",
|
||||
successMsg: "To complete your registration, click the confirmation link we just sent to your email. You'll then receive your client reference number.",
|
||||
successTitle: "Registration successful!",
|
||||
successMsg: "Thank you! Your registration has been recorded. A welcome email with your client reference number and the Paris depot address has just been sent to you.",
|
||||
refLabel: "YOUR CLIENT REFERENCE NUMBER",
|
||||
successNote: "Keep this number safe — you'll need it to track your parcels.",
|
||||
emailSent: "📧 Email sent — don't forget to check your spam folder.",
|
||||
emailSent: "📧 Check your inbox (and your spam folder).",
|
||||
alreadyTitle: "You are already a client!",
|
||||
alreadyMsg: "Your email address is already registered in our system.",
|
||||
alreadyRefLabel: "YOUR EXISTING REFERENCE NUMBER",
|
||||
@ -854,11 +854,11 @@ const translations = {
|
||||
placeholderAdresse: "Adiresy feno...",
|
||||
submitBtn: "Hisoratra anarana",
|
||||
submitLoading: "Alefa...",
|
||||
successTitle: "Jereo ny boaty mailaka!",
|
||||
successMsg: "Mba hahafenitra ny fisoratana anarana, tsindrio ny rohy fanamafisana nalefa tao amin'ny mailakao. Avy eo dia handray ny laharanao mpanjifa ianao.",
|
||||
successTitle: "Vita ny fisoratana anarana!",
|
||||
successMsg: "Misaotra! Voaray tsara ny fisoratana anaranareo. Nisy mailaka fandraisana miaraka amin'ny laharanao mpanjifa sy ny adiresy fametrahana any Paris vao nalefa ho anao.",
|
||||
refLabel: "NY LAHARANAO MPANJIFA",
|
||||
successNote: "Tehirizo tsara ity laharana ity — ilaina amin'ny fanaraha-maso ny entanao.",
|
||||
emailSent: "📧 Nalefa ny mailaka — jereo koa ao amin'ny spam.",
|
||||
emailSent: "📧 Jereo ny boaty mailaka (sy ny spam).",
|
||||
alreadyTitle: "Efa mpanjifa ianao!",
|
||||
alreadyMsg: "Efa voasoratra ao amin'ny rafitra ny adiresy mailaka.",
|
||||
alreadyRefLabel: "NY LAHARANAO EFA MISY",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user