cdp-edge 2.2.1 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,9 @@ Toda a sua análise deve ser baseada na infraestrutura nativa da Cloudflare.
26
26
  | Página de obrigado | `Purchase_Success` | 🔴 crítico |
27
27
  | **Preço Formatado (R$, $, €)** | `value_extraction` (Atrelado ao CTA) | 🔴 crítico |
28
28
  | **Campo de Formulário Oculto (CSS hide/opacity 0)** | `honeypot_field` | 🔴 crítico |
29
+ | **[IMÓVEIS] Mapa / botão "Ver localização" / iframe Google Maps** | `FindLocation` | 🔴 crítico imobiliário |
30
+ | **[IMÓVEIS] Simulador de financiamento / parcelas / FGTS / Caixa** | `CustomizeProduct` | 🔴 crítico imobiliário |
31
+ | **[IMÓVEIS] Botão "Favoritar" / ícone de coração / "Salvar imóvel"** | `AddToWishlist` | 🟡 essencial imobiliário |
29
32
  | **Aparecimento de Botão Atrasado (Timer)** | `pitch_seen` | 🟡 essencial |
30
33
  | **Classe de Erro CSS (.error, .invalid)** | `form_error_detected` | 🟡 essencial |
31
34
  | **Micro-Evento: Rage Click** | `rage_click` | 🟡 essencial |
@@ -39,8 +42,9 @@ Toda a sua análise deve ser baseada na infraestrutura nativa da Cloudflare.
39
42
  Antes de mapear elementos, identifique o **modelo de negócio** da página para ativar protocolos específicos:
40
43
 
41
44
  1. **Lançamento Imobiliário**:
42
- * Sinais: Galeria de fotos de imóveis, botões WhatsApp, formulário curto, mapas.
43
- * Protocolo: Ativar `geolocation` e `lead_lock_whatsapp`.
45
+ * Sinais: Galeria de fotos de imóveis, botões WhatsApp, formulário curto, mapas, simulador de financiamento, botão favoritar, calendário de agendamento, tour 360°.
46
+ * Protocolo: Ativar `geolocation`, `lead_lock_whatsapp`, `real_estate_events`.
47
+ * Eventos obrigatórios extras: `FindLocation` (mapa/localização), `CustomizeProduct` (simulação financiamento), `AddToWishlist` (favoritar), `Schedule` (agendamento de visita), `Contact` (WhatsApp/telefone), `video_25/50/75/complete` (tour virtual/VSL).
44
48
  2. **Página de Vendas (Infoproduto)**:
45
49
  * Sinais: Botões Hotmart/Kiwify/Ticto, Vídeo (VSL), Depoimentos, Preço.
46
50
  * Protocolo: Ativar `checkout_passthrough` e `vsl_retention`.
@@ -0,0 +1,344 @@
1
+ # Modelo: Lançamento Imobiliário (Cloudflare Native)
2
+
3
+ Template para páginas de lançamento imobiliário com formulário de interesse, galeria, mapa/localização, simulador de financiamento, vídeo de tour, calendário de agendamento e botão WhatsApp.
4
+
5
+ ---
6
+
7
+ ## 🏗️ ARQUITETURA TÉCNICA (Quantum Tier)
8
+
9
+ ```
10
+ Browser (cdpTrack.js)
11
+ ├─ PageView → ao carregar
12
+ ├─ ViewContent → ao entrar na galeria
13
+ ├─ FindLocation → ao interagir com mapa/localização
14
+ ├─ video_25/50/75/complete → ao assistir tour virtual
15
+ ├─ CustomizeProduct → ao usar simulador de financiamento
16
+ ├─ AddToWishlist → ao favoritar imóvel
17
+ ├─ Contact → ao clicar em WhatsApp/telefone
18
+ ├─ Schedule → ao confirmar agendamento de visita
19
+ └─ Lead → ao enviar formulário (PII completo)
20
+
21
+
22
+ Cloudflare Worker (same-domain /track)
23
+ ├─ Fraud Gate
24
+ ├─ LTV Prediction (Granite 4.0 Micro) + score por eventType
25
+ ├─ D1: upsertProfile, identity graph, distanceKm
26
+ └─ CAPI dispatch: Meta v22.0 + GA4 + TikTok v1.3
27
+ ```
28
+
29
+ ---
30
+
31
+ ## 📘 MAPA DE EVENTOS
32
+
33
+ | Evento | Gatilho | Sinal para as plataformas |
34
+ |---|---|---|
35
+ | `PageView` | Carregamento | Topo do funil |
36
+ | `ViewContent` | Entra na galeria de fotos | Interesse no produto |
37
+ | `FindLocation` | Clica em mapa / "Como chegar" / abre localização | Intenção de visita física — +10 LTV |
38
+ | `video_25` / `video_50` / `video_75` / `video_complete` | Tour virtual / VSL em % assistido | Engajamento profundo |
39
+ | `CustomizeProduct` | Usa simulador de financiamento/parcelas/FGTS | Intenção de compra máxima — +15 LTV |
40
+ | `AddToWishlist` | Clica em "Favoritar" / coração | Interesse persistente — +8 LTV |
41
+ | `Contact` | Clica em WhatsApp ou telefone | Alta intenção de contato |
42
+ | `Schedule` | Confirma agendamento de visita | Conversão de visita |
43
+ | `Lead` | Envia formulário (nome/email/telefone) | Conversão principal com PII |
44
+
45
+ ---
46
+
47
+ ## 🛠️ PASSO 1: CONFIGURAÇÃO DO SDK
48
+
49
+ ### 1.1 Header
50
+ ```html
51
+ <script src="/js/cdpTrack.js" async></script>
52
+ <script>
53
+ window.cdpConfig = {
54
+ metaId: 'SEU_PIXEL_ID',
55
+ ttId: 'SEU_TIKTOK_ID',
56
+ trackEndpoint: '/track'
57
+ };
58
+ </script>
59
+ ```
60
+
61
+ ---
62
+
63
+ ## 🛠️ PASSO 2: EVENTOS DE COMPORTAMENTO
64
+
65
+ ### 2.1 PageView (automático via SDK)
66
+ O SDK dispara automaticamente. Não precisa de código adicional.
67
+
68
+ ---
69
+
70
+ ### 2.2 ViewContent — Galeria de fotos
71
+ ```javascript
72
+ // Dispara quando usuário abre/rola a galeria de imagens
73
+ document.querySelector('.galeria-imovel, [data-section="gallery"]')?.addEventListener('click', () => {
74
+ cdpTrack.track('ViewContent', {
75
+ contentName: 'Galeria — [NOME DO EMPREENDIMENTO]',
76
+ contentCategory: 'imovel',
77
+ funnel_stage: 'gallery_view',
78
+ });
79
+ });
80
+ ```
81
+
82
+ ---
83
+
84
+ ### 2.3 FindLocation — Mapa / Localização
85
+ ```javascript
86
+ // Dispara quando usuário clica no mapa, "Como chegar" ou "Ver localização"
87
+ document.querySelectorAll(
88
+ 'a[href*="maps"], a[href*="waze"], [data-section="localizacao"], #mapa, .btn-localizacao, .btn-como-chegar'
89
+ ).forEach(el => {
90
+ el.addEventListener('click', () => {
91
+ cdpTrack.track('FindLocation', {
92
+ contentName: 'Localização — [NOME DO EMPREENDIMENTO]',
93
+ contentCategory: 'imovel',
94
+ funnel_stage: 'map_view',
95
+ // Coordenadas do empreendimento (preencher):
96
+ property_lat: -23.5505, // latitude
97
+ property_lng: -46.6333, // longitude
98
+ });
99
+ });
100
+ });
101
+ ```
102
+
103
+ ---
104
+
105
+ ### 2.4 Vídeo / Tour Virtual
106
+ ```javascript
107
+ // Para vídeo HTML5 nativo:
108
+ const video = document.querySelector('video#tour-virtual, video.tour-360');
109
+ if (video) {
110
+ const fired = new Set();
111
+ video.addEventListener('timeupdate', () => {
112
+ const pct = Math.floor((video.currentTime / video.duration) * 100);
113
+ if (pct >= 25 && !fired.has(25)) {
114
+ fired.add(25);
115
+ cdpTrack.track('video_25', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
116
+ }
117
+ if (pct >= 50 && !fired.has(50)) {
118
+ fired.add(50);
119
+ cdpTrack.track('video_50', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
120
+ }
121
+ if (pct >= 75 && !fired.has(75)) {
122
+ fired.add(75);
123
+ cdpTrack.track('video_75', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
124
+ }
125
+ });
126
+ video.addEventListener('ended', () => {
127
+ cdpTrack.track('video_complete', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
128
+ });
129
+ }
130
+
131
+ // Para YouTube embed (via YouTube API):
132
+ // Adicionar ?enablejsapi=1 na URL do iframe e usar onStateChange
133
+ // Ver: https://developers.google.com/youtube/iframe_api_reference
134
+ ```
135
+
136
+ ---
137
+
138
+ ### 2.5 CustomizeProduct — Simulador de Financiamento
139
+ ```javascript
140
+ // Dispara quando usuário interage com o simulador de parcelas/FGTS/Caixa
141
+ const simulador = document.querySelector(
142
+ '#simulador, .simulador-financiamento, [data-section="simulacao"], form.simulador'
143
+ );
144
+
145
+ if (simulador) {
146
+ // Dispara ao 1º interact (foco em qualquer campo do simulador)
147
+ let simuladorFired = false;
148
+ simulador.addEventListener('focusin', () => {
149
+ if (simuladorFired) return;
150
+ simuladorFired = true;
151
+ cdpTrack.track('CustomizeProduct', {
152
+ contentName: 'Simulador de Financiamento — [NOME DO EMPREENDIMENTO]',
153
+ contentCategory: 'imovel',
154
+ funnel_stage: 'financing_simulation',
155
+ intentionLevel: 'comprador',
156
+ });
157
+ });
158
+
159
+ // Dispara também ao clicar em "Simular" / "Calcular"
160
+ simulador.querySelector('button[type="submit"], .btn-simular, .btn-calcular')?.addEventListener('click', () => {
161
+ const valorImovel = simulador.querySelector('input[name="valor"], #valor-imovel')?.value;
162
+ cdpTrack.track('CustomizeProduct', {
163
+ contentName: 'Simulação Concluída — [NOME DO EMPREENDIMENTO]',
164
+ contentCategory: 'imovel',
165
+ funnel_stage: 'financing_simulation',
166
+ intentionLevel: 'comprador',
167
+ value: valorImovel ? parseFloat(valorImovel.replace(/\D/g, '')) : undefined,
168
+ currency: 'BRL',
169
+ });
170
+ });
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ### 2.6 AddToWishlist — Favoritar Imóvel
177
+ ```javascript
178
+ // Dispara quando usuário clica em favoritar / ícone de coração
179
+ document.querySelectorAll('.btn-favoritar, .icon-heart, [data-action="favoritar"], .favorito').forEach(el => {
180
+ el.addEventListener('click', () => {
181
+ cdpTrack.track('AddToWishlist', {
182
+ contentName: '[NOME DO EMPREENDIMENTO]',
183
+ contentCategory: 'imovel',
184
+ funnel_stage: 'wishlist',
185
+ });
186
+ });
187
+ });
188
+ ```
189
+
190
+ ---
191
+
192
+ ### 2.7 Contact — WhatsApp / Telefone
193
+ ```javascript
194
+ // WhatsApp
195
+ document.querySelectorAll('a[href*="wa.me"], a[href*="whatsapp.com"], .btn-whatsapp').forEach(el => {
196
+ el.addEventListener('click', () => {
197
+ cdpTrack.track('Contact', {
198
+ contentName: 'WhatsApp — [NOME DO EMPREENDIMENTO]',
199
+ contentCategory: 'imovel_whatsapp',
200
+ funnel_stage: 'whatsapp_click',
201
+ intentionLevel: 'comprador',
202
+ });
203
+ });
204
+ });
205
+
206
+ // Telefone
207
+ document.querySelectorAll('a[href^="tel:"], .btn-ligar, .btn-telefone').forEach(el => {
208
+ el.addEventListener('click', () => {
209
+ cdpTrack.track('Contact', {
210
+ contentName: 'Telefone — [NOME DO EMPREENDIMENTO]',
211
+ contentCategory: 'imovel_telefone',
212
+ funnel_stage: 'phone_click',
213
+ intentionLevel: 'comprador',
214
+ });
215
+ });
216
+ });
217
+ ```
218
+
219
+ ---
220
+
221
+ ### 2.8 Schedule — Calendário de Agendamento de Visita
222
+ ```javascript
223
+ // Dispara quando usuário CONFIRMA o agendamento (não ao abrir o calendário)
224
+ // Adaptar ao provider: Calendly, Google Calendar, formulário próprio
225
+
226
+ // Exemplo com Calendly:
227
+ window.addEventListener('message', (e) => {
228
+ if (e.data?.event === 'calendly.event_scheduled') {
229
+ cdpTrack.track('Schedule', {
230
+ contentName: 'Visita Agendada — [NOME DO EMPREENDIMENTO]',
231
+ contentCategory: 'imovel',
232
+ funnel_stage: 'schedule_confirmed',
233
+ intentionLevel: 'comprador',
234
+ // Dados do lead do Calendly (se disponíveis via payload):
235
+ email: e.data?.payload?.invitee?.email,
236
+ firstName: e.data?.payload?.invitee?.first_name,
237
+ });
238
+ }
239
+ });
240
+
241
+ // Exemplo com formulário próprio de agendamento:
242
+ document.querySelector('#form-agendamento')?.addEventListener('submit', async (e) => {
243
+ e.preventDefault();
244
+ const data = new FormData(e.target);
245
+ await cdpTrack.track('Schedule', {
246
+ contentName: 'Visita Agendada — [NOME DO EMPREENDIMENTO]',
247
+ contentCategory: 'imovel',
248
+ funnel_stage: 'schedule_confirmed',
249
+ intentionLevel: 'comprador',
250
+ email: data.get('email'),
251
+ phone: data.get('phone'),
252
+ firstName: data.get('nome')?.split(' ')[0],
253
+ });
254
+ e.target.submit();
255
+ });
256
+ ```
257
+
258
+ ---
259
+
260
+ ### 2.9 Lead — Formulário Principal de Interesse
261
+ ```javascript
262
+ document.querySelector('#form-interesse, #form-lead, form.form-contato')?.addEventListener('submit', async (e) => {
263
+ e.preventDefault();
264
+
265
+ await cdpTrack.track('Lead', {
266
+ // PII — enviados hasheados pelo Worker
267
+ email: e.target.email?.value?.trim(),
268
+ phone: e.target.phone?.value?.trim() || e.target.telefone?.value?.trim(),
269
+ firstName: e.target.nome?.value?.split(' ')[0]?.trim(),
270
+ lastName: e.target.nome?.value?.split(' ').slice(1).join(' ')?.trim(),
271
+
272
+ // Contexto
273
+ contentName: '[NOME DO EMPREENDIMENTO]',
274
+ contentCategory: 'imovel',
275
+ intentionLevel: 'comprador',
276
+ funnel_stage: 'lead_form',
277
+
278
+ // UTMs capturados pelo SDK automaticamente
279
+ // Coordenadas do imóvel para distância geoespacial no Worker:
280
+ property_lat: -23.5505,
281
+ property_lng: -46.6333,
282
+ });
283
+
284
+ // Redirecionar para obrigado
285
+ window.location.href = '/obrigado';
286
+ });
287
+ ```
288
+
289
+ ---
290
+
291
+ ## 📊 LTV por Evento — O que o Worker calcula
292
+
293
+ | Evento | Bonus LTV Score | Multiplicador Valor |
294
+ |---|---|---|
295
+ | `Lead` (utm_source=facebook, intention=comprador) | ~65–80 pts | 3.5× → **High** |
296
+ | `CustomizeProduct` | +15 pts automático | Score sobe para High |
297
+ | `FindLocation` | +10 pts automático | Puxa Medium → High |
298
+ | `AddToWishlist` | +8 pts automático | Sinal de retargeting |
299
+ | `Schedule` (visita confirmada) | intention=comprador → +20 pts | Máximo High |
300
+ | `Contact` (WhatsApp) | Sem LTV (evento de sinal) | — |
301
+
302
+ ---
303
+
304
+ ## 🔄 FLUXO COMPLETO DO LEAD IMOBILIÁRIO
305
+
306
+ ```
307
+ Usuário chega na landing
308
+
309
+ ├─ PageView → sinal de alcance
310
+ ├─ ViewContent (galeria) → interesse qualificado
311
+ ├─ video_50 (tour) → engajamento profundo
312
+ ├─ FindLocation (mapa) → intenção de visita física (+10 LTV)
313
+ ├─ CustomizeProduct (simulador) → intenção máxima (+15 LTV)
314
+ ├─ Contact (WhatsApp) → ação de contato
315
+ ├─ Schedule (agendamento) → visita confirmada
316
+ └─ Lead (formulário) → conversão principal
317
+
318
+
319
+ Worker: LTV score ~75-90 → High → valor 3.5× injetado
320
+
321
+
322
+ Meta CAPI + GA4 + TikTok recebem Lead com value=R$689
323
+
324
+
325
+ Algoritmo de Meta aprende a buscar mais leads de alto valor
326
+ ```
327
+
328
+ ---
329
+
330
+ ## 📋 CHECKLIST DE IMPLEMENTAÇÃO
331
+
332
+ - [ ] SDK `cdpTrack.js` carregando no `<head>`
333
+ - [ ] `PageView` disparando (automático)
334
+ - [ ] `ViewContent` na galeria
335
+ - [ ] `FindLocation` no mapa e botão "Como chegar"
336
+ - [ ] Vídeo/tour instrumentado (`video_25`, `video_75`, `video_complete`)
337
+ - [ ] `CustomizeProduct` no simulador de financiamento
338
+ - [ ] `AddToWishlist` no botão de favoritar
339
+ - [ ] `Contact` nos botões de WhatsApp e telefone
340
+ - [ ] `Schedule` no calendário (Calendly ou formulário próprio)
341
+ - [ ] `Lead` no formulário principal com PII e `property_lat/lng`
342
+ - [ ] Verificar `/health` retornando `d1: ok, kv: ok, ai: ok`
343
+ - [ ] Testar evento `Lead` no Meta Events Manager → Test Events
344
+ - [ ] Confirmar LTV na resposta JSON do `/track` (`class: "High"`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-edge",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "description": "CDP Edge - Quantum Tracking - Sistema multi-agente para tracking digital Cloudflare Native (Workers + D1)",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -100,6 +100,12 @@ export async function predictLtv(env, payload, request, customSystemPrompt = nul
100
100
  if (payload.phone) score += 4;
101
101
  if (payload.firstName) score += 2;
102
102
 
103
+ // 5b. Tipo de evento imobiliário (0–15) — sinais de intenção de compra física
104
+ const evType = (payload.eventType || '').toLowerCase();
105
+ if (evType === 'customizeproduct') score += 15; // simulação de financiamento → intenção máxima
106
+ else if (evType === 'findlocation') score += 10; // viu mapa/localização → visita física iminente
107
+ else if (evType === 'addtowishlist') score += 8; // favoritou → interesse persistente
108
+
103
109
  // 6. Proximidade ao imóvel físico (0–15) — apenas quando distância calculada
104
110
  const distKm = parseFloat(payload.distanceKm ?? payload.user_distance_km ?? -1);
105
111
  if (distKm >= 0) {
@@ -86,14 +86,16 @@ export const VALID_EVENT_NAMES = new Set([
86
86
  'AddToCart','CompleteRegistration','Contact','Schedule',
87
87
  'StartTrial','Subscribe','SubmitApplication','Search',
88
88
  'video_start','video_25','video_50','video_75','video_complete',
89
+ // Imóveis — intenção de visita física, financiamento e favoritar
90
+ 'FindLocation','CustomizeProduct','AddToWishlist',
89
91
  ]);
90
92
 
91
93
  // ── Taxonomia de funil (funnel_stage → profundidade semântica) ────────────────
92
94
  // Fonte de verdade para interpretar funnel_stage em qualquer ponto do sistema.
93
95
  export const FUNNEL_TAXONOMY = {
94
- top: ['scroll_50', 'time_30s', 'page_view', 'gallery_view'],
95
- mid: ['map_view', 'gallery_click', 'price_hover', 'time_3min'],
96
- bottom: ['route_click', 'whatsapp_click', 'cta_hover'],
96
+ top: ['scroll_50', 'time_30s', 'page_view', 'gallery_view', 'AddToWishlist'],
97
+ mid: ['map_view', 'gallery_click', 'price_hover', 'time_3min', 'FindLocation'],
98
+ bottom: ['route_click', 'whatsapp_click', 'cta_hover', 'CustomizeProduct'],
97
99
  conversion: ['schedule_confirmed', 'lead_form', 'purchase', 'visit_booked'],
98
100
  };
99
101
 
@@ -1867,6 +1867,12 @@ async function predictLtv(env, payload, request, customSystemPrompt = null) {
1867
1867
  if (payload.phone) score += 4;
1868
1868
  if (payload.firstName) score += 2;
1869
1869
 
1870
+ // 5b. Tipo de evento imobiliário (0–15) — sinais de intenção de compra física
1871
+ const evType = (payload.eventType || '').toLowerCase();
1872
+ if (evType === 'customizeproduct') score += 15; // simulação de financiamento → intenção máxima
1873
+ else if (evType === 'findlocation') score += 10; // viu mapa/localização → visita física iminente
1874
+ else if (evType === 'addtowishlist') score += 8; // favoritou → interesse persistente
1875
+
1870
1876
  score = Math.min(100, score);
1871
1877
 
1872
1878
  // Classificação
@@ -1901,6 +1907,7 @@ async function predictLtv(env, payload, request, customSystemPrompt = null) {
1901
1907
  country,
1902
1908
  has_email: !!payload.email,
1903
1909
  has_phone: !!payload.phone,
1910
+ event_type: payload.eventType || null,
1904
1911
  })},
1905
1912
  ];
1906
1913
  const aiRes = await env.AI.run('@cf/ibm-granite/granite-4.0-h-micro', { messages: prompt, max_tokens: 32 });
@@ -3968,7 +3975,9 @@ export default {
3968
3975
  'PageView','ViewContent','Lead','Purchase','InitiateCheckout',
3969
3976
  'AddToCart','CompleteRegistration','Contact','Schedule',
3970
3977
  'StartTrial','Subscribe','SubmitApplication','Search',
3971
- 'video_start','video_25','video_50','video_75','video_complete'
3978
+ 'video_start','video_25','video_50','video_75','video_complete',
3979
+ // Imóveis — intenção de visita física, financiamento e favoritar
3980
+ 'FindLocation','CustomizeProduct','AddToWishlist',
3972
3981
  ]);
3973
3982
  const STR_FIELDS = ['email','phone','firstName','lastName','city','state','zip','userId',
3974
3983
  'utmSource','utmMedium','utmCampaign','utmContent','utmTerm',
@@ -4075,8 +4084,9 @@ export default {
4075
4084
  // ── LTV Prediction (+ A/B Testing de Prompts) ────────────────────────────
4076
4085
  // Lead, Contact, Schedule: sem valor monetário real → injetar LTV preditivo
4077
4086
  // Isso treina os algoritmos de Meta/TikTok a priorizar leads de alto valor
4078
- const LTV_EVENTS = ['Lead', 'Contact', 'Schedule', 'CompleteRegistration'];
4087
+ const LTV_EVENTS = ['Lead', 'Contact', 'Schedule', 'CompleteRegistration', 'FindLocation', 'CustomizeProduct', 'AddToWishlist'];
4079
4088
  if (LTV_EVENTS.includes(eventName) && !payload.value) {
4089
+ payload.eventType = eventName; // expõe ao predictLtv para scoring por tipo de evento
4080
4090
  // A/B Testing: busca variação ativa (usa KV cache — ~0ms de latência extra)
4081
4091
  const abVariation = await getLtvAbVariation(env);
4082
4092
  const ltv = await predictLtv(env, payload, request, abVariation?.system_prompt || null);
@@ -4,6 +4,7 @@ name = "server-edge-tracker"
4
4
  main = "index.js"
5
5
  compatibility_date = "2025-01-01"
6
6
  compatibility_flags = ["nodejs_compat"]
7
+ workers_dev = true
7
8
 
8
9
  # ── Worker Routes — same-domain tracking (imune a bloqueios) ─────────────────
9
10
  # Substituir SEU_DOMINIO pelo domínio do cliente antes do deploy
@@ -0,0 +1,344 @@
1
+ # Modelo: Lançamento Imobiliário (Cloudflare Native)
2
+
3
+ Template para páginas de lançamento imobiliário com formulário de interesse, galeria, mapa/localização, simulador de financiamento, vídeo de tour, calendário de agendamento e botão WhatsApp.
4
+
5
+ ---
6
+
7
+ ## 🏗️ ARQUITETURA TÉCNICA (Quantum Tier)
8
+
9
+ ```
10
+ Browser (cdpTrack.js)
11
+ ├─ PageView → ao carregar
12
+ ├─ ViewContent → ao entrar na galeria
13
+ ├─ FindLocation → ao interagir com mapa/localização
14
+ ├─ video_25/50/75/complete → ao assistir tour virtual
15
+ ├─ CustomizeProduct → ao usar simulador de financiamento
16
+ ├─ AddToWishlist → ao favoritar imóvel
17
+ ├─ Contact → ao clicar em WhatsApp/telefone
18
+ ├─ Schedule → ao confirmar agendamento de visita
19
+ └─ Lead → ao enviar formulário (PII completo)
20
+
21
+
22
+ Cloudflare Worker (same-domain /track)
23
+ ├─ Fraud Gate
24
+ ├─ LTV Prediction (Granite 4.0 Micro) + score por eventType
25
+ ├─ D1: upsertProfile, identity graph, distanceKm
26
+ └─ CAPI dispatch: Meta v22.0 + GA4 + TikTok v1.3
27
+ ```
28
+
29
+ ---
30
+
31
+ ## 📘 MAPA DE EVENTOS
32
+
33
+ | Evento | Gatilho | Sinal para as plataformas |
34
+ |---|---|---|
35
+ | `PageView` | Carregamento | Topo do funil |
36
+ | `ViewContent` | Entra na galeria de fotos | Interesse no produto |
37
+ | `FindLocation` | Clica em mapa / "Como chegar" / abre localização | Intenção de visita física — +10 LTV |
38
+ | `video_25` / `video_50` / `video_75` / `video_complete` | Tour virtual / VSL em % assistido | Engajamento profundo |
39
+ | `CustomizeProduct` | Usa simulador de financiamento/parcelas/FGTS | Intenção de compra máxima — +15 LTV |
40
+ | `AddToWishlist` | Clica em "Favoritar" / coração | Interesse persistente — +8 LTV |
41
+ | `Contact` | Clica em WhatsApp ou telefone | Alta intenção de contato |
42
+ | `Schedule` | Confirma agendamento de visita | Conversão de visita |
43
+ | `Lead` | Envia formulário (nome/email/telefone) | Conversão principal com PII |
44
+
45
+ ---
46
+
47
+ ## 🛠️ PASSO 1: CONFIGURAÇÃO DO SDK
48
+
49
+ ### 1.1 Header
50
+ ```html
51
+ <script src="/js/cdpTrack.js" async></script>
52
+ <script>
53
+ window.cdpConfig = {
54
+ metaId: 'SEU_PIXEL_ID',
55
+ ttId: 'SEU_TIKTOK_ID',
56
+ trackEndpoint: '/track'
57
+ };
58
+ </script>
59
+ ```
60
+
61
+ ---
62
+
63
+ ## 🛠️ PASSO 2: EVENTOS DE COMPORTAMENTO
64
+
65
+ ### 2.1 PageView (automático via SDK)
66
+ O SDK dispara automaticamente. Não precisa de código adicional.
67
+
68
+ ---
69
+
70
+ ### 2.2 ViewContent — Galeria de fotos
71
+ ```javascript
72
+ // Dispara quando usuário abre/rola a galeria de imagens
73
+ document.querySelector('.galeria-imovel, [data-section="gallery"]')?.addEventListener('click', () => {
74
+ cdpTrack.track('ViewContent', {
75
+ contentName: 'Galeria — [NOME DO EMPREENDIMENTO]',
76
+ contentCategory: 'imovel',
77
+ funnel_stage: 'gallery_view',
78
+ });
79
+ });
80
+ ```
81
+
82
+ ---
83
+
84
+ ### 2.3 FindLocation — Mapa / Localização
85
+ ```javascript
86
+ // Dispara quando usuário clica no mapa, "Como chegar" ou "Ver localização"
87
+ document.querySelectorAll(
88
+ 'a[href*="maps"], a[href*="waze"], [data-section="localizacao"], #mapa, .btn-localizacao, .btn-como-chegar'
89
+ ).forEach(el => {
90
+ el.addEventListener('click', () => {
91
+ cdpTrack.track('FindLocation', {
92
+ contentName: 'Localização — [NOME DO EMPREENDIMENTO]',
93
+ contentCategory: 'imovel',
94
+ funnel_stage: 'map_view',
95
+ // Coordenadas do empreendimento (preencher):
96
+ property_lat: -23.5505, // latitude
97
+ property_lng: -46.6333, // longitude
98
+ });
99
+ });
100
+ });
101
+ ```
102
+
103
+ ---
104
+
105
+ ### 2.4 Vídeo / Tour Virtual
106
+ ```javascript
107
+ // Para vídeo HTML5 nativo:
108
+ const video = document.querySelector('video#tour-virtual, video.tour-360');
109
+ if (video) {
110
+ const fired = new Set();
111
+ video.addEventListener('timeupdate', () => {
112
+ const pct = Math.floor((video.currentTime / video.duration) * 100);
113
+ if (pct >= 25 && !fired.has(25)) {
114
+ fired.add(25);
115
+ cdpTrack.track('video_25', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
116
+ }
117
+ if (pct >= 50 && !fired.has(50)) {
118
+ fired.add(50);
119
+ cdpTrack.track('video_50', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
120
+ }
121
+ if (pct >= 75 && !fired.has(75)) {
122
+ fired.add(75);
123
+ cdpTrack.track('video_75', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
124
+ }
125
+ });
126
+ video.addEventListener('ended', () => {
127
+ cdpTrack.track('video_complete', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
128
+ });
129
+ }
130
+
131
+ // Para YouTube embed (via YouTube API):
132
+ // Adicionar ?enablejsapi=1 na URL do iframe e usar onStateChange
133
+ // Ver: https://developers.google.com/youtube/iframe_api_reference
134
+ ```
135
+
136
+ ---
137
+
138
+ ### 2.5 CustomizeProduct — Simulador de Financiamento
139
+ ```javascript
140
+ // Dispara quando usuário interage com o simulador de parcelas/FGTS/Caixa
141
+ const simulador = document.querySelector(
142
+ '#simulador, .simulador-financiamento, [data-section="simulacao"], form.simulador'
143
+ );
144
+
145
+ if (simulador) {
146
+ // Dispara ao 1º interact (foco em qualquer campo do simulador)
147
+ let simuladorFired = false;
148
+ simulador.addEventListener('focusin', () => {
149
+ if (simuladorFired) return;
150
+ simuladorFired = true;
151
+ cdpTrack.track('CustomizeProduct', {
152
+ contentName: 'Simulador de Financiamento — [NOME DO EMPREENDIMENTO]',
153
+ contentCategory: 'imovel',
154
+ funnel_stage: 'financing_simulation',
155
+ intentionLevel: 'comprador',
156
+ });
157
+ });
158
+
159
+ // Dispara também ao clicar em "Simular" / "Calcular"
160
+ simulador.querySelector('button[type="submit"], .btn-simular, .btn-calcular')?.addEventListener('click', () => {
161
+ const valorImovel = simulador.querySelector('input[name="valor"], #valor-imovel')?.value;
162
+ cdpTrack.track('CustomizeProduct', {
163
+ contentName: 'Simulação Concluída — [NOME DO EMPREENDIMENTO]',
164
+ contentCategory: 'imovel',
165
+ funnel_stage: 'financing_simulation',
166
+ intentionLevel: 'comprador',
167
+ value: valorImovel ? parseFloat(valorImovel.replace(/\D/g, '')) : undefined,
168
+ currency: 'BRL',
169
+ });
170
+ });
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ### 2.6 AddToWishlist — Favoritar Imóvel
177
+ ```javascript
178
+ // Dispara quando usuário clica em favoritar / ícone de coração
179
+ document.querySelectorAll('.btn-favoritar, .icon-heart, [data-action="favoritar"], .favorito').forEach(el => {
180
+ el.addEventListener('click', () => {
181
+ cdpTrack.track('AddToWishlist', {
182
+ contentName: '[NOME DO EMPREENDIMENTO]',
183
+ contentCategory: 'imovel',
184
+ funnel_stage: 'wishlist',
185
+ });
186
+ });
187
+ });
188
+ ```
189
+
190
+ ---
191
+
192
+ ### 2.7 Contact — WhatsApp / Telefone
193
+ ```javascript
194
+ // WhatsApp
195
+ document.querySelectorAll('a[href*="wa.me"], a[href*="whatsapp.com"], .btn-whatsapp').forEach(el => {
196
+ el.addEventListener('click', () => {
197
+ cdpTrack.track('Contact', {
198
+ contentName: 'WhatsApp — [NOME DO EMPREENDIMENTO]',
199
+ contentCategory: 'imovel_whatsapp',
200
+ funnel_stage: 'whatsapp_click',
201
+ intentionLevel: 'comprador',
202
+ });
203
+ });
204
+ });
205
+
206
+ // Telefone
207
+ document.querySelectorAll('a[href^="tel:"], .btn-ligar, .btn-telefone').forEach(el => {
208
+ el.addEventListener('click', () => {
209
+ cdpTrack.track('Contact', {
210
+ contentName: 'Telefone — [NOME DO EMPREENDIMENTO]',
211
+ contentCategory: 'imovel_telefone',
212
+ funnel_stage: 'phone_click',
213
+ intentionLevel: 'comprador',
214
+ });
215
+ });
216
+ });
217
+ ```
218
+
219
+ ---
220
+
221
+ ### 2.8 Schedule — Calendário de Agendamento de Visita
222
+ ```javascript
223
+ // Dispara quando usuário CONFIRMA o agendamento (não ao abrir o calendário)
224
+ // Adaptar ao provider: Calendly, Google Calendar, formulário próprio
225
+
226
+ // Exemplo com Calendly:
227
+ window.addEventListener('message', (e) => {
228
+ if (e.data?.event === 'calendly.event_scheduled') {
229
+ cdpTrack.track('Schedule', {
230
+ contentName: 'Visita Agendada — [NOME DO EMPREENDIMENTO]',
231
+ contentCategory: 'imovel',
232
+ funnel_stage: 'schedule_confirmed',
233
+ intentionLevel: 'comprador',
234
+ // Dados do lead do Calendly (se disponíveis via payload):
235
+ email: e.data?.payload?.invitee?.email,
236
+ firstName: e.data?.payload?.invitee?.first_name,
237
+ });
238
+ }
239
+ });
240
+
241
+ // Exemplo com formulário próprio de agendamento:
242
+ document.querySelector('#form-agendamento')?.addEventListener('submit', async (e) => {
243
+ e.preventDefault();
244
+ const data = new FormData(e.target);
245
+ await cdpTrack.track('Schedule', {
246
+ contentName: 'Visita Agendada — [NOME DO EMPREENDIMENTO]',
247
+ contentCategory: 'imovel',
248
+ funnel_stage: 'schedule_confirmed',
249
+ intentionLevel: 'comprador',
250
+ email: data.get('email'),
251
+ phone: data.get('phone'),
252
+ firstName: data.get('nome')?.split(' ')[0],
253
+ });
254
+ e.target.submit();
255
+ });
256
+ ```
257
+
258
+ ---
259
+
260
+ ### 2.9 Lead — Formulário Principal de Interesse
261
+ ```javascript
262
+ document.querySelector('#form-interesse, #form-lead, form.form-contato')?.addEventListener('submit', async (e) => {
263
+ e.preventDefault();
264
+
265
+ await cdpTrack.track('Lead', {
266
+ // PII — enviados hasheados pelo Worker
267
+ email: e.target.email?.value?.trim(),
268
+ phone: e.target.phone?.value?.trim() || e.target.telefone?.value?.trim(),
269
+ firstName: e.target.nome?.value?.split(' ')[0]?.trim(),
270
+ lastName: e.target.nome?.value?.split(' ').slice(1).join(' ')?.trim(),
271
+
272
+ // Contexto
273
+ contentName: '[NOME DO EMPREENDIMENTO]',
274
+ contentCategory: 'imovel',
275
+ intentionLevel: 'comprador',
276
+ funnel_stage: 'lead_form',
277
+
278
+ // UTMs capturados pelo SDK automaticamente
279
+ // Coordenadas do imóvel para distância geoespacial no Worker:
280
+ property_lat: -23.5505,
281
+ property_lng: -46.6333,
282
+ });
283
+
284
+ // Redirecionar para obrigado
285
+ window.location.href = '/obrigado';
286
+ });
287
+ ```
288
+
289
+ ---
290
+
291
+ ## 📊 LTV por Evento — O que o Worker calcula
292
+
293
+ | Evento | Bonus LTV Score | Multiplicador Valor |
294
+ |---|---|---|
295
+ | `Lead` (utm_source=facebook, intention=comprador) | ~65–80 pts | 3.5× → **High** |
296
+ | `CustomizeProduct` | +15 pts automático | Score sobe para High |
297
+ | `FindLocation` | +10 pts automático | Puxa Medium → High |
298
+ | `AddToWishlist` | +8 pts automático | Sinal de retargeting |
299
+ | `Schedule` (visita confirmada) | intention=comprador → +20 pts | Máximo High |
300
+ | `Contact` (WhatsApp) | Sem LTV (evento de sinal) | — |
301
+
302
+ ---
303
+
304
+ ## 🔄 FLUXO COMPLETO DO LEAD IMOBILIÁRIO
305
+
306
+ ```
307
+ Usuário chega na landing
308
+
309
+ ├─ PageView → sinal de alcance
310
+ ├─ ViewContent (galeria) → interesse qualificado
311
+ ├─ video_50 (tour) → engajamento profundo
312
+ ├─ FindLocation (mapa) → intenção de visita física (+10 LTV)
313
+ ├─ CustomizeProduct (simulador) → intenção máxima (+15 LTV)
314
+ ├─ Contact (WhatsApp) → ação de contato
315
+ ├─ Schedule (agendamento) → visita confirmada
316
+ └─ Lead (formulário) → conversão principal
317
+
318
+
319
+ Worker: LTV score ~75-90 → High → valor 3.5× injetado
320
+
321
+
322
+ Meta CAPI + GA4 + TikTok recebem Lead com value=R$689
323
+
324
+
325
+ Algoritmo de Meta aprende a buscar mais leads de alto valor
326
+ ```
327
+
328
+ ---
329
+
330
+ ## 📋 CHECKLIST DE IMPLEMENTAÇÃO
331
+
332
+ - [ ] SDK `cdpTrack.js` carregando no `<head>`
333
+ - [ ] `PageView` disparando (automático)
334
+ - [ ] `ViewContent` na galeria
335
+ - [ ] `FindLocation` no mapa e botão "Como chegar"
336
+ - [ ] Vídeo/tour instrumentado (`video_25`, `video_75`, `video_complete`)
337
+ - [ ] `CustomizeProduct` no simulador de financiamento
338
+ - [ ] `AddToWishlist` no botão de favoritar
339
+ - [ ] `Contact` nos botões de WhatsApp e telefone
340
+ - [ ] `Schedule` no calendário (Calendly ou formulário próprio)
341
+ - [ ] `Lead` no formulário principal com PII e `property_lat/lng`
342
+ - [ ] Verificar `/health` retornando `d1: ok, kv: ok, ai: ok`
343
+ - [ ] Testar evento `Lead` no Meta Events Manager → Test Events
344
+ - [ ] Confirmar LTV na resposta JSON do `/track` (`class: "High"`)