From a2dd183b797b695455287daeeee9405b75e18274 Mon Sep 17 00:00:00 2001 From: Serge RAKOTO HARRY-NAIVO Date: Thu, 7 May 2026 15:10:03 +0200 Subject: [PATCH] fix(worker): replace Forms API submission with CRM API direct Forms API (api.hsforms.com/submissions/v3/integration/submit/...) can return 200 OK without actually creating a contact in HubSpot \xe2\x80\x94 silent failures triggered by anti-spam filters, form configuration, or whatever state the form GUID was left in. Symptom in production: E2E flow completes (welcome email sent with ref MVA-005), but contact is never created in HubSpot CRM. Reproduced with serge+test1@mind4solutions.com on 2026-05-07. Fix: switch to direct CRM API POST /crm/v3/objects/contacts which is deterministic (= returns 409 if exists, error otherwise). Falls back to PATCH on 409 to handle duplicate-email reinscriptions. Refs: WordPress \xe2\x86\x92 static migration, post-Phase F bugfix. --- cloudflare-worker/hubspot-proxy.js | 70 ++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/cloudflare-worker/hubspot-proxy.js b/cloudflare-worker/hubspot-proxy.js index c3a9bcb..d40eb48 100644 --- a/cloudflare-worker/hubspot-proxy.js +++ b/cloudflare-worker/hubspot-proxy.js @@ -154,32 +154,64 @@ export default { refNumber = await getNextRef(token); } - // 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}`, + // 2) Création directe via CRM API (= more deterministic que Forms API + // qui peut accepter une submission sans réellement créer le contact + // à cause des filtres anti-spam ou de la config du Form HubSpot). + // Requires scope crm.objects.contacts.write. + // En cas de 409 (contact déjà existant), fallback sur PATCH par ID + // pour update les propriétés (= notamment reference_client). + const crmRes = await fetch( + `${HUBSPOT_API}/crm/v3/objects/contacts`, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type' : 'application/json', + 'Authorization': `Bearer ${token}`, + }, 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-globalfret.com/contact.html', - pageName: 'Verified signup (MVA Global Fret)', + properties: { + firstname : tokenData.firstname || '', + lastname : tokenData.lastname || '', + phone : tokenData.phone || '', + email : tokenData.email, + address : tokenData.address || '', + reference_client : refNumber, }, }), } ); - if (!formRes.ok) { - const errTxt = await formRes.text(); - throw new Error(`HubSpot Forms API failed ${formRes.status}: ${errTxt}`); + if (crmRes.status === 409) { + // Contact existe déjà — update via PATCH par ID + const search = await searchContactByEmail(token, tokenData.email); + const existing = (search.results || [])[0]; + if (existing?.id) { + const patchRes = await fetch( + `${HUBSPOT_API}/crm/v3/objects/contacts/${existing.id}`, + { + method: 'PATCH', + headers: { + 'Content-Type' : 'application/json', + 'Authorization': `Bearer ${token}`, + }, + body: JSON.stringify({ + properties: { + firstname : tokenData.firstname || existing.properties?.firstname || '', + lastname : tokenData.lastname || existing.properties?.lastname || '', + phone : tokenData.phone || existing.properties?.phone || '', + address : tokenData.address || '', + reference_client : refNumber, + }, + }), + } + ); + if (!patchRes.ok) { + const errTxt = await patchRes.text(); + throw new Error(`HubSpot CRM patch failed ${patchRes.status}: ${errTxt.slice(0, 200)}`); + } + } + } else if (!crmRes.ok) { + const errTxt = await crmRes.text(); + throw new Error(`HubSpot CRM create failed ${crmRes.status}: ${errTxt.slice(0, 200)}`); } // 3) Envoie le welcome email avec ref + adresse Paris -- 2.45.2