Multisite Order Sync in WooCommerce
I’ve been talking a lot about WordPress Multisite synchronisation throughout my blog. And also I have multiple plugins for that, for example – if you are looking for a way to sync WooCommerce products, you can check this plugin, only product inventory – this one.
And right now I would like to talk about WooCommerce orders, specifically about a scenario when you want all the order across your Multisite network to appear on a single store and (optionally) to be removed from the original store where this order has been made.
When an Order is Created (Purchase is Made), Automatically Create its Copy on the Other Store
There are actually two ways how we can achieve that:
- We can try to use
switch_to_blog()
andrestore_current_blog()
hooks during the order creation process and see what happens. In my opinion it is just a guessing game when literally everything could go wrong. For example the customers could stay on another blog database and who knows what can happen. - Another option is just using
woocommerce_new_order
action hook, switching to another blog inside of it and create a new order programmatically (there is another tutorial on that) using all the data we have.
In my opinion the second option is safe, so let’s better use it.
add_action( 'woocommerce_new_order', 'rudr_sync_order', 10, 2 );
function rudr_sync_order( $order_id, $order ) {
// let's prepare some order data first, here we have everything we need
$order_data = $order->get_data();
// we don't know product IDs on Site 2, so we must use SKUs, in that case let's reorganize line_items array
$order_data[ 'products' ] = array();
foreach( $order_data[ 'line_items' ] as $line_item ) {
if( ! $sku = get_post_meta( $line_item->get_product_id(), '_sku', true ) ) {
continue;
}
$order_data[ 'products' ][] = array(
'sku' => $sku,
'qty' => $line_item->get_quantity(),
// in the case we assume that prices of products on Site 1 and Site 2 are the same, so I skup subtotal etc
);
}
switch_to_blog( $blog_id );
// super-crucial to remove the hook first, otherwise we will have unlimited amount of orders!!!
remove_action( 'woocommerce_new_order', __FUNCTION__, 10, 2 );
// create an order
$synced_order = wc_create_order();
// you can decide what order status should be or you can use the one from $order_data
$synced_order->set_status( 'on-hold' );
// products
foreach( $order_data[ 'products' ] as $item ) {
$synced_order->add_product( wc_get_product( wc_get_product_id_by_sku( $item[ 'sku' ] ) ), $item[ 'qty' ] );
}
$synced_order->calculate_totals();
// set customer data
$synced_order->set_address( $order_data[ 'billing' ], 'billing' );
$synced_order->set_address( $order_data[ 'shipping' ], 'shipping' );
if( $order_data[ 'customer_id' ] ) {
add_user_to_blog( $blog_id, $order_data[ 'customer_id' ], 'subscriber' );
$synced_order->set_customer_id( $order_data[ 'customer_id' ] );
}
// some other data
$synced_order->set_payment_method( $order_data[ 'payment_method' ] );
$synced_order->set_payment_method_title( $order_data[ 'payment_method_title' ] );
$synced_order->set_order_key( $order_data[ 'order_key' ] );
$synced_order->save();
// add some products
restore_current_blog();
}
Now let’s dive into the details of this code snippet:
- Lines
8-22
, here I re-create an array of order items (products in the order). I am doing that because we will need to attach the same products on Site 2 to the order. How to we know what products are the same? By SKUs of course. So I get SKUs usingget_post_meta()
function cause there is no reason to initiate the wholeWC_Product
object just for that and I add only products that has it to the result array. And here I would like to remind you that you can easily sync products from Site 1 to Site 2 using my plugin. - Line
24
– don’t forget to replace$blog_id
variable with the Store ID where all the order are going to be. - Line
27
– Please don’t forget to disconnect the function fromwoocommerce_new_order
hook before creating a new order! Otherwise the orders will start being created in a loop. - Lines
47-50
, here you can dive into official WooCommerceWC_Order
documentation and my tutorial about creating an order programmatically and add some more data to sync.
For me it worked out well and a synced order on Site 2 has been created.

Delete Orders from the Initial Store
Since we decided to have all the multisite network orders on a single store (Store 2 for example), we probably don’t need them on their initial stores, right?
The first thing that comes to a mind is to add wp_delete_post()
function at the end of the function in the previous chapter of this tutorial. But it doesn’t work out well.

So what to do? I assume we need to do some stuff on WooCommerce Thank You page. And in my opinion there are two ways how it can be achieved:
- Create your own custom Thank You page and redirect to it on order creation.
- Modify WooCommerce template (copy to your theme and then modify of course)
templates/checkout/thank-you.php
.
If you decide to use the second way, the modification assumes the following:
<?php
/**
* Thankyou page
*
* This template can be overridden by copying it to yourtheme/woocommerce/checkout/thankyou.php.
*
* ...
*
*/
defined( 'ABSPATH' ) || exit;
wp_delete_post( $order->get_id(), true );
switch_to_blog( $blog_id );
$order = wc_get_order( wc_get_order_id_by_order_key( $_GET[ 'key' ] ) );
What we are doing here:
- First of all we remove the original order from the inital store.
- Then we switch to our Main store and get the same order from their using
$_GET[ 'key' ]
. In order to make it work please don’t remove line50
from the code in the first chapter. - And also don’t forget to add
restore_current_blog()
function at the end of the file.
Display the Orders from the Main Store in Member’s area
Since we decided to remove the orders from their inititial stores, we need to display something in My account > Order, don’t we?
And finally I have really great news for you! The function woocommerce_account_orders()
that get and displays user orders with the help of template my-account/orders.php
is pluggable! It means that we can copy and paste this function in our theme or plugin and its modified version is going to be used automatically.
As simple as that!
function woocommerce_account_orders( $current_page ) {
switch_to_blog( $blog_id );
$current_page = empty( $current_page ) ? 1 : absint( $current_page );
$customer_orders = wc_get_orders(
apply_filters(
'woocommerce_my_account_my_orders_query',
array(
'customer' => get_current_user_id(),
'page' => $current_page,
'paginate' => true,
)
)
);
wc_get_template(
'myaccount/orders.php',
array(
'current_page' => absint( $current_page ),
'customer_orders' => $customer_orders,
'has_orders' => 0 < $customer_orders->total,
'wp_button_class' => wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '',
)
);
restore_current_blog();
}
So, I modified only lines 3
and 27
.

If you have any questions or would like something to add to this guide, welcome to comments, guys. And of course, if you need any custom development – feel free to contact me.

Misha Rudrastyh
Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me
Thank you so much for posting this. I tried using WooMultistore for this, but seems very buggy. It completely wrecked our WC-Subscriptions orders. I’m going to look through the rest of your blog to find all I can.
Note: Why leave on original site?
We have a broad-appeal site for subscribers and a more narrow membership site.
I need to sync the subscription site orders to the membership site so a customer’s order history is there if they become members. But I will leave a copy in the child site.
Also, we need to sync to an ERP from one main site.