FCHubFCHub.co

Migration Guide

Step-by-step guide to migrating WooCommerce data to FluentCart. Back up, preflight, migrate, verify. Every entity type explained in detail.

This guide walks through the complete migration process from start to finish. Follow each step in order — the dependency chain between entity types matters.

Back up your database first

This is non-negotiable. Before running any migration — even a dry run — create a full database backup. If something goes wrong, your backup is the only guaranteed way to restore your WooCommerce data exactly as it was. The migrator's rollback feature deletes FluentCart records, but it does not restore WooCommerce data.

Migration Steps

Back up your database

Use your preferred backup method. Some options:

  • Hosting panel — Most hosts offer one-click database backups via cPanel, Plesk, or a custom panel
  • WP CLIwp db export backup.sql
  • Plugin — UpdraftPlus, BlogVault, or similar backup plugins
  • phpMyAdmin — Export your full database as SQL

Store the backup somewhere safe and accessible. You may need it if the migration produces unexpected results and you want to start completely fresh.

If your store has a large database (over 1 GB), make sure the backup completes fully before proceeding. Partial backups will not help you.

Run preflight checks

Navigate to Tools > CartShift or call the preflight endpoint directly:

curl -X GET "https://yoursite.com/wp-json/cartshift/v1/preflight" \
  -H "X-WP-Nonce: YOUR_NONCE"

All required checks must pass before you proceed. See Preflight Checks for detailed information about each check and how to fix failures.

Review entity counts

Check how many records will be migrated:

curl -X GET "https://yoursite.com/wp-json/cartshift/v1/counts" \
  -H "X-WP-Nonce: YOUR_NONCE"
{
  "products": 142,
  "categories": 12,
  "customers": 856,
  "orders": 3241,
  "subscriptions": 94,
  "coupons": 27
}

Review these numbers against what you expect. If the counts look wrong, investigate your WooCommerce data before migrating. Common causes of unexpected counts:

  • Products — Only simple and variable types are counted. Grouped and external products are excluded.
  • Customers — Includes both registered users with the customer role and unique guest emails from orders.
  • Subscriptions — Returns 0 if WC Subscriptions is not active.

Run a dry run (recommended)

Before writing any data, run the migration in dry-run mode. Every record is validated, mapped, and logged — but nothing is written to FluentCart.

curl -X POST "https://yoursite.com/wp-json/cartshift/v1/migrate" \
  -H "X-WP-Nonce: YOUR_NONCE" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_types": ["products", "customers", "coupons", "orders", "subscriptions"],
    "dry_run": true
  }'

After the dry run completes, check the migration log:

curl -X GET "https://yoursite.com/wp-json/cartshift/v1/log?per_page=100" \
  -H "X-WP-Nonce: YOUR_NONCE"

Look for entries with status: "error". Fix any issues before proceeding with the real migration. Common dry-run findings:

  • Unsupported product types (grouped, external) are logged as skipped
  • Products with missing prices are flagged
  • Guest customers with empty emails are skipped

Start the migration

When you are ready, start the real migration:

curl -X POST "https://yoursite.com/wp-json/cartshift/v1/migrate" \
  -H "X-WP-Nonce: YOUR_NONCE" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_types": ["products", "customers", "coupons", "orders", "subscriptions"]
  }'

The entity_types array controls which data gets migrated. You can migrate selectively:

{ "entity_types": ["products", "customers"] }

Migration order

Regardless of the order you specify in entity_types, the migrator always processes entities in dependency order: products > customers > coupons > orders > subscriptions. This ensures that orders can reference the correct product and customer IDs.

The migration runs synchronously — the REST request will not return until all entity types have been processed or the migration is cancelled. For large stores, this can take several minutes.

Monitor progress

While the migration runs, poll the progress endpoint from a separate request:

curl -X GET "https://yoursite.com/wp-json/cartshift/v1/progress" \
  -H "X-WP-Nonce: YOUR_NONCE"
{
  "migration_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "running",
  "dry_run": false,
  "started_at": "2025-03-15 10:30:00",
  "completed_at": null,
  "entities": {
    "products": {
      "status": "completed",
      "total": 142,
      "processed": 138,
      "skipped": 4,
      "errors": 0
    },
    "customers": {
      "status": "running",
      "total": 856,
      "processed": 312,
      "skipped": 5,
      "errors": 0
    },
    "coupons": {
      "status": "pending",
      "total": 0,
      "processed": 0,
      "skipped": 0,
      "errors": 0
    },
    "orders": {
      "status": "pending",
      "total": 0,
      "processed": 0,
      "skipped": 0,
      "errors": 0
    },
    "subscriptions": {
      "status": "pending",
      "total": 0,
      "processed": 0,
      "skipped": 0,
      "errors": 0
    }
  }
}

Each entity reports:

  • statuspending, running, or completed
  • total — Total records to process
  • processed — Successfully migrated records
  • skipped — Records skipped (already migrated, duplicates, unsupported types)
  • errors — Records that failed to migrate

When the migration finishes, the overall status changes to completed and completed_at gets a timestamp.

Cancel if needed

If something goes wrong during migration, you can cancel it:

curl -X POST "https://yoursite.com/wp-json/cartshift/v1/cancel" \
  -H "X-WP-Nonce: YOUR_NONCE"

The migrator checks for cancellation between every record, so it stops cleanly. Records already migrated before cancellation remain in FluentCart — use rollback if you want to remove them.

Verify migrated data

After migration completes, verify your data in FluentCart:

  1. Products — Check that products appear in FluentCart with correct titles, prices, descriptions, images, and variations
  2. Categories — Verify the category hierarchy is intact
  3. Customers — Check customer records with addresses in FluentCart
  4. Orders — Verify order history, line items, totals, and statuses
  5. Subscriptions — Check subscription statuses, billing intervals, and linked products
  6. Coupons — Verify coupon codes, types, and amounts

Also review the migration log for any errors:

curl -X GET "https://yoursite.com/wp-json/cartshift/v1/log?per_page=100" \
  -H "X-WP-Nonce: YOUR_NONCE"

Deactivate the plugin

Once you have verified everything, deactivate CartShift. It has served its purpose.

Go to Plugins and click Deactivate on "CartShift". The cartshift_migration_state option is cleaned up on deactivation.

Keep the plugin installed (but deactivated) for a while in case you need to reference the migration log or ID mapping table. Only delete it when you are completely confident in the migrated data.

Entity Migration Details

Each entity type has specific mapping behavior. Understanding these details helps you verify your migrated data and troubleshoot any issues.

Products

ProductMigrator + ProductMapper + VariationMapper

Supported types: Simple and variable products. Grouped and external products are skipped.

What gets mapped:

WooCommerceFluentCart
Product namepost_title (CPT: fluent-products)
Descriptionpost_content
Short descriptionpost_excerpt
Product status (publish/draft/private)post_status (via StatusMapper)
Slugpost_name
Featured imageCopied via set_post_thumbnail()
Product categoriesMapped to product-categories taxonomy

Product details (fct_product_details table):

  • Fulfillment type: physical, digital, or service based on WC product properties
  • Variation type: simple or advanced_variations
  • Stock availability and management settings
  • Min/max price range calculated from variations

Variations (fct_product_variations table):

  • Simple products get one "Default" variation
  • Variable products get one variation per WC variation
  • Prices converted to cents (integer) — $29.99 becomes 2999
  • Sale price becomes compare_price (original regular price)
  • SKU uniqueness enforced — duplicates get a -wc{id} suffix
  • Stock quantities, backorder settings, and downloadable flags preserved
  • WC Subscription metadata (billing period, trial, signup fee) mapped to other_info

Category migration runs before products. Categories are sorted so parents are created before children. The "Uncategorized" default category is skipped. Categories already existing in FluentCart (matched by slug) are reused, not duplicated.

Customers

CustomerMigrator + CustomerMapper

Registered customers:

WordPress users with the customer role. Each user becomes a FluentCart customer record with billing/shipping addresses.

WooCommerceFluentCart
user_emailemail
billing_first_name (or first_name)first_name
billing_last_name (or last_name)last_name
billing_country, billing_city, etc.country, city, state, postcode
User IDuser_id (links FC customer to WP user)

Guest customers:

Extracted from orders that have no customer_id (or customer_id = 0). Each unique billing email becomes a FluentCart customer. The migrator supports both HPOS (wc_orders table) and legacy post-based order storage.

Addresses:

Both billing and shipping addresses are created as CustomerAddresses records in FluentCart. Phone numbers and company names are stored in the address meta field.

Deduplication: If a customer with the same email already exists in FluentCart, the migrator stores the ID mapping and skips creation.

Orders

OrderMigrator + OrderMapper + StatusMapper

Orders are the most complex entity to migrate. Each WooCommerce order maps to multiple FluentCart tables.

Order record (fct_orders):

WooCommerceFluentCart
Order statusstatus and payment_status (via StatusMapper)
Payment methodpayment_method and payment_method_title
Currencycurrency
Subtotal, tax, shipping, totalConverted to cents
Customer notenote
IP addressip_address
Customer IDResolved via ID map

Status mapping:

WC StatusFC Order StatusFC Payment Status
pendingpendingpending
processingprocessingpaid
on-holdon-holdpending
completedcompletedpaid
cancelledfailedfailed
refundedrefundedrefunded
failedfailedfailed

Order items (fct_order_items):

Each line item references the FluentCart product and variation via the ID map. Unit prices and line totals are converted to cents. Subscription product metadata is preserved.

Order addresses (fct_order_addresses):

Billing and shipping addresses stored per-order. Company names and phone numbers in the meta field.

Transactions (fct_order_transactions):

The primary payment creates a charge transaction. Refunds create separate refund transactions linked to the same order. WooCommerce transaction IDs are preserved in the meta field.

Order types:

  • Regular orders become onetime
  • Orders containing subscription products become subscription
  • Renewal orders (child orders from WC Subscriptions) become renewal

HPOS support: The migrator works with both HPOS (High-Performance Order Storage) and legacy post-based orders. Customer counting and guest extraction auto-detect which storage is active.

Subscriptions

SubscriptionMigrator + SubscriptionMapper

Requires WC Subscriptions to be active. If WC Subscriptions is not detected, the subscription migrator returns null and is skipped entirely.

What gets mapped:

WooCommerceFluentCart
Billing period (day/week/month/year)billing_interval (daily/weekly/monthly/yearly)
Recurring totalrecurring_total (cents)
Recurring taxrecurring_tax_total (cents)
Signup fee (calculated from parent order)signup_fee (cents)
Payment countbill_count
Bill times (length / interval)bill_times
Trial end datetrial_ends_at and trial_days
Next payment datenext_billing_date
End dateexpire_at
Cancelled datecanceled_at
Start datecreated_at
Customer IDResolved via ID map
Parent order IDResolved via ID map
Product and variation IDsResolved via ID map

Subscription status mapping:

WC StatusFC Status
activeactive
on-holdpaused
cancelledcanceled
expiredexpired
pending-cancelexpiring
pendingpending
switchedcanceled

Per-product subscriptions

FluentCart subscriptions are per-product. If a WC Subscription contains multiple line items, the migrator uses the first item's product and variation. This matches FluentCart's subscription model.

Coupons

CouponMigrator + CouponMapper

What gets mapped:

WooCommerceFluentCart
Coupon codecode (uppercased)
Discount typetype (percentage or fixed)
Amountamount (cents for fixed, float for percentage)
Individual usestackable (inverted: individual_use=true becomes stackable=no)
Usage countuse_count
Descriptionnotes
Date createdstart_date
Expiry dateend_date

Coupon type mapping:

WC TypeFC Type
percentpercentage
fixed_cartfixed
fixed_productfixed

Conditions stored in the conditions JSON field:

  • Minimum order amount
  • Maximum order amount
  • Usage limit (max uses)
  • Per-customer usage limit
  • Exclude sale items flag
  • Free shipping flag

Deduplication: If a coupon with the same code already exists in FluentCart, it is skipped and the mapping is stored.

Batch Processing

All entity types are processed in batches of 50 records. This is controlled by the $batchSize property in AbstractMigrator.

The batch loop works as follows:

  1. Count total records for the entity type
  2. Fetch a batch of records (page 1, 2, 3...)
  3. Process each record in the batch (map, validate, write)
  4. Update progress after each record
  5. Check for cancellation between records
  6. Move to the next batch until all records are processed or the batch returns fewer than 50 records

This approach keeps memory usage predictable regardless of store size. A store with 10,000 orders processes them in 200 batches of 50.

ID Mapping

The cartshift_id_map table is central to the migration. Every time a WooCommerce record is successfully migrated, its WC ID and FC ID are stored along with the entity type.

Entity types in the ID map:

Entity TypeDescription
categoryProduct category term IDs
productProduct post IDs
product_detailProduct detail row IDs
variationProduct variation IDs
customerRegistered customer IDs
guest_customerGuest customer IDs (using CRC32 of email)
customer_addressCustomer address IDs
orderOrder IDs
order_itemOrder item IDs
order_addressOrder address IDs
order_transactionOrder transaction IDs
couponCoupon IDs
subscriptionSubscription IDs

The ID map is used for:

  • Cross-referencing — Orders look up customer and product IDs from the map
  • Skip detection — Already-migrated records are skipped by checking the map
  • Rollback — The rollback process uses the map to find and delete FluentCart records

Migration Log

Every processed record generates a log entry in the cartshift_migration_log table:

ColumnDescription
migration_idUUID of the migration run
entity_typeEntity being migrated
wc_idWooCommerce record ID
statussuccess, skipped, or error
messageHuman-readable description of what happened
created_atTimestamp

Retrieve the log via the REST endpoint:

curl -X GET "https://yoursite.com/wp-json/cartshift/v1/log?page=1&per_page=50" \
  -H "X-WP-Nonce: YOUR_NONCE"

The response includes pagination information:

{
  "entries": [ ... ],
  "total": 4360,
  "page": 1,
  "per_page": 50,
  "pages": 88
}

On this page