DesenvolvedorWordPress WhatsApp

E-mails transacionais do WooCommerce: customizar e criar

"Pedido recebido", "pedido enviado", "sua fatura": cada um desses é uma classe WC_Email disparada por uma transição de status. Entender essa engrenagem é o que separa quem injeta conteúdo com segurança de quem dispara mil e-mails sem querer numa migração.

Precisa de desenvolvimento sob medida?

Atualizado em 19/06/2026

Os e-mails que a sua loja envia — confirmação de pedido, aviso de processamento, nota de conclusão, fatura — não são templates soltos: cada um é uma classe que estende WC_Email, registrada no sistema de e-mails e disparada por uma transição de status do pedido. Quem entende essa arquitetura customiza com cirurgia (injeta um aviso, muda um assunto, cria um e-mail novo) e, principalmente, evita o acidente clássico de disparar centenas de mensagens numa importação. Este artigo cobre os três níveis de customização — hooks de conteúdo, override de template e criação de um e-mail próprio — além da parte que mais derruba projetos: entregabilidade.

Como o sistema de e-mails funciona

O WooCommerce mantém uma coleção de objetos WC_Email, acessível via WC()->mailer()->get_emails() e gerenciável em WooCommerce › Ajustes › E-mails. Cada e-mail tem um id único (é a chave para quase tudo), um destinatário (cliente ou administrador), assunto, cabeçalho e dois templates: um HTML e um texto puro.

O gatilho da maioria é uma transição de status. Quando um pedido passa para "processing", o WooCommerce dispara o hook woocommerce_order_status_processing, ao qual o e-mail "processando pedido" está conectado pelo seu trigger(). É por isso que mudar status por código tem efeito colateral: usar $order->update_status( 'completed' ) envia o e-mail de pedido concluído — o que normalmente é desejável, mas em uma migração de milhares de pedidos é desastre.

E-mail (id)ParaGatilho (status / momento)
new_orderAdminPedido pago entra (pending → processing/completed)
customer_processing_orderClienteStatus muda para processando
customer_completed_orderClienteStatus muda para concluído
customer_on_hold_orderClienteStatus muda para aguardando (on-hold)
customer_refunded_orderClientePedido reembolsado
cancelled_order / failed_orderAdminStatus cancelado / falhou
customer_invoiceClienteManual (fatura/pagamento pendente, a partir do pedido)
customer_noteClienteManual (nota adicionada ao cliente)
customer_new_accountClienteCriação de conta

Cuidado com disparos em massa. Importações, sincronizações com ERP e migrações frequentemente alteram status de muitos pedidos. Cada transição pode disparar um e-mail real. Antes de rodar um script desses, considere suspender o mailer (remover temporariamente os hooks de envio) ou usar métodos que não transicionam status. Testar em staging não é opcional aqui.

Nível 1 — Injetar conteúdo com hooks

O jeito mais seguro de customizar (porque sobrevive a atualizações) é injetar conteúdo nos hooks que o template de pedido expõe: woocommerce_email_before_order_table e woocommerce_email_after_order_table (antes/depois da tabela de itens) e woocommerce_email_order_meta (na área de metadados). O detalhe crítico: o callback roda para todos os e-mails, então cheque $email->id para agir só onde deseja — e escape tudo:

<?php
add_action( 'woocommerce_email_before_order_table', 'devwp_aviso_no_email', 20, 4 );

/**
 * @param WC_Order $order        Pedido.
 * @param bool     $sent_to_admin Se vai para o admin.
 * @param bool     $plain_text   Se é a versão texto puro.
 * @param WC_Email $email        O objeto do e-mail (tem ->id).
 */
function devwp_aviso_no_email( $order, $sent_to_admin, $plain_text, $email ) {
    // Só no e-mail de pedido CONCLUÍDO enviado ao CLIENTE
    if ( empty( $email->id ) || 'customer_completed_order' !== $email->id ) {
        return;
    }

    $prazo = esc_html( get_option( 'devwp_prazo_garantia', '7 dias' ) );

    if ( $plain_text ) {
        echo "\nTrocas e devolucoes em ate {$prazo}.\n";
    } else {
        echo '<p style="margin:16px 0;padding:12px;background:#f7f7f7;">'
           . 'Trocas e devoluções em até <strong>' . $prazo . '</strong>.'
           . '</p>';
    }
}

Para mudar o assunto ou o título de um e-mail específico, existem filtros dinâmicos que terminam com o id do e-mail — não precisa checar $email->id porque o filtro já é específico. Também dá para acrescentar cabeçalhos (por exemplo, um Bcc) via woocommerce_email_headers:

<?php
// Assunto do e-mail de pedido concluído (filtro dinâmico por id)
add_filter( 'woocommerce_email_subject_customer_completed_order', function ( $subject, $order ) {
    return sprintf( 'Seu pedido #%s foi enviado!', $order->get_order_number() );
}, 10, 2 );

// Adiciona um Bcc de auditoria a todos os e-mails (cuidado com privacidade)
add_filter( 'woocommerce_email_headers', function ( $headers, $email_id, $order ) {
    if ( 'customer_completed_order' === $email_id ) {
        $headers .= 'Bcc: [email protected]' . "\r\n";
    }
    return $headers;
}, 10, 3 );

Nível 2 — Sobrescrever o template do e-mail

Quando a mudança é estrutural (layout, ordem dos blocos, branding pesado), copie o template do e-mail para o tema. Os arquivos vivem em woocommerce/templates/emails/; a cópia vai para seu-tema/woocommerce/emails/ com o mesmo nome. O WooCommerce então usa a sua versão. Cabeçalho e rodapé compartilhados são email-header.php e email-footer.php:

// Copie mantendo o caminho relativo (de -> para):

wp-content/plugins/woocommerce/templates/emails/customer-completed-order.php
        ->  wp-content/themes/SEU-TEMA/woocommerce/emails/customer-completed-order.php

// Cabeçalho e rodapé (afetam TODOS os e-mails):
.../woocommerce/templates/emails/email-header.php
        ->  .../themes/SEU-TEMA/woocommerce/emails/email-header.php
.../woocommerce/templates/emails/email-footer.php
        ->  .../themes/SEU-TEMA/woocommerce/emails/email-footer.php

Sobrescrever um template congela aquele arquivo na versão em que você copiou: melhorias e correções que o WooCommerce publicar nele não chegam à sua loja. Por isso, prefira os hooks do Nível 1 sempre que possível e reserve o override para o que não dá para fazer por hook. Quando atualizar o WooCommerce, revise se os templates copiados ficaram defasados (o painel de Status avisa quando há templates desatualizados). Detalhes em sobrescrever templates do WooCommerce.

Nível 3 — Criar um e-mail transacional novo

Para um e-mail que o WooCommerce não tem (por exemplo, "pedido pronto para retirada"), você cria uma classe que estende WC_Email. Ela define o id, o título/descrição que aparecem em Ajustes › E-mails, os templates e — o mais importante — um método trigger() que monta e envia a mensagem. No construtor, você conecta o trigger() ao hook que deve dispará-lo:

<?php
add_action( 'plugins_loaded', function () {

    if ( ! class_exists( 'WC_Email' ) ) {
        return; // WooCommerce ainda não carregou
    }

    class DevWP_Email_Pronto_Retirada extends WC_Email {

        public function __construct() {
            $this->id             = 'devwp_pronto_retirada';
            $this->title          = 'Pedido pronto para retirada';
            $this->description    = 'Avisa o cliente que o pedido pode ser retirado.';
            $this->customer_email = true;
            $this->template_html  = 'emails/devwp-pronto-retirada.php';
            $this->template_plain = 'emails/plain/devwp-pronto-retirada.php';
            // Permite carregar os templates da pasta do seu plugin
            $this->template_base  = plugin_dir_path( __FILE__ ) . 'templates/';

            // Dispara quando o pedido entra em status custom "pronto-retirada"
            add_action( 'woocommerce_order_status_pronto-retirada_notification',
                        array( $this, 'trigger' ), 10, 2 );

            parent::__construct();

            $this->recipient = $this->get_option( 'recipient', '' );
        }

        public function get_default_subject() {
            return 'Seu pedido #{order_number} está pronto para retirada';
        }
        public function get_default_heading() {
            return 'Pode retirar seu pedido';
        }

        public function trigger( $order_id, $order = false ) {
            $this->setup_locale();

            if ( $order_id && ! $order instanceof WC_Order ) {
                $order = wc_get_order( $order_id );
            }
            if ( $order instanceof WC_Order ) {
                $this->object    = $order;
                $this->recipient = $order->get_billing_email();
                // placeholders {order_number}, {order_date}, etc.
                $this->placeholders['{order_number}'] = $order->get_order_number();
            }

            if ( $this->is_enabled() && $this->get_recipient() ) {
                $this->send(
                    $this->get_recipient(),
                    $this->get_subject(),
                    $this->get_content(),
                    $this->get_headers(),
                    $this->get_attachments()
                );
            }

            $this->restore_locale();
        }

        public function get_content_html() {
            return wc_get_template_html( $this->template_html, array(
                'order'         => $this->object,
                'email_heading' => $this->get_heading(),
                'sent_to_admin' => false,
                'plain_text'    => false,
                'email'         => $this,
            ), '', $this->template_base );
        }
    }
} );

Criada a classe, falta registrá-la para que o WooCommerce a instancie e a mostre no painel. É o papel do filtro woocommerce_email_classes — adicione a sua classe ao array, indexada pelo id:

<?php
add_filter( 'woocommerce_email_classes', function ( $emails ) {
    $emails['DevWP_Email_Pronto_Retirada'] = new DevWP_Email_Pronto_Retirada();
    return $emails;
} );

// Se o gatilho é um status custom, registre o "_notification" também:
add_action( 'woocommerce_order_status_pronto-retirada', function ( $order_id ) {
    do_action( 'woocommerce_order_status_pronto-retirada_notification', $order_id );
} );

A partir daqui o seu e-mail é cidadão de primeira classe: aparece em Ajustes › E-mails, pode ser ligado/desligado, ter assunto e destinatário editados, e usa o mesmo cabeçalho/rodapé dos demais. Basta criar os arquivos de template em templates/emails/ dentro do plugin.

Entregabilidade e o modo de teste

De nada adianta um e-mail bonito que não chega. Por padrão o WordPress usa wp_mail() sobre o mail() do PHP, sem autenticação e com remetente que raramente bate com o domínio — combinação que cai em spam ou é recusada. A correção profissional é enviar por SMTP autenticado ou por um serviço transacional (Amazon SES, Postmark, SendGrid, Mailgun), com SPF, DKIM e DMARC configurados no DNS do domínio.

Sempre teste. Faça um pedido real em staging e percorra os status para ver cada e-mail. Inspecione a versão HTML e a texto puro. Use uma ferramenta que mostre se passou em SPF/DKIM/DMARC. Lembre que o "inliner" de CSS do WooCommerce ajuda, mas clientes como Outlook ainda descartam CSS moderno — mantenha o markup simples e baseado em tabelas, herdando do template padrão.

Armadilhas comuns

Boas práticas

E-mails transacionais são a voz da sua loja em cada etapa do pedido — e se ligam diretamente às transições de status que você dispara pelos hooks do WooCommerce e pela CRUD API de pedidos. Quando a customização for de layout, faça com cuidado o override de templates. Se a sua loja precisa de e-mails customizados, melhoria de entregabilidade ou um fluxo de notificações sob medida, conheça os serviços de suporte e manutenção WooCommerce ou veja os demais artigos de desenvolvimento.

Seus e-mails de pedido não chegam ou precisam de customização?

Configuro entregabilidade (SMTP, SPF/DKIM), customizo e crio e-mails transacionais WooCommerce com segurança. Me chame no WhatsApp.

WhatsApp: (43) 99932-9697

ou e-mail: [email protected]

Perguntas frequentes

Quando os e-mails do WooCommerce são disparados?

A maioria é disparada por transições de status do pedido. Ao mudar para "processando" sai o e-mail de pedido em processamento; ao ir para "concluído", o de pedido concluído; ao ser criado um pedido pago, o "novo pedido" para o administrador. Outros são manuais (a fatura/invoice e a nota do cliente são enviadas a partir do pedido no admin). Por isso, mexer em status por código — inclusive via update_status() — pode disparar e-mails reais; em importações e migrações, é fácil enviar centenas de mensagens sem querer.

Como customizo o conteúdo de um e-mail sem editar o template?

Use os hooks de conteúdo: woocommerce_email_before_order_table, woocommerce_email_after_order_table e woocommerce_email_order_meta injetam HTML em pontos específicos de todos os e-mails de pedido. Como o callback roda para todos os e-mails, sempre cheque $email->id para agir só onde quer (por exemplo, apenas em customer_completed_order) e escape tudo que imprimir. Para mudar texto fixo, há filtros como woocommerce_email_subject_{id} e woocommerce_email_heading_{id}.

Como sobrescrevo o visual (template) de um e-mail?

Copiando o template de woocommerce/templates/emails/ para seu-tema/woocommerce/emails/, mantendo o mesmo nome e caminho relativo. O WooCommerce passa a usar a sua cópia. Para o cabeçalho/rodapé compartilhados, são emails/email-header.php e emails/email-footer.php. Cuidado: ao sobrescrever, você "congela" aquele arquivo — atualizações do WooCommerce não o alcançam, então é preciso revisar a cópia quando o plugin atualiza. Veja sobrescrever templates.

Como crio um e-mail transacional totalmente novo?

Você cria uma classe que estende WC_Email (definindo id, title, templates e um método trigger()), registra-a no filtro woocommerce_email_classes para que apareça em Ajustes › E-mails, e dispara o trigger() a partir de um hook (geralmente uma transição de status como woocommerce_order_status_completed, ou um do_action próprio). A partir daí ela é tratada como qualquer e-mail nativo: ligar/desligar, assunto, destinatário, tudo gerenciável no painel.

Por que os e-mails do WooCommerce não chegam (caem no spam)?

Quase sempre é entregabilidade, não bug. Por padrão o WordPress envia com wp_mail()/PHP mail(), sem autenticação, de um remetente que não bate com o domínio — receita para spam ou bloqueio. A correção é enviar por SMTP autenticado (ou um serviço transacional como Amazon SES, Postmark, SendGrid), com SPF, DKIM e DMARC do domínio configurados. Teste sempre antes de ir para produção; um e-mail de pedido que não chega é venda perdida e suporte aberto.

Por que meu HTML some ou aparece quebrado no e-mail?

Porque e-mail não é navegador. Clientes como Outlook e Gmail descartam CSS externo, muito CSS moderno e até alguns seletores; o padrão prático ainda é HTML com tabelas e estilos inline. O WooCommerce até roda um "inliner" de CSS por padrão, mas markup complexo ou JavaScript simplesmente não funciona. Mantenha o conteúdo simples, teste em vários clientes e evite reinventar a estrutura do template — herde do email-header.php/email-footer.php existentes.

Referências oficiais

  1. WooCommerce — Email FAQ
  2. WooCommerce — Template Structure
  3. WooCommerce — Developer Documentation
  4. WooCommerce — Documentation
Tem alguma dúvida? Ficarei feliz em ajudar. Clique Aqui