How to apply a custom coupon to a WooCommerce Subscription recurring amount?

I have managed to find a way, but welcome a better one.

As mentioned in the OP comments, the function remove_coupons() in class WC_Subscriptions_Coupon in file woocommerce-subscriptions/includes/class-wc-subscriptions-coupon.php is removing coupons that are not ‘recurring_fee’ or ‘recurring_percent’. There is no filter added we can tap into to assign our own custom coupon types, so there does not seem to be any chance of getting past this.

So to get around this, we can create our own filter using the same code as the remove_coupons() method, in this case called remove_coupons_custom() and then add this filter in the same way as it is in the original class.

But the original method still runs and will remove our coupon, so we need to then remove the original filter from running by adding remove_action( ‘woocommerce_before_calculate_totals’, ‘WC_Subscriptions_Coupon::remove_coupons’, 10 ); in the top of our new function.

I do not like this approach in case the original code is changed to a degree that breaks or stops ours working, but is the only way I can see to do it at the moment, unless the original code is modified to add a filter we can tap into to add our own custom types.

function remove_coupons_custom( $cart )
{
    // Remove original action added by WC_Subscriptions_Coupon class
    remove_action( 'woocommerce_before_calculate_totals', 'WC_Subscriptions_Coupon::remove_coupons', 10 );

    // Create WC_Subscriptions_Coupon instance so we can replace self reference below in original method
    $WC_Subscriptions_Coupon = new WC_Subscriptions_Coupon;

    $calculation_type = WC_Subscriptions_Cart::get_calculation_type();

    // Only hook when totals are being calculated completely (on cart & checkout pages)
    if ( 'none' == $calculation_type || ! WC_Subscriptions_Cart::cart_contains_subscription() || ( ! is_checkout() && ! is_cart() && ! defined( 'WOOCOMMERCE_CHECKOUT' ) && ! defined( 'WOOCOMMERCE_CART' ) ) ) {
        return;
    }

    $applied_coupons = $cart->get_applied_coupons();

    // If we're calculating a sign-up fee or recurring fee only amount, remove irrelevant coupons
    if ( ! empty( $applied_coupons ) ) {

        // Keep track of which coupons, if any, need to be reapplied immediately
        $coupons_to_reapply = array();

        foreach ( $applied_coupons as $coupon_code ) {

            $coupon      = new WC_Coupon( $coupon_code );
            $coupon_type = wcs_get_coupon_property( $coupon, 'discount_type' );

            if ( in_array( $coupon_type, array( 'recurring_fee', 'recurring_percent', 'sign_up_and_recurring_percent' ) ) ) {  // always apply coupons to their specific calculation case
                if ( 'recurring_total' == $calculation_type ) {
                    $coupons_to_reapply[] = $coupon_code;
                } elseif ( 'none' == $calculation_type && ! WC_Subscriptions_Cart::all_cart_items_have_free_trial() ) { // sometimes apply recurring coupons to initial total
                    $coupons_to_reapply[] = $coupon_code;
                } else {
                    $WC_Subscriptions_Coupon::$removed_coupons[] = $coupon_code;
                }
            } elseif ( ( 'none' == $calculation_type ) && ! in_array( $coupon_type, array( 'recurring_fee', 'recurring_percent' ) ) ) { // apply all coupons to the first payment
                $coupons_to_reapply[] = $coupon_code;
            } else {
                $WC_Subscriptions_Coupon::$removed_coupons[] = $coupon_code;
            }
        }
        // Now remove all coupons (WC only provides a function to remove all coupons)
        $cart->remove_coupons();

        // And re-apply those which relate to this calculation
        $cart->applied_coupons = $coupons_to_reapply;

        if ( isset( $cart->coupons ) ) { // WC 2.3+
            $cart->coupons = $cart->get_coupons();
        }
    }
}
add_action( 'woocommerce_before_calculate_totals', 'remove_coupons_custom', 10 );

enter image description here