
本文详解如何在用户访问 woocommerce 结账页面且购物车非空时,安全、可靠地发送个性化邮件通知,避免重复触发与运行错误,并提供兼容 woocommerce 邮件模板的专业方案。
在 WooCommerce 开发中,常有业务需求需在用户进入结账流程时主动发送提示邮件(例如:“您的订单即将完成,请确认信息”)。但若错误选用钩子(如 woocommerce_after_shop_loop_item)或忽略上下文校验,极易导致邮件被多次发送、后台报错甚至影响页面加载——这正是原始代码中问题的核心:该钩子实际作用于商品列表页,与结账流程完全无关;同时未校验 WC()->cart 实例是否存在,也未防御空用户名/邮箱等边界情况。
✅ 正确的钩子选择:woocommerce_before_checkout_form
WooCommerce 提供了专用于结账页渲染前的钩子 woocommerce_before_checkout_form,它仅在 /checkout/ 页面、表单渲染前执行一次,天然满足“仅在结账页触发”的语义要求,从根本上杜绝跨页面误触发。
✅ 安全健壮的执行逻辑
以下为推荐实现,已整合用户登录态、购物车可用性、字段非空等多层防护:
function send_checkout_entry_notification() {
// 1. 仅对已登录用户生效
if ( ! is_user_logged_in() ) {
return;
}
// 2. 确保 WooCommerce 购物车系统已初始化(避免 fatal error)
if ( ! WC()->cart instanceof WC_Cart ) {
return;
}
// 3. 购物车必须非空
if ( WC()->cart->is_empty() ) {
return;
}
$current_user = wp_get_current_user();
$email = $current_user->user_email;
$name = $current_user->user_firstname;
// 4. 严格校验关键字段(邮箱和姓名均不能为空)
if ( empty( $email ) || empty( $name ) ) {
error_log( 'Checkout notification skipped: missing user email or first name for user ID ' . $current_user->ID );
return;
}
// 5. 构建邮件内容(支持国际化)
$to = $email;
$subject = sprintf( __( 'Hello %s, your purchase is almost ready!', 'your-textdomain' ), esc_html( $name ) );
$body = sprintf(
__( "Hi %s,\n\nYou're about to complete your order on our store. Your cart currently contains %d item(s).", 'your-textdomain' ),
esc_html( $name ),
WC()->cart->get_cart_contents_count()
);
$headers = array( 'Content-Type: text/html; charset=UTF-8' );
// 6. 发送邮件(使用 WordPress 原生 wp_mail)
$sent = wp_mail( $to, $subject, $body, $headers );
if ( ! $sent ) {
error_log( 'Failed to send checkout notification to ' . $email );
}
}
add_action( 'woocommerce_before_checkout_form', 'send_checkout_entry_notification' );? 进阶方案:复用 WooCommerce 邮件样式(推荐生产环境)
若需邮件外观与 WooCommerce 默认通知(如订单确认邮件)保持一致(含品牌头图、CSS 样式、响应式布局),应调用 WooCommerce 内置邮件类 WC_Email,而非裸用 wp_mail():
// 替换上述第5–6步为以下代码:
if ( ! empty( $email ) && ! empty( $name ) ) {
$mailer = WC()->mailer();
$to = $email;
$subject = __( 'Your Checkout Is Ready', 'your-textdomain' );
$message = sprintf(
__( "Hello %s,<br><br>You're one step away from completing your order. Your cart has %d item(s).<br><br>Thank you for shopping with us!", 'your-textdomain' ),
esc_html( $name ),
WC()->cart->get_cart_contents_count()
);
// 自动包裹为 WooCommerce 标准邮件结构(含 header/footer 和内联 CSS)
$formatted_message = $mailer->wrap_message(
sprintf( __( 'Hello %s', 'your-textdomain' ), esc_html( $name ) ),
$message
);
$headers = "Content-Type: text/html\r\n";
$mailer->send( $to, $subject, $formatted_message, $headers );
}⚠️ 关键注意事项
- 勿在 AJAX 或无刷新场景中依赖此钩子:woocommerce_before_checkout_form 仅在完整页面加载时触发。若使用 AJAX 加载结账表单(如某些主题或插件),需配合 wc_ajax_update_order_review 等钩子另行处理。
- 避免敏感操作放在前端钩子中:本方案仅作轻量通知。如需同步更新库存、生成预订单等,应改用 woocommerce_checkout_process(验证阶段)或 woocommerce_thankyou(支付成功后)等更可靠的后端钩子。
- 务必启用日志记录:示例中已加入 error_log(),建议在开发/测试环境开启 WordPress 调试日志(WP_DEBUG_LOG = true),便于快速定位发送失败原因。
- 遵守 GDPR 与邮件规范:首次发送前应确保用户已明确同意接收营销类通知;邮件正文须包含有效的退订链接(可通过 wp_mail() 的 $headers 添加 List-Unsubscribe 头)。
通过以上结构化实现,您将获得一个稳定、可维护、符合 WooCommerce 最佳实践的结账页邮件通知机制——既精准触达用户,又不干扰核心购物流程。










