O GA4 perde encomendas do WooCommerce: como encontrar a diferença e corrigir o registo de compras antes da Black Friday

Captura de ecrã: documentação para programadores do Google Analytics, 30 de outubro de 2026

O GA4 perde encomendas do WooCommerce: como encontrar a diferença e corrigir o registo de compras antes da Black Friday

Última verificação: 30 de outubro de 2026
29 min de leitura
Guia
Especialista WooCommerce

O painel do WooCommerce mostra um número, o GA4 mostra outro mais pequeno, e todos os anos, entre a Black Friday e o Natal, alguém pergunta qual dos dois está certo. O WooCommerce está certo quanto às encomendas. O GA4 está certo quanto ao que chegou ao GA4. A pergunta útil é que encomendas desapareceram e porquê, e a resposta sai de duas exportações e de uma junção pelo ID da transação.

Este artigo dá-lhe esse procedimento, o código para o executar e uma compra enviada pelo servidor que pode acrescentar antes das semanas de pico. Numa loja portuguesa há uma camada a mais: o MB WAY e as referências Multibanco pagam-se fora da página da loja, e isso mexe nos números em dois sentidos. Tudo o que afirmo sobre o GA4, o WooCommerce e a ifthenpay vem da documentação ou do código-fonte de cada um, lidos a 30 de outubro de 2026.

#O que encontrou, de facto, a auditoria do Search Engine Journal?

A 28 de outubro de 2026 o Search Engine Journal publicou uma auditoria de Christian Goodrich, responsável de pesquisa orgânica na agência SOZO. Comparou o GA4 com os registos de encomendas de oito marcas de comércio eletrónico do Reino Unido; seis deixaram-no cruzar os dados. O autor declara que a agência trabalhou com as oito. Os retalhistas são anónimos e o artigo não diz que plataforma usam, por isso nada ali é uma conclusão sobre o WooCommerce em particular, nem sobre o mercado português.

O “66%” do título não aparece no corpo do texto. Os números do retalhista em causa são estes:

“O GA4 registou 81% das encomendas web da loja no 4.º trimestre de 2024, 41% no 4.º trimestre de 2025 e apenas 32% no período do Natal.”

Search Engine Journal, 28 de outubro de 2026, tradução própria

Captar 32% significa perder 68% no Natal, e o título arredonda isso para dois terços. No mesmo trimestre as encomendas web dessa loja subiram de 5.874 para 7.686, enquanto o GA4 mostrava uma queda de cerca de um terço. Quem decidisse o orçamento de janeiro a olhar só para o GA4 teria cortado num canal que estava a crescer.

As causas que o autor aponta, nas palavras dele ou em paráfrase próxima:

  • Uma falha de consentimento constante. Todas as semanas há compradores que recusam o rastreio, por isso o GA4 nunca vê todas as encomendas.
  • Um banner de consentimento que se desligou sozinho. Plataformas de consentimento vendidas com uma quota mensal de visualizações de página esgotaram-na em dezembro, e o banner deixou de aparecer até à data de faturação seguinte.
  • Um apagão seguido de um carregamento tardio. Uma loja registou 2.215 encomendas em cinco semanas enquanto o GA4 não registou nenhuma; cerca de 1.000 foram carregadas mais tarde, quase todas como Unassigned.
  • Compras duplicadas. Numa loja, 657 de 3.365 números de encomenda tinham mais do que um evento purchase, o que somou 735 compras. No total trimestral, os duplicados esconderam parte da perda.
  • Bots. Bots que executam código semelhante ao de um navegador foram contados como visitantes.

Há uma frase dele que orienta o resto deste artigo:

“O rastreio do lado do servidor não altera as suas obrigações de consentimento, por isso a falha constante mantém-se.”

Search Engine Journal, 28 de outubro de 2026, tradução própria

Concordo, e o código mais abaixo foi construído à volta disso. O artigo não fala de gateways de pagamento nem da página de encomenda recebida. Numa loja WooCommerce é por aí que eu começaria, pela razão da secção seguinte.

#Porque é que o WooCommerce perde compras no GA4?

A extensão gratuita Google Analytics for WooCommerce é a configuração por omissão em muitas lojas. A documentação diz claramente de que depende:

“Transações de compra: registe as compras no seu site. Isto exige um gateway de pagamento que redirecione para a página de agradecimento/encomenda recebida depois do pagamento.”

Documentação do WooCommerce, Google Analytics for WooCommerce, tradução própria

O evento purchase é enviado pelo navegador do comprador na página de encomenda recebida. Tudo o que impeça essa página de carregar, ou a etiqueta de correr nela, retira a compra do GA4 enquanto o WooCommerce continua a registar a encomenda. As razões habituais:

  1. Consentimento recusado. A mesma documentação diz que no EEE, no Reino Unido e na Suíça os visitantes têm de dar consentimento antes do rastreio e que, sem ele, o GA4 e o Google Ads não captam dados do utilizador nessas regiões. A extensão não traz banner próprio; lê o consentimento através da WP Consent API a partir de um plugin de consentimento separado.
  2. Bloqueadores de anúncios e erros de script. A lista de resolução de problemas do próprio WooCommerce inclui extensões do navegador, bloqueadores de anúncios, erros de JavaScript e várias instâncias de rastreio em simultâneo.
  3. O comprador não volta da página de pagamento. As páginas de pagamento externas devolvem o comprador com um redirecionamento. Se ele fechar o separador depois de pagar, ou se a aplicação do banco nunca devolver o controlo ao navegador, a encomenda fica paga pela notificação do servidor do gateway e a compra no navegador nunca dispara.
  4. Uma camada de cache à frente da página de encomenda recebida. O WooCommerce envia cabeçalhos sem cache e define DONOTCACHEPAGE nas páginas do carrinho, da finalização da compra e da conta, e a página de encomenda recebida é um endpoint da finalização da compra (WC_Cache_Helper::prevent_caching no núcleo do WooCommerce). O risco está numa regra de CDN ou de proxy que guarda HTML em cache sem respeitar esses cabeçalhos.
  5. IDs de transação errados ou repetidos. A página de ajuda da Google sobre eliminação de duplicados por ID de transação avisa que, se o ID vier vazio, todas essas compras são deduplicadas em conjunto, e que reutilizar IDs leva a contar a menos.

A extensão também pode contar a mais, e isto pesa numa loja portuguesa. No código-fonte publicado no GitHub (trunk, cabeçalho de versão 2.4.4, lido a 30 de outubro de 2026), a compra é preparada no hook woocommerce_thankyou para qualquer encomenda que não esteja falhada nem cancelada e cuja chave de encomenda no URL seja válida. Uma encomenda paga por referência Multibanco que fica à espera do pagamento, ou uma transferência bancária em espera, é enviada como compra quer venha a ser paga quer não. A documentação dos estados de encomenda define “em espera” como uma encomenda que aguarda confirmação do pagamento e “pagamento pendente” como uma encomenda recebida sem pagamento feito. Por isso a reconciliação faz-se só com estados pagos.

O mesmo código-fonte dá-lhe um diagnóstico gratuito. Quando a página de encomenda recebida abre com uma chave válida, a extensão escreve na encomenda o meta _ga_tracked = 1, no servidor, antes de o navegador enviar o que quer que seja. Isso divide as encomendas em falta em dois grupos:

  • Encomenda paga sem _ga_tracked: a página de encomenda recebida nunca abriu. Veja o regresso do gateway.
  • _ga_tracked presente, compra ausente no GA4: a página abriu e o navegador perdeu o evento. Veja o consentimento, os bloqueadores e os erros de script.

A extensão envia transaction_id como $order->get_order_number() por omissão (filtro woocommerce_ga_order_id). Use esse mesmo valor em tudo o que vem a seguir; caso contrário, plugins de numeração sequencial de encomendas estragam a junção.

#Como funcionam o MB WAY e as referências Multibanco depois da compra?

Dois métodos de pagamento muito usados em Portugal têm uma coisa em comum: o pagamento acontece fora da página da loja e a confirmação chega ao servidor por outra via. Li a documentação da API da ifthenpay, um dos fornecedores que muitas lojas usam para estes dois métodos, para ver o fluxo concreto.

MB WAY. Na API MB WAY da ifthenpay, a loja envia o pedido de pagamento com o número de telemóvel do comprador, e a resposta imediata traz a mensagem Pending. O parâmetro opcional notificationUrl é descrito como o URL que recebe uma notificação com o estado final do pagamento, que pode ser pago, recusado pelo utilizador ou expirado. Há também um endpoint /mbway/status para consultar o estado. Ou seja, no momento em que a encomenda é criada o pagamento ainda não existe; o comprador aprova-o na aplicação do telemóvel e a loja só sabe o resultado depois.

Referências Multibanco. Na API Multibanco da ifthenpay, a loja pede uma entidade e uma referência para a encomenda, e o comprador paga mais tarde numa caixa multibanco, no homebanking ou na aplicação do banco, conforme a página geral da documentação. O parâmetro expiryDays define a validade: zero expira à meia-noite do próprio dia, há valores até 31 dias e acima, e sem o parâmetro a referência não expira. A documentação de callbacks explica que a ifthenpay chama o URL de callback que a loja configurou depois de um pagamento confirmado.

Daqui saem duas consequências para o GA4, em sentidos opostos:

  • Multibanco pode inflacionar. Se o plugin mostra a página de agradecimento com a referência e deixa a encomenda em espera ou pendente até ao pagamento, a extensão do GA4 conta a compra nesse momento. As referências que expiram sem pagamento ficam no GA4 como compras e nunca chegam a um estado pago no WooCommerce. Na reconciliação aparecem como IDs do GA4 sem encomenda web correspondente.
  • MB WAY pode perder. Se o plugin espera pela aprovação no telemóvel antes de mostrar a página de agradecimento, um comprador que aprova na aplicação e não volta ao separador deixa uma encomenda paga sem compra no GA4. Se, pelo contrário, mostra a página logo, acontece o mesmo que no Multibanco: compras que podem nunca ser pagas.

Que fluxo o seu plugin segue (o da ifthenpay, o da Eupago, o da SIBS ou outro) é coisa que se confirma numa encomenda de teste, não na documentação da API. O padrão geral é sempre o mesmo: a notificação ao servidor é o sinal fiável de pagamento, e a página de agradecimento é uma cortesia para o comprador. Os plugins de gateway do WooCommerce transformam essa notificação num estado pago, e é nesse evento que vale a pena pendurar a compra do GA4.

#Como exportar as encomendas do WooCommerce para a comparação?

Use apenas encomendas web, em estados pagos, num intervalo de datas fechado. O procedimento do Search Engine Journal exclui encomendas de marketplaces, de subscrições e manuais, e o WooCommerce permite fazê-lo através de created_via: a finalização clássica grava checkout e a finalização em blocos (Store API) grava store-api, segundo o código do núcleo do WooCommerce.

Este script funciona com HPOS e com o armazenamento antigo em posts, porque passa por wc_get_orders(). Execute-o com o WP-CLI em staging ou em produção (só faz leituras):

<?php
// wp eval-file export-orders.php 2025-11-01 2025-12-31 > orders.csv
list( $from, $to ) = array_slice( $args, 0, 2 );

$out = fopen( 'php://stdout', 'w' );
fputcsv( $out, array( 'transaction_id', 'created', 'status', 'created_via', 'payment_method', 'device', 'thankyou_seen', 'has_ga_cookie', 'total' ) );

$page = 1;
do {
	$orders = wc_get_orders( array(
		'type'         => 'shop_order',
		'status'       => array( 'processing', 'completed', 'refunded' ),
		'date_created' => $from . '...' . $to,
		'limit'        => 500,
		'paged'        => $page++,
		'orderby'      => 'ID',
		'order'        => 'ASC',
	) );
	foreach ( $orders as $order ) {
		fputcsv( $out, array(
			$order->get_order_number(),
			$order->get_date_created() ? $order->get_date_created()->date( 'Y-m-d' ) : '',
			$order->get_status(),
			$order->get_created_via(),
			$order->get_payment_method(),
			$order->get_meta( '_wc_order_attribution_device_type' ) ?: 'unknown',
			'1' === (string) $order->get_meta( '_ga_tracked' ) ? 'yes' : 'no',
			$order->get_meta( '_wpp_ga_client_id' ) ? 'yes' : 'no',
			$order->get_total(),
		) );
	}
} while ( count( $orders ) === 500 );

Três colunas merecem explicação. device vem da atribuição de encomendas do WooCommerce, que desde a versão 8.5 grava _wc_order_attribution_device_type como Mobile, Tablet ou Desktop, a partir do user agent. thankyou_seen é a marca _ga_tracked da extensão, descrita acima. has_ga_cookie fica em “no” até instalar o código do servidor mais abaixo; depois disso indica que encomendas traziam cookie de análise na finalização da compra. A coluna payment_method mostra o identificador técnico de cada gateway, por isso o MB WAY e o Multibanco aparecem em linhas separadas, que é exatamente o que interessa aqui.

As encomendas reembolsadas ficam porque foram pagas, e o GA4 devia ter uma compra para elas. Se preferir SQL numa loja com HPOS, o mesmo filtro fica assim (ajuste o prefixo wp_):

SELECT o.id, o.status, o.payment_method, o.total_amount, o.currency,
       o.date_created_gmt, od.created_via
FROM wp_wc_orders o
JOIN wp_wc_order_operational_data od ON od.order_id = o.id
WHERE o.type = 'shop_order'
  AND o.status IN ('wc-processing', 'wc-completed', 'wc-refunded')
  AND o.date_created_gmt >= '2025-11-01' AND o.date_created_gmt < '2026-01-01'
  AND od.created_via IN ('checkout', 'store-api');

O SQL devolve o id interno. Se um plugin alterar os números das encomendas, converta-o para o número antes da junção, ou use a exportação em PHP.

#Como exportar as compras do GA4 por ID de transação?

Use a dimensão ID da transação com a métrica Compras de comércio eletrónico. No esquema da Data API do GA4, ecommercePurchases conta eventos purchase, enquanto transactions conta vários tipos de evento, incluindo refund. O Search Engine Journal deixa o mesmo aviso:

“Evite a métrica de transações, que conta reembolsos além das compras.”

Search Engine Journal, 28 de outubro de 2026, tradução própria

Na interface, crie uma exploração de formato livre com o ID da transação nas linhas e Compras de comércio eletrónico como valor, defina o mesmo intervalo de datas da exportação de encomendas e exporte para CSV. Se a interface estiver em português, renomeie as colunas no ficheiro para “Transaction ID” e “Ecommerce purchases”, que são os nomes que o script espera. Pela Data API a dimensão é transactionId e a métrica ecommercePurchases. Acrescente deviceCategory ou browser numa segunda exportação se quiser a divisão do próprio GA4. Antes de confiar no ficheiro, verifique duas coisas:

  • Limites de dados. A página de ajuda da Google sobre limites de dados diz que são definidos pelo sistema, não podem ser ajustados e podem reter linhas quando o relatório inclui dados demográficos ou Google Signals. Limite a exploração ao ID da transação e à contagem de compras.
  • Dados modelados. A modelação comportamental do modo de consentimento só aparece na identidade de relatórios combinada e exige pelo menos 1.000 eventos por dia com analytics_storage='denied' durante 7 dias, mais 1.000 utilizadores diários com consentimento em 7 dos últimos 28 dias. Os utilizadores modelados não trazem IDs de transação de encomendas reais. Esta comparação trata de dados observados.

#Como calcular a diferença por método de pagamento e por dispositivo?

Este script junta os dois ficheiros e mostra a taxa de captação total e por método de pagamento, por dispositivo e pela marca da página de agradecimento. Conta também os IDs de transação com mais do que um evento purchase, que é a verificação de duplicados da auditoria do SEJ, e os IDs do GA4 que não correspondem a nenhuma encomenda web. Python 3, só com a biblioteca padrão:

#!/usr/bin/env python3
# python3 reconcile.py orders.csv ga4.csv
# ga4.csv: GA4 export with the columns "Transaction ID" and "Ecommerce purchases".
import csv
import sys
from collections import Counter, defaultdict


def read_ga4(path):
    with open(path, encoding="utf-8-sig") as f:
        rows = [line for line in f if line.strip() and not line.startswith("#")]
    counts = {}
    for r in csv.DictReader(rows):
        tid = (r.get("Transaction ID") or "").strip()
        if tid and tid != "(not set)":
            counts[tid] = counts.get(tid, 0) + int(float(r.get("Ecommerce purchases") or 0))
    return counts


def main(orders_path, ga4_path):
    ga4 = read_ga4(ga4_path)
    with open(orders_path, encoding="utf-8") as f:
        orders = [o for o in csv.DictReader(f) if o["created_via"] in ("checkout", "store-api")]

    groups = defaultdict(Counter)
    for o in orders:
        seen = ga4.get(o["transaction_id"], 0) > 0
        for key in ("payment_method", "device", "thankyou_seen"):
            groups[(key, o[key])]["orders"] += 1
            groups[(key, o[key])]["in_ga4"] += seen

    total = len(orders)
    found = sum(1 for o in orders if ga4.get(o["transaction_id"], 0) > 0)
    print(f"web orders: {total}, found in GA4: {found}, capture: {found / total:.0%}" if total else "no orders")
    for (key, val), c in sorted(groups.items()):
        print(f"{key:15} {val:20} orders {c['orders']:6}  in GA4 {c['in_ga4']:6}  capture {c['in_ga4'] / c['orders']:.0%}")

    dupes = {t: n for t, n in ga4.items() if n > 1}
    print(f"transaction IDs with more than one purchase event: {len(dupes)} (extra events: {sum(dupes.values()) - len(dupes)})")
    unknown = set(ga4) - {o["transaction_id"] for o in orders}
    print(f"GA4 transaction IDs with no matching web order: {len(unknown)}")


if __name__ == "__main__":
    main(*sys.argv[1:3])

Execute-o uma vez para novembro e dezembro do ano passado e outra para as últimas quatro semanas. Uma percentagem isolada diz pouco; quem faz o trabalho é a divisão por grupos.

#Como ler o resultado?

O que vêCausa mais provávelOnde procurar
Um método de pagamento muito abaixo dos outrosOs compradores não voltam da página ou da aplicação desse métodoURL de regresso do gateway, página de encomenda recebida no telemóvel, thankyou_seen desse método
MB WAY abaixo do cartãoAprovação na aplicação sem regresso ao separador da lojaEncomenda de teste no telemóvel, fechando o separador depois de aprovar
thankyou_seen = no em muitas encomendas pagasPágina de encomenda recebida não abriuRedirecionamentos do gateway, compradores que fecham o separador, erros de plugins nessa página
thankyou_seen = yes mas captação baixaO navegador perdeu o eventoTaxa de consentimento, bloqueadores de anúncios, erros na consola, o consentimento por omissão que a extensão define
Telemóvel muito abaixo do computadorMudança para a aplicação de pagamento ou navegadores dentro de aplicaçõesTestar a compra num telemóvel com cada método
Captação boa em outubro, cai em dezembroAlgo partiu sob carga ou uma quota esgotou-seLimites do plano da plataforma de consentimento, deploys nessa semana, regras da CDN
Muitos IDs com mais do que uma compraPágina de encomenda recebida recarregada ou duas etiquetasEtiquetas duplicadas no GTM e em plugins, comportamento ao recarregar
IDs do GA4 sem encomenda web correspondenteEncomendas pendentes ou em espera contadas, referências Multibanco expiradas, encomendas de testeEstado dessas encomendas no WooCommerce, método de pagamento de cada uma

Uma diferença constante que não muda por método de pagamento nem por dispositivo é, com grande probabilidade, consentimento. Essa parte deve ficar. São os visitantes que disseram não, e o GA4 está a dizer a verdade sobre eles ao não os contar.

#Como enviar a compra a partir do servidor com o Measurement Protocol?

O Measurement Protocol do GA4 recebe eventos por HTTP a partir do seu servidor. A Google é explícita quanto ao papel dele:

“A intenção do Measurement Protocol é complementar a recolha automática através do gtag, do Tag Manager e do Google Analytics for Firebase, não substituí-la.”

Google Analytics, visão geral do Measurement Protocol, tradução própria

Por isso mantenha a etiqueta no navegador para o comportamento e use o servidor só para a compra. O desenho abaixo segue a documentação ponto por ponto:

  • client_id. A referência aceita o cookie do ID de cliente e pede que se envie o valor completo do cookie. O código guarda o valor do cookie _ga na encomenda durante a finalização da compra, sem o analisar.
  • session_id. A única forma documentada de o ler é gtag('get', '<target>', 'session_id', callback), segundo a referência do gtag.js. A visão geral diz que os dados de geografia e de dispositivo da sessão se aplicam se o session_id for incluído no evento e enviado ao Measurement Protocol nas 24 horas seguintes ao início da sessão. O código lê-o na página de finalização da compra e guarda-o durante uma hora.
  • Consentimento. O objeto consent do pedido cobre apenas ad_user_data e ad_personalization, e sem ele o GA4 usa as definições de consentimento das interações online correspondentes desse cliente. O código não inventa nada: sem cookie _ga na finalização da compra não há ID de cliente, e essa encomenda não é enviada.
  • Erros. O Measurement Protocol devolve 2xx mesmo para um payload mal formado, e a Google diz que, perante uma resposta que não seja 2xx, se corrige o pedido em vez de repetir o mesmo. Valide primeiro em /debug/mp/collect, que devolve validationMessages e não regista nada.
  • Prazo. Os eventos podem ter data retroativa até 72 horas. Uma compra enviada quando o pagamento é confirmado não precisa de carimbo temporal. Uma referência Multibanco paga cinco dias depois é outra conversa: a compra chega ao GA4 na data do pagamento, fora da sessão original, e isso é aceitável desde que saiba que é assim.
  • Recolha na UE. O guia de envio da Google indica region1.google-analytics.com como anfitrião se quiser a recolha na União Europeia.

Guarde isto como plugin must-use (wp-content/mu-plugins/ga4-mp-purchase.php) e defina o ID de medição e o segredo da API no wp-config.php. O segredo da API cria-se nas definições do stream de dados Web do GA4.

<?php
/**
 * Plugin Name: GA4 purchase via Measurement Protocol
 * Description: Sends the GA4 purchase from the server when WooCommerce records the payment.
 */

defined( 'ABSPATH' ) || exit;

// In wp-config.php, never in the theme:
// define( 'WPP_GA4_MEASUREMENT_ID', 'G-XXXXXXXXXX' );
// define( 'WPP_GA4_API_SECRET', 'secret-from-the-data-stream-settings' );

// 1. Checkout page: ask gtag for the GA4 session_id and keep it for an hour.
add_action( 'wp_footer', function () {
	if ( ! defined( 'WPP_GA4_MEASUREMENT_ID' ) || ! is_checkout() || is_order_received_page() ) {
		return;
	}
	?>
	<script>
	(function () {
		if (typeof gtag !== 'function' || document.cookie.indexOf('_ga=') === -1) return;
		gtag('get', '<?php echo esc_js( WPP_GA4_MEASUREMENT_ID ); ?>', 'session_id', function (sid) {
			if (sid) document.cookie = 'wpp_ga_sid=' + encodeURIComponent(sid) + '; path=/; max-age=3600; SameSite=Lax; Secure';
		});
	})();
	</script>
	<?php
} );

// 2. Order creation (classic and block checkout): copy the identifiers into order meta.
function wpp_ga4_store_ids( $order ) {
	if ( empty( $_COOKIE['_ga'] ) ) {
		return; // No analytics cookie: consent was denied or the tag never ran.
	}
	$order->update_meta_data( '_wpp_ga_client_id', sanitize_text_field( wp_unslash( $_COOKIE['_ga'] ) ) );
	$sid = isset( $_COOKIE['wpp_ga_sid'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['wpp_ga_sid'] ) ) : '';
	if ( ctype_digit( $sid ) ) {
		$order->update_meta_data( '_wpp_ga_session_id', $sid );
	}
	$order->save_meta_data();
}
add_action( 'woocommerce_checkout_order_created', 'wpp_ga4_store_ids' );
add_action( 'woocommerce_store_api_checkout_order_processed', 'wpp_ga4_store_ids' );

// 3. One POST to /mp/collect. Returns true on 2xx.
function wpp_ga4_mp_send( $order, array $event ) {
	if ( ! defined( 'WPP_GA4_MEASUREMENT_ID' ) || ! defined( 'WPP_GA4_API_SECRET' ) ) {
		return false;
	}
	$client_id = $order->get_meta( '_wpp_ga_client_id' );
	if ( ! $client_id ) {
		return false; // No consent signal stored at checkout: send nothing.
	}
	$session_id = $order->get_meta( '_wpp_ga_session_id' );
	if ( $session_id ) {
		$event['params']['session_id'] = $session_id;
	}
	$event['params']['engagement_time_msec'] = 1;

	$url = add_query_arg(
		array(
			'measurement_id' => WPP_GA4_MEASUREMENT_ID,
			'api_secret'     => WPP_GA4_API_SECRET,
		),
		'https://region1.google-analytics.com/mp/collect' // EU collection; www.google-analytics.com otherwise.
	);
	$response = wp_remote_post( $url, array(
		'timeout' => 5,
		'headers' => array( 'Content-Type' => 'application/json' ),
		'body'    => wp_json_encode( array(
			'client_id' => $client_id,
			'events'    => array( $event ),
		) ),
	) );
	$code = wp_remote_retrieve_response_code( $response );
	if ( $code < 200 || $code > 299 ) {
		// Google's docs: fix the request, do not retry the same one.
		wc_get_logger()->warning( 'GA4 MP ' . $event['name'] . ' failed for order ' . $order->get_order_number() . ': ' . ( is_wp_error( $response ) ? $response->get_error_message() : $code ), array( 'source' => 'ga4-mp' ) );
		return false;
	}
	return true;
}

// 4. Purchase: fired when the gateway confirms payment, or when the order reaches a paid status.
function wpp_ga4_send_purchase( $order_id ) {
	$order = wc_get_order( $order_id );
	if ( ! $order || $order->get_meta( '_wpp_ga_mp_sent' ) ) {
		return;
	}
	$items = array();
	$value = 0.0;
	foreach ( $order->get_items() as $item ) {
		$qty     = (float) $item->get_quantity();
		$price   = $qty > 0 ? round( (float) $item->get_total() / $qty, 2 ) : 0.0;
		$product = $item->get_product();
		$items[] = array(
			'item_id'   => ( $product && $product->get_sku() ) ? $product->get_sku() : (string) $item->get_product_id(),
			'item_name' => $item->get_name(),
			'price'     => $price,
			'quantity'  => $qty,
		);
		$value += $price * $qty;
	}
	$sent = wpp_ga4_mp_send( $order, array(
		'name'   => 'purchase',
		'params' => array(
			'transaction_id' => (string) $order->get_order_number(), // Same ID the browser tag and the export use.
			'currency'       => $order->get_currency(),
			'value'          => round( $value, 2 ), // Sum of price * quantity, as in Google's example.
			'tax'            => (float) $order->get_total_tax(),
			'shipping'       => (float) $order->get_shipping_total(),
			'items'          => $items,
		),
	) );
	if ( $sent ) {
		// ponytail: meta flag, no lock; two webhooks in the same second could both send.
		$order->update_meta_data( '_wpp_ga_mp_sent', time() );
		$order->save_meta_data();
	}
}
add_action( 'woocommerce_payment_complete', 'wpp_ga4_send_purchase' );
add_action( 'woocommerce_order_status_processing', 'wpp_ga4_send_purchase' );
add_action( 'woocommerce_order_status_completed', 'wpp_ga4_send_purchase' );

// 5. Refunds: the same transaction_id, so GA4 nets them against the purchase.
add_action( 'woocommerce_order_refunded', function ( $order_id, $refund_id ) {
	$order  = wc_get_order( $order_id );
	$refund = wc_get_order( $refund_id );
	if ( ! $order || ! $refund || ! $order->get_meta( '_wpp_ga_mp_sent' ) ) {
		return;
	}
	$params = array(
		'transaction_id' => (string) $order->get_order_number(),
		'currency'       => $order->get_currency(),
		'value'          => (float) $refund->get_amount(),
	);
	// Refunded line items come back with negative quantities; an amount-only refund has none.
	foreach ( $refund->get_items() as $item ) {
		$product           = $item->get_product();
		$params['items'][] = array(
			'item_id'  => ( $product && $product->get_sku() ) ? $product->get_sku() : (string) $item->get_product_id(),
			'quantity' => abs( (float) $item->get_quantity() ),
		);
	}
	wpp_ga4_mp_send( $order, array( 'name' => 'refund', 'params' => $params ) );
}, 10, 2 );

Porquê estes hooks: woocommerce_payment_complete dispara quando um gateway chama WC_Order::payment_complete(), normalmente a partir do tratamento da notificação do servidor, com o ID da encomenda e o ID da transação do gateway. É o caminho típico de uma notificação MB WAY ou de um callback Multibanco tratados por um plugin bem escrito. As transferências bancárias e o pagamento na entrega nunca o chamam; chegam a um estado pago quando o lojista ou o gateway mudam o estado, e isso é apanhado por woocommerce_order_status_processing e woocommerce_order_status_completed. A marca _wpp_ga_mp_sent impede que a segunda e a terceira chamada voltem a enviar.

Os reembolsos saem em woocommerce_order_refunded, que também dispara em reembolsos parciais. O guia de comércio eletrónico da Google pede o transaction_id e os artigos reembolsados com item_id e quantity, por isso o código acrescenta as linhas reembolsadas quando existem. A extensão Google Analytics for WooCommerce não envia nenhum evento de reembolso (o código-fonte não tem tratamento de reembolsos), o que é mais uma razão para a métrica Transações e a sua lista de encomendas se afastarem. Os hooks de captura cobrem as duas finalizações de compra: woocommerce_checkout_order_created na clássica com shortcode e woocommerce_store_api_checkout_order_processed na de blocos, ambos chamados durante o pedido do próprio comprador, por isso os cookies estão lá. É esse o ponto que torna o MB WAY tratável: o cookie é lido quando o comprador escreve o número de telemóvel, e a compra só sai quando a aprovação chega ao servidor, mesmo que ele nunca volte ao separador.

O value é calculado como o exemplo de comércio eletrónico da Google o comenta, a soma de preço vezes quantidade de todos os artigos, com impostos e portes nos seus próprios campos. Se a etiqueta do navegador calcular o valor de outra forma (com IVA incluído, por exemplo), alinhe as duas antes de comparar receita.

Mais uma informação da Google: o Measurement Protocol está num estado de produto maduro e finalizado e vai continuar a funcionar, sem planos de descontinuação, e para novas integrações de servidor para servidor a Google recomenda agora a sua Data Manager API. Para o evento de compra de uma única loja, o Measurement Protocol é o trabalho mais pequeno, e eu ficaria com ele.

#Como evitar contar a mesma encomenda duas vezes?

A página de ajuda sobre duplicados descreve a eliminação de compras duplicadas do mesmo utilizador por ID de transação em streams Web. Não diz nada sobre o Measurement Protocol, por isso não conto com o GA4 a fundir uma compra do navegador e uma compra do servidor para a mesma encomenda. Escolha um dos dois caminhos documentados:

  • Desligar a compra no navegador. No Google Analytics for WooCommerce, desmarque “Purchase Transactions”. O resto do funil (visualizações de produto, adicionar ao carrinho, início da finalização da compra) continua a vir do navegador. Perde o diagnóstico _ga_tracked para encomendas novas, por isso faça a reconciliação uma vez antes de mudar.
  • Enviar a compra do servidor para uma segunda propriedade. É o conselho do Search Engine Journal para quem começa agora: enviar para uma propriedade GA4 separada durante o 4.º trimestre, para que nada seja contado duas vezes. Compare as duas propriedades com a exportação de encomendas em janeiro e decida nessa altura.

Duas semanas antes da Black Friday é má altura para mudar o evento de compra de que dependem as licitações e os relatórios. O segundo caminho mantém os números atuais intactos enquanto recolhe provas.

#Que definições do modo de consentimento mudam os números?

A Google descreve duas implementações do modo de consentimento. No modo básico as etiquetas só carregam depois de o visitante interagir com o banner, e nada é enviado antes do consentimento. No modo avançado as etiquetas carregam com valores recusados por omissão e enviam pings sem cookies enquanto o consentimento está recusado; é a configuração que a modelação comportamental exige. Nenhum dos modos transforma uma recusa numa compra com ID de transação.

O que vale a pena verificar numa loja WooCommerce antes do pico:

  • O valor por omissão que a extensão define. O Google Analytics for WooCommerce define o consentimento por omissão na inicialização e permite alterá-lo com o filtro woocommerce_ga_gtag_consent_modes. Se o seu plugin de consentimento define os seus próprios valores, o WooCommerce documenta esse filtro como forma de impedir a extensão de os sobrescrever.
  • A atualização do consentimento. A documentação do WooCommerce diz que a integração do modo de consentimento tem de atualizar os estados depois de o visitante dar consentimento. Teste se o estado passa a concedido na mesma visualização de página e não na seguinte, ou a primeira página depois do consentimento perde-se.
  • Os limites do plano da plataforma de consentimento. O banner que se desligou em dezembro na auditoria do SEJ era uma quota de faturação. Compare a sua com as visualizações de página de dezembro do ano passado.
  • O que acontece depois de “rejeitar”. O meu artigo sobre um banner de cookies rejeitado em que o rastreamento continua trata da falha oposta, e essa é um problema legal.

A configuração geral do GA4, a etiqueta no WordPress e o consentimento por omissão estão no meu guia para adicionar o Google Analytics 4 ao WordPress. As encomendas feitas por agentes de IA sem navegador são um caso à parte, com a sua própria questão de ID de cliente, tratado em analítica do checkout do WooCommerce feito por agentes. Se também tiver uma loja Shopify, checkouts no Shopify sem encomendas percorre o mesmo tipo de diferença nessa plataforma.

Em Portugal a regra está no artigo 5.º da Lei n.º 41/2004, de 18 de agosto, que transpôs a Diretiva 2002/58/CE (privacidade nas comunicações eletrónicas). A redação atual do artigo 5.º foi dada pela Lei n.º 46/2012, de 29 de agosto, que transpôs a Diretiva 2009/136/CE. O n.º 1 diz isto:

“O armazenamento de informações e a possibilidade de acesso à informação armazenada no equipamento terminal de um assinante ou utilizador apenas são permitidos se estes tiverem dado o seu consentimento prévio, com base em informações claras e completas nos termos da Lei de Proteção de Dados Pessoais, nomeadamente quanto aos objetivos do processamento.”

Lei n.º 46/2012, Diário da República

O n.º 2 só afasta essa exigência para o armazenamento técnico ou o acesso que tenha como única finalidade transmitir uma comunicação numa rede de comunicações eletrónicas, ou que seja estritamente necessário para prestar um serviço da sociedade da informação pedido expressamente pelo assinante ou utilizador. Um cookie de análise do GA4 não serve para transmitir a comunicação nem é necessário para o comprador concluir a encomenda. Ao contrário do Reino Unido, que desde 2026 tem uma exceção para fins estatísticos com condições, o texto português não prevê essa exceção.

Para este artigo, a consequência prática é simples: a compra enviada pelo servidor só sai para encomendas que tinham cookie de análise na finalização da compra, ou seja, de compradores que deram consentimento. Não verifiquei para este texto se o artigo 5.º sofreu alterações posteriores a 2012; confirme a versão consolidada no Diário da República antes de citar o artigo num documento seu.

#O que verificar antes da Black Friday?

  • Exportar as encomendas e os IDs de transação do GA4 de novembro e dezembro do ano passado, correr a reconciliação, anotar a taxa de captação por método de pagamento e por dispositivo.
  • Repetir para as últimas quatro semanas. Uma queda entre as duas é um problema de configuração que ainda se resolve.
  • Fazer uma encomenda real num telemóvel com cada método: cartão, MB WAY, referência Multibanco. No MB WAY, aprovar na aplicação e não voltar ao separador; confirmar que a encomenda passa a paga e o que o GA4 regista.
  • Gerar uma referência Multibanco e deixá-la expirar sem pagar; ver se aparece como compra no GA4.
  • Procurar IDs de transação duplicados e encontrar a segunda etiqueta, se existir.
  • Confirmar que encomendas pendentes e em espera não chegam ao GA4 como compras, ou aceitar e documentar que chegam.
  • Comparar o limite do plano da plataforma de consentimento com o tráfego de dezembro passado, e enviar os avisos dela a quem pode pagar o upgrade.
  • Confirmar que a CDN não guarda em cache a página de encomenda recebida.
  • Se acrescentar a compra pelo servidor, validá-la em /debug/mp/collect e enviá-la para uma propriedade separada durante o pico.
  • Marcar no calendário uma verificação semanal até ao fim das campanhas de Natal: compras no GA4 a dividir pelas encomendas web do WooCommerce. A auditoria do SEJ mostra que os apagões foram notados com semanas de atraso.

#Quando vale a pena chamar um programador?

A exportação e a reconciliação levam uma tarde a quem esteja à vontade com o WP-CLI. A compra pelo servidor mexe na finalização da compra, nas notificações de pagamento e no consentimento, três sítios onde um erro custa dinheiro ou cria um problema legal, e tem de ser testada com cada gateway que a loja usa, MB WAY e Multibanco incluídos. Se preferir ter isso feito e testado em staging antes do pico, faço este tipo de trabalho como programador de WooCommerce.

Próximo passo

Transforme o artigo numa implementação real

Este bloco reforça a ligação interna e conduz o leitor para o passo seguinte mais útil dentro da arquitetura do site.

Explorar outros serviços WordPress e base de conhecimento

Reforce o seu negócio com suporte técnico profissional em áreas-chave do ecossistema WordPress.

Perguntas frequentes

Respostas práticas para aplicar o tema na execução real.

Porque é que o GA4 mostra menos compras do que o WooCommerce?#
A extensão gratuita Google Analytics for WooCommerce envia a compra a partir da página de encomenda recebida, por isso falta qualquer encomenda cujo comprador nunca carrega essa página, recusa os cookies de análise ou bloqueia a etiqueta. Pagamentos que se concluem fora da loja, eventos duplicados e reembolsos contados na métrica Transações afastam ainda mais os números.
Que métrica do GA4 se deve comparar com as encomendas do WooCommerce?#
Conte IDs de transação distintos com a métrica Compras de comércio eletrónico, que conta eventos purchase. A métrica Transações também conta eventos refund, por isso não coincide com uma exportação de encomendas.
As referências Multibanco podem inflacionar as compras no GA4?#
Podem. A extensão envia a compra quando a página de agradecimento abre e só exclui encomendas falhadas ou canceladas. Uma encomenda que fica à espera do pagamento da referência é contada como compra mesmo que a referência expire sem ser paga. Compare apenas encomendas em estados pagos.
O Measurement Protocol recupera encomendas de quem recusou os cookies?#
Não deve. Sem consentimento não existe cookie _ga nem ID de cliente para enviar, e o envio pelo servidor não altera as obrigações de consentimento do artigo 5.º da Lei n.º 41/2004. O Measurement Protocol recupera encomendas com consentimento que o navegador perdeu.
O GA4 elimina o duplicado entre uma compra do navegador e uma do servidor?#
A Google documenta a eliminação de duplicados por ID de transação para streams Web e para o mesmo utilizador, e a página não menciona o Measurement Protocol. Não conte com isso: desligue a compra no navegador ou envie a compra do servidor para uma propriedade separada enquanto testa.

Precisa de FAQ adaptado ao setor e mercado? Crio uma versão alinhada com os seus objetivos de negócio.

Fale connosco

Artigos Relacionados

Analítica de checkout de agentes WooCommerce

Os agentes de IA colocam encomendas no WooCommerce do lado do servidor, por isso os pixels do navegador em que o seu relatório assenta nunca disparam. O que se avaria, porque a Conversions API não é um resgate automático e como instrumentar corretamente o checkout do agente.