WooCommerce Subscriptions: Fix Stripe “Recurring Payment Failed” Error with FunnelKit
If you’re seeing this error on WooCommerce subscription renewals:
Recurring payment for order failed. Reason: You passed an empty string for ‘payment_method’. We assume empty values are an attempt to unset a parameter; however ‘payment_method’ cannot be unset. You should remove ‘payment_method’ from your request or supply a non-empty value.
This happens when WooCommerce Subscriptions tries to process a renewal but can’t find the Stripe payment token on the order. The payment tokens are stored in order meta fields like `_payment_method_token`, `_wc_stripe_customer`, `_stripe_customer_id`, and `_stripe_source_id`. When these fields are missing or empty, Stripe throws an error because it has no payment method to charge.
The most common cause is when plugins create separate orders as part of a purchase flow (like upsells, order bumps, or cart splits). Sometimes these new orders don’t get the Stripe payment data copied over from the original order. So you end up with an order that contains a subscription product but has no way to process renewals.
FunnelKit Upsells is one plugin where this happens. When it creates a separate order for an upsell that contains a subscription, in some cases, it doesn’t automatically copy the payment tokens. This snippet fixes that by hooking into FunnelKit’s order creation and copying the necessary Stripe data.
This code is specifically for FunnelKit/WooFunnels Upsells. If you’re using a different plugin that creates separate orders (CartFlows, custom checkout flows…etc), the same concept applies but you’ll need to find the right hooks for your plugin.
/**
* Fix FunnelKit Upsell Stripe Payment Token Copy Issue
*
* Problem: FunnelKit OneClick Upsells creates separate upsell orders but doesn't
* copy Stripe payment tokens from the main order. This breaks subscription renewals
* with error: "You passed an empty string for payment_method"
*
* Solution: Copy Stripe payment token meta from primary order to upsell order
* Affected Gateways: stripe, stripe_cc, stripe_applepay, stripe_googlepay
* Compatible With: WooCommerce Stripe Gateway & Payment Plugins for Stripe WooCommerce
*/
add_action( 'wfocu_offer_new_order_created_stripe', 'wfocu_copy_stripe_payment_tokens_to_upsell', 5, 1 );
add_action( 'wfocu_offer_new_order_created_stripe_cc', 'wfocu_copy_stripe_payment_tokens_to_upsell', 5, 1 );
add_action( 'wfocu_offer_new_order_created_stripe_applepay', 'wfocu_copy_stripe_payment_tokens_to_upsell', 5, 1 );
add_action( 'wfocu_offer_new_order_created_stripe_googlepay', 'wfocu_copy_stripe_payment_tokens_to_upsell', 5, 1 );
function wfocu_copy_stripe_payment_tokens_to_upsell( $upsell_order ) {
// Ensure we have a valid order object
if ( ! $upsell_order instanceof WC_Order ) {
return;
}
// Get the primary order ID from the upsell order meta
$primary_order_id = $upsell_order->get_meta( '_wfocu_primary_order', true );
if ( empty( $primary_order_id ) ) {
error_log( sprintf(
'WFOCU Stripe Token Copy: Upsell order #%d has no primary order reference',
$upsell_order->get_id()
) );
return;
}
// Get the primary order
$primary_order = wc_get_order( $primary_order_id );
if ( ! $primary_order instanceof WC_Order ) {
error_log( sprintf(
'WFOCU Stripe Token Copy: Could not load primary order #%d for upsell #%d',
$primary_order_id,
$upsell_order->get_id()
) );
return;
}
// Define Stripe payment token meta keys that need to be copied
$stripe_meta_keys = array(
'_payment_method_token', // Primary Stripe payment method token (pm_xxxxx)
'_wc_stripe_customer', // Stripe customer ID (cus_xxxxx)
'_stripe_customer_id', // Alternative Stripe customer ID storage
'_stripe_source_id', // Stripe source ID (legacy, but copy if present)
);
$copied_keys = array();
// Copy each meta key from primary to upsell order
foreach ( $stripe_meta_keys as $meta_key ) {
$meta_value = $primary_order->get_meta( $meta_key, true );
// Only copy if the value exists and is not empty
if ( ! empty( $meta_value ) ) {
$upsell_order->update_meta_data( $meta_key, $meta_value );
$copied_keys[] = $meta_key;
}
}
// Save the upsell order with updated meta
if ( ! empty( $copied_keys ) ) {
$upsell_order->save_meta_data();
// Log success for debugging
error_log( sprintf(
'WFOCU Stripe Token Copy: Successfully copied tokens from order #%d to upsell #%d. Keys: %s',
$primary_order_id,
$upsell_order->get_id(),
implode( ', ', $copied_keys )
) );
// Add order note for reference
$upsell_order->add_order_note( sprintf(
'Stripe payment tokens copied from primary order #%d',
$primary_order_id
) );
} else {
// Log warning if no tokens were found
error_log( sprintf(
'WFOCU Stripe Token Copy: WARNING - No Stripe payment tokens found on primary order #%d to copy to upsell #%d',
$primary_order_id,
$upsell_order->get_id()
) );
}
}
This works with both WooCommerce Stripe Gateway and Payment Plugins for Stripe WooCommerce. If you are using Woo Payments or another plugin, the meta field names might be slightly different, so check your order meta if the snippet is not working.
If you need to troubleshoot, the code logs everything to your debug log. Just search for “FunnelKit Stripe” to see what’s happening.
Need Help?
Learn how to add custom code to WordPress or reach out for custom development help.
About the Author
More Code Snippets
-
Reorder the Columns on the WooCommerce Orders List
Put your WooCommerce orders list columns in whatever order works…
-
Set the Default Country on WooCommerce Checkout
Pre-fill the WooCommerce checkout country (and optionally state) dropdown with…
-
Bulk Delete Expired Unused WooCommerce Coupons (Batched, Safe)
One-time bulk cleanup utility for stores with thousands of expired,…