Blog
Eventos de comércio eletrónico sem plataforma de loja: pôr um checkout próprio a reportar ao GA4
Publicado · 6 min de leitura
Uma plataforma de loja dá-lhe, pelo menos, um plugin com que discutir. Um checkout construído por si — uma aplicação React, um fluxo de reservas, um registo SaaS com o Stripe por trás — não lhe dá nada: nenhum evento dispara até o seu próprio código o enviar. A boa notícia é que o contrato é pequeno: um punhado de nomes, uma forma de objeto e algumas regras sobre onde pertence cada envio. Está tudo aqui, incluindo a parte que a maioria dos guias deixa de fora — as compras que acontecem quando ninguém está na sua página.
O contrato: um nome e um objeto ecommerce
Tudo aqui chega ao Analytics da mesma forma. A sua página envia um evento para window.dataLayer com o nome que o GA4 lhe dá, levando um objeto ecommerce na forma do GA4. Um acionador do Tag Manager reconhece o nome, e uma etiqueta de evento do GA4 lê o objeto e reencaminha-o para o seu contentor de servidor. Se disse à SignalHost que o seu site é uma loja — ou carregou em «Configurar» em «Eventos de conversão» na página do contentor — esses acionadores e etiquetas já existem no seu contentor web, com o prefixo SH no nome. O que falta é o envio.
Duas regras apanham a maioria dos erros. Limpe primeiro o objeto ecommerce anterior, ou um view_item de há três páginas segue à boleia dentro do evento seguinte. E em cada evento que envolva dinheiro, envie value como número e currency como código ISO: um value como string ou uma currency em falta é aceite sem queixa e discretamente deixado de fora da receita.
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: 'begin_checkout',
ecommerce: {
currency: 'EUR',
value: 49.00,
items: [
{ item_id: 'plan-pro', item_name: 'Pro', price: 49.00, quantity: 1 }
]
}
});Que evento, e onde pertence
Um fluxo personalizado tem os mesmos momentos que uma plataforma de loja. Só tem de os encontrar no seu próprio código.
- view_item_list — uma página ou um componente que mostra vários produtos ou planos. Uma vez, quando a lista é mostrada, não a cada deslocamento.
- view_item — a página de um único produto ou plano.
- add_to_cart e remove_from_cart — no handler do botão, depois de o carrinho ter mudado de facto.
- begin_checkout — o momento em que o visitante entra no checkout: a primeira renderização da página de checkout, ou o clique que abre uma janela de checkout.
- add_payment_info — quando os dados de pagamento são aceites, não quando o formulário aparece.
- purchase — uma vez, quando a encomenda é confirmada. A maior parte deste artigo trata de acertar neste.
- sign_up e start_trial — num negócio de subscrições, as conversões que acontecem antes de qualquer dinheiro mudar de mãos. A SignalHost constrói etiquetas para ambos, a par dos eventos de loja.
Envie a partir do handler que causou a alteração, não a partir de uma renderização. Uma aplicação de página única volta a renderizar à vontade, e o React executa os efeitos duas vezes em desenvolvimento: um envio dentro de um efeito duplica na build de desenvolvimento e repete-se sempre que um componente é montado de novo.
A compra na sua página de confirmação: leia a encomenda do seu servidor
Quando o visitante regressa a uma página sua depois de pagar, é nessa página que o purchase pertence — com duas condições. Obtenha a encomenda do seu próprio backend, não do URL: os parâmetros de consulta podem ser editados, perdem-se em redirecionamentos e também chegam em pagamentos falhados. O Stripe, por exemplo, devolve o visitante ao mesmo endereço quer o pagamento tenha passado quer não, e indica qual dos dois num parâmetro próprio. E envie-o uma única vez: um recarregamento, o botão de retroceder ou uma página de recibo guardada nos favoritos voltam todos a ela.
// On your confirmation page. The order comes from YOUR server, by an id the
// page already knows — never from price or status parameters in the URL.
const order = await fetch(`/api/orders/${orderId}`).then((r) => r.json());
const key = `purchase-sent-${order.id}`;
if (order.status === 'paid' && !localStorage.getItem(key)) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: order.id,
value: order.total,
tax: order.tax,
currency: order.currency,
items: order.items.map((i) => ({
item_id: i.sku, item_name: i.name, price: i.price, quantity: i.quantity
}))
}
});
localStorage.setItem(key, '1');
}O transaction_id é uma rede de segurança, não um plano. O Analytics descarta um transaction_id repetido dentro de uma sessão, mas não um recarregamento da página de recibo na manhã seguinte.
Pagamento numa página alheia: Stripe Checkout, PayPal, um redirecionamento para o banco
Uma página de pagamento alojada é a escolha habitual num checkout feito à medida, e tem uma aresta cortante: o visitante pode nunca regressar. Paga, fecha o separador, e a sua página de confirmação nunca chega a ser renderizada. Com cartões, isso são alguns por cento das compras. Com transferências bancárias, faturas e métodos de pagamento diferido pode ser a maioria, porque o dinheiro chega horas ou dias depois.
Por isso, decida, para cada método de pagamento, onde está a verdade. Se o seu backend fica a saber do pagamento através de um webhook, o webhook é o sítio fiável para o reportar — veja a secção seguinte — e a página de confirmação não pode enviar também o purchase, ou cada pagamento com cartão é contado duas vezes. Se o único sítio onde fica a saber é a página de regresso, reporte-o aí e aceite a lacuna.
Compras sem nenhum navegador presente: envie-as a partir do seu servidor
As subscrições tornam isto inevitável. Um período experimental converte ao fim de sete dias, uma renovação acontece todos os meses, uma fatura é paga por transferência na semana seguinte. Cada uma é uma compra, e nenhuma acontece à frente de um navegador. A resposta é enviar o evento de servidor para servidor, a partir do webhook que fica a saber do pagamento, para o mesmo endpoint de tagging que as suas páginas usam — o seu nome de anfitrião SignalHost ou o seu próprio domínio — para que passe pelo seu contentor de servidor como qualquer outro hit.
Precisa de uma coisa do checkout: o ID de cliente do Analytics do visitante, para que a compra fique atribuída à pessoa cujas visitas levaram a ela. Está no cookie _ga, que o seu pedido de checkout já transporta quando vai para o seu próprio domínio. Guarde-o com a encomenda. Não haver cookie _ga significa que o visitante não deu consentimento para analytics: envie o hit com o consentimento negado e um ID descartável, ou não o envie de todo — nunca invente uma identidade para ele.
// In your checkout request handler: keep the visitor's Analytics id with the order.
const ga = req.cookies._ga; // "GA1.1.1234567890.1700000000"
order.gaClientId = ga ? ga.split('.').slice(-2).join('.') : null;
// Later, in your payment webhook — a trial converting, an invoice paid:
const params = new URLSearchParams({
v: '2',
tid: 'G-XXXXXXXXXX', // your GA4 measurement ID
cid: order.gaClientId ?? `${Date.now()}.${Math.floor(Math.random() * 1e9)}`,
gcs: order.gaClientId ? 'G101' : 'G100', // no _ga cookie = no consent
en: 'purchase',
'ep.transaction_id': invoice.id,
'epn.value': '49.00',
cu: 'EUR',
pr1: 'idplan-pro~nmPro~pr49.00~qt1',
});
await fetch(`https://metrics.example.com/g/collect?${params}`, { method: 'POST' });Escolha uma origem por evento e mantenha-se fiel a ela. É exatamente assim que o signalhost.io mede as suas próprias vendas: o navegador envia sign_up, begin_checkout e start_trial; o webhook do Stripe envia purchase quando a primeira fatura é paga; nada envia ambos. O Measurement Protocol do Google também funcionaria, mas vai diretamente para o Google e contorna o seu contentor de servidor, pelo que nenhuma das suas etiquetas do lado do servidor chega a ver a venda.
O consentimento não lhe pede nada a mais
Envie os eventos seja qual for a escolha do visitante. As etiquetas obedecem ao Consent Mode: com consentimento enviam normalmente; sem ele enviam pings sem cookies, que o Analytics modela em vez de reportar. Reter os envios por sua conta só esconde conversões dessa modelação. O que importa é a ordem — o excerto do Tag Manager vem depois do script do seu banner de consentimento, para que as predefinições de consentimento existam antes de o primeiro evento ser enviado.
Verificar que funciona
- Abra o contentor na SignalHost. O cartão de eventos lista o que chegou, por nome, com a parte que trazia consentimento. Um nome que enviou e não vê ali nunca chegou ao seu servidor de tagging.
- Percorra o seu checkout no modo de pré-visualização do Tag Manager. Mostra cada envio, que etiqueta disparou e o objeto ecommerce que foi enviado.
- No Analytics, o DebugView mostra os eventos à medida que chegam. Os relatórios padrão podem demorar um dia.
- Recarregue a página de confirmação duas vezes e conte as compras. Deve continuar a ser uma.
Aqui, as falhas são todas silenciosas: um nome com as maiúsculas erradas, value enviado como string, items aninhados um nível a mais. Nenhuma produz um erro em lado nenhum. Todas produzem um relatório com um buraco.
