mkfashion-sdk 2.7.2 → 2.7.4
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/docs/2026-05-22-sdk-v3-ecommerce-tracking-design.md +311 -0
- package/index.html +79 -178
- package/mkfashion-sdk-2.7.3.tgz +0 -0
- package/mkfashion-sdk-2.7.4.tgz +0 -0
- package/package.json +2 -2
- package/src/mkfashion.js +39 -560
- package/test-e2e.html +175 -0
- package/test-responsive.html +45 -0
- package/mkfashion-sdk-2.5.0.tgz +0 -0
- package/mkfashion-sdk-2.7.0.tgz +0 -0
- package/mkfashion-sdk-2.7.1.tgz +0 -0
- package/mkfashion-sdk-2.7.2.tgz +0 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# SDK v3 — Tracking de E-commerce Completo (Core Tracker) — Design Spec
|
|
2
|
+
|
|
3
|
+
**Empresa:** MetaKosmos
|
|
4
|
+
**Data:** 2026-05-22
|
|
5
|
+
**Status:** Rascunho para revisão
|
|
6
|
+
**Repos afetados:** `mkfashion-sdk`, `mk-collector-api`
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Contexto e motivação
|
|
11
|
+
|
|
12
|
+
### Situação atual (v2.7.2)
|
|
13
|
+
|
|
14
|
+
A `mkfashion-sdk` roda **apenas na PDP** (página de produto). O cliente chama
|
|
15
|
+
`mkfashion.init({ projectId, identifier })` ou `isAvailable(...)` na PDP, e a
|
|
16
|
+
partir daí a SDK:
|
|
17
|
+
|
|
18
|
+
- Auto-captura `page_view`, `page_click`, `page_scroll_depth`, `page_form_submit`,
|
|
19
|
+
`page_engagement` da PDP.
|
|
20
|
+
- Captura eventos do iframe de try-on (`page_modal_opened`, `page_generation_complete`, etc).
|
|
21
|
+
- Detecta `add_to_cart` por heurística de texto de botão.
|
|
22
|
+
|
|
23
|
+
**Limitação:** só enxergamos a PDP. Não vemos a jornada completa do visitante —
|
|
24
|
+
entrada, navegação por categorias, carrinho, checkout e **compra final**. Sem o
|
|
25
|
+
purchase, não conseguimos responder a pergunta de negócio mais importante:
|
|
26
|
+
|
|
27
|
+
> **"Visitantes que usaram o try-on convertem mais do que os que não usaram?"**
|
|
28
|
+
|
|
29
|
+
### Objetivo da v3
|
|
30
|
+
|
|
31
|
+
Transformar a SDK de "widget de PDP" em **core tracker de e-commerce** que roda
|
|
32
|
+
em **todas as páginas** do site, capturando o funil completo:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
entrada → navegação → produto → (try-on) → carrinho → checkout → COMPRA
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Restrições de design (decididas com o stakeholder):**
|
|
39
|
+
|
|
40
|
+
1. **Zero trabalho extra pro cliente** além de importar o script. Nada de chamar
|
|
41
|
+
métodos novos, nada de configurar dataLayer.
|
|
42
|
+
2. **Zero dependência de ferramentas de terceiros** (GA4/GTM podem não existir
|
|
43
|
+
na loja). Captura por sinais NATIVOS do e-commerce.
|
|
44
|
+
3. **Try-on continua igual**: a marca chama `open()`/`isAvailable()` só na PDP,
|
|
45
|
+
exatamente como hoje.
|
|
46
|
+
4. **Compatibilidade**: clientes na v2 (SDK na PDP) continuam funcionando durante
|
|
47
|
+
a migração.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 2. Princípio central: captura por sinais nativos
|
|
52
|
+
|
|
53
|
+
Toda loja — independente de ter GA4, GTM, ou qualquer analytics — possui 3 coisas
|
|
54
|
+
que existem porque a loja precisa **funcionar e ser indexada**:
|
|
55
|
+
|
|
56
|
+
| Sinal nativo | Sempre existe? | Usado para |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| **URLs estruturadas** | ✅ sim | detectar etapa do funil (pdp/cart/checkout/purchase) |
|
|
59
|
+
| **Botões/links clicáveis** | ✅ sim | add_to_cart, begin_checkout (heurística de texto) |
|
|
60
|
+
| **HTML semântico** (JSON-LD, meta, DOM) | ✅ quase sempre | produto, preço, order_id, revenue |
|
|
61
|
+
|
|
62
|
+
**Hierarquia de confiança para cada dado:**
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Conversão (houve compra?) → URL de confirmação → 100% confiável
|
|
66
|
+
Produto visto → URL /produto + JSON-LD → 100%
|
|
67
|
+
Add to cart → clique em botão (heur.) → ~90%
|
|
68
|
+
Revenue da compra → JSON-LD Order > DOM > meta → best-effort
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
> **dataLayer é tratado como BÔNUS oportunístico** — se existir, enriquece; se
|
|
72
|
+
> não, ignoramos. Nunca é dependência.
|
|
73
|
+
|
|
74
|
+
### A distinção crucial: conversão vs revenue
|
|
75
|
+
|
|
76
|
+
- **Conversão (binária)**: "houve uma compra?" → detectável 100% por URL da
|
|
77
|
+
página de confirmação. Já responde a pergunta de negócio principal.
|
|
78
|
+
- **Revenue (valor exato)**: best-effort via JSON-LD/DOM. Se a loja não expõe,
|
|
79
|
+
fica sem valor — mas a conversão continua contada.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 3. Arquitetura em camadas
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
mkfashion-sdk (v3)
|
|
87
|
+
│
|
|
88
|
+
├── core/ ← roda em TODA página
|
|
89
|
+
│ ├── identity.js visitorId + sessionId (localStorage/sessionStorage)
|
|
90
|
+
│ ├── transport.js batch + flush + sendBeacon
|
|
91
|
+
│ ├── pageContext.js detecta tipo de página por URL/JSON-LD
|
|
92
|
+
│ ├── autoTrack.js page_view, click, scroll, form, engagement
|
|
93
|
+
│ └── sanitize.js remove PII e campos pesados antes de enviar
|
|
94
|
+
│
|
|
95
|
+
├── ecommerce/ ← NOVO — funil de compra (sinais nativos)
|
|
96
|
+
│ ├── catalog.js view_item_list, select_item, view_item (URL + JSON-LD)
|
|
97
|
+
│ ├── cart.js add_to_cart (heurística), view_cart (URL)
|
|
98
|
+
│ ├── checkout.js begin_checkout (URL/clique), purchase (URL + extração)
|
|
99
|
+
│ └── extractors.js JSON-LD, meta tags, DOM scraping, dataLayer (bônus)
|
|
100
|
+
│
|
|
101
|
+
└── tryon/ ← módulo existente (sem mudança funcional)
|
|
102
|
+
└── open(), isAvailable(), getAvailability(), postMessage bridge
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Carregamento
|
|
106
|
+
|
|
107
|
+
```html
|
|
108
|
+
<!-- Cliente coloca no <head> do TEMA GLOBAL (todas as páginas) -->
|
|
109
|
+
<script src="https://unpkg.com/mkfashion-sdk@3/src/mkfashion.js"
|
|
110
|
+
data-mk-project="698c7c681d3129430f15dddb"
|
|
111
|
+
async></script>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Single source: a SDK detecta a página, inicia o tracking de core + ecommerce, e
|
|
115
|
+
expõe a API de try-on pra ser chamada na PDP.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 4. Detecção de contexto de página
|
|
120
|
+
|
|
121
|
+
`core/pageContext.js` classifica a página em cascata (primeiro match vence):
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
function detectPageType() {
|
|
125
|
+
// 1. Override explícito (se o cliente quiser forçar)
|
|
126
|
+
if (window.__mkPageType) return window.__mkPageType
|
|
127
|
+
|
|
128
|
+
// 2. JSON-LD (@type indica tipo de página)
|
|
129
|
+
const ld = readJsonLd()
|
|
130
|
+
if (ld?.['@type'] === 'Product') return 'pdp'
|
|
131
|
+
if (ld?.['@type'] === 'Order' || ld?.['@type'] === 'Invoice') return 'purchase'
|
|
132
|
+
if (ld?.['@type'] === 'CollectionPage' || ld?.['@type'] === 'SearchResultsPage') return 'category'
|
|
133
|
+
|
|
134
|
+
// 3. URL patterns (fallback universal)
|
|
135
|
+
const p = location.pathname.toLowerCase()
|
|
136
|
+
if (p === '/' || p === '') return 'home'
|
|
137
|
+
if (/\/(thank[_-]?you|orders?\/|pedido[_-]?(confirmado|realizado)|sucesso|order[_-]confirmation)/.test(p)) return 'purchase'
|
|
138
|
+
if (/\/checkout/.test(p)) return 'checkout'
|
|
139
|
+
if (/\/(cart|carrinho|sacola|bag)(\/|$|\?)/.test(p)) return 'cart'
|
|
140
|
+
if (/\/(products?|produtos?|p)\//.test(p)) return 'pdp'
|
|
141
|
+
if (/\/(collections?|categoria|c)\//.test(p)) return 'category'
|
|
142
|
+
if (/\/(search|busca|s)(\/|\?)/.test(p)) return 'search'
|
|
143
|
+
return 'other'
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Cada `page_view` carrega `pageType` no `params`, dando semântica ao funil.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 5. Mapa de eventos do funil
|
|
152
|
+
|
|
153
|
+
| Etapa | Evento (collector) | Como detecta (sem GA) | Dados |
|
|
154
|
+
|---|---|---|---|
|
|
155
|
+
| Entrada | `page_view` + pageType | sempre | url, referrer, utm, pageType |
|
|
156
|
+
| Listagem | `page_view_item_list` | pageType=category + JSON-LD ItemList | SKUs visíveis |
|
|
157
|
+
| Seleção | `page_select_item` | clique em card de produto (link `/produto/`) | SKU clicado |
|
|
158
|
+
| Produto | `page_view_item` | pageType=pdp + JSON-LD Product | SKU, preço, nome |
|
|
159
|
+
| Try-on | `page_modal_opened`, `page_generation_complete`, ... | já existe (postMessage) | já existe |
|
|
160
|
+
| Add cart | `page_add_to_cart` | heurística de clique (já temos) + JSON-LD price | SKU, texto botão |
|
|
161
|
+
| Carrinho | `page_view_cart` | pageType=cart | — |
|
|
162
|
+
| Checkout | `page_begin_checkout` | pageType=checkout OU clique "Finalizar" | — |
|
|
163
|
+
| **Compra** | `page_purchase` | **pageType=purchase (URL)** | orderId?, revenue?, items? |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 6. Detecção de purchase (o ponto crítico)
|
|
168
|
+
|
|
169
|
+
### 6.1 Conversão (binária) — 100% confiável
|
|
170
|
+
|
|
171
|
+
Ao detectar `pageType === 'purchase'` (via URL/JSON-LD), emite `page_purchase`
|
|
172
|
+
imediatamente. Isso conta a conversão mesmo sem revenue.
|
|
173
|
+
|
|
174
|
+
### 6.2 Revenue (best-effort) — cascata de extractors
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
function extractOrderData() {
|
|
178
|
+
// 1. JSON-LD Order (existe pro SEO, independente de GA)
|
|
179
|
+
const ld = readJsonLd(['Order', 'Invoice'])
|
|
180
|
+
if (ld) return { orderId: ld.orderNumber || ld.confirmationNumber,
|
|
181
|
+
revenue: ld.price || ld.totalPaymentDue?.price,
|
|
182
|
+
currency: ld.priceCurrency }
|
|
183
|
+
|
|
184
|
+
// 2. dataLayer (BÔNUS — só se a loja tiver GA/GTM)
|
|
185
|
+
const dl = window.dataLayer?.find(x => x.event === 'purchase' && x.ecommerce)
|
|
186
|
+
if (dl) return { orderId: dl.ecommerce.transaction_id,
|
|
187
|
+
revenue: dl.ecommerce.value,
|
|
188
|
+
currency: dl.ecommerce.currency,
|
|
189
|
+
items: dl.ecommerce.items }
|
|
190
|
+
|
|
191
|
+
// 3. Meta tags
|
|
192
|
+
const metaTotal = document.querySelector('meta[property="order:total"]')?.content
|
|
193
|
+
if (metaTotal) return { revenue: parseFloat(metaTotal) }
|
|
194
|
+
|
|
195
|
+
// 4. DOM scraping (fallback frágil — best-effort)
|
|
196
|
+
// procura elementos com classe/texto de total: .order-total, #total, "Total: R$"
|
|
197
|
+
const domTotal = scrapeOrderTotal()
|
|
198
|
+
if (domTotal) return { revenue: domTotal, _source: 'dom_scrape' }
|
|
199
|
+
|
|
200
|
+
return null // conversão já contada; sem revenue
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 6.3 Dedup
|
|
205
|
+
|
|
206
|
+
Página de confirmação pode recarregar (F5) → mesma compra contada 2x. Dedup por:
|
|
207
|
+
- `orderId` quando disponível (collector ignora purchase com orderId já visto)
|
|
208
|
+
- fallback: 1 purchase por `sessionId` por janela de 1h
|
|
209
|
+
|
|
210
|
+
### 6.4 Opt-in pra precisão (opcional, não obrigatório)
|
|
211
|
+
|
|
212
|
+
Cliente que QUER revenue garantido e não tem JSON-LD pode chamar 1 linha:
|
|
213
|
+
```js
|
|
214
|
+
mkfashion.trackPurchase({ orderId: 'X', revenue: 599.80, currency: 'BRL' })
|
|
215
|
+
```
|
|
216
|
+
Mas é **opcional** — a captura automática cobre a maioria.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 7. A pergunta de negócio que isso destrava
|
|
221
|
+
|
|
222
|
+
Com `visitorId` consistente em todas as páginas + purchase capturado:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
Coorte A: visitantes que abriram try-on (page_modal_opened)
|
|
226
|
+
Coorte B: visitantes que NÃO abriram
|
|
227
|
+
|
|
228
|
+
Conversão A = purchases(A) / visitantes(A)
|
|
229
|
+
Conversão B = purchases(B) / visitantes(B)
|
|
230
|
+
|
|
231
|
+
Uplift = Conversão A / Conversão B
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Exemplo de resultado esperado:
|
|
235
|
+
```
|
|
236
|
+
Try-on: 8.2% convertem
|
|
237
|
+
Sem try-on: 3.1% convertem
|
|
238
|
+
Uplift: 2.6x ← argumento de venda do produto MK
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 8. Fases de implementação
|
|
244
|
+
|
|
245
|
+
| Fase | Escopo | Risco |
|
|
246
|
+
|---|---|---|
|
|
247
|
+
| **1** | Refatorar SDK em `core/ ecommerce/ tryon/` sem mudar comportamento | baixo |
|
|
248
|
+
| **2** | `pageContext.js` — detecção de tipo de página | baixo |
|
|
249
|
+
| **3** | `extractors.js` — JSON-LD, meta, DOM, dataLayer(bônus) | médio |
|
|
250
|
+
| **4** | `ecommerce/*` — eventos de funil (view_item, cart, checkout, purchase) | médio |
|
|
251
|
+
| **5** | Backend: novos event types + dedup de purchase + agregação de funil | médio |
|
|
252
|
+
| **6** | Cliente migra snippet pra tema global | baixo (config) |
|
|
253
|
+
| **7** | Dashboard de funil + atribuição try-on→conversão | médio |
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 9. Mudanças no backend (mk-collector-api)
|
|
258
|
+
|
|
259
|
+
- Novos `event_name` aceitos (validação já é livre — `page_*`).
|
|
260
|
+
- `sdk-aggregator.service.js`: agregar funil por dia:
|
|
261
|
+
```js
|
|
262
|
+
funnel: {
|
|
263
|
+
sessions, viewItem, addToCart, beginCheckout, purchase,
|
|
264
|
+
purchaseRevenue, conversionRate
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
- Dedup de purchase por `orderId` (nova lógica no controller ou aggregator).
|
|
268
|
+
- `sdk_daily_metrics`: campos novos `purchases`, `revenue`, `funnel`.
|
|
269
|
+
- Atribuição try-on: cruzar visitors com `page_modal_opened` × `page_purchase`
|
|
270
|
+
(no aggregator ou query sob demanda).
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 10. Privacidade / LGPD
|
|
275
|
+
|
|
276
|
+
- **NUNCA** capturar campos de formulário de checkout (email, CPF, endereço, cartão).
|
|
277
|
+
- `core/sanitize.js` filtra chaves sensíveis (`email`, `cpf`, `phone`, `address`,
|
|
278
|
+
`card`, `password`) de qualquer payload antes de enviar.
|
|
279
|
+
- `orderId` e `revenue` não são PII.
|
|
280
|
+
- IP continua sendo descartado server-side (só GeoIP).
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## 11. Riscos e mitigações
|
|
285
|
+
|
|
286
|
+
| Risco | Mitigação |
|
|
287
|
+
|---|---|
|
|
288
|
+
| Loja sem JSON-LD nem dataLayer | Conversão por URL (100%); revenue fica nulo (aceitável) |
|
|
289
|
+
| URL de confirmação atípica | Lista de patterns extensível + override `window.__mkPageType` |
|
|
290
|
+
| Purchase contado 2x (refresh) | Dedup por orderId / sessionId |
|
|
291
|
+
| DOM scraping frágil | Marcado com `_source: 'dom_scrape'` pra saber confiabilidade |
|
|
292
|
+
| Volume 5-10x maior (todas páginas) | Batching + sampling de eventos de baixo valor |
|
|
293
|
+
| PII vazando | sanitize.js + nunca ler campos de form |
|
|
294
|
+
| SPA sem reload entre páginas | history.pushState hook (já temos) re-detecta pageType |
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## 12. Decisões em aberto (pra próxima revisão)
|
|
299
|
+
|
|
300
|
+
1. Nome do pacote: manter `mkfashion-sdk` v3 ou renomear? (decidir depois)
|
|
301
|
+
2. Sampling: amostrar `page_scroll_depth`/`page_engagement` em sites de alto volume?
|
|
302
|
+
3. Atribuição try-on→conversão: pré-computar no aggregator ou query sob demanda?
|
|
303
|
+
4. Dashboard de funil: nova aba ou endpoint `/api/export/funnel`?
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## 13. Referências
|
|
308
|
+
|
|
309
|
+
- SDK atual: `mkfashion-sdk/src/mkfashion.js` (v2.7.2)
|
|
310
|
+
- Collector: `mk-collector-api/` (rotas `/v1/track/sdk`, agregadores)
|
|
311
|
+
- Spec de eventos GA4 ecommerce (usado só como referência de naming dos eventos)
|
package/index.html
CHANGED
|
@@ -1,178 +1,79 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="pt-BR">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title>Provador Virtual</title>
|
|
8
|
-
<script src="./src/mkfashion.js"></script>
|
|
9
|
-
</head>
|
|
10
|
-
|
|
11
|
-
<body>
|
|
12
|
-
|
|
13
|
-
<div id="mkfashion-container"></div>
|
|
14
|
-
|
|
15
|
-
<script>
|
|
16
|
-
(function () {
|
|
17
|
-
var projectid = '69f9e5b0f4eb0c4403319830';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
// VTEX — várias formas de expor a SKU
|
|
82
|
-
if (window.vtxctx && window.vtxctx.skus && window.vtxctx.skus[0]) {
|
|
83
|
-
return { value: String(window.vtxctx.skus[0]), source: 'vtex/vtxctx' };
|
|
84
|
-
}
|
|
85
|
-
if (window.skuJson_0 && window.skuJson_0.skus && window.skuJson_0.skus[0]) {
|
|
86
|
-
return { value: String(window.skuJson_0.skus[0].sku), source: 'vtex/skuJson' };
|
|
87
|
-
}
|
|
88
|
-
// Konfidency (serviço de reviews usado por várias marcas BR — Marisa, etc.)
|
|
89
|
-
var kd = window.konfidencyData;
|
|
90
|
-
if (kd && kd.product && kd.product.variants && kd.product.variants[0]) {
|
|
91
|
-
return { value: String(kd.product.variants[0]), source: 'konfidency/variant' };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Nota: NÃO usamos padrão de URL como fallback (ex: /p/{id} na VTEX
|
|
95
|
-
// retorna o productId, não o SKU). É melhor falhar do que dar dado
|
|
96
|
-
// errado — quem precisar de override deve passar ?sku= na URL.
|
|
97
|
-
|
|
98
|
-
// 7. Fallback hardcoded — útil pra rodar index.html localmente sem
|
|
99
|
-
// estrutura de PDP. Em produção, remova ou ajuste pro seu cenário.
|
|
100
|
-
return { value: '1881', source: 'fallback-hardcoded' };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ============================================================
|
|
104
|
-
// Inicialização
|
|
105
|
-
// ============================================================
|
|
106
|
-
function initMkFashion() {
|
|
107
|
-
var detected = detectIdentifier();
|
|
108
|
-
console.log('[mKFashion] SKU detectado:', detected.value, '(via ' + detected.source + ')');
|
|
109
|
-
|
|
110
|
-
// Callback de carrinho — recebe o produto escolhido no provador
|
|
111
|
-
// e adiciona ao carrinho da plataforma (exemplo: Shopify).
|
|
112
|
-
mkfashion.addToCart(function (payload) {
|
|
113
|
-
console.log('[mKFashion] addToCart payload:', payload);
|
|
114
|
-
|
|
115
|
-
var variantSku = payload.selectedIdentifier;
|
|
116
|
-
if (!variantSku) {
|
|
117
|
-
console.warn('[mKFashion] produto sem variantSku — pulando adição ao carrinho');
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Properties opcionais — passa tamanho/cor se vieram no payload.
|
|
122
|
-
// No Shopify aparecem no carrinho como propriedades de line item.
|
|
123
|
-
var properties = {};
|
|
124
|
-
if (payload.selectedSize) properties['Tamanho'] = payload.selectedSize;
|
|
125
|
-
if (payload.selectedColor) properties['Cor'] = payload.selectedColor;
|
|
126
|
-
|
|
127
|
-
var item = { id: variantSku, quantity: 1 };
|
|
128
|
-
if (Object.keys(properties).length) item.properties = properties;
|
|
129
|
-
|
|
130
|
-
// Endpoint Shopify-style. Ajuste pra sua plataforma:
|
|
131
|
-
// VTEX: POST /api/checkout/pub/orderForm/{id}/items
|
|
132
|
-
// Wake: API própria
|
|
133
|
-
// Magento: rest/V1/carts/mine/items
|
|
134
|
-
fetch('/cart/add.js', {
|
|
135
|
-
method: 'POST',
|
|
136
|
-
headers: { 'Content-Type': 'application/json' },
|
|
137
|
-
body: JSON.stringify({ items: [item] })
|
|
138
|
-
})
|
|
139
|
-
.then(function (response) { return response.json(); })
|
|
140
|
-
.then(function (data) {
|
|
141
|
-
console.log('[mKFashion] produto adicionado ao carrinho:', data);
|
|
142
|
-
// document.dispatchEvent(new CustomEvent('cart:updated'));
|
|
143
|
-
})
|
|
144
|
-
.catch(function (error) {
|
|
145
|
-
console.error('[mKFashion] erro ao adicionar ao carrinho:', error);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
mkfashion.onInteraction(function (data) {
|
|
150
|
-
console.log(data.category, data.action);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
mkfashion.init({
|
|
154
|
-
projectId: projectid,
|
|
155
|
-
identifier: detected.value,
|
|
156
|
-
target: '#mkfashion-container'
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function waitForMkFashion() {
|
|
161
|
-
if (typeof mkfashion !== 'undefined') {
|
|
162
|
-
initMkFashion();
|
|
163
|
-
} else {
|
|
164
|
-
setTimeout(waitForMkFashion, 100);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (document.readyState === "loading") {
|
|
169
|
-
document.addEventListener("DOMContentLoaded", waitForMkFashion);
|
|
170
|
-
} else {
|
|
171
|
-
waitForMkFashion();
|
|
172
|
-
}
|
|
173
|
-
})();
|
|
174
|
-
</script>
|
|
175
|
-
|
|
176
|
-
</body>
|
|
177
|
-
|
|
178
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="pt-BR">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Provador Virtual</title>
|
|
8
|
+
<script src="./src/mkfashion.js"></script>
|
|
9
|
+
</head>
|
|
10
|
+
|
|
11
|
+
<body>
|
|
12
|
+
|
|
13
|
+
<div id="mkfashion-container"></div>
|
|
14
|
+
|
|
15
|
+
<script>
|
|
16
|
+
(function () {
|
|
17
|
+
var projectid = '69f9e5b0f4eb0c4403319830';
|
|
18
|
+
var identifier = '1881';
|
|
19
|
+
|
|
20
|
+
function initMkFashion() {
|
|
21
|
+
mkfashion.addToCart(function (payload) {
|
|
22
|
+
console.log('Adicionando ao carrinho:', payload);
|
|
23
|
+
|
|
24
|
+
var variantSku = payload.selectedIdentifier;
|
|
25
|
+
|
|
26
|
+
fetch('/cart/add.js', {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify({
|
|
32
|
+
items: [{
|
|
33
|
+
quantity: 1,
|
|
34
|
+
id: variantSku
|
|
35
|
+
}]
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
.then(function (response) {
|
|
39
|
+
return response.json();
|
|
40
|
+
})
|
|
41
|
+
.then(function (data) {
|
|
42
|
+
console.log('Produto adicionado:', data);
|
|
43
|
+
// document.dispatchEvent(new CustomEvent('cart:updated'));
|
|
44
|
+
})
|
|
45
|
+
.catch(function (error) {
|
|
46
|
+
console.error('Erro carrinho:', error);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
mkfashion.onInteraction(function (data) {
|
|
51
|
+
console.log(data.category, data.action);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
mkfashion.init({
|
|
55
|
+
projectId: projectid,
|
|
56
|
+
identifier: identifier,
|
|
57
|
+
target: '#mkfashion-container'
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function waitForMkFashion() {
|
|
62
|
+
if (typeof mkfashion !== 'undefined') {
|
|
63
|
+
initMkFashion();
|
|
64
|
+
} else {
|
|
65
|
+
setTimeout(waitForMkFashion, 100);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (document.readyState === "loading") {
|
|
70
|
+
document.addEventListener("DOMContentLoaded", waitForMkFashion);
|
|
71
|
+
} else {
|
|
72
|
+
waitForMkFashion();
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
</body>
|
|
78
|
+
|
|
79
|
+
</html>
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mkfashion-sdk",
|
|
3
|
-
"version": "2.7.
|
|
4
|
-
"description": "SDK para integrar o provador virtual mKFashion com suporte a Wake Commerce
|
|
3
|
+
"version": "2.7.4",
|
|
4
|
+
"description": "SDK para integrar o provador virtual mKFashion com suporte a Wake Commerce",
|
|
5
5
|
"main": "src/mkfashion.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Abra test.html no navegador\""
|