@beworke/pixel-bemony 0.1.0-rc.1
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.
- package/CHANGELOG.md +157 -0
- package/README.md +176 -0
- package/RELEASE_CANDIDATE.md +37 -0
- package/dist/browser/index.cjs.js +2598 -0
- package/dist/browser/index.cjs.js.map +1 -0
- package/dist/browser/index.d.mts +184 -0
- package/dist/browser/index.d.ts +184 -0
- package/dist/browser/index.esm.js +2557 -0
- package/dist/browser/index.esm.js.map +1 -0
- package/dist/browser/pixel-bemony.global.js +9848 -0
- package/dist/browser/pixel-bemony.global.js.map +1 -0
- package/dist/createBemonyPixel-BGRD66p-.d.mts +474 -0
- package/dist/createBemonyPixel-BGRD66p-.d.ts +474 -0
- package/dist/index.cjs.js +2154 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.mts +320 -0
- package/dist/index.d.ts +320 -0
- package/dist/index.esm.js +2081 -0
- package/dist/index.esm.js.map +1 -0
- package/docs/BACKEND_PURCHASE_AND_REDIRECTS.md +125 -0
- package/docs/BROWSER_EVENT_ENRICHMENT.md +116 -0
- package/docs/SCENARIOS.md +92 -0
- package/docs/SECURITY_LIMITATIONS.md +45 -0
- package/docs/TEST_MATRIX.md +39 -0
- package/docs/TROUBLESHOOTING.md +81 -0
- package/examples/README.md +73 -0
- package/examples/browser-bootstrap.html +47 -0
- package/examples/checkout-price-code.html +37 -0
- package/examples/funnel/donsell/downsell_1.html +130 -0
- package/examples/funnel/donsell/downsell_2.html +130 -0
- package/examples/funnel/donsell/downsell_3.html +130 -0
- package/examples/funnel/offer-page.html +105 -0
- package/examples/funnel/upsell/upsell_1.html +130 -0
- package/examples/funnel/upsell/upsell_2.html +130 -0
- package/examples/funnel/upsell/upsell_3.html +139 -0
- package/examples/offer-page-price-code.html +22 -0
- package/examples/one-click-price-code.html +38 -0
- package/examples/security/checkout-sim.css +408 -0
- package/examples/security/checkout-sim.js +414 -0
- package/examples/security/one-click/pri_downsell_1.html +85 -0
- package/examples/security/one-click/pri_downsell_1a.html +85 -0
- package/examples/security/one-click/pri_downsell_1b.html +78 -0
- package/examples/security/one-click/pri_downsell_2.html +78 -0
- package/examples/security/one-click/pri_downsell_2a.html +78 -0
- package/examples/security/one-click/pri_downsell_2b.html +78 -0
- package/examples/security/one-click/pri_downsell_3.html +78 -0
- package/examples/security/one-click/pri_downsell_3a.html +78 -0
- package/examples/security/one-click/pri_downsell_3b.html +78 -0
- package/examples/security/one-click/pri_upsell_1.html +78 -0
- package/examples/security/one-click/pri_upsell_1a.html +78 -0
- package/examples/security/one-click/pri_upsell_1b.html +78 -0
- package/examples/security/one-click/pri_upsell_2.html +78 -0
- package/examples/security/one-click/pri_upsell_2a.html +78 -0
- package/examples/security/one-click/pri_upsell_2b.html +78 -0
- package/examples/security/one-click/pri_upsell_3.html +78 -0
- package/examples/security/one-click/pri_upsell_3a.html +78 -0
- package/examples/security/one-click/pri_upsell_3b.html +78 -0
- package/examples/security/one-click/pri_upsell_3c.html +78 -0
- package/examples/security/pri1766322.html +333 -0
- package/examples/security/pri5529930.html +333 -0
- package/examples/security/pri6670988.html +334 -0
- package/examples/security/thank-you.html +65 -0
- package/examples/sim/funnel-flow.js +258 -0
- package/examples/sim/one-click-sim.js +167 -0
- package/examples/upsell-price-code.html +41 -0
- package/examples/url-context-navigation.html +96 -0
- package/package.json +63 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Cenários — guias rápidos
|
|
2
|
+
|
|
3
|
+
Todos usam `createBemonyPixel({ scenario, environment, platform, page, … })`.
|
|
4
|
+
|
|
5
|
+
> O Pixel **não** consulta catálogo no bootstrap. Ver
|
|
6
|
+
> [`BROWSER_EVENT_ENRICHMENT.md`](./BROWSER_EVENT_ENRICHMENT.md).
|
|
7
|
+
|
|
8
|
+
## offer-page
|
|
9
|
+
|
|
10
|
+
**Quando:** VSL / página de oferta.
|
|
11
|
+
**Auto:** clique em links `https://security.bemony.com/pri*` → `ADD_TO_CART`
|
|
12
|
+
(`productId` do Core = `priceCode` observado). Sem auto `VIEW_CONTENT`.
|
|
13
|
+
**Navegação:** `sid` + `afid` + `oid`/`fid`/`fsid` via UrlContext.
|
|
14
|
+
**API:** `selectOffer`, `getPublicSessionId`.
|
|
15
|
+
**Exemplos:** `examples/offer-page-price-code.html`, `examples/offer-page.html`.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
await createBemonyPixel({
|
|
19
|
+
scenario: 'offer-page',
|
|
20
|
+
environment: 'development',
|
|
21
|
+
platform: {
|
|
22
|
+
pixelId: 'pix_7704308',
|
|
23
|
+
allowedDomains: ['security.bemony.com'],
|
|
24
|
+
},
|
|
25
|
+
page: {
|
|
26
|
+
public: {
|
|
27
|
+
pageId: 'vsl-main',
|
|
28
|
+
funnelId: 'funnel_1',
|
|
29
|
+
funnelStepId: 'step_offer',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
pixel.selectOffer({ priceCode: 'pri_123' });
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<a href="https://security.bemony.com/pri_123">Comprar</a>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## checkout
|
|
42
|
+
|
|
43
|
+
**Quando:** checkout principal.
|
|
44
|
+
**Auto:** `startCheckout` no init (`autoInitiateCheckout: false` desliga).
|
|
45
|
+
**API:** `startCheckout`, `captureLead`, bumps, `trackPaymentMethod`.
|
|
46
|
+
**Exemplo:** `examples/checkout-price-code.html`.
|
|
47
|
+
|
|
48
|
+
Payment method = enum (`pix`, `credit_card`, …) — **sem PAN/CVV**.
|
|
49
|
+
|
|
50
|
+
## upsell-offer
|
|
51
|
+
|
|
52
|
+
**Quando:** upsell **ou** downsell pós-checkout (mesmo scenario).
|
|
53
|
+
**API:** `viewUpsell`, `acceptUpsell`, `declineUpsell` (API manual).
|
|
54
|
+
**Accept (DOM):** clique em `/pri*` → ADD_TO_CART automático (LinkAuto), igual offer-page.
|
|
55
|
+
**Decline (DOM):** UrlContext decora o `href` igual a `data-no-thanks` do script; navegação nativa.
|
|
56
|
+
**Exemplo:** `examples/funnel/upsell/`, `examples/upsell-price-code.html`.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
pixel.acceptUpsell({ priceCode: 'pri_upsell_1a' });
|
|
60
|
+
pixel.declineUpsell({ navigateTo: '/thank-you' });
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<script
|
|
65
|
+
data-scenario="upsell-offer"
|
|
66
|
+
data-no-thanks="../donsell/downsell_1.html"
|
|
67
|
+
></script>
|
|
68
|
+
<a href="../../security/one-click/pri_upsell_1a.html">Premium</a>
|
|
69
|
+
<a href="../donsell/downsell_1.html">Não, obrigado</a>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## one-click-checkout
|
|
73
|
+
|
|
74
|
+
**Quando:** confirmação one-click com Order já estabelecida.
|
|
75
|
+
**API:** `trackOneClickConfirmation`, `setPublicOrderId`.
|
|
76
|
+
**Exemplo:** `examples/one-click-price-code.html`.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
pixel.trackOneClickConfirmation({ priceCode: 'pri_upsell_1' });
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## thank-you
|
|
83
|
+
|
|
84
|
+
**Quando:** página de obrigado.
|
|
85
|
+
**Comportamento:** PAGE_VIEW via Core; `purchase.enabled: false`.
|
|
86
|
+
**API:** `getPublicOrderId()`.
|
|
87
|
+
**Purchase:** servidor — ver `BACKEND_PURCHASE_AND_REDIRECTS.md`.
|
|
88
|
+
|
|
89
|
+
## Browser bootstrap (transversal)
|
|
90
|
+
|
|
91
|
+
Script global com `data-scenario`, `data-funnel-id`, `data-funnel-step-id`
|
|
92
|
+
para correlação local — **não** dispara consulta de catálogo.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Security limitations — `@beworke/pixel-bemony`
|
|
2
|
+
|
|
3
|
+
## Garantias desta camada
|
|
4
|
+
|
|
5
|
+
| Controle | Comportamento |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Token one-click | Memória efêmera; limpo no `destroy`; denylist de forward/redirect/`data-*` |
|
|
8
|
+
| Purchase browser | `purchase.enabled: false` em todos os presets |
|
|
9
|
+
| Dados de cartão | Fora do escopo — só enum de método de pagamento |
|
|
10
|
+
| Endpoints | Staging/prod exigem endpoint **injetado** (auto-init usa endpoint público padrão); nenhum secret embutido |
|
|
11
|
+
| Snapshots | `getDebugSnapshot` não inclui token |
|
|
12
|
+
| Entry principal | Sem side effects / sem auto-init |
|
|
13
|
+
| UrlContext | Denylist Bemony + Core; `sid`/`afid` managed; UTMs/click IDs forwarded |
|
|
14
|
+
| CTAs | Sem token/PII em atributos; decoração por hostname permitido |
|
|
15
|
+
|
|
16
|
+
## Limitações conhecidas
|
|
17
|
+
|
|
18
|
+
1. **CMP:** apenas gate `waitForConsent` — sem integração CMP nativa.
|
|
19
|
+
2. **Decline upsell:** sem evento tipado no Core; não inventar evento falso.
|
|
20
|
+
3. **PURCHASE server:** contratos PLACEHOLDER — path/auth finais fora do pacote.
|
|
21
|
+
4. **Dedupe browser+server:** futuro; não resolvido na V1.
|
|
22
|
+
5. **IIFE global:** embute Core; superfície maior que o ESM.
|
|
23
|
+
6. **`apply*Scenario` públicos:** avançados; hosts normais devem usar a factory.
|
|
24
|
+
7. **`afid`:** nunca gerado pelo Pixel; ausência total = sem parâmetro na URL.
|
|
25
|
+
8. **`data-no-sid`:** legado exclui a decoração inteira do link (não só o sid).
|
|
26
|
+
|
|
27
|
+
## sid vs afid vs IDs internos
|
|
28
|
+
|
|
29
|
+
| Identificador | Papel |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `sid` | Sessão pública de funil (URL-safe); pode ser gerada |
|
|
32
|
+
| `afid` | Afiliado; só lido/persistido/propagado |
|
|
33
|
+
| `visitor_id` | Visitante Core (não é sid) |
|
|
34
|
+
| `session_id` | Sessão interna Core (não é sid) |
|
|
35
|
+
|
|
36
|
+
## O que o consumidor deve fazer
|
|
37
|
+
|
|
38
|
+
- Nunca colocar token em URL, HTML `data-*`, logs ou storage persistente.
|
|
39
|
+
- Validar `allowedDomains` em produção.
|
|
40
|
+
- Emitir PURCHASE apenas no backend após pagamento confirmado.
|
|
41
|
+
- Tratar one-click payment no PSP/backend; Pixel só registra intenção/confirmação de domínio.
|
|
42
|
+
|
|
43
|
+
## Reportar
|
|
44
|
+
|
|
45
|
+
Se um artefato (tarball, exemplo, dist) contiver token real ou endpoint com credencial, trate como incidente e regenere o artifact.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Matriz de testes — `@beworke/pixel-bemony`
|
|
2
|
+
|
|
3
|
+
Contagem de referência pós-B11: ver saída Vitest mais recente no relatório B11.
|
|
4
|
+
|
|
5
|
+
| Área | Arquivo(s) | Cobertura |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| Contratos / config | `createBemonyPixelConfig.test.ts` | composição, ambientes, token denylist |
|
|
8
|
+
| Package surface | `package.test.ts`, `hardening.b9.test.ts` | exports, sem bootstrap no `.`, sem purchase |
|
|
9
|
+
| Lifecycle | `BemonyPixel.lifecycle.test.ts` | init/destroy, concorrência, snapshot sem token |
|
|
10
|
+
| SSR | `BemonyPixel.ssr.test.ts`, `browser.ssr.test.ts` | construção/import sem DOM |
|
|
11
|
+
| offer-page | `offer-page.test.ts` | view/select/session + LinkAutoSource |
|
|
12
|
+
| checkout | `checkout.test.ts` | lead, bumps, payment enum |
|
|
13
|
+
| upsell | `upsell-offer.test.ts` | view/accept/decline |
|
|
14
|
+
| one-click | `one-click-checkout.test.ts` | confirmation, token privado |
|
|
15
|
+
| thank-you | `thank-you.test.ts` | PAGE_VIEW only, redirects validators |
|
|
16
|
+
| browser bootstrap | `browser.bootstrap.test.ts`, `browser.global.test.ts` | singleton, consent, data-*, IIFE |
|
|
17
|
+
| B10 auto-init HTTP | `browser.default-autoinit-http.test.ts` | defaults, script mínimo, HTTP mock |
|
|
18
|
+
| **B11 url + afid + link** | `browser.url-context-propagation.test.ts` | sid/afid, managed/forwarded, ADD_TO_CART, continuidade |
|
|
19
|
+
|
|
20
|
+
## Gates obrigatórios
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm --filter @beworke/pixel-bemony lint
|
|
24
|
+
pnpm --filter @beworke/pixel-bemony type-check
|
|
25
|
+
pnpm --filter @beworke/pixel-bemony test
|
|
26
|
+
pnpm --filter @beworke/pixel-bemony build
|
|
27
|
+
pnpm --filter @beworke/pixel test
|
|
28
|
+
pnpm pack:pixel-bemony && pnpm validate:pixel-bemony-package && pnpm smoke:pixel-bemony-package
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Smoke HTTP real (opt-in)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
BEMONY_REAL_API_SMOKE=1 pnpm --filter @beworke/pixel-bemony test
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Critérios de aceite (DoD)
|
|
38
|
+
|
|
39
|
+
Ver `REPORTS/B10_DEFAULT_GLOBAL_AUTOINIT_HTTP.md` e spec interna de aceite.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Troubleshooting — `@beworke/pixel-bemony`
|
|
2
|
+
|
|
3
|
+
## `NOT_BROWSER` / bootstrap falha no SSR
|
|
4
|
+
|
|
5
|
+
`bootstrapBemonyPixel` e `installBemonyPixelAutoInit` exigem `window`.
|
|
6
|
+
No SSR use só `createBemonyPixelConfig` / construção sem `init`, ou chame bootstrap no client.
|
|
7
|
+
|
|
8
|
+
## Staging/production sem `apiEndpoint`
|
|
9
|
+
|
|
10
|
+
A factory programática exige `platform.apiEndpoint` / `overrides.apiEndpoint` em staging/production.
|
|
11
|
+
O **auto-init do bundle global** injeta `BEMONY_DEFAULT_API_ENDPOINT` automaticamente.
|
|
12
|
+
Development (API programática) usa mock do Core.
|
|
13
|
+
|
|
14
|
+
## `CONSENT_DENIED`
|
|
15
|
+
|
|
16
|
+
`waitForConsent` retornou `false`. Inicie manualmente após o CMP:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
await bootstrapBemonyPixel({ …, force: true, waitForConsent: () => true });
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Auto-init não roda
|
|
23
|
+
|
|
24
|
+
1. Entry `.` **não** auto-inicia (sem side effect).
|
|
25
|
+
2. Use o IIFE `pixel-bemony.global.js` (auto-init por padrão) **ou** `installBemonyPixelAutoInit()`.
|
|
26
|
+
3. Verifique opt-out: `data-auto-init="false"` / `"0"`.
|
|
27
|
+
4. Aguarde `await window.BemonyPixel.ready` ou `whenReady()`.
|
|
28
|
+
|
|
29
|
+
## `FORBIDDEN_ATTRIBUTE` / token em data-*
|
|
30
|
+
|
|
31
|
+
Remova qualquer `data-*-token` ou `data-secret`. Token one-click só via API efêmera do app.
|
|
32
|
+
|
|
33
|
+
## `MULTIPLE_SCRIPTS` / `AMBIGUOUS_SCRIPT`
|
|
34
|
+
|
|
35
|
+
Deixe apenas um script do bundle global (ou um `data-bemony-pixel` com `data-auto-init`).
|
|
36
|
+
|
|
37
|
+
## Instância duplicada
|
|
38
|
+
|
|
39
|
+
`createBemonyPixel` **não** é singleton.
|
|
40
|
+
`bootstrapBemonyPixel` é 1/`window`. Use `force: true` para HMR/reinit.
|
|
41
|
+
|
|
42
|
+
## Auto-init não decora links
|
|
43
|
+
|
|
44
|
+
1. Confirme hostname em `allowedDomains` (default: `security.bemony.com`).
|
|
45
|
+
2. Sem selector Bemony — qualquer `<a href>` para domínio permitido é elegível.
|
|
46
|
+
3. Protocolos `mailto:` / `tel:` / `javascript:` não são decorados.
|
|
47
|
+
4. `data-no-url-context` ou `download` excluem o link.
|
|
48
|
+
5. `data-no-sid` (legado) exclui a decoração inteira.
|
|
49
|
+
|
|
50
|
+
## `afid` não aparece
|
|
51
|
+
|
|
52
|
+
O Pixel **nunca gera** `afid`. Precisa existir na URL, cookie ou localStorage. Sem fonte = ausente.
|
|
53
|
+
|
|
54
|
+
## ADD_TO_CART não dispara no clique
|
|
55
|
+
|
|
56
|
+
Só links cujo pathname contém um segmento `pri` + dígitos no hostname permitido
|
|
57
|
+
(ex.: `/pri6670988` ou `/examples/security/pri6670988.html`).
|
|
58
|
+
`/account`, `/login` etc. podem ser decorados, mas não emitem ADD_TO_CART.
|
|
59
|
+
|
|
60
|
+
## CORS / API indisponível
|
|
61
|
+
|
|
62
|
+
- Sirva o HTML por HTTP local (`pnpm dlx serve packages/pixel-bemony`), não `file://`.
|
|
63
|
+
- O Core envia `POST` para `{apiEndpoint}/pixels/public/event` (ou `/batch`).
|
|
64
|
+
- Endpoint padrão: `BEMONY_DEFAULT_API_ENDPOINT` (público de infraestrutura, não credencial).
|
|
65
|
+
- Falhas HTTP são tratadas pelo retry/fila do Core; veja `bemony:pixel:error` e o Network do DevTools.
|
|
66
|
+
|
|
67
|
+
## Decline upsell “não aparece no dashboard”
|
|
68
|
+
|
|
69
|
+
Esperado: `declineUpsell` não emite evento Core (gap). Trate analytics de decline no app se necessário.
|
|
70
|
+
|
|
71
|
+
## Thank-you sem PURCHASE
|
|
72
|
+
|
|
73
|
+
Esperado. PURCHASE é responsabilidade do backend — ver `BACKEND_PURCHASE_AND_REDIRECTS.md`.
|
|
74
|
+
|
|
75
|
+
## Métodos antes de `ready`
|
|
76
|
+
|
|
77
|
+
Aguarde `createBemonyPixel` / `window.BemonyPixel.whenReady()` concluir. Estado `failed` → destrua e recrie.
|
|
78
|
+
|
|
79
|
+
## Bundle global grande
|
|
80
|
+
|
|
81
|
+
`./global` embute `@beworke/pixel`. Em apps bundladas, prefira ESM + tree-shake do entry `.` / `./browser`.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Funil de examples — simulação ponta a ponta
|
|
2
|
+
|
|
3
|
+
Dois grupos de páginas, espelhando o fluxo real Bemony:
|
|
4
|
+
|
|
5
|
+
| Pasta | Papel |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `examples/funnel/` | Domínio do **cliente** (VSL, upsell/downsell externos) |
|
|
8
|
+
| `examples/security/` | Domínio **Bemony** (checkout + one-click) |
|
|
9
|
+
| `examples/*-price-code.html` | Exemplos mínimos **sem catálogo** |
|
|
10
|
+
|
|
11
|
+
Nenhuma página consulta catálogo no bootstrap. O browser captura `priceCode`;
|
|
12
|
+
o backend enriquece. Ver `docs/BROWSER_EVENT_ENRICHMENT.md`.
|
|
13
|
+
|
|
14
|
+
## Como rodar
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm --filter @beworke/pixel-bemony build
|
|
18
|
+
pnpm dlx serve packages/pixel-bemony
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Abra:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
http://localhost:3000/examples/offer-page-price-code.html
|
|
25
|
+
http://localhost:3000/examples/funnel/offer-page.html?utm_source=demo&afid=aff_demo
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Fluxo
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
offer → checkout → upsell N
|
|
32
|
+
aceito → one-click → upsell N+1 (ou thank-you se N = último)
|
|
33
|
+
recusado → downsell N
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
A URL de “No Thanks” / decline vem da aplicação (`navigateTo` ou `href` do link).
|
|
37
|
+
|
|
38
|
+
## Helpers
|
|
39
|
+
|
|
40
|
+
- `examples/sim/funnel-flow.js` — preserva `sid` / `afid` / `oid` / UTMs
|
|
41
|
+
- `examples/sim/one-click-sim.js` — init programático one-click
|
|
42
|
+
- `examples/security/checkout-sim.js` — checkout + redirect pós-pagamento
|
|
43
|
+
|
|
44
|
+
## Upsell / downsell externos
|
|
45
|
+
|
|
46
|
+
O integrador cola **links prontos** (CMS). O exemplo não monta `href` em JS.
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<script
|
|
50
|
+
src="pixel-bemony.global.js"
|
|
51
|
+
data-scenario="upsell-offer"
|
|
52
|
+
data-funnel-id="funnel_1"
|
|
53
|
+
data-funnel-step-id="step_upsell_1"
|
|
54
|
+
data-no-thanks="../donsell/downsell_1.html"
|
|
55
|
+
></script>
|
|
56
|
+
|
|
57
|
+
<a href="../../security/one-click/pri_upsell_1a.html">Quero Premium</a>
|
|
58
|
+
<a href="../donsell/downsell_1.html">Não, obrigado</a>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- **Aceitar:** `href` com `/pri*` (priceCode na URL); Pixel trackeia e decora.
|
|
62
|
+
- **Recusar:** mesmo `href` que `data-no-thanks` no script; Pixel inclui esse href no selector de decoração e trata o clique.
|
|
63
|
+
- Decoração de `sid`/`oid`/`afid` continua no Core (UrlContext), sem o exemplo resolver destino.
|
|
64
|
+
|
|
65
|
+
## O que validar
|
|
66
|
+
|
|
67
|
+
1. Offer inicializa **sem** resolver / catálogo
|
|
68
|
+
2. Clique em `/pri*` → ADD_TO_CART com `productId` = priceCode
|
|
69
|
+
3. `sid` e `afid` sobrevivem offer → checkout → upsell
|
|
70
|
+
4. `oid` nasce no checkout e segue até thank-you
|
|
71
|
+
5. Upsell/downsell: buy `/pri*` + `data-no-thanks` no script (= href do decline)
|
|
72
|
+
6. One-click confirma com priceCode; pagamento no backend
|
|
73
|
+
7. Thank-you sem purchase browser
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-BR">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Pixel Bemony — Auto-init HTTP</title>
|
|
7
|
+
</head>
|
|
8
|
+
|
|
9
|
+
<body>
|
|
10
|
+
<main>
|
|
11
|
+
<h1>VSL — Offer Page</h1>
|
|
12
|
+
<p>Validação do auto-init global (B10) + continuidade de navegação (B11).</p>
|
|
13
|
+
<p>
|
|
14
|
+
<a href="https://security.bemony.com/pri6670988">Comprar oferta 1</a>
|
|
15
|
+
</p>
|
|
16
|
+
</main>
|
|
17
|
+
|
|
18
|
+
<script
|
|
19
|
+
src="../dist/browser/pixel-bemony.global.js"
|
|
20
|
+
data-funnel-id="funnel_1"
|
|
21
|
+
data-funnel-step-id="step_1"
|
|
22
|
+
></script>
|
|
23
|
+
|
|
24
|
+
<script>
|
|
25
|
+
window.BemonyPixel.ready
|
|
26
|
+
.then(function (pixel) {
|
|
27
|
+
if (!pixel) {
|
|
28
|
+
throw new Error('Auto-init não produziu uma instância.');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log('[bemony-example] ready', {
|
|
32
|
+
state: pixel.getState(),
|
|
33
|
+
initialized: pixel.isInitialized(),
|
|
34
|
+
scenario: pixel.getScenario(),
|
|
35
|
+
sid: pixel.getPublicSessionId(),
|
|
36
|
+
identity: pixel.getConfig().identity,
|
|
37
|
+
transport: pixel.getConfig().modules.transport,
|
|
38
|
+
endpoint: pixel.getConfig().transport.apiEndpoint,
|
|
39
|
+
urlContext: pixel.getDebugSnapshot().urlContext,
|
|
40
|
+
});
|
|
41
|
+
})
|
|
42
|
+
.catch(function (error) {
|
|
43
|
+
console.error('[bemony-example] bootstrap error', error);
|
|
44
|
+
});
|
|
45
|
+
</script>
|
|
46
|
+
</body>
|
|
47
|
+
</html>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-BR">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title>Bemony Checkout — priceCode</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<h1>Checkout</h1>
|
|
9
|
+
<p>
|
|
10
|
+
Checkout inicia sem catálogo. bumps usam
|
|
11
|
+
<code>selectOrderBump({ priceCode })</code>.
|
|
12
|
+
</p>
|
|
13
|
+
<button type="button" id="bump">Adicionar bump</button>
|
|
14
|
+
<script type="module">
|
|
15
|
+
import { createBemonyPixel } from '../dist/index.js';
|
|
16
|
+
|
|
17
|
+
const pixel = await createBemonyPixel({
|
|
18
|
+
environment: 'development',
|
|
19
|
+
scenario: 'checkout',
|
|
20
|
+
platform: {
|
|
21
|
+
pixelId: 'pix_demo',
|
|
22
|
+
allowedDomains: ['security.bemony.com'],
|
|
23
|
+
},
|
|
24
|
+
page: {
|
|
25
|
+
public: {
|
|
26
|
+
funnelId: 'funnel_123',
|
|
27
|
+
funnelStepId: 'step_checkout',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
document.getElementById('bump').onclick = () => {
|
|
33
|
+
pixel.selectOrderBump({ priceCode: 'pri_bump_123' });
|
|
34
|
+
};
|
|
35
|
+
</script>
|
|
36
|
+
</body>
|
|
37
|
+
</html>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-BR">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Funnel · Downsell 1</title>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: Georgia, 'Times New Roman', serif;
|
|
10
|
+
max-width: 36rem;
|
|
11
|
+
margin: 0 auto;
|
|
12
|
+
padding: 2rem 1rem;
|
|
13
|
+
color: #162018;
|
|
14
|
+
line-height: 1.5;
|
|
15
|
+
}
|
|
16
|
+
.offer-card {
|
|
17
|
+
margin: 1.25rem 0;
|
|
18
|
+
padding: 1.1rem 1rem 1rem;
|
|
19
|
+
border: 1px solid #c5d0c8;
|
|
20
|
+
border-radius: 6px;
|
|
21
|
+
background: rgba(255, 255, 255, 0.55);
|
|
22
|
+
}
|
|
23
|
+
.offer-card h2 {
|
|
24
|
+
margin: 0 0 0.35rem;
|
|
25
|
+
font-size: 1.15rem;
|
|
26
|
+
}
|
|
27
|
+
.offer-card p {
|
|
28
|
+
margin: 0 0 0.85rem;
|
|
29
|
+
font-size: 0.95rem;
|
|
30
|
+
color: #3a4a40;
|
|
31
|
+
}
|
|
32
|
+
.offer-card .price {
|
|
33
|
+
font-weight: 700;
|
|
34
|
+
margin-bottom: 0.75rem;
|
|
35
|
+
}
|
|
36
|
+
a.accept,
|
|
37
|
+
a.decline {
|
|
38
|
+
display: block;
|
|
39
|
+
margin: 0.5rem 0 0;
|
|
40
|
+
padding: 0.85rem 1rem;
|
|
41
|
+
text-decoration: none;
|
|
42
|
+
text-align: center;
|
|
43
|
+
border-radius: 4px;
|
|
44
|
+
}
|
|
45
|
+
.accept {
|
|
46
|
+
background: #122019;
|
|
47
|
+
color: #fff;
|
|
48
|
+
}
|
|
49
|
+
.decline {
|
|
50
|
+
border: 1px solid #9aa89f;
|
|
51
|
+
color: #243028;
|
|
52
|
+
margin-top: 1.25rem;
|
|
53
|
+
}
|
|
54
|
+
pre {
|
|
55
|
+
background: #142019;
|
|
56
|
+
color: #dceee6;
|
|
57
|
+
padding: 0.85rem;
|
|
58
|
+
font-size: 0.72rem;
|
|
59
|
+
overflow: auto;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
body {
|
|
63
|
+
background: linear-gradient(180deg, #f3efe6, #e7f0ea);
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
<p>Bemony · página externa</p>
|
|
69
|
+
<h1>Downsell 1</h1>
|
|
70
|
+
<p>
|
|
71
|
+
Buy links: <code>href</code> com <code>/pri*</code> (identidade na URL).
|
|
72
|
+
Recusa: <code>data-no-thanks</code> no <strong>script</strong> (mesmo
|
|
73
|
+
<code>href</code> no link de decline). O Pixel decora buy + esse href
|
|
74
|
+
(<code>sid</code>/<code>oid</code>…) e trackeia o aceite.
|
|
75
|
+
</p>
|
|
76
|
+
|
|
77
|
+
<article class="offer-card">
|
|
78
|
+
<h2>Oferta light</h2>
|
|
79
|
+
<p>Versão reduzida com o essencial.</p>
|
|
80
|
+
<div class="price">R$ 27</div>
|
|
81
|
+
<a class="accept" href="../../security/one-click/pri_downsell_1a.html"
|
|
82
|
+
>Quero esta oferta</a
|
|
83
|
+
>
|
|
84
|
+
</article>
|
|
85
|
+
|
|
86
|
+
<article class="offer-card">
|
|
87
|
+
<h2>Oferta parcelada</h2>
|
|
88
|
+
<p>Mesmo conteúdo em 3x.</p>
|
|
89
|
+
<div class="price">3x R$ 19</div>
|
|
90
|
+
<a class="accept" href="../../security/one-click/pri_downsell_1b.html"
|
|
91
|
+
>Quero esta oferta</a
|
|
92
|
+
>
|
|
93
|
+
</article>
|
|
94
|
+
<a class="decline" href="../upsell/upsell_2.html">Não, obrigado</a>
|
|
95
|
+
<pre id="funnel-debug">aguardando…</pre>
|
|
96
|
+
<script
|
|
97
|
+
src="../../../dist/browser/pixel-bemony.global.js"
|
|
98
|
+
data-scenario="upsell-offer"
|
|
99
|
+
data-funnel-id="funnel_1"
|
|
100
|
+
data-funnel-step-id="step_downsell_1"
|
|
101
|
+
data-no-thanks="../upsell/upsell_2.html"
|
|
102
|
+
data-debug="true"
|
|
103
|
+
></script>
|
|
104
|
+
<script>
|
|
105
|
+
// Debug only — não altera hrefs nem resolve click.
|
|
106
|
+
(window.BemonyPixel && window.BemonyPixel.whenReady
|
|
107
|
+
? window.BemonyPixel.whenReady()
|
|
108
|
+
: Promise.reject()
|
|
109
|
+
)
|
|
110
|
+
.then(function (pixel) {
|
|
111
|
+
var el = document.getElementById('funnel-debug');
|
|
112
|
+
if (!el) return;
|
|
113
|
+
el.textContent = JSON.stringify(
|
|
114
|
+
{
|
|
115
|
+
scenario: pixel.getScenario(),
|
|
116
|
+
sid: pixel.getPublicSessionId(),
|
|
117
|
+
oid: pixel.getPublicOrderId && pixel.getPublicOrderId(),
|
|
118
|
+
note: 'buy=/pri* ; no-thanks=script data-no-thanks → mesmo href',
|
|
119
|
+
},
|
|
120
|
+
null,
|
|
121
|
+
2
|
|
122
|
+
);
|
|
123
|
+
})
|
|
124
|
+
.catch(function () {
|
|
125
|
+
var el = document.getElementById('funnel-debug');
|
|
126
|
+
if (el) el.textContent = 'pixel não pronto';
|
|
127
|
+
});
|
|
128
|
+
</script>
|
|
129
|
+
</body>
|
|
130
|
+
</html>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-BR">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Funnel · Downsell 2</title>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: Georgia, 'Times New Roman', serif;
|
|
10
|
+
max-width: 36rem;
|
|
11
|
+
margin: 0 auto;
|
|
12
|
+
padding: 2rem 1rem;
|
|
13
|
+
color: #162018;
|
|
14
|
+
line-height: 1.5;
|
|
15
|
+
}
|
|
16
|
+
.offer-card {
|
|
17
|
+
margin: 1.25rem 0;
|
|
18
|
+
padding: 1.1rem 1rem 1rem;
|
|
19
|
+
border: 1px solid #c5d0c8;
|
|
20
|
+
border-radius: 6px;
|
|
21
|
+
background: rgba(255, 255, 255, 0.55);
|
|
22
|
+
}
|
|
23
|
+
.offer-card h2 {
|
|
24
|
+
margin: 0 0 0.35rem;
|
|
25
|
+
font-size: 1.15rem;
|
|
26
|
+
}
|
|
27
|
+
.offer-card p {
|
|
28
|
+
margin: 0 0 0.85rem;
|
|
29
|
+
font-size: 0.95rem;
|
|
30
|
+
color: #3a4a40;
|
|
31
|
+
}
|
|
32
|
+
.offer-card .price {
|
|
33
|
+
font-weight: 700;
|
|
34
|
+
margin-bottom: 0.75rem;
|
|
35
|
+
}
|
|
36
|
+
a.accept,
|
|
37
|
+
a.decline {
|
|
38
|
+
display: block;
|
|
39
|
+
margin: 0.5rem 0 0;
|
|
40
|
+
padding: 0.85rem 1rem;
|
|
41
|
+
text-decoration: none;
|
|
42
|
+
text-align: center;
|
|
43
|
+
border-radius: 4px;
|
|
44
|
+
}
|
|
45
|
+
.accept {
|
|
46
|
+
background: #122019;
|
|
47
|
+
color: #fff;
|
|
48
|
+
}
|
|
49
|
+
.decline {
|
|
50
|
+
border: 1px solid #9aa89f;
|
|
51
|
+
color: #243028;
|
|
52
|
+
margin-top: 1.25rem;
|
|
53
|
+
}
|
|
54
|
+
pre {
|
|
55
|
+
background: #142019;
|
|
56
|
+
color: #dceee6;
|
|
57
|
+
padding: 0.85rem;
|
|
58
|
+
font-size: 0.72rem;
|
|
59
|
+
overflow: auto;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
body {
|
|
63
|
+
background: linear-gradient(180deg, #f3efe6, #e7f0ea);
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
<p>Bemony · página externa</p>
|
|
69
|
+
<h1>Downsell 2</h1>
|
|
70
|
+
<p>
|
|
71
|
+
Buy links: <code>href</code> com <code>/pri*</code> (identidade na URL).
|
|
72
|
+
Recusa: <code>data-no-thanks</code> no <strong>script</strong> (mesmo
|
|
73
|
+
<code>href</code> no link de decline). O Pixel decora buy + esse href
|
|
74
|
+
(<code>sid</code>/<code>oid</code>…) e trackeia o aceite.
|
|
75
|
+
</p>
|
|
76
|
+
|
|
77
|
+
<article class="offer-card">
|
|
78
|
+
<h2>Mini curso</h2>
|
|
79
|
+
<p>Módulo único introdutório.</p>
|
|
80
|
+
<div class="price">R$ 17</div>
|
|
81
|
+
<a class="accept" href="../../security/one-click/pri_downsell_2a.html"
|
|
82
|
+
>Quero esta oferta</a
|
|
83
|
+
>
|
|
84
|
+
</article>
|
|
85
|
+
|
|
86
|
+
<article class="offer-card">
|
|
87
|
+
<h2>Checklist PDF</h2>
|
|
88
|
+
<p>Material digital imediato.</p>
|
|
89
|
+
<div class="price">R$ 9</div>
|
|
90
|
+
<a class="accept" href="../../security/one-click/pri_downsell_2b.html"
|
|
91
|
+
>Quero esta oferta</a
|
|
92
|
+
>
|
|
93
|
+
</article>
|
|
94
|
+
<a class="decline" href="../upsell/upsell_3.html">Não, obrigado</a>
|
|
95
|
+
<pre id="funnel-debug">aguardando…</pre>
|
|
96
|
+
<script
|
|
97
|
+
src="../../../dist/browser/pixel-bemony.global.js"
|
|
98
|
+
data-scenario="upsell-offer"
|
|
99
|
+
data-funnel-id="funnel_1"
|
|
100
|
+
data-funnel-step-id="step_downsell_2"
|
|
101
|
+
data-no-thanks="../upsell/upsell_3.html"
|
|
102
|
+
data-debug="true"
|
|
103
|
+
></script>
|
|
104
|
+
<script>
|
|
105
|
+
// Debug only — não altera hrefs nem resolve click.
|
|
106
|
+
(window.BemonyPixel && window.BemonyPixel.whenReady
|
|
107
|
+
? window.BemonyPixel.whenReady()
|
|
108
|
+
: Promise.reject()
|
|
109
|
+
)
|
|
110
|
+
.then(function (pixel) {
|
|
111
|
+
var el = document.getElementById('funnel-debug');
|
|
112
|
+
if (!el) return;
|
|
113
|
+
el.textContent = JSON.stringify(
|
|
114
|
+
{
|
|
115
|
+
scenario: pixel.getScenario(),
|
|
116
|
+
sid: pixel.getPublicSessionId(),
|
|
117
|
+
oid: pixel.getPublicOrderId && pixel.getPublicOrderId(),
|
|
118
|
+
note: 'buy=/pri* ; no-thanks=script data-no-thanks → mesmo href',
|
|
119
|
+
},
|
|
120
|
+
null,
|
|
121
|
+
2
|
|
122
|
+
);
|
|
123
|
+
})
|
|
124
|
+
.catch(function () {
|
|
125
|
+
var el = document.getElementById('funnel-debug');
|
|
126
|
+
if (el) el.textContent = 'pixel não pronto';
|
|
127
|
+
});
|
|
128
|
+
</script>
|
|
129
|
+
</body>
|
|
130
|
+
</html>
|