diff --git a/cloudflare-worker/DEPLOIEMENT.md b/cloudflare-worker/DEPLOIEMENT.md
index cb60e7b..8c031d8 100644
--- a/cloudflare-worker/DEPLOIEMENT.md
+++ b/cloudflare-worker/DEPLOIEMENT.md
@@ -55,7 +55,7 @@ Dans **Paramètres → Stockage et bases de données → Bindings KV** :
3. Namespace : **Créer** un nouveau namespace nommé `mva-welcome-tracker`
4. Sauvegarder
-Si déployé via `wrangler` : mettre à jour `wrangler.toml` avec l'ID du namespace KV créé (remplacer `REPLACE_AT_DEPLOY_TIME`).
+Si déployé via `wrangler` : `wrangler.toml` contient déjà l'ID du namespace KV (`c02656ba22064923ab1c6db06b0f4a56` sur le compte CF `sergemind4s@gmail.com`). Pour un autre compte, recréer le namespace via `wrangler kv namespace create WELCOME_KV` puis remplacer l'ID dans `wrangler.toml`.
### 4. Vérifier le scope HubSpot
diff --git a/cloudflare-worker/hubspot-proxy.js b/cloudflare-worker/hubspot-proxy.js
index a0c4bdd..d8508bd 100644
--- a/cloudflare-worker/hubspot-proxy.js
+++ b/cloudflare-worker/hubspot-proxy.js
@@ -35,8 +35,6 @@
// ============================================================
const HUBSPOT_API = 'https://api.hubapi.com';
-const HUBSPOT_PORTAL_ID = '148163754';
-const HUBSPOT_FORM_GUID = '1d9b75c9-8b60-4966-aa18-4bf503452e9a';
const corsHeaders = {
'Access-Control-Allow-Origin' : '*',
@@ -271,43 +269,6 @@ export default {
}
}
- // ── 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);
@@ -348,32 +309,46 @@ async function searchContactByEmail(token, email) {
}
async function getNextRef(token) {
- const res = await fetch(`${HUBSPOT_API}/crm/v3/objects/contacts/search`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${token}`,
- },
- body: JSON.stringify({
+ // Paginate through ALL HubSpot contacts with `reference_client` property to
+ // find the true numeric maximum. Previous version used `limit: 100` without
+ // pagination — produced collisions once the contact count exceeded 100
+ // because HubSpot search results don't guarantee ordering by ref. With
+ // pagination, we walk the full set: 100 per page × N pages until no more.
+ // For ~1000 contacts = 10 API calls. Acceptable cost given that this runs
+ // once per signup confirmation (= rare path).
+ let maxNum = 0;
+ let after; // undefined on first iteration
+ do {
+ const body = {
filterGroups: [{
filters: [{ propertyName: 'reference_client', operator: 'HAS_PROPERTY' }],
}],
properties: ['reference_client'],
limit: 100,
- }),
- });
- if (!res.ok) {
- throw new Error(`HubSpot search failed: ${res.status}`);
- }
- const data = await res.json();
- let maxNum = 0;
- (data.results || []).forEach(c => {
- const m = (c.properties?.reference_client || '').match(/^MVA-(\d+)$/);
- if (m) {
- const n = parseInt(m[1], 10);
- if (n > maxNum) maxNum = n;
+ };
+ if (after) body.after = after;
+
+ const res = await fetch(`${HUBSPOT_API}/crm/v3/objects/contacts/search`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`,
+ },
+ body: JSON.stringify(body),
+ });
+ if (!res.ok) {
+ throw new Error(`HubSpot search failed: ${res.status}`);
}
- });
+ const data = await res.json();
+ (data.results || []).forEach(c => {
+ const m = (c.properties?.reference_client || '').match(/^MVA-(\d+)$/);
+ if (m) {
+ const n = parseInt(m[1], 10);
+ if (n > maxNum) maxNum = n;
+ }
+ });
+ after = data.paging?.next?.after;
+ } while (after);
return 'MVA-' + String(maxNum + 1).padStart(3, '0');
}
diff --git a/confirmation.html b/confirmation.html
index c3e37ac..dcef293 100644
--- a/confirmation.html
+++ b/confirmation.html
@@ -13,9 +13,6 @@
-
-
-