Programmatically Create Order in WooCommerce: The Complete Guide

Programmatically Create Order in WooCommerce: The Complete Guide

Creating orders manually through the WordPress dashboard is fine for day-to-day management, but as a developer, you often encounter scenarios where you need to programmatically create an order in WooCommerce.

Maybe you are migrating data from an old shop, building a custom checkout flow for a landing page, or processing orders from a third-party API. Whatever the reason, WooCommerce provides robust tools to handle this without touching the raw database tables.

In this guide, I’ll walk you through how to create an order using PHP, ensuring all the necessary data—products, customer info, and totals—are recorded correctly.

Why Create Orders Programmatically?

Before we dive into the code, let’s look at a few scenarios where this is a lifesaver:

  1. Importing Orders: Moving from Shopify or Magento to WooCommerce often requires a custom script to import historical order data.
  2. Custom Funnels: You might have a specialized sales page that bypasses the standard WooCommerce cart/checkout flow but still needs to generate a valid order.
  3. Subscription Renewals: If you are building a custom subscription engine, you'll need to generate renewal orders automatically.
  4. Phone Orders: Building a frontend interface for sales reps to quickly punch in orders without accessing the WP-Admin.

The Two Approaches

There are generally two ways to do this in WooCommerce:

  1. The Helper Function: wc_create_order() — Good for quick, simple orders.
  2. The CRUD Class (Recommended): new WC_Order() — The object-oriented approach that offers the most control.

We will focus primarily on the CRUD (Create, Read, Update, Delete) approach, as it is the standard for modern WooCommerce development (version 3.0+).

Step 1: Create the Order Object

First, we instantiate a new order. If you pass an ID, it tries to fetch that order. If you leave it empty, it prepares a fresh order object.

// Define the parameters for the order
$order_args = array(
    'status'        => 'pending', // Default status
    'customer_id'   => 123,       // Optional: Assign to specific user ID
    'customer_note' => 'Order created programmatically.',
    'parent'        => null,
    'created_via'   => 'programmatically',
);

// Method A: Using the helper function (Simpler)
$order = wc_create_order( $order_args );

// Method B: The Object-Oriented Way (Better for detailed manipulation)
$order = new WC_Order();
$order->set_customer_id( 123 ); // Set existing customer ID
$order->set_currency( get_woocommerce_currency() );
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );        
Pro Tip: Always set the created_via metadata. It helps significantly when debugging later to know which orders were created by your script versus real customers.

Step 2: Add Products (Line Items)

An order isn't an order without products. You need the Product ID to add items. You can also specify quantity and variations.

// Get the product object
$product_id = 45; // Replace with your actual Product ID
$product = wc_get_product( $product_id );

if ( $product ) {
    // Add 2 of this product to the order
    // You can also pass args like 'subtotal' or 'total' if you want to override prices
    $item_id = $order->add_product( $product, 2 ); 
    
    // Optional: Add custom meta data to this specific line item
    // Useful for personalized products (e.g., "Engraving Text")
    if ( $item_id ) {
        wc_add_order_item_meta( $item_id, 'engraving_text', 'Happy Birthday!' );
    }
}        

Step 3: Set Billing and Shipping Addresses

WooCommerce expects an array for addresses. Even if you are selling digital goods, a billing address is usually required for tax calculation.

$address = array(
    'first_name' => 'John',
    'last_name'  => 'Doe',
    'company'    => 'Tech Corp',
    'email'      => 'john.doe@example.com',
    'phone'      => '555-0199',
    'address_1'  => '123 Main St',
    'address_2'  => 'Apt 4B',
    'city'       => 'San Francisco',
    'state'      => 'CA',
    'postcode'   => '94103',
    'country'    => 'US',
);

// Set the addresses
$order->set_address( $address, 'billing' );
$order->set_address( $address, 'shipping' );        

Step 4: Calculate Totals (Crucial!)

This is the step most developers forget. When you add products manually, WooCommerce does not automatically sum up the costs, taxes, and shipping until you tell it to.

If you skip this, your order will have a total of $0.00.

// Calculate taxes and totals based on the items added
$order->calculate_totals();

// Finally, save the order to the database
$order->set_status( 'completed' ); // or 'processing', 'on-hold'
$order->save();        

Complete Code Snippet

Here is the full, copy-paste-friendly function. You can place this in your functions.php or a custom plugin.

function my_custom_create_order( $user_id, $product_id, $qty = 1 ) {
    
    if ( ! class_exists( 'WC_Order' ) ) {
        return false;
    }

    // 1. Create the Order Object
    $order = new WC_Order();
    
    // 2. Set Customer
    $order->set_customer_id( $user_id );
    $order->set_currency( get_woocommerce_currency() );
    
    // 3. Add Product
    $product = wc_get_product( $product_id );
    if ( ! $product ) {
        return new WP_Error( 'error', 'Product not found' );
    }
    $order->add_product( $product, $qty );

    // 4. Set Addresses (Hardcoded for example, usually passed in args)
    $address = array(
        'first_name' => 'Jane',
        'last_name'  => 'Smith',
        'email'      => 'jane@test.com',
        'address_1'  => '123 Web Dev Lane',
        'city'       => 'Austin',
        'state'      => 'TX',
        'postcode'   => '78701',
        'country'    => 'US',
    );
    $order->set_address( $address, 'billing' );
    $order->set_address( $address, 'shipping' );

    // 5. Payment Method (Optional but recommended)
    $order->set_payment_method( 'bacs' );
    $order->set_payment_method_title( 'Direct Bank Transfer' );

    // 6. Calculate Totals & Save
    $order->calculate_totals();
    $order->update_status( 'completed', 'Order created via custom script.' );
    
    // Returns the Order ID
    return $order->get_id();
}        

Troubleshooting Common Issues

1. The Order Total is Zero

You likely forgot $order->calculate_totals(). This method iterates through all line items, applies taxes based on the customer address, and sums everything up.

2. Emails Are Not Sending

Creating an order programmatically does not always trigger the default transactional emails automatically, especially if you set the status immediately. To force the email, you might need:

$email_notifications = WC()->mailer()->get_emails();
$email_notifications['WC_Email_New_Order']->trigger( $order->get_id(), $order );        

3. SKU or Stock Not Reducing

By default, add_product() checks stock management settings. However, if you are forcing orders through scripts, ensure you aren't bypassing stock checks if that matters for your inventory.

Conclusion

Programmatically creating orders in WooCommerce gives you immense power to extend the platform. Whether you are building an importer or a custom checkout app, the WC_Order Class is your best friend.

Remember to always sanitize your inputs if this data is coming from a frontend form, and test your code on a staging site before running it on a live store.

"Let's Build Your Perfect WordPress Website – Contact Now!"

“We can collaborate through freelance marketplaces such as Fiverr or Upwork.”


To view or add a comment, sign in

More articles by TANMOY BISWAS

Others also viewed

Explore content categories