WooCommerce Order Number Prefix Snippet
Add a prefix, suffix or zero-padding to WooCommerce order numbers, with sequential numbering explained honestly.
Your details
Your snippet
<?php
/**
* Format WooCommerce order numbers.
*/
add_filter( 'woocommerce_order_number', 'ea_order_number_format', 10, 2 );
function ea_order_number_format( $order_number, $order ) {
$number = $order->get_id();
return 'ST-' . str_pad( $number, 5, '0', STR_PAD_LEFT ) . '';
}This only changes what's displayed (order pages, emails, admin). The order's real ID, used in URLs and by every plugin and report, is untouched.
Order IDs skip numbers (drafts, abandoned checkouts and trashed orders all consume one), so gaps in the numbers below don't mean missing orders. Turn on the sequential counter if you need no-gaps numbering.
The difference between an order ID and an order number
get_order_number() (what's shown to customers, on invoices, and in admin) runs the order's ID through the woocommerce_order_number filter and returns it as a string. The underlying order ID never changes; you're only changing how it's displayed. That's why a prefix and padding are safe: they're cosmetic.
Order IDs skip numbers. WordPress's post ID sequence (which order IDs are built on, even with HPOS) also counts drafts, auto-drafts, trashed orders and abandoned block-checkout attempts, so “#1042” being followed by “#1057” doesn't mean 14 orders went missing.
True sequential numbering (1, 2, 3… with no gaps) needs its own counter, stored separately and incremented only when a real order is placed through checkout. This snippet includes an optional version of that using an order meta field and a site option, with two honest limits: it only assigns a number when an order is placed through checkout (not when you create one manually in admin), and two customers checking out in the same instant could theoretically get the same number without extra locking. For an invoicing-grade sequential number (legally required in some countries), use a dedicated numbering plugin that handles concurrency properly.
Adding it
Paste the snippet into a site-specific plugin or your child theme's functions.php, place a test order, and check the order number on the thank-you page, the customer's My Account → Orders list, and the admin order list all match.
If you switch from an ID-based prefix to sequential numbering later, past orders won't have a sequential number stored, so this snippet falls back to their order ID for any order placed before the change.
Questions people ask
Will this change my order URLs or the order ID stored in the database?
No. woocommerce_order_number only changes what's displayed. The order's real ID, used in URLs and by every plugin and report, is untouched.
Why do I have gaps in my order numbers?
Abandoned checkouts (WooCommerce creates a draft order the moment someone reaches the payment step, even if they never pay), admin test orders, and failed payment attempts all consume an ID. Gaps don't mean lost sales.
Can I start the sequential counter at a specific number, like continuing from an old system?
Yes, set the starting number field before adding the snippet; it only affects orders placed after that point.
Does this work with subscriptions or renewal orders?
Yes, a renewal order is still a normal order object, so both the prefix and the sequential counter (if enabled) apply to it the same way.
Related tools
Last reviewed 30 September 2026.
Sources: WooCommerce source: class-wc-order.php · WooCommerce source: wc-order-functions.php
