Created
July 15, 2025 07:10
-
-
Save xlplugins/7f6dee957d0f4daf379cfa7d83cae73b to your computer and use it in GitHub Desktop.
Funnelkit Checkout: Conflict with WooCommerce Fees and Discounts and PayPal for WooCommerce
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
class WCFAD_Dynamic_Pricing { | |
public function __construct() { | |
add_action( 'wfacp_before_process_checkout_template_loader', [ $this, 'remove_actions' ] ); | |
add_action( 'wfacp_after_checkout_page_found', [ $this, 'remove_actions' ] ); | |
} | |
public function remove_actions() { | |
remove_action( 'woocommerce_before_calculate_totals', 'wcfad_before_calculate_totals', 2, 1 ); | |
add_action( 'woocommerce_before_calculate_totals',[$this, 'wcfad_before_calculate_totals'],3 ); | |
} | |
public function wcfad_before_calculate_totals( $cart_obj = false ) { | |
if( is_admin() && ! defined( 'DOING_AJAX' ) ) { | |
return; | |
} | |
if( did_action( 'woocommerce_before_calculate_totals' ) >= 4 ) { | |
return; | |
} | |
// Check that we have at least one pricing rule | |
// $rules = get_option( 'wcfad_dynamic_pricing_rules', array() ); | |
$rules = wcfad_get_dynamic_pricing_rules(); | |
if ( ! $cart_obj ) { | |
// this is false in a mini cart, so create it | |
$cart_obj = WC()->cart; | |
} | |
//$cart = WC()->cart->get_cart(); | |
$cart = $cart_obj->get_cart(); // since 1.9.2, let's try to use $cart_obj | |
// Check the total | |
$cart_total = 0; | |
// Use when deciding what notice to display | |
$cart_total_before_discount = 0; | |
// Use this to check if we've got an order total rule | |
$order_total_rule = false; | |
foreach( $cart as $cart_item_key=>$cart_item ) { | |
if ( 'yes' === wcfad_is_role_pricing_enabled() && empty( $cart_item['product_extras'] ) && apply_filters( 'wcfad_disable_user_role_pricing_before_calculate_totals', true ) ) { | |
// 2.0.3, the following are needed by order total rules | |
$quantity = $cart_item['quantity']; | |
$price = $cart_item['data']->get_price(); | |
$cart_total += ( $price * $quantity ); | |
$cart_total_before_discount += ( $price * $quantity ) ; | |
continue; // skip | |
// 2.0.3: $cart_item['data']->get_price() also triggers the filter woocommerce_product_get_price(), which causes double adjustments for user-role pricing. We only proceed if cart item has AOU | |
} | |
$product_id = $cart_item['data']->get_id(); | |
$add_ons = 0; | |
// Check for any role based pricing rules, provided the price hasn't been set by a calculation field | |
if( isset( $cart_item['product_extras']['original_price'] ) && empty( $cart_item['product_extras']['use_calc_set_price'] ) ) { | |
remove_action( 'woocommerce_before_calculate_totals', 'pewc_wc_calculate_total', 10, 1 ); | |
// Added in 1.8.6 to prevent discounts being doubled for global user role rules | |
// Commented out in 1.9.2 because after removing the actions, products with no add-ons lose their user-role pricing | |
//remove_action( 'woocommerce_product_get_price', 'wcfad_get_regular_price', 10, 2 ); | |
//remove_action( 'woocommerce_product_variation_get_price', 'wcfad_get_regular_price', 10, 2 ); | |
// Since 1.9.2, this is used in wcfad_get_regular_price() | |
$cart_item['data']->update_meta_data( 'wcfad_has_aou', 'yes' ); | |
// If add-ons are present, just apply the discount to the base price | |
$price = $cart_item['product_extras']['original_price']; | |
// Split the base product price from the add-on costs | |
$add_ons = floatval( $cart_item['product_extras']['price_with_extras'] ) - floatval( $cart_item['product_extras']['original_price'] ); | |
// Since 1.9.3, to be used later in bulk discounts, e.g. wcfad_set_qualifying_bulk_product_meta() | |
$cart_item['data']->update_meta_data( 'wcfad_add_on_price_only', $add_ons ); | |
// the line below might not be needed | |
//$add_ons = wc_get_price_excluding_tax($cart_item['data'], array('price'=>$add_ons, 'quantity'=>1)); | |
} else { | |
// Remove this filter so that we don't double the adjustment | |
remove_filter( 'woocommerce_product_get_price', 'wcfad_get_regular_price', 10, 2 ); | |
remove_filter( 'woocommerce_product_variation_get_price', 'wcfad_get_regular_price', 10, 2 ); | |
$price = $cart_item['data']->get_price(); | |
// if below is not commented out, line items are correct, total is incorrect | |
// total is only incorrect if a product has dynamic pricing -> fees | |
add_filter( 'woocommerce_product_get_price', 'wcfad_get_regular_price', 10, 2 ); | |
add_filter( 'woocommerce_product_variation_get_price', 'wcfad_get_regular_price', 10, 2 ); | |
} | |
if( ! empty( $cart_item['product_extras']['use_calc_set_price'] ) && isset( $cart_item['product_extras']['price_with_extras'] ) ) { | |
// added in 1.8.9 so that the discounted calc price is not overwritten when totals are calculated | |
remove_action( 'woocommerce_before_calculate_totals', 'pewc_wc_calculate_total', 10, 1 ); | |
// Ensure the price is set to the calculated price in the cart | |
// Commented out in 1.9.2 because after removing the actions, products with no add-ons lose their user-role pricing | |
//remove_filter( 'woocommerce_product_get_price', 'wcfad_get_regular_price', 10, 2 ); | |
//remove_filter( 'woocommerce_product_variation_get_price', 'wcfad_get_regular_price', 10, 2 ); | |
// Since 1.9.2, this is used in wcfad_get_regular_price() | |
$cart_item['data']->update_meta_data( 'wcfad_has_aou', 'yes' ); | |
$price = $cart_item['product_extras']['price_with_extras']; | |
} | |
if( ! empty( $cart_item['product_extras']['use_calc_set_price'] ) ) { | |
// Ensure the price is set to the calculated price in the cart | |
// Commented out on 1.8.9 because this puts back the original price and not the calculated price | |
/*remove_filter( 'woocommerce_product_get_price', 'wcfad_get_regular_price', 10, 2 ); | |
remove_filter( 'woocommerce_product_variation_get_price', 'wcfad_get_regular_price', 10, 2 ); | |
$price = $cart_item['data']->get_price();*/ | |
} | |
// Check for role-based price - the normal filters for this are removed above | |
$price = wcfad_get_regular_price( $price, $cart_item['data'] ); | |
$best_price = floatval( $price ); | |
// Add the add-on fields back on to the discount product price | |
$best_price += floatval( $add_ons ); | |
// Set the product price to the best price set according to user role | |
$cart_item['data']->set_price( $best_price ); | |
// Reset all items back to original price before we validate each rule | |
if( ! $add_ons && empty( $cart_item['product_extras']['use_calc_set_price'] ) && $cart_item['data']->get_meta( 'wcfad_original_price' ) ) { | |
// Set each product back to its original price | |
$original_price = $cart_item['data']->get_meta( 'wcfad_original_price' ); | |
// Reset all items back to original price | |
// Do we need to set this to the min of $original_price and $best_price? | |
// 2.0.3, added wcfad_set_min_price_before_rule_validation filter so that prices with user-role pricing fees can be displayed correctly in the cart. Issue only happens with AOU | |
if ( apply_filters( 'wcfad_set_min_price_before_rule_validation', false ) ) { | |
$cart_item['data']->set_price( min( $best_price, $original_price ) ); | |
} | |
$cart_item['data']->update_meta_data( 'wcfad_rule_set', 0 ); | |
$cart_item['data']->update_meta_data( 'wcfad_get_qualifies', 0 ); | |
$cart_item['data']->update_meta_data( 'wcfad_original_price', 0 ); | |
$cart_item['data']->update_meta_data( 'wcfad_original_total', 0 ); | |
$cart_item['data']->update_meta_data( 'wcfad_free_items', 0 ); | |
$cart_item['data']->update_meta_data( 'wcfad_paid_items', 0 ); | |
$cart_item['data']->update_meta_data( 'wcfad_discounted_total', 0 ); | |
$cart_item['data']->update_meta_data( 'wcfad_discounted_price', 0 ); | |
} else { | |
// $check_price = $cart_item['data']->get_price(); | |
} | |
// $price = $value['data']->get_price(); | |
$quantity = $cart_item['quantity']; | |
$cart_total += ( $best_price * $quantity ); | |
$cart_total_before_discount += ( $best_price * $quantity ) ; | |
} | |
// Create an array of notices | |
// $rule_id = array( $product_id => $quantity ); | |
$notices = array(); | |
if( wcfad_is_dynamic_pricing_enabled() == 'yes' ) { | |
$user_roles = wcfad_get_current_user_roles(); | |
foreach( $rules as $rule ) { | |
// For 2.0, need to get the rule formatted as in previous versions | |
$rule = wcfad_get_dynamic_pricing_rule_by_id( $rule ); | |
$rule_id = ! empty( $rule['rule_id'] ) ? $rule['rule_id'] : false; | |
$validation_function = ''; | |
// Iterate through each rule | |
if( ! empty( $rule['rule'] ) && wcfad_is_rule_active( $rule ) ) { | |
// 2.0.3. Check for roles | |
$roles = isset( $rule['roles'] ) ? $rule['roles'] : array(); | |
if( $roles ) { | |
if( ! is_user_logged_in() ) { | |
// If there's a role defined for this and the user is logged out, then the rule can't apply | |
continue; | |
} | |
$has_role = wcfad_is_user_role_included( $user_roles, $rule['roles'] ); | |
if( empty( $has_role ) ) { | |
continue; | |
} | |
} | |
$rule_type = $rule['rule']; | |
if( $rule_type == 'order' ) { | |
$order_total_rule = $rule; | |
} else { | |
$validation_function = 'wcfad_validate_rule_' . $rule_type; | |
if( $validation_function && function_exists( $validation_function ) ) { | |
$notices[$rule_id] = call_user_func( $validation_function, $rule, $cart, $rule_id ); | |
} | |
} | |
} | |
} | |
// 2.0.4, added ! defined( 'DOING_AJAX' ) to avoid duplicate notices | |
if( $notices && ! defined( 'DOING_AJAX' ) ) { | |
wc_clear_notices(); | |
foreach( $notices as $rule_id=>$notice ) { | |
if( empty( $notices[$rule_id] ) ) { | |
continue; | |
} | |
$rule = wcfad_get_dynamic_pricing_rule_by_id( $rule_id ); | |
$count_by = ! empty( $rule['count_by'] ) ? $rule['count_by'] : false; | |
if( $count_by == 'all' ) { | |
// Get the combined amounts/quantities for this cart-wide rule | |
$combined_total = 0; // Could be quantity or value | |
if( $notice ) { | |
foreach( $notice as $value ) { | |
$combined_total += $value; | |
} | |
} | |
// Because this is cart-wide, the notices function will get the product IDs from the rule - we just need to pass the combined total | |
wcfad_do_notice( $rule, array( '1' => $combined_total ) ); | |
} else { | |
// Do each line item in this rule individually | |
if( $notice ) { | |
foreach( $notice as $value ) { | |
wcfad_do_notice( $rule, $notice ); | |
} | |
} | |
} | |
} | |
} | |
// Check if we've got an order total rule | |
if( $order_total_rule && did_action( 'woocommerce_calculate_totals' ) < 2 ) { | |
$tier = wcfad_get_matching_tier( $order_total_rule, $cart, $cart_total ); | |
$adjustment = ! empty( $tier['type'] ) ? $tier['type'] : ''; | |
$amount = ! empty( $tier['amount'] ) ? $tier['amount'] : ''; | |
$label = ! empty( $tier['label'] ) ? $tier['label'] : ''; | |
if( strpos( $adjustment, 'percentage' ) > -1 && $amount ) { | |
// Adjust each line item | |
foreach( $cart_obj->get_cart() as $hash=>$value ) { | |
$original_price = $adjusted_price = $value['data']->get_price(); | |
if( $adjustment == 'percentage-discount' ) { | |
if ($original_price <= 0) | |
continue; // only do this for positive prices | |
$adjusted_price = $original_price - ( $original_price * $amount/100 ); | |
$value['data']->set_price( $adjusted_price ); | |
} else if( $adjustment == 'percentage-fee' ) { | |
$adjusted_price = $original_price + ( $original_price * $amount/100 ); | |
$value['data']->set_price( $adjusted_price ); | |
} | |
// we do the following in case there are no other adjustments yet, so there's an indication that there's a discount | |
// but if there's already an adjustment, it takes the adjusted price and adjusts it again | |
$value['data']->update_meta_data( 'wcfad_adjusted_items', $value['quantity'] ); | |
$value['data']->update_meta_data( 'wcfad_original_price', $original_price ); | |
$value['data']->update_meta_data( 'wcfad_original_total', $original_price * $value['quantity'] ); | |
$value['data']->update_meta_data( 'wcfad_adjusted_price', $adjusted_price ); | |
$value['data']->update_meta_data( 'wcfad_line_total', $adjusted_price * $value['quantity'] ); | |
// if there was an adjustment already, get the previous label, then add the order total label | |
$wcfad_label = $value['data']->get_meta( 'wcfad_label' ); | |
if ( $wcfad_label != '' ) | |
$wcfad_label .= ', '; | |
if ( $label != '' ) { | |
// use tier label if available | |
$value['data']->update_meta_data( 'wcfad_label', $wcfad_label . $label ); | |
} | |
else if ( $order_total_rule['label'] != '' ) { | |
// use rule label if tier label is not available | |
$value['data']->update_meta_data( 'wcfad_label', $wcfad_label . $order_total_rule['label'] ); | |
} | |
} | |
} | |
if( wcfad_is_order_notice_active() == 'yes' ) { | |
// Let the user know what they need to add to qualify for the next best tier | |
if( $order_total_rule['tiers'] ) { | |
wcfad_do_order_notice( $order_total_rule, $cart_total, $cart_total_before_discount ); | |
} | |
} | |
} | |
} | |
} | |
public function is_enable() { | |
return function_exists('wcfad_before_calculate_totals'); | |
} | |
} | |
new WCFAD_Dynamic_Pricing(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment