Compare commits

...

1 Commits

Author SHA1 Message Date
Serge RAKOTO HARRY-NAIVO
a2dd183b79 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.
2026-05-07 15:10:03 +02:00

View File

@ -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