Created
August 31, 2026 14:43
-
-
Save xlplugins/e103b1a60e55b20b01f89aac27aeabbd to your computer and use it in GitHub Desktop.
merge upsell subscription into one
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Runs after FunnelKit has created the upsell subscription (priority 99, so the | |
| * gateway handlers that store the payment token on it have already run). | |
| * | |
| * @param WC_Subscription $new_subscription Subscription FunnelKit just created. | |
| * @param string $hash Offer product hash (unused). | |
| * @param WC_Order $subscription_order Order the upsell was recorded against. | |
| */ | |
| add_action( 'wfocu_subscription_created_for_upsell', function ( $new_subscription, $hash, $subscription_order ) { | |
| if ( ! function_exists( 'wcs_get_subscriptions_for_order' ) || ! $new_subscription instanceof WC_Subscription ) { | |
| return; | |
| } | |
| $log = function ( $message ) { | |
| if ( function_exists( 'WFOCU_Core' ) && ! is_null( WFOCU_Core()->log ) ) { | |
| WFOCU_Core()->log->log( '[merge-subscription] ' . $message ); | |
| } | |
| }; | |
| // The upsell may have been recorded against a NEW order. The parent | |
| // subscription belongs to the ORIGINAL order, so ask FunnelKit for it and | |
| // only fall back to the order we were handed. | |
| $parent_order = null; | |
| if ( function_exists( 'WFOCU_Core' ) && ! is_null( WFOCU_Core()->data ) ) { | |
| $parent_order = WFOCU_Core()->data->get_parent_order(); | |
| } | |
| if ( ! $parent_order instanceof WC_Order ) { | |
| $parent_order = $subscription_order; | |
| } | |
| if ( ! $parent_order instanceof WC_Order ) { | |
| $log( 'no parent order, leaving the new subscription alone' ); | |
| return; | |
| } | |
| $parent_subscriptions = wcs_get_subscriptions_for_order( $parent_order->get_id(), array( 'order_type' => 'parent' ) ); | |
| // Drop the one we have just been given, in case it is linked to this order. | |
| foreach ( $parent_subscriptions as $key => $candidate ) { | |
| if ( $candidate->get_id() === $new_subscription->get_id() ) { | |
| unset( $parent_subscriptions[ $key ] ); | |
| } | |
| } | |
| if ( 1 !== count( $parent_subscriptions ) ) { | |
| $log( sprintf( 'expected exactly 1 parent subscription, found %d - leaving both in place', count( $parent_subscriptions ) ) ); | |
| return; | |
| } | |
| $parent_subscription = array_pop( $parent_subscriptions ); | |
| // Two subscriptions can only share a schedule if they bill on the same one. | |
| if ( | |
| $parent_subscription->get_billing_period() !== $new_subscription->get_billing_period() | |
| || (int) $parent_subscription->get_billing_interval() !== (int) $new_subscription->get_billing_interval() | |
| ) { | |
| $log( sprintf( | |
| 'billing schedule differs (parent %s/%s vs upsell %s/%s) - cannot merge', | |
| $parent_subscription->get_billing_interval(), | |
| $parent_subscription->get_billing_period(), | |
| $new_subscription->get_billing_interval(), | |
| $new_subscription->get_billing_period() | |
| ) ); | |
| return; | |
| } | |
| if ( ! $parent_subscription->has_status( array( 'active', 'pending', 'on-hold' ) ) ) { | |
| $log( 'parent subscription is ' . $parent_subscription->get_status() . ' - not merging into it' ); | |
| return; | |
| } | |
| try { | |
| $moved = 0; | |
| $copied_items = array(); | |
| foreach ( $new_subscription->get_items() as $item ) { | |
| $copied_items[] = $item->get_id(); | |
| $copy = new WC_Order_Item_Product(); | |
| $copy->set_props( array( | |
| 'product_id' => $item->get_product_id(), | |
| 'variation_id' => $item->get_variation_id(), | |
| 'quantity' => $item->get_quantity(), | |
| 'subtotal' => $item->get_subtotal(), | |
| 'subtotal_tax' => $item->get_subtotal_tax(), | |
| 'total' => $item->get_total(), | |
| 'total_tax' => $item->get_total_tax(), | |
| 'taxes' => $item->get_taxes(), | |
| 'name' => $item->get_name(), | |
| ) ); | |
| foreach ( $item->get_meta_data() as $meta ) { | |
| $copy->add_meta_data( $meta->key, $meta->value, true ); | |
| } | |
| $copy->add_meta_data( '_wfocu_merged_upsell', 'yes', true ); | |
| $parent_subscription->add_item( $copy ); | |
| $moved ++; | |
| } | |
| if ( 0 === $moved ) { | |
| $log( 'upsell subscription had no line items' ); | |
| return; | |
| } | |
| $parent_subscription->calculate_totals( wc_tax_enabled() ); | |
| $parent_subscription->add_order_note( sprintf( | |
| /* translators: %d: upsell subscription id */ | |
| __( 'Upsell product merged in from subscription #%d (FunnelKit one-click upsell).', 'funnel-builder' ), | |
| $new_subscription->get_id() | |
| ) ); | |
| $parent_subscription->save(); | |
| /** | |
| * Return false to keep the duplicate subscription as "cancelled" instead | |
| * of deleting it. Deleting keeps the customer's account page clean. | |
| */ | |
| if ( apply_filters( 'wfocu_delete_merged_upsell_subscription', true, $new_subscription, $parent_subscription ) ) { | |
| /* | |
| * Delete the line items explicitly first. WC_Subscription::delete() | |
| * clears wp_wc_orders / wp_posts / wp_wc_orders_meta but leaves the | |
| * rows in wp_woocommerce_order_items and _itemmeta behind, and those | |
| * orphans keep the deleted subscription visible in admin lists. | |
| */ | |
| foreach ( $copied_items as $item_id ) { | |
| wc_delete_order_item( $item_id ); | |
| } | |
| $new_subscription->delete( true ); | |
| } else { | |
| // Keeping it as an audit trail - cancelled subscriptions do not bill, | |
| // so the items can stay on it. | |
| $new_subscription->update_status( 'cancelled', __( 'Merged into the parent subscription.', 'funnel-builder' ) ); | |
| } | |
| $log( sprintf( 'merged %d item(s) into subscription #%d', $moved, $parent_subscription->get_id() ) ); | |
| } catch ( Exception $e ) { | |
| $log( 'merge failed: ' . $e->getMessage() ); | |
| } | |
| }, 99, 3 ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment