Cena clássica: um cliente compra na sua loja como convidado. Recebe o produto, fica feliz. Volta dali a duas semanas, decide criar uma conta para acompanhar pedidos futuros. Faz o cadastro com o mesmo email do checkout anterior.
Vai em Minha Conta → Pedidos. Vazio.
O pedido antigo existe, está pago, foi entregue — mas não aparece pra ele. O cliente acha que perdeu o histórico, abre um ticket no seu suporte e você precisa explicar que "é só um detalhe técnico". Multiplique isso por 30 clientes/mês e você tem um problema real.
O que acontece por baixo
Quando o checkout do WooCommerce salva um pedido feito como guest, ele preenche o campo customer_id com 0. O email do cliente fica salvo em billing_email, mas não tem nenhum vínculo entre esse pedido e qualquer usuário do WordPress.
Quando o cliente cria conta depois, o WordPress cria uma row em wp_users com um ID novo. Nada no Woo faz a ponte automaticamente. A lógica "se o email do user bate com o email de algum pedido com customer_id=0, vincula" simplesmente não existe no core.
Não é um bug. É uma decisão de design. O Woo prefere ser conservador e não tomar decisões automáticas sobre dados do cliente — afinal, dois clientes diferentes podem compartilhar um email (raro, mas possível em famílias, por exemplo).
O fix manual: a query SQL que todo dev do Woo já rodou
A solução manual é uma query simples. Quando você sabe o user_id e o email, atualiza todos os pedidos:
UPDATE wp_wc_orders
SET customer_id = 42
WHERE billing_email = 'cliente@exemplo.com'
AND customer_id = 0;
(Com HPOS desativado, troque wp_wc_orders por wp_posts + meta, mas o conceito é o mesmo.)
Isso funciona. Mas tem problemas em produção:
- Manual demais — você precisa lembrar de rodar para cada cliente.
- Sem auditoria — não fica gravado quem vinculou, quando vinculou, ou de onde veio o trigger.
- Sem idempotência — se rodar duas vezes, ou se um outro user_id estiver vinculado, você sobrescreve dados sem perceber.
- Não escala — se você quer vincular o histórico inteiro de uma vez (backfill), uma query crua não te dá feedback de quantos vinculou, pulou ou deu erro.
A solução robusta: hookar user_register
O WordPress dispara a action user_register sempre que um usuário novo é criado. Você pode hookar nela e, no momento do cadastro, varrer os pedidos guest com o mesmo email e vinculá-los — automaticamente, sem o cliente precisar pedir.
add_action( 'user_register', 'vincular_pedidos_no_cadastro' );
function vincular_pedidos_no_cadastro( $user_id ) {
$user = get_userdata( $user_id );
if ( ! $user ) return;
$email = strtolower( trim( $user->user_email ) );
if ( ! is_email( $email ) ) return;
$orders = wc_get_orders( array(
'billing_email' => $email,
'limit' => -1,
'return' => 'ids',
) );
foreach ( $orders as $order_id ) {
$order = wc_get_order( $order_id );
if ( (int) $order->get_customer_id() === 0 ) {
$order->set_customer_id( $user_id );
$order->save();
}
}
}
Isso já resolve 80% dos casos. Mas falta um detalhe importante para produção: idempotência.
Cuidado com a sobrescrita
O snippet acima vincula todo pedido com email igual. Mas e se um pedido já tem customer_id diferente de zero? Por exemplo:
- Cliente A comprou com email
familia@exemplo.com, logado. - Cliente B (esposa do A) faz cadastro depois com o mesmo email da conta de email compartilhada.
Sem proteção, você acabou de transferir os pedidos do A para o B. Em produção, isso é desastre.
A versão segura compara o email do user atual com o email do pedido antes de sobrescrever:
$current_customer_id = (int) $order->get_customer_id();
if ( $current_customer_id > 0 ) {
$current_user = get_userdata( $current_customer_id );
$current_mail = $current_user ? strtolower( $current_user->user_email ) : '';
// Não sobrescreve se o email atual diverge
if ( $current_mail !== '' && $current_mail !== $email ) {
continue;
}
}
Auditoria: quem fez o que, quando
Em produção você quer poder reconstruir o que aconteceu. Salve metadados de auditoria:
$order->update_meta_data( '_wc_email_order_linked_at', current_time( 'mysql' ) );
$order->update_meta_data( '_wc_email_order_linked_by', 'auto_register' );
$order->save();
Assim, daqui a 6 meses, quando um cliente reclamar que o pedido foi parar na conta errada, você abre o pedido no admin e vê: vinculado automaticamente no cadastro, em tal data. Sem mistério.
🔗 Já fizemos isso por você
O Intersomos Order Linker faz tudo descrito acima: hook no user_register, idempotência, auditoria, e ainda inclui uma ferramenta de bulk no admin para fazer backfill do histórico inteiro de uma vez. Grátis, HPOS-ready.
E os pedidos antigos? O backfill
O hook user_register só dispara em cadastros novos. Se você quer rodar pra todos os pedidos guest da sua loja inteira, precisa de um script de backfill que:
- Lista todos os usuários do WordPress;
- Pra cada user, busca pedidos com mesmo email e
customer_id=0; - Aplica a mesma lógica idempotente acima;
- Retorna estatísticas: encontrados, vinculados, pulados, erros.
Isso é trivial de escrever, mas o detalhe é o relatório. Quando você roda numa loja com 5.000 pedidos, precisa ver no admin "vinculei 1.247 pedidos, pulei 84 porque o email atual diverge, encontrei 12 erros" — não um output cru de PHP.
Conclusão
O WooCommerce não vincula pedidos guest automaticamente porque essa é uma decisão deliberada de não tocar dados sem permissão. Mas pra 99% dos casos reais, o cliente quer que vincule e fica frustrado quando não vincula. A solução é um hook, idempotência, auditoria — e, idealmente, uma ferramenta visual de backfill.
Achou útil? Compartilhe: