Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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

StatusName in the admin panelMeaning
pendingPending paymentThe order is created, no money yet
on-holdOn holdPayment is being checked by hand
processingProcessingPaid, being prepared for shipping
completedCompletedShipped or handed over
cancelledCancelledCancelled by the customer or a manager
refundedRefundedThe money was returned
failedFailedThe 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

HookWhen it firesArguments
woocommerce_order_status_processingThe order moved to the given statusID, order
woocommerce_order_status_pending_to_processingA specific transitionID, order
woocommerce_order_status_changedAny changeID, old, new, order
woocommerce_payment_completePayment receivedID
woocommerce_new_orderThe order was createdID, 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.

Thanks for reading!

Donate to the site — we work hard for you.