@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.
Files changed (67) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/README.md +176 -0
  3. package/RELEASE_CANDIDATE.md +37 -0
  4. package/dist/browser/index.cjs.js +2598 -0
  5. package/dist/browser/index.cjs.js.map +1 -0
  6. package/dist/browser/index.d.mts +184 -0
  7. package/dist/browser/index.d.ts +184 -0
  8. package/dist/browser/index.esm.js +2557 -0
  9. package/dist/browser/index.esm.js.map +1 -0
  10. package/dist/browser/pixel-bemony.global.js +9848 -0
  11. package/dist/browser/pixel-bemony.global.js.map +1 -0
  12. package/dist/createBemonyPixel-BGRD66p-.d.mts +474 -0
  13. package/dist/createBemonyPixel-BGRD66p-.d.ts +474 -0
  14. package/dist/index.cjs.js +2154 -0
  15. package/dist/index.cjs.js.map +1 -0
  16. package/dist/index.d.mts +320 -0
  17. package/dist/index.d.ts +320 -0
  18. package/dist/index.esm.js +2081 -0
  19. package/dist/index.esm.js.map +1 -0
  20. package/docs/BACKEND_PURCHASE_AND_REDIRECTS.md +125 -0
  21. package/docs/BROWSER_EVENT_ENRICHMENT.md +116 -0
  22. package/docs/SCENARIOS.md +92 -0
  23. package/docs/SECURITY_LIMITATIONS.md +45 -0
  24. package/docs/TEST_MATRIX.md +39 -0
  25. package/docs/TROUBLESHOOTING.md +81 -0
  26. package/examples/README.md +73 -0
  27. package/examples/browser-bootstrap.html +47 -0
  28. package/examples/checkout-price-code.html +37 -0
  29. package/examples/funnel/donsell/downsell_1.html +130 -0
  30. package/examples/funnel/donsell/downsell_2.html +130 -0
  31. package/examples/funnel/donsell/downsell_3.html +130 -0
  32. package/examples/funnel/offer-page.html +105 -0
  33. package/examples/funnel/upsell/upsell_1.html +130 -0
  34. package/examples/funnel/upsell/upsell_2.html +130 -0
  35. package/examples/funnel/upsell/upsell_3.html +139 -0
  36. package/examples/offer-page-price-code.html +22 -0
  37. package/examples/one-click-price-code.html +38 -0
  38. package/examples/security/checkout-sim.css +408 -0
  39. package/examples/security/checkout-sim.js +414 -0
  40. package/examples/security/one-click/pri_downsell_1.html +85 -0
  41. package/examples/security/one-click/pri_downsell_1a.html +85 -0
  42. package/examples/security/one-click/pri_downsell_1b.html +78 -0
  43. package/examples/security/one-click/pri_downsell_2.html +78 -0
  44. package/examples/security/one-click/pri_downsell_2a.html +78 -0
  45. package/examples/security/one-click/pri_downsell_2b.html +78 -0
  46. package/examples/security/one-click/pri_downsell_3.html +78 -0
  47. package/examples/security/one-click/pri_downsell_3a.html +78 -0
  48. package/examples/security/one-click/pri_downsell_3b.html +78 -0
  49. package/examples/security/one-click/pri_upsell_1.html +78 -0
  50. package/examples/security/one-click/pri_upsell_1a.html +78 -0
  51. package/examples/security/one-click/pri_upsell_1b.html +78 -0
  52. package/examples/security/one-click/pri_upsell_2.html +78 -0
  53. package/examples/security/one-click/pri_upsell_2a.html +78 -0
  54. package/examples/security/one-click/pri_upsell_2b.html +78 -0
  55. package/examples/security/one-click/pri_upsell_3.html +78 -0
  56. package/examples/security/one-click/pri_upsell_3a.html +78 -0
  57. package/examples/security/one-click/pri_upsell_3b.html +78 -0
  58. package/examples/security/one-click/pri_upsell_3c.html +78 -0
  59. package/examples/security/pri1766322.html +333 -0
  60. package/examples/security/pri5529930.html +333 -0
  61. package/examples/security/pri6670988.html +334 -0
  62. package/examples/security/thank-you.html +65 -0
  63. package/examples/sim/funnel-flow.js +258 -0
  64. package/examples/sim/one-click-sim.js +167 -0
  65. package/examples/upsell-price-code.html +41 -0
  66. package/examples/url-context-navigation.html +96 -0
  67. 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>