
本文详解如何在用户访问 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,nnYou'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/htmlrn"; $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 最佳实践的结账页邮件通知机制——既精准触达用户,又不干扰核心购物流程。