Order statuses
An order status shows the stage the order is at: awaiting payment, paid, shipped. WooCommerce changes the status itself on payment, and a manager changes it by hand in the admin panel. Every change fires a hook, and automation is built on it: pass the order to the warehouse, send a message.
Standard statuses
| Status | Name in the admin panel | Meaning |
|---|---|---|
pending | Pending payment | The order is created, no money yet |
on-hold | On hold | Payment is being checked by hand |
processing | Processing | Paid, being prepared for shipping |
completed | Completed | Shipped or handed over |
cancelled | Cancelled | Cancelled by the customer or a manager |
refunded | Refunded | The money was returned |
failed | Failed | The payment did not go through |
Changing the status
// wp-content/plugins/acme-shop/scripts/complete-order.php
$order = wc_get_order(1024);
$order->update_status('completed', 'Handed to the courier.');update_status() changes the status, saves the order and adds a note. Run the file with wp eval-file and open the order in WooCommerce → Orders: the status is Completed, a record has appeared in the Order notes box, and an email went to the customer.
Reacting to a status
The code of this and the following sections goes into includes/orders.php:
// wp-content/plugins/acme-shop/includes/orders.php
add_action('woocommerce_order_status_processing', function (int $orderId, WC_Order $order) {
$order->add_order_note('The order was passed to the workshop.');
}, 10, 2);The hook fires when an order moves to Processing — that is, when it is paid. Place an order with cash on delivery: the line The order was passed to the workshop. appears in the order notes.
Status hooks
| Hook | When it fires | Arguments |
|---|---|---|
woocommerce_order_status_processing | The order moved to the given status | ID, order |
woocommerce_order_status_pending_to_processing | A specific transition | ID, order |
woocommerce_order_status_changed | Any change | ID, old, new, order |
woocommerce_payment_complete | Payment received | ID |
woocommerce_new_order | The order was created | ID, order |
// wp-content/plugins/acme-shop/includes/orders.php
add_action('woocommerce_order_status_changed', function (int $orderId, string $from, string $to, WC_Order $order) {
error_log("Order $orderId: $from → $to");
}, 10, 4);Every status change lands as a line in the wp-content/debug.log log if WP_DEBUG and WP_DEBUG_LOG are enabled in wp-config.php.
Checking the status
if ($order->has_status(['processing', 'completed'])) {
// the order is paid
}
if ($order->is_paid()) {
// the same, shorter
}Your own status
The workshop needs a Ready status between payment and shipping. It is registered with two filters:
// wp-content/plugins/acme-shop/includes/orders.php
add_filter('woocommerce_register_shop_order_post_statuses', function (array $statuses): array {
$statuses['wc-ready'] = [
'label' => 'Ready',
'public' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop('Ready (%s)', 'Ready (%s)'),
];
return $statuses;
});
add_filter('wc_order_statuses', function (array $statuses): array {
$statuses['wc-ready'] = 'Ready';
return $statuses;
});The first filter tells the system about the status, the second adds it to the dropdown in the order editor. On registration the name is written with the wc- prefix, while in update_status('ready') and in the woocommerce_order_status_ready hook it goes without it.
Common mistake
Sending data to an external system is attached to a status hook, and duplicates appear: a manager moved the order to Processing, back to On hold and to Processing again — the hook fired twice. Mark the completed action in the order meta and check the mark before repeating.