From 2b148a8682811eb6207038a33501fed2f8f47c7f Mon Sep 17 00:00:00 2001 From: MVA Global Fret Date: Wed, 6 May 2026 15:54:58 +0200 Subject: [PATCH] Worker: use Forms API for contact creation (no scope required) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Direct CRM API needed crm.objects.contacts.write scope which the existing Personal Access Key doesn't have. Using HubSpot Forms API instead — same endpoint the browser used to call directly, but now called server-side from the Worker after email verification. This means HubSpot contact creation works without granting any additional scopes to the token: anyone can submit a public form. Also updates the welcome email warning to tell the customer to copy the address EXACTLY as shown (the reference is now baked into line 1 of the address) and not to modify anything. Co-Authored-By: Claude Opus 4.7 (1M context) --- cloudflare-worker/hubspot-proxy.js | 79 ++++++++++++++++++------------ 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/cloudflare-worker/hubspot-proxy.js b/cloudflare-worker/hubspot-proxy.js index 3d9bf5a..a159920 100644 --- a/cloudflare-worker/hubspot-proxy.js +++ b/cloudflare-worker/hubspot-proxy.js @@ -61,6 +61,8 @@ const FALLBACK_EMAILJS_SERVICE_ID = 'service_aeamo3x'; const FALLBACK_EMAILJS_TEMPLATE_ID= 'template_s1kr2et'; const HUBSPOT_API = 'https://api.hubapi.com'; +const HUBSPOT_PORTAL_ID = '148163754'; +const HUBSPOT_FORM_GUID = '1d9b75c9-8b60-4966-aa18-4bf503452e9a'; const EMAILJS_API = 'https://api.emailjs.com/api/v1.0/email/send'; const corsHeaders = { @@ -197,36 +199,47 @@ export default { } try { - // 1) Génère la prochaine référence client (MVA-XXX) - const refNumber = await getNextRef(token); + // 1) Récupère la ref existante si le contact est déjà dans HubSpot + // (réinscription après suppression d'un test, ou création via + // l'ancien flow Forms API). Sinon génère la prochaine ref. + let refNumber; + try { + const existing = await searchContactByEmail(token, tokenData.email); + const existingResult = (existing.results || [])[0]; + const existingRef = existingResult?.properties?.reference_client; + refNumber = existingRef || await getNextRef(token); + } catch (_) { + // Si la search échoue (scope manquant, etc.), fallback : génère + // une nouvelle ref. Le Forms API gérera la dédup côté HubSpot. + refNumber = await getNextRef(token); + } - // 2) Crée le contact dans HubSpot avec toutes les propriétés - const createRes = await fetch(`${HUBSPOT_API}/crm/v3/objects/contacts`, { - method: 'POST', - headers: { - 'Content-Type' : 'application/json', - 'Authorization': `Bearer ${token}`, - }, - body: JSON.stringify({ - properties: { - firstname : tokenData.firstname, - lastname : tokenData.lastname, - phone : tokenData.phone, - email : tokenData.email, - address : tokenData.address, - reference_client : refNumber, - }, - }), - }); - if (!createRes.ok) { - const errTxt = await createRes.text(); - // Si l'email existe déjà (409 conflict), on récupère le contact - // existant et on lui envoie quand même son welcome (cas très rare : - // doublon créé via une autre source pendant la fenêtre de 24h) - if (createRes.status === 409) { - throw new Error(`Contact existe déjà dans HubSpot (409). Contactez l'admin.`); + // 2) Soumet via Forms API HubSpot (sans auth, pas besoin de scope + // write). Forms API crée le contact OU met à jour s'il existe. + const formRes = await fetch( + `https://api.hsforms.com/submissions/v3/integration/submit/${HUBSPOT_PORTAL_ID}/${HUBSPOT_FORM_GUID}`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + fields: [ + { name: 'firstname', value: tokenData.firstname || '' }, + { name: 'lastname', value: tokenData.lastname || '' }, + { name: 'phone', value: tokenData.phone || '' }, + { name: 'email', value: tokenData.email }, + { name: 'address', value: tokenData.address || '' }, + { name: 'reference_client', value: refNumber }, + ], + context: { + pageUri : 'https://mva-global-fret.github.io/site-mva-global-fret/contact.html', + pageName: 'Verified signup (MVA Global Fret)', + }, + }), } - throw new Error(`HubSpot create failed ${createRes.status}: ${errTxt}`); + ); + if (!formRes.ok) { + const errTxt = await formRes.text(); + throw new Error(`HubSpot Forms API failed ${formRes.status}: ${errTxt}`); } // 3) Envoie le welcome email avec ref + adresse Paris @@ -637,9 +650,13 @@ async function sendWelcomeViaBrevo(env, contact) { ${parisAddr}
-

- 📌 Sur le colis, indiquez votre numéro de référence ${ref} - juste après le nom du destinataire entre parenthèses. +

+ ⚠️ Important : ne modifiez rien à ces informations. +

+

+ Recopiez l'adresse exactement telle qu'elle est indiquée ci-dessus, + sans rien retirer ni ajouter. Votre numéro de référence ${ref} + fait partie intégrante de l'adresse — c'est ce qui garantit que votre colis nous arrive bien.