Addresses the 3 Important issues + 4 follow-ups flagged by the
final code review subagent.
## Important fixes
- **getNextRef pagination** (cloudflare-worker/hubspot-proxy.js):
previously used HubSpot search with limit:100 without pagination,
causing reference-number collisions once contact count exceeded
100 (= search results don't guarantee ordering by ref). Now
paginates through all results via paging.next.after cursor to
find the true numeric maximum. ~10 API calls for 1000 contacts.
- **Turnstile reset after Worker calls** (js/form-handler.js):
Cloudflare Turnstile tokens are single-use server-side. Without
explicit reset, a re-submit would 403 silently from siteverify.
New helper resetTurnstile() clears window.turnstileToken and
calls window.turnstile.reset() to force a fresh challenge. Called
after both requestVerification and sendWelcomeBack flows.
- **notifyDuplicateViaFormspree removed** (js/form-handler.js):
was actively POSTing returning customers' name+email to Formspree
(FORMSPREE_ID='mojrvokp' = real endpoint, sentinel guard never
triggered). PII leak to a third-party service inconsistent with
the new Resend-only architecture. Function + call site removed.
## Dead code purge
- form-handler.js: removed generateRefNumber (= dead, no callers),
submitToHubSpot, submitToFormspree, notifyDuplicateViaFormspree
functions. Removed constants HUBSPOT_PORTAL_ID, HUBSPOT_FORM_GUID,
FORMSPREE_ID, EMAILJS_PUBLIC_KEY, EMAILJS_SERVICE_ID,
EMAILJS_TEMPLATE_ID, EMAILJS_TEMPLATE_WELCOME_BACK. Removed
emailjs.init() block. Net -111 lines.
- contact.html + confirmation.html: removed <script> tag loading
EmailJS browser SDK from jsDelivr (~30KB gzipped per page).
- cloudflare-worker/hubspot-proxy.js: removed unused HUBSPOT_PORTAL_ID
+ HUBSPOT_FORM_GUID constants. Removed listSubscriptions and
subscribe action handlers (= 0 callers in frontend, debug-only,
reachable by unauthenticated POST without Turnstile guard).
- cloudflare-worker/DEPLOIEMENT.md: KV ID note updated to reflect
the actual ID in wrangler.toml.
## Net diff
-116 lines (= 92 added, 208 removed across 4 files).
Refs: post-cutover polish, addresses code review 2026-05-07.
Architecture finale (Option A choisie) :
1. User submit form → contact créé en HubSpot avec reference_client
2. HubSpot envoie l'email de double opt-in (sans la ref ni l'adresse Paris)
3. User clique 'Confirmer' → HubSpot met hs_emailconfirmationstatus = CONFIRMED
4. Cron Cloudflare (toutes les 5 min) :
- Liste les contacts CONFIRMED + créés après le cutoff
- Filtre via Cloudflare KV (welcomed:<email>) pour idempotence
- Envoie le welcome email via EmailJS REST API avec :
• firstname
• reference_client
• paris_address (depuis env var PARIS_DEPOT_ADDRESS)
- Marque envoyé dans KV avec TTL 1 an
Protection :
- L'adresse du dépôt Paris ne quitte JAMAIS Cloudflare/EmailJS
- Elle n'arrive au client que dans le mail de bienvenue post-opt-in
- Bots qui n'ont pas un vrai email ne peuvent pas valider → ne reçoivent rien
- Anti-spam et anti-cartons-vides blindé
Ajout d'une action 'triggerWelcomeQueue' pour debug/manual run.
Doc complète dans cloudflare-worker/DEPLOIEMENT.md (étapes 1 à 6).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>