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:
The Two Approaches
There are generally two ways to do this in WooCommerce:
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' );
Recommended by LinkedIn
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!"