@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,334 @@
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>Oferta pri6670988 — Checkout sim</title>
7
+ <link rel="stylesheet" href="checkout-sim.css" />
8
+ </head>
9
+ <body>
10
+ <div class="wrap">
11
+ <div class="main">
12
+ <header>
13
+ <h1 class="page-title">Checkout</h1>
14
+ <p class="page-sub">
15
+ Oferta pri6670988 (R$&nbsp;97) · lead → bump ADD/REMOVE · Complete
16
+ order → payment. Continuação do <code>sid</code>/<code>afid</code>
17
+ da VSL.
18
+ </p>
19
+ </header>
20
+
21
+ <form id="checkout-form">
22
+ <section class="card">
23
+ <h2>Customer Information</h2>
24
+ <div class="field">
25
+ <label for="email">Email</label>
26
+ <input
27
+ id="email"
28
+ name="email"
29
+ type="email"
30
+ autocomplete="email"
31
+ placeholder="user@example.com"
32
+ />
33
+ </div>
34
+ <div class="field">
35
+ <label for="phone">Phone number</label>
36
+ <input
37
+ id="phone"
38
+ name="phone"
39
+ type="tel"
40
+ autocomplete="tel"
41
+ placeholder="(11) 99999-9999"
42
+ />
43
+ <p class="hint">In case we need to call about the order.</p>
44
+ </div>
45
+ <label class="check">
46
+ <input type="checkbox" name="smsAlerts" checked />
47
+ <span>
48
+ <div class="check-label">
49
+ Get SMS alerts for order and shipping confirmations.
50
+ </div>
51
+ <div class="check-desc">
52
+ Consent is not required for purchase.
53
+ </div>
54
+ </span>
55
+ </label>
56
+ </section>
57
+
58
+ <section class="card">
59
+ <h2>Shipping Information</h2>
60
+ <div class="field">
61
+ <label for="address">Street Address</label>
62
+ <input
63
+ id="address"
64
+ name="address"
65
+ type="text"
66
+ autocomplete="street-address"
67
+ placeholder="Rua Exemplo, 123"
68
+ />
69
+ </div>
70
+ <button type="button" class="linkish" id="addr2-toggle">
71
+ + Add additional address line
72
+ </button>
73
+ <div class="field" id="addr2-field" hidden>
74
+ <label for="address2">Address line 2</label>
75
+ <input
76
+ id="address2"
77
+ name="address2"
78
+ type="text"
79
+ placeholder="Apto, bloco, etc."
80
+ />
81
+ </div>
82
+ <div class="row-3">
83
+ <div class="field">
84
+ <label for="state">State / Region</label>
85
+ <select id="state" name="state">
86
+ <option value="">Select…</option>
87
+ <option value="SP">São Paulo</option>
88
+ <option value="RJ">Rio de Janeiro</option>
89
+ <option value="MG">Minas Gerais</option>
90
+ <option value="PR">Paraná</option>
91
+ </select>
92
+ </div>
93
+ <div class="field">
94
+ <label for="city">City</label>
95
+ <input
96
+ id="city"
97
+ name="city"
98
+ type="text"
99
+ placeholder="São Paulo"
100
+ />
101
+ </div>
102
+ <div class="field">
103
+ <label for="postal">Postal Code</label>
104
+ <input
105
+ id="postal"
106
+ name="postal"
107
+ type="text"
108
+ autocomplete="postal-code"
109
+ placeholder="01310-100"
110
+ />
111
+ </div>
112
+ </div>
113
+ <label class="check">
114
+ <input type="checkbox" name="billingSame" checked />
115
+ <span>
116
+ <div class="check-label">Billing same as Shipping</div>
117
+ <div class="check-desc">
118
+ Uncheck to enter a different billing address.
119
+ </div>
120
+ </span>
121
+ </label>
122
+ </section>
123
+
124
+ <section class="card">
125
+ <h2>Payment Information</h2>
126
+ <p class="card-desc">All transactions are secure and encrypted.</p>
127
+ <p class="secure">We only accept Visa and MasterCard.</p>
128
+ <div class="pay-tabs" role="tablist" aria-label="Payment methods">
129
+ <button
130
+ type="button"
131
+ class="pay-tab"
132
+ data-method="CREDIT_CARD"
133
+ aria-selected="true"
134
+ >
135
+ Credit Card
136
+ </button>
137
+ <button
138
+ type="button"
139
+ class="pay-tab"
140
+ data-method="PAYPAL"
141
+ aria-selected="false"
142
+ >
143
+ PayPal
144
+ </button>
145
+ <button type="button" class="pay-tab" disabled>Apple Pay</button>
146
+ <button type="button" class="pay-tab" disabled>Google Pay</button>
147
+ </div>
148
+ <div id="card-fields">
149
+ <div class="field">
150
+ <label for="cardNumber">Card Number</label>
151
+ <input
152
+ id="cardNumber"
153
+ name="cardNumber"
154
+ type="text"
155
+ inputmode="numeric"
156
+ autocomplete="cc-number"
157
+ placeholder="1234 5678 9012 3456"
158
+ />
159
+ <p class="hint">Só UI — o Pixel nunca envia PAN/CVV.</p>
160
+ </div>
161
+ <div class="row-2">
162
+ <div class="field">
163
+ <label for="expiration">Expiration Date</label>
164
+ <input
165
+ id="expiration"
166
+ name="expiration"
167
+ type="text"
168
+ autocomplete="cc-exp"
169
+ placeholder="MM / YY"
170
+ />
171
+ </div>
172
+ <div class="field">
173
+ <label for="cvc">Security Code</label>
174
+ <input
175
+ id="cvc"
176
+ name="cvc"
177
+ type="text"
178
+ autocomplete="cc-csc"
179
+ placeholder="123"
180
+ />
181
+ </div>
182
+ </div>
183
+ </div>
184
+ </section>
185
+
186
+ <button class="btn-primary" type="submit">Complete order</button>
187
+ </form>
188
+ </div>
189
+
190
+ <aside class="aside">
191
+ <section class="card">
192
+ <h2>Order Summary</h2>
193
+ <p class="card-desc">
194
+ Estado do cart (commerce.cart.initialItems + bumps).
195
+ </p>
196
+ <div class="meta-row">
197
+ <span class="muted">Pixel</span>
198
+ <span id="pixel-status" class="pill">Init…</span>
199
+ </div>
200
+ <p id="cart-empty" class="empty-cart" hidden>Carrinho vazio.</p>
201
+ <ul id="cart-lines" class="lines"></ul>
202
+ <div class="total-row">
203
+ <strong>Total do pedido</strong>
204
+ <span id="cart-total" class="amount">—</span>
205
+ </div>
206
+ </section>
207
+
208
+ <section class="card">
209
+ <div class="bump-head">
210
+ <span class="pill bump">Order bump</span>
211
+ <h2>Entrega expressa</h2>
212
+ </div>
213
+ <p class="card-desc">
214
+ Marque para adicionar ao cart — dispara ADD_TO_CART via
215
+ <code>data-track</code>. Desmarcar dispara REMOVE_FROM_CART.
216
+ </p>
217
+ <label class="check" for="bump-check">
218
+ <input id="bump-check" type="checkbox" />
219
+ <span>
220
+ <div class="check-label">Order Bump Especial</div>
221
+ <div class="check-desc">
222
+ +R$&nbsp;47,00 · <code>order_bump_01</code>
223
+ </div>
224
+ </span>
225
+ </label>
226
+ <!-- Triggers — detectors auto (não usar attribute hidden) -->
227
+ <button
228
+ id="bump-add"
229
+ class="sr-trigger"
230
+ type="button"
231
+ data-track="add_to_cart"
232
+ data-product-id="order_bump_01"
233
+ data-product-name="Order Bump Especial"
234
+ data-price="47"
235
+ data-currency="BRL"
236
+ data-quantity="1"
237
+ tabindex="-1"
238
+ aria-hidden="true"
239
+ >
240
+ add bump
241
+ </button>
242
+ <button
243
+ id="bump-remove"
244
+ class="sr-trigger"
245
+ type="button"
246
+ data-track="remove_from_cart"
247
+ data-product-id="order_bump_01"
248
+ tabindex="-1"
249
+ aria-hidden="true"
250
+ >
251
+ remove bump
252
+ </button>
253
+ </section>
254
+
255
+ <section class="card debug">
256
+ <h2>Debug · eventos</h2>
257
+ <div class="kv">
258
+ <span class="muted">Transport sent</span>
259
+ <span id="dbg-transport" class="mono">—</span>
260
+ </div>
261
+ <div class="kv">
262
+ <span class="muted">sid</span>
263
+ <span id="dbg-sid" class="mono">—</span>
264
+ </div>
265
+ <div class="kv">
266
+ <span class="muted">afid</span>
267
+ <span id="dbg-afid" class="mono">—</span>
268
+ </div>
269
+ <p class="label">Cart runtime</p>
270
+ <p id="dbg-cart" class="mono">—</p>
271
+ <p class="label">Detectors ativos</p>
272
+ <p id="dbg-detectors" class="mono">—</p>
273
+ <p class="label">Dedupe key count</p>
274
+ <p id="dbg-dedupe" class="mono">—</p>
275
+ </section>
276
+ </aside>
277
+ </div>
278
+
279
+ <script src="../sim/funnel-flow.js"></script>
280
+ <script>
281
+ window.__CHECKOUT_SIM__ = {
282
+ currency: 'BRL',
283
+ nextHref: '../funnel/upsell/upsell_1.html',
284
+ main: {
285
+ id: 'pri6670988',
286
+ offerId: 'pri6670988',
287
+ name: 'Oferta Principal',
288
+ price: 97,
289
+ },
290
+ bump: {
291
+ id: 'order_bump_01',
292
+ offerId: 'offer_bump',
293
+ name: 'Order Bump Especial',
294
+ price: 47,
295
+ },
296
+ };
297
+
298
+ window.BemonyPixelConfig = {
299
+ scenario: 'checkout',
300
+ page: {
301
+ public: {
302
+ offerId: 'pri6670988',
303
+ productId: 'pri6670988',
304
+ },
305
+ },
306
+ commerce: {
307
+ currency: 'BRL',
308
+ products: [
309
+ {
310
+ id: 'pri6670988',
311
+ name: 'Oferta Principal',
312
+ type: 'main',
313
+ price: 97,
314
+ currency: 'BRL',
315
+ },
316
+ {
317
+ id: 'order_bump_01',
318
+ name: 'Order Bump Especial',
319
+ type: 'order_bump',
320
+ price: 47,
321
+ currency: 'BRL',
322
+ },
323
+ ],
324
+ },
325
+ };
326
+ </script>
327
+ <script
328
+ src="../../dist/browser/pixel-bemony.global.js"
329
+ data-scenario="checkout"
330
+ data-debug="true"
331
+ ></script>
332
+ <script src="checkout-sim.js"></script>
333
+ </body>
334
+ </html>
@@ -0,0 +1,65 @@
1
+ <!doctype html>
2
+ <html lang="pt-BR">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Security · Thank you</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: 32rem;
11
+ margin: 0 auto;
12
+ padding: 2rem 1rem;
13
+ background: linear-gradient(165deg, #eef6f2, #f7f3ea);
14
+ color: #17201b;
15
+ line-height: 1.5;
16
+ }
17
+ pre {
18
+ background: #142019;
19
+ color: #dceee6;
20
+ padding: 0.85rem;
21
+ font-size: 0.72rem;
22
+ overflow: auto;
23
+ }
24
+ </style>
25
+ </head>
26
+ <body>
27
+ <p>Bemony · security</p>
28
+ <h1>Obrigado!</h1>
29
+ <p>
30
+ Scenario <code>thank-you</code>. Sem purchase browser; sem catálogo.
31
+ Purchase continua server-side — esta página não inventa Order a partir
32
+ dos prices do funil.
33
+ </p>
34
+ <p id="order"></p>
35
+ <pre id="funnel-debug">aguardando…</pre>
36
+
37
+ <script src="../sim/funnel-flow.js"></script>
38
+ <script
39
+ src="../../dist/browser/pixel-bemony.global.js"
40
+ data-environment="development"
41
+ data-scenario="thank-you"
42
+ data-funnel-id="funnel_1"
43
+ data-funnel-step-id="step_thank_you"
44
+ data-allowed-domains="localhost,127.0.0.1,security.bemony.com"
45
+ ></script>
46
+ <script>
47
+ (function () {
48
+ var sim = window.BemonyFunnelSim;
49
+ var oid = sim.readOid();
50
+ if (oid) {
51
+ document.getElementById('order').textContent = 'Order pública: ' + oid;
52
+ }
53
+ sim.whenReady().then(function (pixel) {
54
+ sim.logPixel('thank-you', pixel);
55
+ sim.wireDebugPanel(pixel);
56
+ var fromPixel = pixel.getPublicOrderId && pixel.getPublicOrderId();
57
+ if (fromPixel) {
58
+ document.getElementById('order').textContent =
59
+ 'Order pública: ' + fromPixel;
60
+ }
61
+ });
62
+ })();
63
+ </script>
64
+ </body>
65
+ </html>
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Helper compartilhado dos examples do funil Bemony (simulação local).
3
+ *
4
+ * Funciona em:
5
+ * - HTTP: `pnpm dlx serve packages/pixel-bemony` → /examples/...
6
+ * - file://: abrir HTML direto no disco (paths absolutos `/examples/...` quebram)
7
+ *
8
+ * Preserva params públicos (sid, afid, oid, utm_*, fbclid, …) entre páginas.
9
+ */
10
+ (function (global) {
11
+ var MANAGED = {
12
+ sid: true,
13
+ afid: true,
14
+ oid: true,
15
+ fid: true,
16
+ fsid: true,
17
+ };
18
+
19
+ var ATTRIBUTION = {
20
+ utm_source: true,
21
+ utm_medium: true,
22
+ utm_campaign: true,
23
+ utm_content: true,
24
+ utm_term: true,
25
+ fbclid: true,
26
+ gclid: true,
27
+ ttclid: true,
28
+ };
29
+
30
+ /**
31
+ * Prefixo até `/examples` na URL atual.
32
+ * file:///C:/Users/.../pixel-bemony/examples/funnel/upsell/upsell_1.html
33
+ * → .../pixel-bemony/examples
34
+ * http://localhost:3000/examples/funnel/...
35
+ * → /examples
36
+ */
37
+ function examplesRootUrl() {
38
+ var url = new URL(global.location.href);
39
+ var path = url.pathname.replace(/\\/g, '/');
40
+ var marker = '/examples';
41
+ var idx = path.lastIndexOf(marker);
42
+ if (idx === -1) {
43
+ throw new Error(
44
+ '[BemonyFunnelSim] URL atual não contém /examples/: ' + url.href
45
+ );
46
+ }
47
+ url.pathname = path.slice(0, idx + marker.length);
48
+ url.search = '';
49
+ url.hash = '';
50
+ // garantir barra final para new URL(rel, base) resolver como diretório
51
+ if (!url.pathname.endsWith('/')) {
52
+ url.pathname += '/';
53
+ }
54
+ return url;
55
+ }
56
+
57
+ /**
58
+ * Converte path lógico sob examples/ em href absoluto correto (file:// ou http).
59
+ * Aceita: "funnel/upsell/upsell_1.html" ou "/examples/funnel/..."
60
+ */
61
+ function resolveExamplesHref(logicalPath) {
62
+ var clean = String(logicalPath || '').replace(/\\/g, '/');
63
+ clean = clean.replace(/^\/+/, '');
64
+ if (clean.indexOf('examples/') === 0) {
65
+ clean = clean.slice('examples/'.length);
66
+ }
67
+ return new URL(clean, examplesRootUrl()).href;
68
+ }
69
+
70
+ function currentParams() {
71
+ return new URLSearchParams(global.location.search);
72
+ }
73
+
74
+ function mergeParams(extra) {
75
+ var out = currentParams();
76
+ if (extra) {
77
+ Object.keys(extra).forEach(function (key) {
78
+ var value = extra[key];
79
+ if (value === null || value === undefined || value === '') {
80
+ out.delete(key);
81
+ } else {
82
+ out.set(key, String(value));
83
+ }
84
+ });
85
+ }
86
+ return out;
87
+ }
88
+
89
+ function withParams(href, extra) {
90
+ var absolute;
91
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(href)) {
92
+ // http:, https:, file:, etc.
93
+ absolute = href;
94
+ } else if (href.charAt(0) === '.') {
95
+ // relativo ao documento atual (../funnel/...)
96
+ absolute = new URL(href, global.location.href).href;
97
+ } else {
98
+ // path lógico sob examples/ (funnel/upsell/...)
99
+ absolute = resolveExamplesHref(href);
100
+ }
101
+ var url = new URL(absolute, global.location.href);
102
+ var merged = mergeParams(extra);
103
+ merged.forEach(function (value, key) {
104
+ if (MANAGED[key] || ATTRIBUTION[key] || key.indexOf('utm_') === 0) {
105
+ url.searchParams.set(key, value);
106
+ }
107
+ });
108
+ return url.href;
109
+ }
110
+
111
+ function navigate(href, extra) {
112
+ global.location.assign(withParams(href, extra));
113
+ }
114
+
115
+ function ensureOid() {
116
+ var params = currentParams();
117
+ var oid = params.get('oid');
118
+ if (oid && oid.trim()) return oid.trim();
119
+ oid = 'ord_' + Math.random().toString(36).slice(2, 10);
120
+ var url = new URL(global.location.href);
121
+ url.searchParams.set('oid', oid);
122
+ global.history.replaceState({}, '', url.href);
123
+ return oid;
124
+ }
125
+
126
+ function readOid() {
127
+ return currentParams().get('oid');
128
+ }
129
+
130
+ function getApi() {
131
+ var bp = global.BemonyPixel;
132
+ if (!bp) return null;
133
+ if (typeof bp.whenReady === 'function') return bp;
134
+ if (
135
+ bp.bemonyPixelGlobalApi &&
136
+ typeof bp.bemonyPixelGlobalApi.whenReady === 'function'
137
+ ) {
138
+ return bp.bemonyPixelGlobalApi;
139
+ }
140
+ return bp;
141
+ }
142
+
143
+ function whenReady() {
144
+ var api = getApi();
145
+ if (api && typeof api.whenReady === 'function') {
146
+ return api.whenReady();
147
+ }
148
+ return new Promise(function (resolve, reject) {
149
+ var n = 0;
150
+ function tick() {
151
+ var again = getApi();
152
+ if (again && typeof again.whenReady === 'function') {
153
+ again.whenReady().then(resolve, reject);
154
+ return;
155
+ }
156
+ if (n++ > 80) {
157
+ reject(new Error('BemonyPixel.whenReady indisponível'));
158
+ return;
159
+ }
160
+ global.setTimeout(tick, 50);
161
+ }
162
+ tick();
163
+ });
164
+ }
165
+
166
+ function logPixel(label, pixel) {
167
+ console.log('[' + label + ']', {
168
+ scenario: pixel.getScenario && pixel.getScenario(),
169
+ state: pixel.getState && pixel.getState(),
170
+ sid: pixel.getPublicSessionId && pixel.getPublicSessionId(),
171
+ oid: readOid(),
172
+ url: global.location.href,
173
+ });
174
+ }
175
+
176
+ function wireDebugPanel(pixel, rootId) {
177
+ var root = global.document.getElementById(rootId || 'funnel-debug');
178
+ if (!root) return;
179
+ root.textContent = JSON.stringify(
180
+ {
181
+ scenario: pixel.getScenario && pixel.getScenario(),
182
+ sid: pixel.getPublicSessionId && pixel.getPublicSessionId(),
183
+ oid: readOid(),
184
+ },
185
+ null,
186
+ 2
187
+ );
188
+ }
189
+
190
+ /**
191
+ * Paths lógicos relativos a examples/ (NÃO começam com /).
192
+ * resolveExamplesHref() monta a URL correta em file:// e http://.
193
+ *
194
+ * Pasta tipada historicamente como "donsell" (typo mantido no FS).
195
+ */
196
+ var FLOW = {
197
+ lastStage: 3,
198
+ offer: 'funnel/offer-page.html',
199
+ checkoutByPrice: {
200
+ pri6670988: 'security/pri6670988.html',
201
+ pri5529930: 'security/pri5529930.html',
202
+ pri1766322: 'security/pri1766322.html',
203
+ },
204
+ afterCheckout: 'funnel/upsell/upsell_1.html',
205
+ upsellOffer: {
206
+ 1: 'funnel/upsell/upsell_1.html',
207
+ 2: 'funnel/upsell/upsell_2.html',
208
+ 3: 'funnel/upsell/upsell_3.html',
209
+ },
210
+ upsellOneClick: {
211
+ 1: 'security/one-click/pri_upsell_1.html',
212
+ 2: 'security/one-click/pri_upsell_2.html',
213
+ 3: 'security/one-click/pri_upsell_3.html',
214
+ },
215
+ downsellOffer: {
216
+ 1: 'funnel/donsell/downsell_1.html',
217
+ 2: 'funnel/donsell/downsell_2.html',
218
+ 3: 'funnel/donsell/downsell_3.html',
219
+ },
220
+ downsellOneClick: {
221
+ 1: 'security/one-click/pri_downsell_1.html',
222
+ 2: 'security/one-click/pri_downsell_2.html',
223
+ 3: 'security/one-click/pri_downsell_3.html',
224
+ },
225
+ thankYou: 'security/thank-you.html',
226
+ };
227
+
228
+ function nextAfterUpsellAccept(stage) {
229
+ if (stage >= FLOW.lastStage) return FLOW.thankYou;
230
+ return FLOW.upsellOffer[stage + 1];
231
+ }
232
+
233
+ function nextAfterUpsellDecline(stage) {
234
+ return FLOW.downsellOffer[stage] || FLOW.thankYou;
235
+ }
236
+
237
+ function nextAfterDownsell(stage) {
238
+ if (stage >= FLOW.lastStage) return FLOW.thankYou;
239
+ return FLOW.upsellOffer[stage + 1];
240
+ }
241
+
242
+ global.BemonyFunnelSim = {
243
+ withParams: withParams,
244
+ navigate: navigate,
245
+ ensureOid: ensureOid,
246
+ readOid: readOid,
247
+ whenReady: whenReady,
248
+ logPixel: logPixel,
249
+ wireDebugPanel: wireDebugPanel,
250
+ resolveExamplesHref: resolveExamplesHref,
251
+ FLOW: FLOW,
252
+ nextAfterUpsellAccept: nextAfterUpsellAccept,
253
+ nextAfterUpsellDecline: nextAfterUpsellDecline,
254
+ nextAfterDownsell: nextAfterDownsell,
255
+ /** Token demo — só memória local; nunca usar em produção. */
256
+ DEMO_ONE_CLICK_TOKEN: 'demo_oc_token_LOCAL_SIM_ONLY',
257
+ };
258
+ })(typeof window !== 'undefined' ? window : globalThis);