WooCommerce ships with an order list, a details page and a browser Print button. What it does not ship with is a way to make a ticket come out of a machine in the kitchen the second a customer pays. That gap is why "woocommerce automatic order printing" is a question people still ask in 2026.
The short answer: hook into woocommerce_order_status_processing, build the ticket text, and POST it to a printer that is reachable from the internet. A plugin does this for you; twenty lines in a small custom plugin do it exactly the way you want. Both work without a computer running in the shop.
Which route to take
| Route | Effort | Custom layout | Survives theme updates | Best for |
|---|---|---|---|---|
| Connector plugin | Install and configure | Template settings | Yes | Most shops, agency deliveries |
| Custom hook | ~20 lines | Total | Yes, if in a plugin | Specific logic, multi-vendor |
| WP-Cron polling | Higher | Total | Yes | Shops where hooks are unreliable |
The plugin route
The WooCommerce connector installs like any other extension: activate it, paste your API credentials, choose the printer and the order statuses that trigger a print. The WooCommerce plugin documentation walks through the settings, including receipt customisation and printing a duplicate copy.
If your shop is a normal shop, stop here. The rest of this article is for the cases that are not.
The hook route
WooCommerce fires a status hook every time an order changes state. The one you almost always want is woocommerce_order_status_processing: it fires once payment has been confirmed and the order is ready to be prepared.
<?php
/**
* Plugin Name: Print orders on payment
* Put this in wp-content/plugins/, not in the theme's functions.php:
* a theme update or a theme switch would silently stop your printing.
*/
add_action( 'woocommerce_order_status_processing', 'my_print_order', 10, 2 );
function my_print_order( $order_id, $order ) {
// Guard against double printing when a status is set twice.
if ( $order->get_meta( '_printed_at' ) ) {
return;
}
$lines = array(
'ORDER #' . $order->get_order_number(),
$order->get_date_created()->date( 'd/m/Y H:i' ),
'',
);
foreach ( $order->get_items() as $item ) {
$lines[] = $item->get_quantity() . 'x ' . $item->get_name();
}
$lines[] = '';
$lines[] = 'TOTAL: ' . $order->get_total() . ' ' . $order->get_currency();
wp_remote_post(
'https://www.expedy.fr/api/v2/printers/' . MY_PRINTER_UID . '/print',
array(
'timeout' => 5,
'blocking' => false, // do not make the customer wait for the printer
'headers' => array(
'Authorization' => MY_API_SID . ':' . MY_API_TOKEN,
'Content-Type' => 'application/json',
),
'body' => wp_json_encode(
array(
'printer_msg' => implode( "\n", $lines ),
'origin' => 'woo-' . $order_id,
)
),
)
);
$order->update_meta_data( '_printed_at', current_time( 'mysql' ) );
$order->save();
}
Three things in that code are deliberate.
'blocking' => false. Without it, the shopper's checkout request waits for the print API to answer. On a slow connection that is a visibly slower thank-you page, and if the printer is unreachable it can look like a checkout failure. Fire and forget, then reconcile separately.
The _printed_at guard. An order can enter processing more than once: a payment gateway callback arriving twice, a manual status change, a plugin that re-saves the order. Without the guard, each of those is another ticket.
A plugin, not functions.php. Code in the theme dies with the theme. Every agency has inherited at least one site where printing stopped on the day the theme was changed, and nobody connected the two events for a week.
Where WooCommerce specifically bites
Statuses are not universal. Shops using bank transfer or cash on delivery never pass through processing the same way. If your orders sit in on-hold, hook that status instead, or hook woocommerce_order_status_changed and decide inside the function.
WP-Cron is not a cron. If you fall back to polling for unprinted orders, remember that WP-Cron only runs when someone visits the site. On a low-traffic shop, an order can wait an hour for its ticket. Use a real system cron hitting wp-cron.php if you go this route.
Multi-vendor. On a marketplace built with WCFM or Dokan, each vendor needs their own printer. The pattern is the same, but the printer_uid comes from vendor metadata rather than a constant: see the WCFM documentation.
Character encoding. WordPress happily stores emoji in product names. Thermal printers do not print them, and on some models an unexpected byte sequence drops the connection. Run product names through a transliteration before building the ticket.
Formatting beyond plain text
Bold, double-height headers, a logo, a QR code, an automatic cut: all are tags inside printer_msg. Start with the receipt layout reference, then logo printing if you want branding on the ticket.
Getting started
Create a free account, get your API credentials, and print a test ticket with a plain curl before you touch WooCommerce at all. Isolating the print chain from the WordPress chain saves a surprising amount of debugging: when something fails later, you already know which half to look at.
FAQ
Which WooCommerce hook should trigger the print?
woocommerce_order_status_processing in most shops: it fires once payment is confirmed. Shops using bank transfer or cash on delivery may need on-hold instead, or woocommerce_order_status_changed with the decision made inside the function.
Can I print WooCommerce orders without a plugin?
Yes. A small custom plugin with an action on the order status hook and a wp_remote_post call to the print endpoint is around twenty lines. Put it in wp-content/plugins/, never in the theme's functions.php.
Why does the checkout feel slower after adding printing?
Because the HTTP call to the print API blocks the checkout request. Pass 'blocking' => false to wp_remote_post so the shopper is never waiting on a printer.
How do I stop an order printing twice?
Store a meta flag such as _printed_at on the order and return early when it is already set. Order statuses can be applied more than once by gateway callbacks or manual changes.
Can each vendor on a multi-vendor marketplace get their own printer?
Yes. Store the printer_uid in the vendor metadata and read it when building the call. WCFM and Dokan marketplaces are handled this way.