# Template: Afiliado Sem Landing Page Própria
> Você é afiliado de um produto e envia tráfego direto para o checkout da plataforma
> (Hotmart, Kiwify, Eduzz, Monetizze, CartPanda, Ticto).
> Não tem domínio próprio — o link de afiliado é a URL de destino dos seus anúncios.
> Infraestrutura: Cloudflare Workers + D1 (100% Native)

**Quando usar este modelo:**
- Afiliado que divulga link de produto de terceiros
- Sem landing page: o anúncio aponta direto para `pay.hotmart.com/XXXXX`
- Quer rastrear vendas no Meta/TikTok/Google sem pixel na página do produtor

**O problema central:**
O pixel do Meta está no seu gerenciador, mas o checkout é do produtor — você não tem acesso à página. O produtor te dá um webhook de compra, mas esse webhook não tem fbp/fbc/UTMs porque o pixel nunca rodou na página de checkout.

**A solução:**
Criar uma página de ponte (bridge page) mínima no seu domínio. O usuário passa por ela, o CDP Edge captura todos os dados e injeta o `cdp_uid` no link de checkout. Quando o webhook de compra chegar, o D1 faz o lookup e envia o evento com atribuição completa.

---

## Arquitetura

```
Anúncio Meta/TikTok/Google
        │
        ▼
┌───────────────────────────────┐
│  Bridge Page (seu domínio)    │  ← leva 2s, redireciona auto
│  bridge.seudominio.com        │
│                               │
│  1. Gera cdp_uid              │
│  2. Captura fbclid/ttclid     │
│  3. Salva fbp/fbc/UTMs no D1  │
│  4. Injeta cdp_uid no link    │
│  5. Redireciona para checkout │
└───────────────────────────────┘
        │
        ▼
┌──────────────────────────────────┐
│  Checkout da Plataforma          │
│  pay.hotmart.com/XXXXX?xcod=uid  │
│                                  │
│  Usuário compra                  │
│  Plataforma envia webhook →      │
│    Worker → D1 lookup(uid)       │
│    Meta Purchase CAPI ✅          │
└──────────────────────────────────┘
```

---

## Passo 1 — Bridge Page (index.html)

Crie uma página mínima no seu domínio. Pode ser hospedada no Cloudflare Pages (grátis).

```html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Redirecionando...</title>
  <!-- Pixel Meta browser (para ViewContent + fbp/fbc) -->
  <script>
    !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
    n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
    n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
    t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
    document,'script','https://connect.facebook.net/en_US/fbevents.js');
    fbq('init', 'SEU_PIXEL_ID');
    fbq('track', 'PageView');
  </script>
</head>
<body>
  <p style="font-family:sans-serif;text-align:center;margin-top:40px">
    Aguarde, você está sendo redirecionado...
  </p>

  <script type="module">
    import { init, track, getUserIdWithFallback, getUTMsWithFallback } from '/js/cdpTrack.js';

    await init();

    const uid     = getUserIdWithFallback();
    const utms    = getUTMsWithFallback();

    // Disparar ViewContent (sinal de interesse antes do checkout)
    await track('ViewContent', {
      content_name: 'Bridge Page - Afiliado',
      content_type: 'product',
      uid,
    });

    // Construir URL de checkout com cdp_uid injetado
    // passCheckoutParams() já faz isso para os links na página,
    // mas para redirect direto usamos a função abaixo:
    const checkoutUrl = buildCheckoutUrl('https://pay.hotmart.com/SEU_LINK_DE_AFILIADO', uid, utms);

    // Redireciona após 800ms (tempo suficiente para o evento ser enviado)
    setTimeout(() => { window.location.href = checkoutUrl; }, 800);

    /**
     * Constrói a URL de checkout com parâmetros de atribuição
     */
    function buildCheckoutUrl(baseUrl, uid, utms) {
      const url = new URL(baseUrl);

      // Hotmart: xcod = user_id, sck = UTMs pipe-separados
      if (baseUrl.includes('hotmart.com')) {
        if (uid) url.searchParams.set('xcod', uid);
        const sck = [utms.utm_source, utms.utm_medium, utms.utm_campaign, utms.utm_content, utms.utm_term]
          .map(v => v || 'direto').join('|');
        if (utms.utm_source) url.searchParams.set('sck', sck);
      }

      // Kiwify: src = utm_source
      else if (baseUrl.includes('kiwify.com.br')) {
        if (utms.utm_source) url.searchParams.set('src', utms.utm_source);
        if (utms.utm_medium) url.searchParams.set('utm_medium', utms.utm_medium);
        if (utms.utm_campaign) url.searchParams.set('utm_campaign', utms.utm_campaign);
      }

      // Eduzz / Monetizze: src = utm_source
      else if (baseUrl.includes('eduzz.com') || baseUrl.includes('monetizze.com.br')) {
        if (utms.utm_source) url.searchParams.set('src', utms.utm_source);
      }

      // CartPanda: utm_* direto
      else if (baseUrl.includes('cartpanda.com')) {
        Object.entries(utms).forEach(([k, v]) => { if (v) url.searchParams.set(k, v); });
      }

      // Ticto: utm_* + user_id
      else if (baseUrl.includes('ticto.app')) {
        Object.entries(utms).forEach(([k, v]) => { if (v) url.searchParams.set(k, v); });
        if (uid) url.searchParams.set('user_id', uid);
      }

      // Genérico: repassa tudo
      else {
        Object.entries(utms).forEach(([k, v]) => { if (v) url.searchParams.set(k, v); });
        if (uid) url.searchParams.set('cdp_uid', uid);
      }

      return url.toString();
    }
  </script>
</body>
</html>
```

---

## Passo 2 — Worker Route (Same-Domain)

O Worker precisa estar no mesmo domínio da bridge page para capturar fbp/fbc como first-party cookies.

```toml
# wrangler.toml — adicionar route da bridge page
[[routes]]
pattern = "bridge.seudominio.com/*"
zone_name = "seudominio.com"
```

```bash
wrangler deploy
```

---

## Passo 3 — Configurar Webhook na Plataforma

### Hotmart
```
Hotmart → Ferramentas → Webhooks → Adicionar Webhook
URL: https://bridge.seudominio.com/webhook/hotmart
Eventos: Compra Aprovada, Compra Cancelada, Reembolso
```

### Kiwify
```
Kiwify → Configurações → Webhooks
URL: https://bridge.seudominio.com/webhook/kiwify
Status: Ativo
```

### Ticto
```
Ticto → Configurações → Webhook
URL: https://bridge.seudominio.com/webhook/ticto
Eventos: purchase_approved
```

O Worker (`worker.js`) já tem handlers para `/webhook/hotmart`, `/webhook/kiwify` e `/webhook/ticto`. Eles:
1. Extraem `xcod`/`sck`/`user_id` do payload
2. Fazem D1 lookup pelo `cdp_uid`
3. Recuperam `fbp`, `fbc`, `ttp`, UTMs
4. Enviam `Purchase` para Meta CAPI + GA4 + TikTok

---

## Passo 4 — Fallback de Fingerprint (Automático)

O SDK já salva automaticamente uid + UTMs no `localStorage` (`_cdp_aff`).

**Cenário coberto:** usuário vê o anúncio, clica, a página de bridge abre em nova aba — o cookie pode não ser lido em alguns navegadores nesse contexto.

```javascript
// Automaticamente chamado em init():
_saveAffiliateContext(); // salva uid + UTMs no localStorage

// Ao montar a bridge page:
const uid  = getUserIdWithFallback();  // cookie → localStorage → ''
const utms = getUTMsWithFallback();    // URL params → localStorage → {}
```

O `getUserIdWithFallback()` tenta em ordem:
1. Cookie `_cdp_uid` (primeira opção)
2. localStorage `_cdp_aff.uid` (fallback — 30 dias de validade)
3. String vazia (sem identificação)

---

## Passo 5 — Anúncio Meta: URL de Destino

Configure o anúncio apontando para a bridge page, não para o checkout direto:

```
URL de Destino do Anúncio:
  https://bridge.seudominio.com/?utm_source=meta&utm_medium=paid&utm_campaign=NOME_CAMP&utm_content=NOME_AD

❌ NÃO usar:
  https://pay.hotmart.com/XXXXX (perde fbp/fbc/UTMs)
```

O Meta injeta o `fbclid` automaticamente na URL. O SDK captura e salva no D1.

---

## Passo 6 — Verificar Atribuição

Após uma venda de teste:

```bash
# Ver se o cdp_uid chegou no webhook e fez lookup
wrangler d1 execute cdp-edge-db --command="
  SELECT
    w.raw_payload,
    l.fbp,
    l.fbc,
    l.utm_source,
    l.utm_campaign
  FROM webhook_events w
  LEFT JOIN leads l ON l.user_id = json_extract(w.raw_payload, '$.xcod')
  ORDER BY w.created_at DESC
  LIMIT 1;
"

# Confirmar evento no Meta Events Manager
# Gerenciador de Eventos → filtrar por Purchase → verificar match_keys
```

---

## Checklist de Verificação

```
[ ] Bridge page no seu domínio (Cloudflare Pages recomendado — grátis)
[ ] Worker Route configurado: bridge.seudominio.com/*
[ ] SDK instalado na bridge page com getUserIdWithFallback()
[ ] URL de destino do anúncio aponta para bridge page (não checkout direto)
[ ] UTMs na URL do anúncio (utm_source, utm_medium, utm_campaign)
[ ] Webhook configurado na plataforma para o Worker
[ ] Teste: compra de desenvolvimento → D1 lookup → Purchase no Meta Events Manager
[ ] Verificar fbp no D1 (confirma que fbclid foi capturado)
[ ] Verificar utm_source no D1 (confirma que UTMs chegaram)
[ ] Meta Events Manager: compra com match_keys email/phone (se plataforma enviar no webhook)
```

---

## Fluxo Completo

```
1. Anúncio Meta clicado
   └─ URL: bridge.seudominio.com/?fbclid=XXX&utm_source=meta&utm_campaign=CAMP

2. Bridge Page carrega (800ms)
   └─ cdp_uid gerado (cookie 365 dias + localStorage backup)
   └─ fbclid capturado → _fbc calculado
   └─ UTMs salvos no localStorage (_cdp_aff)
   └─ PageView → Worker → D1 salva {fbp, fbc, utm_source, utm_campaign, ip, ua}
   └─ ViewContent → Worker → Meta ViewContent CAPI

3. Redirect para checkout
   └─ URL: pay.hotmart.com/XXX?xcod=cdp_uid&sck=meta|paid|CAMP||

4. Usuário compra
   └─ Hotmart webhook → Worker /webhook/hotmart
   └─ Extrai xcod = cdp_uid
   └─ D1 lookup(cdp_uid) → fbp, fbc, utm_source, utm_campaign, ip
   └─ Meta Purchase CAPI: fbp + fbc + utm_source → atribuição completa ✅
   └─ GA4 Purchase + TikTok CompletePayment
```

---

## Notas de Privacidade / LGPD

- A bridge page não coleta email ou telefone
- Apenas identifica a sessão com um ID anônimo (`cdp_uid`)
- Os dados de compra (email, telefone) chegam pelo webhook da plataforma e são hasheados antes de enviar ao CAPI
- O redirecionamento automático é transparente — nenhum dado sensível é exposto na URL
