PHPwoocommerceadvanced
WooCommerce Custom Email Notifications
Create custom email notifications for WooCommerce events and triggers
Faisal Yaqoob
December 23, 2025
#woocommerce#email#notifications#automation#customer-service
Code
php
1 // Create custom email class 2 if (!class_exists('WC_Custom_Order_Email')) { 3 class WC_Custom_Order_Email extends WC_Email { 4
5 public function __construct() { 6 $this->id = 'custom_order_email'; 7 $this->title = __('Custom Order Email', 'woocommerce'); 8 $this->description = __('Custom email sent for special order conditions', 'woocommerce'); 9 $this->template_html = 'emails/custom-order-email.php'; 10 $this->template_plain = 'emails/plain/custom-order-email.php'; 11 $this->template_base = get_stylesheet_directory() . '/woocommerce/'; 12 $this->placeholders = array( 13 '{order_date}' => '', 14 '{order_number}' => '', 15 ); 16
17 // Triggers for this email 18 add_action('woocommerce_order_status_pending_to_processing_notification', array($this, 'trigger'), 10, 2); 19 add_action('woocommerce_order_status_pending_to_completed_notification', array($this, 'trigger'), 10, 2); 20
21 // Call parent constructor 22 parent::__construct(); 23
24 // Other settings 25 $this->recipient = $this->get_option('recipient', get_option('admin_email')); 26 } 27
28 /** 29 * Get email subject. 30 */ 31 public function get_default_subject() { 32 return __('New order received - Order {order_number}', 'woocommerce'); 33 } 34
35 /** 36 * Get email heading. 37 */ 38 public function get_default_heading() { 39 return __('New Order: {order_number}', 'woocommerce'); 40 } 41
42 /** 43 * Trigger the sending of this email. 44 */ 45 public function trigger($order_id, $order = false) { 46 $this->setup_locale(); 47
48 if ($order_id && !is_a($order, 'WC_Order')) { 49 $order = wc_get_order($order_id); 50 } 51
52 if (is_a($order, 'WC_Order')) { 53 $this->object = $order; 54 $this->placeholders['{order_date}'] = wc_format_datetime($this->object->get_date_created()); 55 $this->placeholders['{order_number}'] = $this->object->get_order_number(); 56 } 57
58 if ($this->is_enabled() && $this->get_recipient()) { 59 $this->send($this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments()); 60 } 61
62 $this->restore_locale(); 63 } 64
65 /** 66 * Get content html. 67 */ 68 public function get_content_html() { 69 return wc_get_template_html( 70 $this->template_html, 71 array( 72 'order' => $this->object, 73 'email_heading' => $this->get_heading(), 74 'additional_content' => $this->get_additional_content(), 75 'sent_to_admin' => false, 76 'plain_text' => false, 77 'email' => $this, 78 ), 79 '', 80 $this->template_base 81 ); 82 } 83
84 /** 85 * Get content plain. 86 */ 87 public function get_content_plain() { 88 return wc_get_template_html( 89 $this->template_plain, 90 array( 91 'order' => $this->object, 92 'email_heading' => $this->get_heading(), 93 'additional_content' => $this->get_additional_content(), 94 'sent_to_admin' => false, 95 'plain_text' => true, 96 'email' => $this, 97 ), 98 '', 99 $this->template_base 100 ); 101 } 102
103 /** 104 * Initialize settings form fields. 105 */ 106 public function init_form_fields() { 107 $this->form_fields = array( 108 'enabled' => array( 109 'title' => __('Enable/Disable', 'woocommerce'), 110 'type' => 'checkbox', 111 'label' => __('Enable this email notification', 'woocommerce'), 112 'default' => 'yes', 113 ), 114 'recipient' => array( 115 'title' => __('Recipient(s)', 'woocommerce'), 116 'type' => 'text', 117 'description' => sprintf(__('Enter recipients (comma separated) for this email. Defaults to %s.', 'woocommerce'), '<code>' . esc_attr(get_option('admin_email')) . '</code>'), 118 'placeholder' => '', 119 'default' => '', 120 'desc_tip' => true, 121 ), 122 'subject' => array( 123 'title' => __('Subject', 'woocommerce'), 124 'type' => 'text', 125 'desc_tip' => true, 126 'description' => sprintf(__('Available placeholders: %s', 'woocommerce'), '<code>{order_date}, {order_number}</code>'), 127 'placeholder' => $this->get_default_subject(), 128 'default' => '', 129 ), 130 'heading' => array( 131 'title' => __('Email heading', 'woocommerce'), 132 'type' => 'text', 133 'desc_tip' => true, 134 'description' => sprintf(__('Available placeholders: %s', 'woocommerce'), '<code>{order_date}, {order_number}</code>'), 135 'placeholder' => $this->get_default_heading(), 136 'default' => '', 137 ), 138 'additional_content' => array( 139 'title' => __('Additional content', 'woocommerce'), 140 'description' => __('Text to appear below the main email content.', 'woocommerce'), 141 'css' => 'width:400px; height: 75px;', 142 'placeholder' => __('N/A', 'woocommerce'), 143 'type' => 'textarea', 144 'default' => '', 145 'desc_tip' => true, 146 ), 147 'email_type' => array( 148 'title' => __('Email type', 'woocommerce'), 149 'type' => 'select', 150 'description' => __('Choose which format of email to send.', 'woocommerce'), 151 'default' => 'html', 152 'class' => 'email_type wc-enhanced-select', 153 'options' => $this->get_email_type_options(), 154 'desc_tip' => true, 155 ), 156 ); 157 } 158 } 159 }
WooCommerce Custom Email Notifications
Create custom email notifications for specific WooCommerce events like low stock alerts, abandoned carts, or custom order statuses.
// Create custom email class
if (!class_exists('WC_Custom_Order_Email')) {
class WC_Custom_Order_Email extends WC_Email {
public function __construct() {
$this->id = 'custom_order_email';
$this->title = __('Custom Order Email', 'woocommerce');
$this->description = __('Custom email sent for special order conditions', 'woocommerce');
$this->template_html = 'emails/custom-order-email.php';
$this->template_plain = 'emails/plain/custom-order-email.php';
$this->template_base = get_stylesheet_directory() . '/woocommerce/';
$this->placeholders = array(
'{order_date}' => '',
'{order_number}' => '',
);
// Triggers for this email
add_action('woocommerce_order_status_pending_to_processing_notification', array($this, 'trigger'), 10, 2);
add_action('woocommerce_order_status_pending_to_completed_notification', array($this, 'trigger'), 10, 2);
// Call parent constructor
parent::__construct();
// Other settings
$this->recipient = $this->get_option('recipient', get_option('admin_email'));
}
/**
* Get email subject.
*/
public function get_default_subject() {
return __('New order received - Order {order_number}', 'woocommerce');
}
/**
* Get email heading.
*/
public function get_default_heading() {
return __('New Order: {order_number}', 'woocommerce');
}
/**
* Trigger the sending of this email.
*/
public function trigger($order_id, $order = false) {
$this->setup_locale();
if ($order_id && !is_a($order, 'WC_Order')) {
$order = wc_get_order($order_id);
}
if (is_a($order, 'WC_Order')) {
$this->object = $order;
$this->placeholders['{order_date}'] = wc_format_datetime($this->object->get_date_created());
$this->placeholders['{order_number}'] = $this->object->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();
}
/**
* Get content html.
*/
public function get_content_html() {
return wc_get_template_html(
$this->template_html,
array(
'order' => $this->object,
'email_heading' => $this->get_heading(),
'additional_content' => $this->get_additional_content(),
'sent_to_admin' => false,
'plain_text' => false,
'email' => $this,
),
'',
$this->template_base
);
}
/**
* Get content plain.
*/
public function get_content_plain() {
return wc_get_template_html(
$this->template_plain,
array(
'order' => $this->object,
'email_heading' => $this->get_heading(),
'additional_content' => $this->get_additional_content(),
'sent_to_admin' => false,
'plain_text' => true,
'email' => $this,
),
'',
$this->template_base
);
}
/**
* Initialize settings form fields.
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __('Enable/Disable', 'woocommerce'),
'type' => 'checkbox',
'label' => __('Enable this email notification', 'woocommerce'),
'default' => 'yes',
),
'recipient' => array(
'title' => __('Recipient(s)', 'woocommerce'),
'type' => 'text',
'description' => sprintf(__('Enter recipients (comma separated) for this email. Defaults to %s.', 'woocommerce'), '<code>' . esc_attr(get_option('admin_email')) . '</code>'),
'placeholder' => '',
'default' => '',
'desc_tip' => true,
),
'subject' => array(
'title' => __('Subject', 'woocommerce'),
'type' => 'text',
'desc_tip' => true,
'description' => sprintf(__('Available placeholders: %s', 'woocommerce'), '<code>{order_date}, {order_number}</code>'),
'placeholder' => $this->get_default_subject(),
'default' => '',
),
'heading' => array(
'title' => __('Email heading', 'woocommerce'),
'type' => 'text',
'desc_tip' => true,
'description' => sprintf(__('Available placeholders: %s', 'woocommerce'), '<code>{order_date}, {order_number}</code>'),
'placeholder' => $this->get_default_heading(),
'default' => '',
),
'additional_content' => array(
'title' => __('Additional content', 'woocommerce'),
'description' => __('Text to appear below the main email content.', 'woocommerce'),
'css' => 'width:400px; height: 75px;',
'placeholder' => __('N/A', 'woocommerce'),
'type' => 'textarea',
'default' => '',
'desc_tip' => true,
),
'email_type' => array(
'title' => __('Email type', 'woocommerce'),
'type' => 'select',
'description' => __('Choose which format of email to send.', 'woocommerce'),
'default' => 'html',
'class' => 'email_type wc-enhanced-select',
'options' => $this->get_email_type_options(),
'desc_tip' => true,
),
);
}
}
}
Register Custom Email
// Add custom email to WooCommerce
add_filter('woocommerce_email_classes', 'register_custom_order_email');
function register_custom_order_email($email_classes) {
// Include custom email class
require_once 'class-wc-custom-order-email.php';
// Add to WooCommerce email classes
$email_classes['WC_Custom_Order_Email'] = new WC_Custom_Order_Email();
return $email_classes;
}
Email Template (HTML)
Create file: woocommerce/emails/custom-order-email.php
<?php
/**
* Custom Order Email (HTML)
*/
if (!defined('ABSPATH')) {
exit;
}
do_action('woocommerce_email_header', $email_heading, $email); ?>
<p><?php printf(__('You have received a new order from %s.', 'woocommerce'), $order->get_formatted_billing_full_name()); ?></p>
<h2>
<?php
printf(
__('Order #%1$s - %2$s', 'woocommerce'),
$order->get_order_number(),
wc_format_datetime($order->get_date_created())
);
?>
</h2>
<?php do_action('woocommerce_email_order_details', $order, $sent_to_admin, $plain_text, $email); ?>
<?php do_action('woocommerce_email_order_meta', $order, $sent_to_admin, $plain_text, $email); ?>
<?php do_action('woocommerce_email_customer_details', $order, $sent_to_admin, $plain_text, $email); ?>
<?php
if ($additional_content) {
echo wp_kses_post(wpautop(wptexturize($additional_content)));
}
do_action('woocommerce_email_footer', $email);
Low Stock Alert Email
// Send email when product stock is low
add_action('woocommerce_low_stock', 'custom_low_stock_email');
function custom_low_stock_email($product) {
$to = get_option('admin_email');
$subject = sprintf(__('Low Stock Alert: %s', 'woocommerce'), $product->get_name());
$message = sprintf(
__('Product "%1$s" (ID: %2$s) is running low on stock. Current stock: %3$s', 'woocommerce'),
$product->get_name(),
$product->get_id(),
$product->get_stock_quantity()
);
$headers = array(
'Content-Type: text/html; charset=UTF-8',
'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>'
);
wp_mail($to, $subject, $message, $headers);
}
Customer Review Request Email
// Send review request email 7 days after order completion
add_action('woocommerce_order_status_completed', 'schedule_review_request_email');
function schedule_review_request_email($order_id) {
// Schedule email for 7 days later
wp_schedule_single_event(
time() + (7 * DAY_IN_SECONDS),
'send_review_request_email',
array($order_id)
);
}
add_action('send_review_request_email', 'send_review_request_email_callback');
function send_review_request_email_callback($order_id) {
$order = wc_get_order($order_id);
if (!$order) {
return;
}
$to = $order->get_billing_email();
$subject = __('How was your recent purchase?', 'woocommerce');
$message = '
<html>
<head>
<title>' . $subject . '</title>
</head>
<body>
<h2>Hi ' . $order->get_billing_first_name() . ',</h2>
<p>Thank you for your recent purchase from ' . get_bloginfo('name') . '!</p>
<p>We hope you\'re enjoying your products. We\'d love to hear your feedback!</p>
<p><a href="' . get_permalink(wc_get_page_id('shop')) . '" style="background: #0073aa; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">Leave a Review</a></p>
<p>Your feedback helps us improve and helps other customers make informed decisions.</p>
<p>Thank you,<br>' . get_bloginfo('name') . '</p>
</body>
</html>
';
$headers = array('Content-Type: text/html; charset=UTF-8');
wp_mail($to, $subject, $message, $headers);
}
Abandoned Cart Email
// Track abandoned carts
add_action('woocommerce_cart_updated', 'track_abandoned_cart');
function track_abandoned_cart() {
if (!is_user_logged_in() || WC()->cart->is_empty()) {
return;
}
$user_id = get_current_user_id();
$cart_data = array(
'cart_contents' => WC()->cart->get_cart(),
'cart_total' => WC()->cart->get_cart_contents_total(),
'timestamp' => time(),
);
update_user_meta($user_id, '_abandoned_cart', $cart_data);
}
// Send abandoned cart email after 1 hour
add_action('init', 'schedule_abandoned_cart_emails');
function schedule_abandoned_cart_emails() {
if (!wp_next_scheduled('send_abandoned_cart_emails')) {
wp_schedule_event(time(), 'hourly', 'send_abandoned_cart_emails');
}
}
add_action('send_abandoned_cart_emails', 'send_abandoned_cart_emails_callback');
function send_abandoned_cart_emails_callback() {
$users = get_users(array('meta_key' => '_abandoned_cart'));
foreach ($users as $user) {
$cart_data = get_user_meta($user->ID, '_abandoned_cart', true);
// Check if cart was abandoned more than 1 hour ago
if (empty($cart_data) || (time() - $cart_data['timestamp']) < 3600) {
continue;
}
// Check if email was already sent
if (get_user_meta($user->ID, '_abandoned_cart_email_sent', true)) {
continue;
}
$to = $user->user_email;
$subject = __('You left items in your cart!', 'woocommerce');
$message = '
<h2>Hi ' . $user->display_name . ',</h2>
<p>We noticed you left some items in your cart.</p>
<p>Total: ' . wc_price($cart_data['cart_total']) . '</p>
<p><a href="' . wc_get_cart_url() . '">Complete your purchase</a></p>
';
$headers = array('Content-Type: text/html; charset=UTF-8');
if (wp_mail($to, $subject, $message, $headers)) {
update_user_meta($user->ID, '_abandoned_cart_email_sent', true);
}
}
}
Send Email on Custom Event
// Send email when custom action occurs
add_action('custom_woocommerce_action', 'send_custom_notification_email', 10, 2);
function send_custom_notification_email($order_id, $custom_data) {
$order = wc_get_order($order_id);
$to = $order->get_billing_email();
$subject = __('Custom Notification', 'woocommerce');
$message = sprintf(
__('Hello %s, this is a custom notification about your order #%s.', 'woocommerce'),
$order->get_billing_first_name(),
$order->get_order_number()
);
$headers = array('Content-Type: text/html; charset=UTF-8');
wp_mail($to, $subject, $message, $headers);
}
Features
- Custom Email Classes: Fully integrated with WooCommerce email system
- Admin Settings: Configurable via WooCommerce > Settings > Emails
- Template Support: Uses WooCommerce email templates
- Multiple Triggers: Low stock, abandoned cart, review requests, custom events
- Scheduled Emails: Time-delayed emails using WP Cron
- HTML & Plain Text: Supports both email formats
- Placeholders: Dynamic content with placeholders
- Customizable: Full control over subject, heading, and content
- Professional: Uses WooCommerce email styling
Dependencies
- WooCommerce
Related Snippets
WooCommerce Custom Order Status
Create and manage custom order statuses in WooCommerce with email notifications
PHPwoocommerceadvanced
phpPreview
// Register custom order statuses
add_action('init', 'register_custom_order_statuses');
function register_custom_order_statuses() {
// Awaiting Shipment status
...#woocommerce#order-status#workflow+2
12/23/2025
View
WooCommerce Bulk Discounts and Quantity Pricing
Implement bulk discounts, quantity-based pricing, and tiered discounts in WooCommerce
PHPwoocommerceadvanced
phpPreview
// Apply bulk discount based on quantity
add_action('woocommerce_before_calculate_totals', 'apply_bulk_discount');
function apply_bulk_discount($cart) {
if (is_admin() && !defined('DOING_AJAX')) {
...#woocommerce#pricing#discounts+2
12/23/2025
View
WooCommerce Custom Checkout Fields
Add custom fields to WooCommerce checkout with validation and order display
PHPwoocommerceintermediate
phpPreview
// Add custom fields to checkout page
add_action('woocommerce_after_order_notes', 'custom_checkout_fields');
function custom_checkout_fields($checkout) {
echo '<div id="custom_checkout_fields"><h3>' . __('Additional Information') . '</h3>';
...#woocommerce#checkout#custom-fields+2
12/23/2025
View