This is a guest post by Misha Rudrastyh of rudrastyh.com. If you like the content, make sure to thank him in the comments!
In this article, I’d like to talk about syncing products and orders between different standalone WooCommerce stores. More specifically, I am going to uncover the following topics:
- What WordPress/WooCommerce hooks do we need to use to trigger the sync?
- How to create the WooCommerce REST API requests inside the callback functions for the above-mentioned hooks.
- How to do it without coding at all, just using a plugin.
Of course, for products, we are going to sync all the product information, like gallery images, attributes, variations, etc. For orders: billing, shipping addresses, custom metadata, customers, etc. But we will come back to this moment a little bit later as well.
What Hooks Do We Need to Use to Trigger Product and Order Synchronization?
In order to get a clear understanding about the hooks we need to discuss in what specific scenarios the sync will be triggered in the first place.
1. Updating products and orders manually from the admin dashboard
Let’s begin with the easy part.
Since products are a custom post type, we can use save_post
or save_post_product
action hooks to trigger the synchronization. I doubt that in one day the WooCommerce team will decide to move products into separate database tables, so we’re free to use these hooks without worrying about the future.
Example:
add_action( 'save_post_product', 'bb_sync_product' );
function bb_sync_product( $product_id ) {
if( ! function_exists( 'wc_get_product' ) ) {
return;
}
$product = wc_get_product( $product_id );
// we can do the sync now
}
What about WooCommerce orders, then? Is it a custom post type as well?
Yes and no. Some WooCommerce stores may have orders as a custom post, and some may not. It depends on whether HPOS (High Performance Order Storage) is in use. In other words, we can not use save_post
or save_post_wc_order
because it will only work in some cases, I would say on less than 50% of stores, because everyone is starting to use HPOS today.
That’s why we need to use another hook here, which is woocommerce_update_order
. Example:
add_action( 'woocommerce_update_order', 'bb_sync_order', 10, 2 );
function bb_sync_order( $order_id, $order ) {
// we can start order syncing right away
}
2. When an order is placed by a customer
All right, this one is going to be a tricky part. Why? – Because when a customer places an order, the WooCommerce hook woocommerce_update_order
is going to be triggered… 5 times! I even counted it in the Block Checkout, not sure how many times it will be triggered in the classic shortcode-based checkout, though.
It means that if we put the syncing code straight into the bb_sync_order function connected to woocommerce_update_order
, then we maybe will have a disaster:
- Customers will have to wait a significant amount of time while the order is being synced 5 times to a connected store. There is a high chance that they won’t complete their checkout process.
- Probably, the order data of a synced order won’t be 100% correct, or you may experience a duplicate order issue.
What is the solution here?
Easy – we just need to trigger the order synchronisation a little bit later, after all the woocommerce_update_order
copies have been triggered.
The woocommerce_thankyou
hook then? Because it is the last hook that is triggered during the customer checkout process in WooCommerce. Well, it is possible to use it, of course, then your order synchronisation function will look something like this:
add_action( 'woocommerce_thankyou', 'bb_sync_order' );
function bb_sync_order( $order_id ) {
// do the sync here
}
However, in my opinion, it is better not to rely on this action hook.
Another option we have here is using the WooCommerce Action Scheduler (kind of WP Cron). The whole idea here is to create a unique scheduled action that will be fired asynchronously a couple of seconds after an order has been placed.
Let’s try to do it right now!
add_action( 'woocommerce_update_order', 'bb_schedule_sync_order', 10, 2 );
function bb_schedule_sync_order( $order_id, $order ) {
// we can start order syncing right away
as_schedule_single_action(
time() + 15, // after 15 seconds from now
'bb_sync_order',
array( $order_id ),
'group-' . $order_id,
true // is unique? yes
);
}
add_action( 'bb_sync_order', 'bb_sync_order' );
function bb_sync_order( $order_id ) {
// now we can do the sync
}
Creating REST API Requests for Product and Order Sync
Great – we have figured out which WordPress hooks we need to use, now it is time to do some work with their callback functions as well.
Create a Consumer Key and Consumer Secret First
First things first, we need to prepare the authentication data:
- Store URL,
- Consumer key,
- Consumer secret.
I think you shouldn’t have any issues with the store URL, but where can we get the consumer key and secret then?
It is quite simple, actually. In the target store, we need to go to WooCommerce settings, then to the “Advanced” tab, and then to the “REST API” section.
Click the “Add key” button and fill out the form:

Make sure that permissions are set to “Read/Write” and hit the “Generate API key” button. After that, the consumer key and secret will be generated, and you will be able to copy and use them in your REST API requests.
A REST API request example when syncing a product
In the code snippet below, I am going to show you an example of syncing a WooCommerce product, which is a simple product. Because my purpose here is to show you the basics of how this whole syncing process works.
Let’s just continue with the bb_sync_product()
function that we created before:
function bb_sync_product( $product_id ) {
if( ! function_exists( 'wc_get_product' ) ) {
return;
}
$product = wc_get_product( $product_id );
$product_data = $product->get_data();
// unset a product ID here because the IDs of products on different stores will be different
unset( $product_data[ 'id' ] );
// provide the details we generated before
$store_url = '';
$consumer_key = '';
$consumer_secret = '';
$woocommerce = new Client( $store_url, $consumer_key, $consumer_secret );
// create (sync) the product
try{
$woocommerce->post( "products", $product_data );
} catch( Exception $error ) {
}
}
As you can see, the function itself is pretty simple; however, you need to keep in mind the following.
I am using the official WooCommerce REST API PHP library, which you can find on GitHub.
You also need to handle product updates, because the above code just creates a new product every time you update an original product. How to do that? For example, you can create a new function, let’s say it will be something like bb_get_remote_product()
, which will return the ID of an already synced product if it was synced before. In that case, the REST API endpoint should be different, for example:
$synced_product_id = bb_get_remote_product( $product_id );
if( $synced_product_id ) {
try{
$woocommerce->put( "products/{$synced_product_id}", $product_data );
} catch( Exception $error ) {
}
} else {
try{
$woocommerce->post( "products", $product_data );
} catch( Exception $error ) {
}
}
But what is going to be inside the bb_get_remote_product(
) function? Well, actually, this moment depends on you; for example, you can run an additional REST API request to get a remote product by SKU, or, as an option, you can store product connections in the custom meta.
If you want to sync variable products (with variations and attributes), you may need to create extra REST API requests for variations and attributes as well.
A REST API request example when syncing an order
Now, let’s talk about order syncing here. Actually, the whole idea is completely the same – we’re going to run a WooCommerce REST API request and that’s it. Let’s try to do it right away inside the bb_sync_order()
function.
function bb_sync_order( $order_id ) {
$order = wc_get_order( $order_id );
// it is always a good idea to double-check things like that
if( ! $order ) {
return;
}
$order_data = $order->get_data();
// provide the details we generated before
$store_url = '';
$consumer_key = '';
$consumer_secret = '';
$woocommerce = new Client( $store_url, $consumer_key, $consumer_secret );
// create (sync) an order
try{
$woocommerce->post( 'orders', $order_data );
} catch( Exception $error ) {
}
}
I think the most important thing we need to discuss here is the connection between products, which are part of an order now (as order line items).
We have two options here:
- We loop through all order line items and sync every product to the target store.
- We loop through all order line items and check with our
bb_get_remote_product()
function whether a product is synced or not, and replace its ID with the synced remote product.
Both options are good! However, the first one can be a little bit slower.
Now, let’s take a look at the code snippet where we’re going to use the second option:
$order_data = $order->get_data();
$order_data_temp = $order_data;
$order_data_temp[ 'line_items' ] = array();
foreach( $order_data[ 'line_items' ] as $line_item ) {
// it is also good to double-check if it is a WC_Order_Item_Product object actually
$item = array(
'name' => $line_item->get_name(),
'quantity' => $line_item->get_quantity(),
'subtotal' => $line_item->get_subtotal(),
'total' => $line_item->get_total(),
);
$synced_product_id = bb_get_remote_product( $line_item->get_product_id() );
if( $synced_product_id ) {
$item[ 'product_id' ] = $synced_product_id;
}
$order_data_temp[ 'line_items' ][] = $item;
}
$order_data = $order_data_temp;
Once again, the code is simplified, so it will only work for products in order items (not for product variations).
Using Plugins for Product and Order Syncing
As you can see from what I showed you above, the whole process of syncing products and orders between standalone WooCommerce stores can be quite a complicated process, which can not be fully covered in a single tutorial.
It would even be better to discuss in different tutorials coupon syncing (as a part of order syncing), product variations, product gallery images, and so on.
It is one of the reasons why I would like to suggest that you take a look at the plugin approach as well, we’re going to use two plugins for that.
- Simple WordPress Crossposting – for product sync,
- Order Sync for WooCommerce – for order sync.
Below is going to be a step-by-step guide on how you can use these two plugins together for your goals.
1. Connecting stores in plugin settings
First things first, we need to connect WooCommerce stores.
It can be done in the plugin settings, and here we will also need to have the consumer key and consumer secret of the target WooCommerce stores. I already showed you before how to create the REST API credentials.
So, let’s navigate to WooCommerce > Settings > Order Sync > Stores:

Don’t forget to connect stores in the Simple WP Crossposting plugin settings as well.
2. How does the synchronization work?
There are two synchronization modes for both products and orders – “Auto” and “Manual”. It can be configured in plugin settings for both plugins.
Auto Mode
Products | Orders |
---|---|
Every time a product is updated, the changes will automatically be synced with connected stores. | Every time an order is updated in the admin dashboard or created by a customer, it will be automatically synced. |
Manual Mode
Products | Orders |
---|---|
When editing a product in the WordPress admin dashboard, you can choose which of the connected stores you would like to sync the changes. | Orders can be synced individually to a specific connected store with the help of “Order Actions” from order edit pages. |
3. Automatically sync products together with orders
Last but not least, you can configure the Order Sync for WooCommerce plugin to automatically sync all products that are included in the order every time an order is created on an initial store.
It can be done in the plugin settings page:

That’s pretty much it. If you have any questions, please feel free to ask in the comments section below.