After spending the last few months building a migration plugin that moves Shopify stores into WooCommerce via the Storefront GraphQL API, I have a much deeper appreciation for how WooCommerce actually stores product data — and where the sharp edges are when you're importing from outside.
A few things I didn't expect:
WooCommerce variable products are deceptively complex to create programmatically
On the surface it seems simple: create a WC_Product_Variable, attach attributes, create WC_Product_Variation children. In practice the order of operations matters a lot.
If you save the parent before registering attributes, the variations don't inherit them correctly. If you forget to call WC_Product_Variable::sync() after saving all variations, the price range shown on the product page is wrong. And if you set variation attributes without slugifying the keys to match attribute_pa_* format, WooCommerce silently fails to match variations in the frontend.
None of this is obviously documented — you piece it together from source code and Stack Overflow threads from 2018.
The _shopify_id meta key is doing a lot of heavy lifting
The core of making the migration idempotent (safe to run multiple times) is storing the Shopify product ID as _shopify_id post meta on every imported product. Before creating anything, the plugin queries:
get_posts([
'post_type' => 'product',
'meta_key' => '_shopify_id',
'meta_value' => $shopify_product_id,
]);
If it finds a match, it either skips or updates. If not, it creates. Simple, but it means every re-run is safe. This same meta key powers the scheduled sync — matching Shopify products to their WooCommerce counterparts without relying on slugs (which can change).
Shopify "collections" map cleanly to WooCommerce categories — almost
Shopify has two types of collections: manual (you pick the products) and smart (rule-based). The Storefront API only exposes which collections a product belongs to, not the collection type or rules.
For WooCommerce categories this is fine — you just create the category and assign the product. But smart collections with rules (e.g., "all products tagged 'summer'") lose their dynamic nature in the migration. They become static categories. That's probably the right trade-off for a migration tool, but worth documenting clearly for users.
The Pro version adds things WooCommerce really needs post-migration
The free version handles the core migration. The Pro version adds what keeps a WooCommerce store actually usable after you flip the switch:
Image download — product and category images from Shopify CDN stored in the WP media library. Without this, products have no images.
Scheduled sync — if the merchant keeps selling on Shopify during the transition (common), prices and stock drift immediately. A cron job that re-fetches from Shopify and updates WooCommerce keeps things consistent.
Tag mapping — Shopify tags are flat strings. WooCommerce has a proper taxonomy system (product tags, attributes, custom taxonomies). Pro lets you route specific Shopify tags to specific WooCommerce taxonomies.
What surprised me most: how many WooCommerce stores need this
I built it for one client. Since publishing to WordPress.org two weeks ago, 157 people have downloaded it with zero marketing. There's clearly a large group of store owners who are on Shopify, want to move to WooCommerce, and have no clean path to do it.
The questions I get most often from users:
"Does it handle variable products?" (yes)
"Will it overwrite products if I run it twice?" (no, by default)
"Can I keep selling on Shopify while the WooCommerce store is being set up?" (yes, that's what scheduled sync is for)
If you're in the WooCommerce ecosystem — building plugins, running stores, or doing migrations — I'd love to know: what's the messiest part of getting data into WooCommerce from external sources? I've solved the Shopify side but I suspect the same pain exists for other platforms.
Plugin is free on WP.org: Laterreta Migrator for Shopify