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.

Baixar grátis (só seu email)

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:

  1. Lista todos os usuários do WordPress;
  2. Pra cada user, busca pedidos com mesmo email e customer_id=0;
  3. Aplica a mesma lógica idempotente acima;
  4. 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:

Twitter WhatsApp LinkedIn

Continue lendo

Rastreio Melhor Envio sumiu do painel?

Os 4 meta keys onde o WooCommerce esconde o tracking.

Conteúdo protegido sem pagar US$ 199/ano

Como vender acesso a posts exclusivos no WooCommerce nativo.