WooCommerce Automatic Order Printing: A Practical Guide

Automatically send WooCommerce packing slips to your warehouse printer. Build a background PHP workflow with secure API credentials, duplicate protection, and practical failure handling.

By the PrintScaler team · · 9 min read

Warehouse packing station with a computer, shipping box, and printer automatically producing a packing slip.

WooCommerce automatic order printing connects an order event to a physical printer, so staff do not have to open each order and click Print. A practical setup generates a packing-slip PDF, queues a background task, and sends that PDF through PrintScaler to the warehouse printer. This guide builds that workflow with a small custom PHP plugin, while keeping document generation, print submission, and physical output separate.

What you need for automatic WooCommerce printing

There are three distinct parts to this workflow:

Component Responsibility
WooCommerce and your document generator Decide when an order is ready and create its packing-slip PDF
Your custom integration Queue the work, choose a printer, and submit the PDF
PrintScaler and its agent Deliver the print job to the computer connected to the printer

PrintScaler is the printing layer, not a WooCommerce packing-slip generator. The integration below is custom code, not a claim that installing PrintScaler adds a native WooCommerce integration.

You will need:

  • A WooCommerce store where you can install a small custom plugin.
  • An existing PDF generator or packing-slip extension that can expose a downloadable PDF URL.
  • A warehouse computer connected to the intended printer.
  • A PrintScaler API key and the destination printer’s ID.
  • A working background-task runner for WooCommerce’s Action Scheduler.

The example prints a PDF packing slip. The same architecture can print invoice PDFs, but the event that makes an invoice printable may differ from the event that releases an order to fulfillment.

Choose the right WooCommerce order event

Start with the business rule, not the API call.

For many stores selling physical goods, entering the processing status is a reasonable point to prepare a packing slip. However, it is not a universal definition of paid, approved, or ready to ship. Payment methods, fraud checks, inventory rules, and custom workflows can change what that status means.

Before implementing anything, answer:

  1. Should unpaid orders ever print?
  2. Must stock allocation or fraud review finish first?
  3. Is one packing slip needed per order, shipment, or warehouse?
  4. Should an edited order produce a replacement document?

This tutorial assumes one packing slip per order, sent to one warehouse when the order enters processing. If you split shipments, use a shipment-level event and identifier instead.

Make the PDF ready before queueing

The PDF must exist before the print task runs. A common mistake is to trigger printing and PDF generation independently, then assume generation will finish first.

Connect your document generator so that it creates the PDF and records its URL before the print task is queued. If your generator runs asynchronously, trigger the queueing step from its document-ready event instead of the order-status hook shown below.

The sample uses a custom order metadata field named _warehouse_packing_slip_pdf_url. This is a convention for this integration, not a built-in WooCommerce field. Your document-generation code must populate it using WooCommerce order methods.

Step 1: Connect the warehouse printer to PrintScaler

Install the PrintScaler agent on the Windows, macOS, Linux, or Raspberry Pi computer that can already print to your warehouse printer.

Then:

  1. Open the PrintScaler dashboard.
  2. Enter the agent’s pairing code to link the computer.
  3. Confirm that the expected printer appears.
  4. Use the dashboard’s test-page button.
  5. Record the printer ID for the integration.

The agent maintains an outbound WebSocket connection. You do not need to expose the warehouse computer to inbound internet connections or configure router port forwarding for this setup.

Keep the computer powered on during fulfillment hours. The agent starts automatically after a reboot, but a powered-off computer cannot produce paper immediately.

Step 2: Store credentials outside the plugin

Add configuration to wp-config.php, above the line that loads WordPress. Prefer environment variables or your hosting platform’s secret-management mechanism for the API key:

// wp-config.php
// Set PRINTSCALER_API_KEY in your hosting environment first.
define('WAREHOUSE_PRINT_API_KEY', getenv('PRINTSCALER_API_KEY'));
define('WAREHOUSE_PRINT_PRINTER_ID', 123);
define('WAREHOUSE_PRINT_SITE_KEY', 'main-store-production');

Replace 123 with the real printer ID. Give each independent store or environment a different site key, and do not change it casually after launch: it participates in duplicate protection.

Never put the API key in browser JavaScript, a public repository, or an order note visible to customers. The request should originate from your WordPress server.

Use a separate configuration for staging. A copied production database should not quietly send test orders to the live warehouse printer.

Step 3: Queue printing instead of blocking checkout

Create a directory named warehouse-order-printing under wp-content/plugins, then create warehouse-order-printing.php inside it.

The first part of the plugin registers a WooCommerce event and queues a background action:

<?php
/**
 * Plugin Name: Warehouse Order Printing
 */

if (!defined('ABSPATH')) {
    exit;
}

add_action('woocommerce_order_status_processing', function ($order_id) {
    if (!function_exists('as_enqueue_async_action')) {
        return;
    }

    as_enqueue_async_action(
        'warehouse_print_packing_slip',
        ['order_id' => (int) $order_id, 'attempt' => 0],
        'warehouse-printing',
        true
    );
});

Activate the plugin after completing the worker code in the next step.

The final true asks Action Scheduler to avoid creating another pending action with the same hook, arguments, and group. That reduces duplicate background work, but it is not sufficient duplicate-print protection: an order can leave and re-enter processing after an earlier action has finished.

The API request therefore needs its own stable idempotency key.

Also verify that scheduled actions actually run on your hosting platform. A quiet store should not depend solely on shopper visits to trigger background processing; configure a reliable scheduler appropriate to your WordPress hosting environment.

Step 4: Submit the packing-slip PDF to PrintScaler

Append this worker to the same plugin file:

add_action('warehouse_print_packing_slip', function ($order_id, $attempt = 0) {
    $order = wc_get_order($order_id);
    if (!$order || $order->get_meta('_warehouse_print_submitted')) {
        return;
    }

    $logger = wc_get_logger();
    $context = ['source' => 'warehouse-printing'];
    $pdf_url = $order->get_meta('_warehouse_packing_slip_pdf_url');

    if (!$pdf_url || !WAREHOUSE_PRINT_API_KEY) {
        $logger->error(
            'Missing PDF URL or API key for order ' . $order_id,
            $context
        );
        return;
    }

    $key = hash('sha256', WAREHOUSE_PRINT_SITE_KEY
        . ':packing-slip:' . $order_id . ':v1');

    $response = wp_remote_post(
        'https://api.printscaler.com/v1/printjobs',
        [
            'timeout' => 20,
            'headers' => [
                'Authorization' => 'Bearer ' . WAREHOUSE_PRINT_API_KEY,
                'Content-Type' => 'application/json',
                'X-Idempotency-Key' => $key,
            ],
            'body' => wp_json_encode([
                'printerId' => (int) WAREHOUSE_PRINT_PRINTER_ID,
                'contentType' => 'pdf_uri',
                'content' => $pdf_url,
                'title' => 'Packing slip - order ' . $order_id,
            ]),
        ]
    );

    $status = is_wp_error($response)
        ? 0 : wp_remote_retrieve_response_code($response);

    if ($status >= 200 && $status < 300) {
        $order->update_meta_data('_warehouse_print_submitted', gmdate('c'));
        $order->save();
        return;
    }

    $retryable = is_wp_error($response)
        || in_array($status, [408, 429], true)
        || $status >= 500;

    if ($retryable && $attempt < 5) {
        as_schedule_single_action(
            time() + min(900, 30 * (2 ** $attempt)),
            'warehouse_print_packing_slip',
            ['order_id' => (int) $order_id, 'attempt' => $attempt + 1],
            'warehouse-printing',
            true
        );
        return;
    }

    $logger->error(
        'Print submission failed for order ' . $order_id
            . '; HTTP status ' . $status,
        $context
    );
}, 10, 2);

This submits a real POST /v1/printjobs request using Bearer authentication and the documented pdf_uri content type. See the print-job API reference for the request model.

The worker deliberately uses WooCommerce order methods rather than direct post metadata functions, keeping order storage access within WooCommerce’s APIs.

Understand what this example does—and does not—record

The _warehouse_print_submitted flag means the API accepted the request. It does not mean the packing slip has physically emerged from the printer.

The worker retries transport failures, rate-limit responses, request timeouts, and server errors with increasing delays. Other unsuccessful responses are logged for investigation rather than retried indefinitely. After the retry budget is exhausted, a staff member needs to investigate and explicitly requeue the task.

For production, also persist the returned print-job identifier according to the API response schema. That lets your order administration workflow reference the specific job rather than relying only on a submission timestamp.

Prevent duplicate slips without blocking legitimate reprints

A timeout creates ambiguity: the server may have accepted the job even though WordPress never received the response. Blindly sending a new request can produce another packing slip.

The example uses an X-Idempotency-Key derived from:

  • The store’s stable identifier.
  • The document type.
  • The internal order ID.
  • A document version.

Every retry for that document uses the same key. PrintScaler’s idempotency mechanism prevents a retried request from creating another print job.

Do not generate a random key on each attempt. That makes each retry look like a new request.

An intentional replacement is different. If staff approve a revised packing slip, give that document a new version and a new idempotency key. An explicit reprint action should also create a new print intent rather than silently bypassing the original workflow’s safeguards.

The sample hardcodes v1 to keep the first-print path readable. A production reprint interface should store versions or print-intent IDs explicitly; simply deleting the submitted flag will not change the API key used for duplicate protection.

Make the PDF accessible without exposing customer data

With pdf_uri, the document URL must be retrievable by the printing workflow without a WordPress administrator’s session cookie.

A URL that works only while you are logged in is not a usable integration URL. Conversely, a permanently public, guessable PDF path can expose names, addresses, and order contents.

Prefer a protected storage workflow that issues an unguessable, appropriately time-limited download URL. Coordinate its lifetime with document retrieval and retry behavior. If the document is fetched after the URL expires, printing can fail; do not assume a URL valid for seconds will survive a queue delay.

PrintScaler also accepts pdf_base64, which sends PDF content in the request rather than supplying a download URL. That avoids the authenticated-download problem, but moves the document bytes into your WordPress request and increases payload size. Choose deliberately based on your document storage and processing constraints.

Avoid logging PDF URLs containing access tokens. Keep API credentials, document contents, and customer addresses out of routine diagnostic logs.

Handle offline printers and fulfillment exceptions

A reliable warehouse workflow distinguishes three questions:

  1. Did WooCommerce create the background action?
  2. Did PrintScaler accept the print request?
  3. Did the print job progress successfully?

PrintScaler tracks states including new, sent_to_client, queued, in_progress, done, error, cancelled, and expired. Jobs can queue while the agent computer is offline and print when it reconnects, subject to configurable expiration.

Set expiration according to the business process. A packing slip that appears much later, after staff have already handled the order manually, can be confusing.

Use the dashboard’s job history during initial rollout. For a fuller integration, consume signed print-job webhooks and verify their HMAC-SHA256 signatures before updating your own records. Keep submission status and job status separate, and do not automatically mark an order shipped just because a print request was accepted.

For multiple warehouses, resolve the printer ID from trusted server-side routing rules. A customer-supplied checkout field should not be allowed to choose an arbitrary printer.

Test the workflow before enabling live orders

Run a small test matrix with clearly marked test documents:

Test Expected result
Order enters processing One packing-slip print request
Same event occurs again No additional first-print job
Request times out after acceptance Retry uses the same idempotency key
PDF metadata is missing Logged configuration/workflow error
Warehouse computer is offline Accepted job queues, subject to expiration
API rejects the credentials Logged failure, not an endless retry loop
Staff requests a replacement New explicit print intent

Check physical output as well: margins, paper size, barcode readability, and whether the packing slip fits your actual stationery. Printer capabilities such as supported paper sizes and trays are available through the API if you need to add PDF print options.

Shipping labels are a separate document workflow. If your carrier produces ZPL rather than PDF, see the guide to printing ZPL from cloud applications.

Estimate print volume and roll out gradually

PrintScaler pricing is per print request, not per page. A multi-page PDF submitted as one request is therefore different from submitting each page separately.

The Free plan includes 100 prints per month and one computer, which can be useful for a controlled pilot. Starter is $9 per month with 5,000 included prints and three computers. Check the current pricing and plan limits against your order volume and warehouse footprint.

Start with one document type and one printer. Keep a manual fallback during the pilot, then expand only after background processing, duplicate protection, and exception handling behave predictably.

Put the order-to-printer workflow into production

Successful automatic order printing starts with a clear fulfillment event and a ready-to-print document. Add a background worker, a stable idempotency key, and operational visibility, and your staff can stop opening every order just to print its packing slip.

Create a free PrintScaler account, connect your warehouse printer, and test the complete workflow with a nonproduction order before turning on live fulfillment.

Frequently asked questions

Can WooCommerce automatically print orders to a local printer?

Yes, with an integration that connects a WooCommerce order event to a printing service and the local printer. In this workflow, custom PHP code submits a packing-slip PDF to PrintScaler, and the PrintScaler agent connects the warehouse computer's printer.

Does PrintScaler generate WooCommerce packing-slip PDFs?

No. Your WooCommerce document extension or custom generator creates the PDF, and PrintScaler handles printing it. The integration must supply either a downloadable PDF URL or base64-encoded PDF content.

Which WooCommerce order status should trigger automatic printing?

The processing status is a common starting point for physical-goods orders, but it must match your fulfillment rules. If payment verification, stock allocation, or document generation happens later, trigger printing after those steps instead.

How do I prevent duplicate WooCommerce packing slips?

Use a stable X-Idempotency-Key for each intended document print and reuse it when retrying the request. Also record accepted submissions in the order's metadata; intentional replacements need a new document version or print-intent identifier.

What happens if the warehouse computer is offline?

PrintScaler can queue jobs while the computer is offline and deliver them when it reconnects. Jobs expire after a configurable period, so choose an expiration policy that avoids printing obsolete packing slips.

Can this workflow print invoices and shipping labels too?

Yes, if your store or shipping system generates the required documents. PDF invoices and labels can use the PDF workflow, while ZPL labels require RAW printing and a compatible printer; each document type should have its own trigger and duplicate-protection key.

Related articles