milosubscriptions includes a built-in Migration tool that moves subscription data from an existing subscription plugin into milosubscriptions. It runs as a wizard with four steps (Detect, Preview, Progress, Complete) and processes subscriptions in the background via Action Scheduler, so the import continues even if you close the browser tab.
Open it from milosubscriptions → Migration.
Supported sources
The tool currently supports two sources:
- WooCommerce Subscriptions (the official plugin by Automattic).
- Subscriptions for WooCommerce (the free plugin by WP Swings).
Sources are detected automatically by looking for subscription data already stored in your database. You do not need to connect to an external system: the source plugin’s data is read from your site’s own tables.
What gets imported
For each subscription, the tool copies:
- Subscription details and billing schedules (period, interval, start date, trial end, next payment date, end date).
- Customer and billing and shipping address information.
- Payment method and payment tokens (where the source stored them).
- Renewal order history (existing renewal orders are re-linked to the new subscription).
- Subscription statuses.
- Coupon discounts (so recurring coupons keep applying to renewals).
- Download permissions for downloadable products.
The import works with both WooCommerce HPOS and the legacy post-meta storage.
Wizard steps
Step 1: Detect
The page loads and scans your database for source data. For each supported source that has unmigrated subscriptions, you see a card with the source name and the count of subscriptions found. Select a source to continue.
If nothing is detected, there is no subscription data from a supported source on your site (or every subscription has already been migrated).
Step 2: Preview (dry run)
Before running the import, milosubscriptions analyzes up to 1,000 source subscriptions and shows a preview that includes:
- Total number of subscriptions to import.
- Number of unique payment methods in use.
- Monthly revenue across the sampled subscriptions.
- Count of subscriptions with coupons.
- Count of subscriptions with downloadable products.
- Number of warnings.
- A breakdown by status (active, on-hold, pending-cancel, pending, cancelled, expired) with counts and percentages.
- A breakdown by payment method.
- Up to 20 detailed warnings (for example, subscriptions whose orders cannot be loaded, or active subscriptions with no stored payment token that will require manual renewal).
The dry run does not modify any data.
Step 3: Progress
When you start the import, milosubscriptions schedules the first batch via Action Scheduler (group milosubscriptions-migration, hook milosubscriptions_process_migration_batch). Each batch processes a fixed number of subscriptions, then schedules the next batch until the full source is done.
While the migration runs, the UI polls for progress and displays:
- How many subscriptions have been processed out of the total.
- The completion percentage.
- A live status message.
You can cancel the migration at any time. Cancelling marks progress as cancelled and unschedules any pending batches.
Step 4: Complete
When every subscription has been processed, the migration is marked complete. Imported subscriptions appear in milosubscriptions → Subscriptions with a note recording their original ID and source.
Behavior during the import
While a batch is running, milosubscriptions:
- Disables WooCommerce emails so customers are not notified during the migration.
- Removes the
milosubscriptions_subscription_createdandmilosubscriptions_subscription_status_updatedactions so side effects of those hooks do not fire. - Logs errors for individual subscriptions to the WooCommerce logger under the source
milosubscriptions-migration, then moves on to the next subscription.
Idempotency
The import is idempotent: you can run it as many times as you want without creating duplicates. Each source subscription is tagged with _milo_migrated_to pointing at its new counterpart, and detection queries skip anything already tagged. Running a second time only picks up new or previously skipped subscriptions.
Meta kept on each side
Every successfully migrated subscription stores:
_milo_migrated_from: the ID of the source subscription._milo_migration_source:wcsfor WooCommerce Subscriptions orwpsfor WP Swings.
The source subscription stores:
_milo_migrated_to: the ID of the new milosubscriptions subscription.
Use these meta keys if you need to cross-reference old and new records in your own queries or exports.
Verification
After the import, you can run a verification report that:
- Counts how many source subscriptions were migrated.
- Compares the source status to the migrated status for each pair.
- Lists any pairs where the statuses do not match.
- Flags any source subscriptions where the migrated subscription cannot be loaded.
Use this to confirm data integrity before deactivating the source plugin.
Payment tokens
Payment tokens are the piece of data that makes automatic renewals keep working after migration. The tool handles them as follows:
- If the source subscription has a stored WooCommerce payment token, it is copied directly to the new subscription, and saved as
_milo_payment_token_id. - If the source does not store tokens through WooCommerce’s standard system (this is common with WooCommerce Subscriptions), the tool falls back to the customer’s most recent saved token for the same gateway and attaches it.
- Common gateway-specific meta is also copied:
_stripe_customer_id,_stripe_source_id,_stripe_card_id, Braintree token keys,_paypal_subscription_id,_ppcp_billing_agreement_id, and a few WP Swings-specific keys.
If an active subscription has no token at all, the dry run surfaces a warning. After migration, that subscription will need a manual renewal until the customer updates their payment method.
Status mapping
For WP Swings subscriptions, status names are mapped to milosubscriptions statuses:
active→ activeon-hold→ on-holdpaused→ on-holdpending→ pendingpending-cancel→ pending-cancelcancelled→ cancelledexpired→ expired
For WooCommerce Subscriptions, statuses are copied directly because the status names are identical.
Troubleshooting
The Detect step shows no sources
Your site has no data from the two supported source plugins, or every subscription has already been migrated. Confirm by looking for the _milo_migrated_to meta on your source subscriptions: if it is set, they have already been imported.
A migrated subscription is missing a next payment date or schedule meta
Check the original subscription. The tool copies the meta keys that each source plugin actually sets. If the source subscription is missing a schedule field, the target will also miss it. Edit the subscription manually to complete the data.
A migrated subscription has no payment token
This usually means the source plugin did not attach a token through WooCommerce’s standard system, and the customer had no matching saved token for that gateway. The subscription is imported, but automatic renewals will fail until the customer updates their payment method.
A subscription threw an error during migration
Open WooCommerce’s logs (WooCommerce → Status → Logs) and look for entries from the milosubscriptions-migration source. Each entry names the source subscription ID and the error message. Fix the underlying issue (missing parent order, unreadable meta, etc.) and run the migration again. The tool will skip already-migrated subscriptions and retry the failed ones.
The progress bar stalled
Migration relies on Action Scheduler. If scheduled actions are not running, the migration will pause. Go to Tools → Scheduled Actions, filter by the milosubscriptions-migration group, and check whether actions are stuck in Pending. If they are, your site’s WP-Cron or real cron is not firing often enough.