Fix Stripe’s Sources API Deprecated Issue in WooCommerce

Fixing the Stripe's Sources API Deprecated Issue in WooCommerce

Over the past year, Stripe fully retired the Sources API for local payment methods like SEPA Direct Debit, replacing it with the newer PaymentIntents + PaymentMethods model. If your WooCommerce store ever accepted SEPA Direct Debit before mid-2024, chances are you still have subscriptions pointing to old src_… IDs — and those are now failing silently or producing a “deprecated” error.

This post documents what happened, how we discovered it, how to identify affected customers, and how we fixed it safely across thousands of live subscriptions.


⚠️ The Symptom

A subset of SEPA subscriptions suddenly started failing renewals.

Stripe’s dashboard showed this error message:

Using a SEPA Debit Source in the source field has been deprecated and is no longer a valid integration path.

WooCommerce, receiving a failed payment webhook, automatically cancelled those subscriptions — even though customers’ bank accounts were perfectly fine.

At first, it looked like a random Stripe connectivity issue. But after checking the metadata, every failing subscription had something in common:

_payment_method       = stripe_sepa_debit
_stripe_source_id     = src_XXXXXXXXXXXX

That was the clue: Stripe no longer accepts SEPA src_… Sources on PaymentIntents.


🧠 Root Cause: The Sources → PaymentMethods Migration

1. Stripe’s side

Historically, SEPA Direct Debit used the Sources API (src_… objects) to represent mandates and customer bank data.
In 2023–2024, Stripe migrated all local payment methods (SEPA, Bacs, iDEAL, etc.) to the PaymentMethods API, which uses pm_… objects tied to PaymentIntents.

To help merchants transition, Stripe introduced an internal, account-level migration called “Reusable Sources → PaymentMethods”, which creates corresponding pm_… entries for reusable Sources.

Any Source not migrated (e.g. missing a reusable mandate) becomes unusable.

🧾 References:

2. WooCommerce’s side

WooCommerce’s Stripe Gateway plugin added an automatic “legacy SEPA repairer” to convert old subscriptions to the new model.
However, this repairer only targeted subscriptions using the old gateway ID stripe_sepa.

Many stores, including ours, had already switched to the new gateway ID (stripe_sepa_debit) before the Stripe migration — so those subscriptions weren’t picked up by the repairer.

They kept their old _stripe_source_id = src_… but were never updated to a pm_….

When those subs renewed, Woo tried to charge a PaymentIntent with a deprecated Source — and Stripe rejected it.

🧾 References:


🔎 Identifying Affected Users

Three quick SQL checks (read-only) revealed the scale of the issue:

1️⃣ Saved tokens still using src_…

SELECT COUNT(*) 
FROM wp_woocommerce_payment_tokens
WHERE gateway_id = 'stripe_sepa'
  AND token LIKE 'src_%';

2️⃣ Subscriptions storing a src_…

SELECT COUNT(*)
FROM wp_postmeta
WHERE meta_key = '_stripe_source_id'
  AND meta_value LIKE 'src_%';

3️⃣ Split by gateway ID

SELECT pm.meta_value AS payment_method, COUNT(*) AS count
FROM wp_posts p
JOIN wp_postmeta pm  ON pm.post_id = p.ID AND pm.meta_key = '_payment_method'
JOIN wp_postmeta src ON src.post_id = p.ID 
WHERE p.post_type = 'shop_subscription'
  AND src.meta_key = '_stripe_source_id'
  AND src.meta_value LIKE 'src_%'
GROUP BY pm.meta_value;

This confirmed it:

  • Only a handful of subscriptions used the legacy stripe_sepa.
  • Tens of thousands used the new stripe_sepa_debit but still stored old src_… IDs.

Those weren’t touched by the default migration.


🧩 Why the Default Migration Missed Them

  1. Enumeration gap:
    Woo’s repairer runs only for subs where _payment_method = stripe_sepa.
    Subs with _payment_method = stripe_sepa_debit but _stripe_source_id = src_… were ignored.
  2. Stripe account not fully migrated:
    If Stripe hadn’t yet created matching pm_… PaymentMethods for old Sources, Woo had nothing to swap in — it simply logged a note and moved on.

The result: thousands of subscriptions still referenced invalid src_… Sources, even though everything else looked fine in Woo.


🧰 The Fix — Step by Step

Step 1. Complete the Stripe Account Migration

First, the Reusable Sources → PaymentMethods migration must be completed on your Stripe account.

You can:

  • Go to Stripe Dashboard → Settings → Payment methods and look for any “Migrate old Sources” notices, or
  • Contact Stripe Support and request: “Please run the reusable Sources → PaymentMethods migration on our account for SEPA Direct Debit.”

After this runs, every reusable SEPA Source will gain a pm_… counterpart.


Step 2. Run or Extend WooCommerce’s Repair Job

Even after Stripe’s migration, Woo needs to update your local metadata.

You have two options:

Option A — Use the built-in repairer (for true legacy stripe_sepa subs)

Woo’s internal repair class:

WC_Stripe_Subscriptions_Repairer_Legacy_SEPA_Tokens

automatically updates subscriptions that use the legacy gateway.
If you have only those, re-running it from Woo → Status → Tools → Restart SEPA token update is enough.

Option B — Custom repair for all src_… (recommended)

We added a small mu-plugin to target any subscription with _stripe_source_id LIKE 'src_%', regardless of gateway id.
It calls the same safe updater Woo uses internally:

$updater = new WC_Stripe_Subscriptions_Legacy_SEPA_Token_Update();
$updater->maybe_update_subscription_source( $subscription );

You can run it from the Tools page or via WP-CLI in batches:

wp eval '
$u = new WC_Stripe_Subscriptions_Legacy_SEPA_Token_Update();
$ids = wc_get_orders([
  "type" => "shop_subscription",
  "status" => "any",
  "return" => "ids",
  "meta_query" => [[ "key" => "_stripe_source_id", "value" => "src_", "compare" => "LIKE" ]]
]);
foreach($ids as $id){ $s = wcs_get_subscription($id); if($s) $u->maybe_update_subscription_source($s); }
echo "Processed " . count($ids) . " subscriptions\n";
'

This safely updates every eligible sub to use a valid pm_… without touching customer mandates.


Step 3. Verify & Clean Up

After running the repairer, re-run your audit queries.
Any subscription still showing src_… now truly lacks a convertible mandate on Stripe — those customers must update their payment method manually.

WooCommerce lets you trigger that with the “Send update payment method email” action directly from the Subscriptions list.


Step 4. Prevent Future Issues

  • Keep the new (UPE/standard) checkout enabled — it only ever creates pm_… PaymentMethods.
  • Make sure “Saved payment methods” are allowed so subscriptions can reuse pm_… safely.
  • Stay current with the WooCommerce Stripe Gateway plugin.
  • Periodically run a quick check for lingering src_… entries to ensure you’re fully clean.

⚙️ Bonus: Why You Can’t Reproduce This With New Users

The Sources API is now completely disabled for new SEPA objects — both in live and test mode.
If you try stripe sources create type=sepa_debit, Stripe returns:

The Sources API has reached end of life.
In order to continue processing your non-card payments, 
you will need to migrate to the Payment Intents and Payment Methods API.

So, you can’t “recreate” a real src_… subscription anymore.
To simulate the bug locally, you can simply set _stripe_source_id = src_fake_test_123 in a test subscription — Woo will still try to pass it to Stripe and trigger the same deprecation error.


💡 Lessons Learned

  1. Stripe’s migration fixed the platform — not your database.
    You still need to run Woo’s local repairer to update subscription metadata.
  2. Don’t assume automatic repairers catch everything.
    Many WooCommerce updates are scoped narrowly to prevent accidental data churn. Sometimes that means important objects are missed.
  3. Bulk audits are your friend.
    Quick SQL or WP-CLI reports can surface thousands of silent time bombs before customers notice.
  4. Always test on staging first.
    Use Stripe’s test mode with fake _stripe_source_id values to verify your fix flow without affecting real customers.

🏁 Results

After completing the Stripe account migration and running the enhanced repairer:

  • All active subscriptions now use pm_… SEPA PaymentMethods.
  • Renewals succeed through PaymentIntents with no source parameter.
  • WooCommerce tokens and user payment methods are consistent.
  • The “Sources API deprecated” errors have completely disappeared.

✅ In Summary

StepActionResponsible
1Complete “Reusable Sources → PaymentMethods” migrationStripe Dashboard / Stripe Support
2Update WooCommerce Stripe Gateway plugin, enable new checkoutWoo admin
3Run repairer (built-in or custom) on all subs with _stripe_source_id LIKE 'src_%'Woo admin
4Audit remaining src_… and send “Update payment method” emailsWoo admin
5Keep UPE checkout active and monitor logsOngoing

🚀 Conclusion

The SEPA “Sources API deprecated” issue was a perfect example of a two-sided migration problem:
Stripe changed the underlying API, but local data in WooCommerce still pointed to legacy identifiers.

By:

  1. Completing the Stripe account-level migration, and
  2. Expanding WooCommerce’s repairer to include all src_… references,

we fully modernized our SEPA Direct Debit subscriptions — without customer disruption, without touching sensitive data, and without downtime.

If your store still shows any _stripe_source_id = src_…, now’s the time to check before your next renewal wave.

  • How To Use the WordPress Register Sidebar Function

    How To Use the WordPress Register Sidebar Function

    In this article, we’ll explore practical approaches for using the WordPress register sidebar function. We’ll also share some advanced techniques to help you get even more out of your sidebars! WordPress Register Sidebar – Single If you’d like to add a sidebar to your WordPress theme, the first step is to let WordPress know about…

  • How to Rename WordPress User Roles Safely

    How to Rename WordPress User Roles Safely

    On a recent client project, we ran into a surprisingly annoying UX bug: user roles with old names that no longer matched the business. The client runs a subscription-based WordPress site. At launch they had separate “Print” and “Digital” products, with roles like: Later, they introduced a combined “Bundle” subscription (print + digital). Marketing and…