Skip to content
Gaurav TiwariDocs

Migrate from WooCommerce Subscriptions

Updated November 7, 2026

Moving live subscriptions to a new plugin is the scary kind of migration. Get it wrong and customers are charged twice, renewals stop without anyone noticing, or customers have to type in cards that worked yesterday.

The WooCommerce Subscriptions Migration module moves your WooCommerce Subscriptions subscriptions into Stackless with their dates, prices, past orders and, for Stripe and PayPal, their saved payment methods. It scans while WooCommerce Subscriptions is active, imports only after it’s deactivated, and checks each subscription afterwards. Only one plugin ever renews a subscription.

Before You Start

  • Switch on WooCommerce Subscriptions Migration under Stackless → Dashboard. It’s off by default, and it works while WooCommerce Subscriptions keeps the Stackless Subscriptions module paused. The screen is Stackless → WCS migration.
  • Set up Stripe (Stackless) with API keys for the same Stripe account the WooCommerce Stripe Gateway uses, in the same mode, live or test. See Set Up Stripe Payments.
  • Set up PayPal (Stackless) with credentials for the same PayPal account WooCommerce PayPal Payments uses. See Set Up PayPal Payments.
  • Plan your products. The migration moves subscriptions, not product settings. Renewals bill each subscription’s own line items, but new sign-ups and plan switches need the product set up as a Stackless subscription. See Create Subscription Products.
  • Take a database backup.

Step 1: Scan

With WooCommerce Subscriptions still active, click Scan WooCommerce Subscriptions. The scan only reads. It changes nothing in WooCommerce Subscriptions, and you can run it as often as you like.

  • Also bring over cancelled and expired subscriptions (as history) is ticked by default. Untick it to leave ended subscriptions out.
  • The scan runs in the background, 50 subscriptions at a time. The page refreshes every 10 seconds, then shows how many subscriptions it scanned and when.
  • It also notes whether the WooCommerce Stripe Gateway and PayPal Payments run in live or test mode, so saved methods are looked up in the right mode later.
  • Scan again replaces everything not imported yet. Subscriptions already imported stay as they are.

Read the Report

After a scan, What will happen shows the counts:

RowWhat it counts
To importSubscriptions that will come over.
ImportedSubscriptions already in Stackless.
Left outSubscriptions that won’t be imported, each with a reason.
FailedSubscriptions the import tried and stopped on with an error.
Keep renewing with StripeLive subscriptions with a saved Stripe payment method to carry over.
Keep renewing with PayPalLive subscriptions with a saved PayPal vault to carry over.
Renew by pay linkLive subscriptions that will renew by emailed invoice.

Before the import, the Stripe and PayPal rows count subscriptions that have a saved method. Each one is looked up at Stripe or PayPal during the import, and afterwards the rows show what actually carried over.

Worth checking lists every subscription that has a note, with its result and, once imported, a link to the Stackless subscription. These notes leave a subscription out:

  • A status that isn’t imported, such as Pending or Switched. It was never paid, or a plan switch replaced it.
  • Ended, when you chose to leave cancelled and expired subscriptions out.
  • An unsupported billing schedule, no products, a missing currency or a missing start date.
  • Active, but with no next payment date and no end date.

These notes come with subscriptions that are still imported:

  • The next payment date has passed. Stackless renews it right after import.
  • It will renew by pay link, with the reason: it renews manually in WooCommerce Subscriptions, it has no saved Stripe payment method or PayPal account, or Stackless can’t charge its gateway automatically.
  • It has an unpaid renewal order. That order stays payable, and paying it renews the subscription.
  • Guest subscription. The customer has no account to manage it from.
  • A product no longer exists. The line keeps its name and price.

How Statuses Map

WooCommerce SubscriptionsStackless
ActiveActive, or Trial while the free trial is still running
On holdOn hold
Pending cancellationPending cancellation, ending on the same date
CancelledCancelled, as history
ExpiredExpired, as history
Pending, Switched and anything elseLeft out

What Carries Over

  • The customer, billing and shipping addresses, currency and customer note
  • The billing schedule; the start, trial end, next payment, end and last payment dates; and how many periods were paid
  • Line items with their quantities, totals, taxes and item details, plus shipping lines, fees and coupons
  • The parent order and past renewal orders, linked to the new subscription. Paid ones count as paid. An unpaid one becomes the subscription’s renewal, so paying it renews the subscription.
  • A note on the subscription, such as “Imported from wcs:123. WooCommerce Subscriptions #123.”

The import charges nothing and sends no emails. Each subscription is saved straight into its status, then its renewal schedule is built from its dates.

Saved Cards and PayPal Accounts

A subscription keeps renewing automatically only when Stackless can charge what it was paying with:

  • WooCommerce Stripe Gateway (cards, SEPA and the other Stripe methods it saves): Stackless reads the subscription’s Stripe customer and payment method, finds that payment method on your Stripe account and checks it’s attached to the same customer. It’s saved as a Stripe (Stackless) payment method of the same type, so a SEPA mandate stays SEPA.
  • WooCommerce PayPal Payments (PayPal, Venmo and cards saved in PayPal’s vault): Stackless reads the saved vault back from PayPal with your PayPal (Stackless) credentials. PayPal and Venmo accounts become saved PayPal accounts, and vaulted cards become saved cards.

Everything else falls back to manual renewal, where the customer is emailed a pay link for each renewal. That covers other gateways, subscriptions that renewed manually, a missing saved method, a method Stripe or PayPal can’t find on your account, wallets other than PayPal and Venmo, and a Stackless Stripe or PayPal module that’s switched off. The reason goes in the subscription’s notes and in the report.

A customer on pay links can move back to automatic renewal later by picking or adding a saved payment method on their subscription. See What Customers Can Do from My Account.

Step 2: Cut Over

The import refuses to run while WooCommerce Subscriptions is active, with the message “Deactivate WooCommerce Subscriptions first, so only Stackless renews these subscriptions.” Otherwise both plugins would schedule and charge the same renewals, and customers would pay twice. For the same reason, the Stackless Subscriptions module stays paused while WooCommerce Subscriptions is active.

  1. Pick a quiet time and scan again just before you switch. Sign-ups after the last scan aren’t in it.
  2. Deactivate WooCommerce Subscriptions under Plugins. Don’t delete it: its data stays as a record.
  3. Check that the Stackless Subscriptions module is on, and that the Stackless Stripe and PayPal gateways use the same accounts.

The screen shows where you stand. You’re ready when it reads “WooCommerce Subscriptions: deactivated · Stackless Subscriptions: running”.

Step 3: Import

  1. Tick “WooCommerce Subscriptions is deactivated and I am ready to import. Nothing is charged by the import itself.”
  2. Click Import, which shows how many subscriptions it will bring over.

The import runs in the background, 20 subscriptions per batch, and the page refreshes every 10 seconds.

  • Resuming: if a batch stops partway, the subscriptions it didn’t finish stay in To import. Click Import again to pick them up. If WooCommerce Subscriptions is reactivated or Stackless Subscriptions is switched off during the import, it stops and says why.
  • No duplicates: each Stackless subscription remembers its WooCommerce Subscriptions number, so a rerun never creates a second copy.
  • Failures: Try the failed ones again moves failed subscriptions back to To import and runs the import.
  • Due right away: a subscription whose next payment date has already passed renews right after import, by charging the saved method or emailing the invoice.

Check the Results

After importing each subscription, Stackless compares it with the WooCommerce Subscriptions record. Any difference shows under Worth checking, with the result Imported, at the start of that subscription’s notes:

  • Status
  • Currency
  • Recurring total, to the cent
  • Next payment date, for live subscriptions with a date still ahead
  • Number of products

Notes from the scan, such as a guest subscription, stay on the row after it, so read the start of each note. Open any subscription with a difference from the link in the table and correct it on its edit screen. See Manage Subscriptions, Emails and Reports.

Step 4: Tidy Up

WooCommerce Subscriptions leaves its own scheduled payments, retries, trial ends, expirations and reminders behind. They do nothing while it’s deactivated, but would charge customers again if it were reactivated. Once you’re happy with the import, click Remove WooCommerce Subscriptions’ scheduled payments. It only runs while WooCommerce Subscriptions is deactivated, and when nothing is left the screen says “No WooCommerce Subscriptions payments are scheduled. Keep it deactivated.”

Checklist for the Day of the Switch

  1. Back up the database.
  2. Confirm Stripe (Stackless) and PayPal (Stackless) use the same accounts and mode as your old gateways.
  3. Set your subscription products up in Stackless.
  4. Scan again and read Worth checking. Note any subscription whose next payment date has passed: it renews straight after import.
  5. Deactivate WooCommerce Subscriptions.
  6. Make sure the screen shows Stackless Subscriptions as running.
  7. Tick the confirmation and click Import.
  8. Read Worth checking again, and open a few imported subscriptions on each gateway to check Next payment, Renewal and Payment method.
  9. Click Remove WooCommerce Subscriptions’ scheduled payments.
  10. Leave WooCommerce Subscriptions deactivated, and check Find Billing Problems with Billing Health after the first renewals run.

Limits

  • Only WooCommerce Subscriptions is imported. Other subscription plugins aren’t.
  • Saved methods carry over only from the WooCommerce Stripe Gateway and WooCommerce PayPal Payments, and only when Stackless’s Stripe or PayPal gateway uses the same account. Everything else renews by pay link.
  • Sign-ups made after the last scan aren’t imported. Scan again right before you deactivate.
  • Pending subscriptions and the old side of a plan switch aren’t imported. The subscription that replaced a switched one is.
  • Product settings aren’t converted.
  • Guest subscriptions come over, but the customer can’t manage them from My Account.
  • Keep WooCommerce Subscriptions deactivated after the import. While it’s active, Stackless Subscriptions pauses and renews nothing.

For Developers

WP-CLI runs the same steps in the foreground, with the same checks:

wp stackless wcs status
wp stackless wcs scan [--include-ended]
wp stackless wcs import [--limit=<number>]
wp stackless wcs clear-wcs-actions

From WP-CLI, scan leaves cancelled and expired subscriptions out unless you pass --include-ended. import needs no confirmation box, but still refuses while WooCommerce Subscriptions is active.

Other migrations can call stackless_import_subscription(). It takes one record as an array and returns the new subscription ID or a WP_Error. The source key makes it safe to call twice: the second call returns the subscription made the first time. Dates are Unix timestamps. Without a token_id and renewal_mode set to auto, the subscription renews by pay link. Nothing is charged, no email is sent, and stackless_subscription_imported fires after each one.

$id = stackless_import_subscription([
    'source'      => 'legacy:1042',
    'status'      => 'sub-active',
    'customer_id' => 17,
    'currency'    => 'USD',
    'period'      => 'month',
    'interval'    => 1,
    'dates'       => [
        'start'        => strtotime('2026-01-05'),
        'next_payment' => strtotime('2026-12-05'),
    ],
    'items'       => [
        ['product_id' => 55, 'name' => 'Pro plan', 'quantity' => 1, 'subtotal' => '29', 'total' => '29'],
    ],
]);

status takes the internal slug, such as sub-active, sub-onhold or sub-paused. A record needs a source, a day, week, month or year period, a three-letter currency, at least one item and a start date, and an active or trial one needs a next payment or end date. The other keys are prices_include_tax, billing, shipping, customer_note, payment_method, payment_method_title, gateway_customer, paid_cycles, parent_id, shipping_lines, fees, coupons, renewal_orders and note, and dates also takes trial_end, end, last_payment and cancelled. The comment above the function in functions.php describes each one, and Hooks, Functions and Templates Reference lists it with the other functions.

Share

Was this helpful?

Still stuck? Open a support ticket