Skip to content

Hooks for Discount Calculation

Filters for modifying discount calculation and price rounding.

Each rule can have a calculation_base_mode setting that determines which price the discount is calculated from:

ValueConstantDescription
current_priceCalculationBaseModes::CURRENT_PRICEDiscount from current price (after previous discounts) - default
original_priceCalculationBaseModes::ORIGINAL_PRICEDiscount always from regular price (regular_price)

Behavior Example — multiple Auto Price rules

Section titled “Behavior Example — multiple Auto Price rules”
Product: regular_price = $100
Rule 1 (Product): -20% → $80
Rule 2 (Bulk): -5%
With calculation_base_mode = 'current_price' on Rule 2:
→ 5% of $80 = $4 → final $76 (stacks on Rule 1)
With calculation_base_mode = 'original_price' on Rule 2:
→ 5% of $100 = $5 → final $95 (overrides Rule 1, recalculated from regular_price)

When multiple Auto Price rules match the same product, they are applied in priority order (lower menu_order first). Each rule’s calculation_base_mode decides whether it stacks on the running price or restarts from regular_price. The stop_processing flag halts iteration over lower-priority rules.

Product: regular_price = $100
Rule 1 (Product): -20% → price = $80
Rule 2 (BOGO): -50%
With calculation_base_mode = 'current_price':
→ 50% of 80 = $40 (discounts stack)
With calculation_base_mode = 'original_price':
→ 50% of 100 = $50 (discount from original price)
use WpifyWooDiscountRule\Services\Rules;
use WpifyWooDiscountRule\Enums\CalculationBaseModes;
// Within a rule
$calc_base_mode = $rule[ Rules::CALCULATION_BASE_MODE ] ?? CalculationBaseModes::CURRENT_PRICE;
if ( CalculationBaseModes::ORIGINAL_PRICE === $calc_base_mode ) {
// Use regular_price
$base = $product->get_regular_price();
} else {
// Use current price (may include other discounts)
$base = $product->get_price();
}

Modifying Data Before Discount Calculation

Section titled “Modifying Data Before Discount Calculation”

This filter allows you to modify data before the actual discount calculation.

apply_filters( 'wpify_woo_discounts_calculate_data', $data, $product, $rule, $range );
ParameterTypeDescription
$dataarrayData for discount calculation (original_price, calculation_base, discount_type, discount_value)
$productWC_ProductProduct the discount is applied to
$rulearrayRule settings (including calculation_base_mode)
$rangearrayCurrent discount range/tier

Adjusting Discount for a Specific Category

Section titled “Adjusting Discount for a Specific Category”
/**
* Increase discount for products in the "sale" category
*
* @param array $data Calculation data
* @param WC_Product $product Product
* @param array $rule Rule
* @param array $range Discount range
*
* @return array
*/
function boost_discount_for_sale_category( $data, $product, $rule, $range ): array {
if ( has_term( 'akce', 'product_cat', $product->get_id() ) ) {
// Increase percentage discount by 5%
if ( $data['discount_type'] === 'percentage' && isset( $data['discount_value'] ) ) {
$data['discount_value'] += 5;
}
}
return $data;
}
add_filter( 'wpify_woo_discounts_calculate_data', 'boost_discount_for_sale_category', 10, 4 );
/**
* Ensure a minimum discount of 10%
*
* @param array $data Calculation data
* @param WC_Product $product Product
* @param array $rule Rule
* @param array $range Discount range
*
* @return array
*/
function ensure_minimum_discount( $data, $product, $rule, $range ): array {
if ( $data['discount_type'] === 'percentage' ) {
$data['discount_value'] = max( 10, $data['discount_value'] ?? 0 );
}
return $data;
}
add_filter( 'wpify_woo_discounts_calculate_data', 'ensure_minimum_discount', 10, 4 );

This filter allows you to modify the result after the discount has been calculated.

apply_filters( 'wpify_woo_discounts_calculate_product_discount', $result, $product, $rule, $range );
ParameterTypeDescription
$resultarrayCalculation result (discounted_price, discount_amount, discount_percentage)
$productWC_ProductProduct
$rulearrayRule settings
$rangearrayDiscount range
/**
* Log all discount calculations
*
* @param array $result Calculation result
* @param WC_Product $product Product
* @param array $rule Rule
* @param array $range Discount range
*
* @return array
*/
function log_discount_calculation( $result, $product, $rule, $range ): array {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( sprintf(
'Discount calculated: Product #%d, Original: %s, Discounted: %s',
$product->get_id(),
$result['original_price'] ?? 'N/A',
$result['discounted_price'] ?? 'N/A'
) );
}
return $result;
}
add_filter( 'wpify_woo_discounts_calculate_product_discount', 'log_discount_calculation', 10, 4 );

This filter allows you to modify the rounding logic of the final discounted price.

apply_filters( 'wpify_woo_discounts_round_price', $rounded_price, $original_price, $rounding_mode, $rule );
ParameterTypeDescription
$rounded_pricefloatPrice after rounding (according to the configured mode)
$original_pricefloatPrice before rounding (after discount)
$rounding_modestringMode: whole, ending_99, ending_90, ending_9
$rulearrayComplete rule data
/**
* Always round down for VIP customers
*
* @param float $rounded_price Rounded price
* @param float $original_price Price before rounding
* @param string $rounding_mode Rounding mode
* @param array $rule Rule
*
* @return float
*/
function vip_rounding( $rounded_price, $original_price, $rounding_mode, $rule ): float {
if ( current_user_can( 'vip_member' ) ) {
return floor( $original_price );
}
return $rounded_price;
}
add_filter( 'wpify_woo_discounts_round_price', 'vip_rounding', 10, 4 );
/**
* Round price to multiples of 10
*
* @param float $rounded_price Rounded price
* @param float $original_price Price before rounding
* @param string $rounding_mode Rounding mode
* @param array $rule Rule
*
* @return float
*/
function round_to_tens( $rounded_price, $original_price, $rounding_mode, $rule ): float {
return round( $original_price / 10 ) * 10;
}
add_filter( 'wpify_woo_discounts_round_price', 'round_to_tens', 10, 4 );

Controlling Discount Application (Subscriptions)

Section titled “Controlling Discount Application (Subscriptions)”

Filter to control whether a discount should be applied. Primarily used for WooCommerce Subscriptions.

apply_filters( 'wpify_woo_discounts_should_apply_discount', $should_apply, $rule );
ParameterTypeDescription
$should_applyboolDefault value (true)
$rulearrayRule data including renewal_behavior
/**
* Never apply bulk discounts on renewal orders
*
* @param bool $should_apply Default value
* @param array $rule Rule
*
* @return bool
*/
function no_bulk_on_renewals( $should_apply, $rule ): bool {
if ( function_exists( 'wcs_cart_contains_renewal' ) && wcs_cart_contains_renewal() ) {
if ( ( $rule['rule_type'] ?? '' ) === 'bulk' ) {
return false;
}
}
return $should_apply;
}
add_filter( 'wpify_woo_discounts_should_apply_discount', 'no_bulk_on_renewals', 10, 2 );

Available renewal behavior values:

  • first_order_only - Discount only on the first order
  • renewals_only - Discount only on renewal orders
  • both - Discount on both (default)

This filter allows you to modify the list of target products after they have been prepared.

apply_filters( 'wpify_woo_discounts_targets_prepared', $targets, $rule );
ParameterTypeDescription
$targetsarrayArray of target products
$rulearrayRule settings
/**
* Exclude low-margin products from discount targets
*
* @param array $targets Target products
* @param array $rule Rule
*
* @return array
*/
function exclude_low_margin_products( $targets, $rule ): array {
return array_filter( $targets, function( $target ) {
$product_id = $target['product_id'] ?? 0;
$margin = get_post_meta( $product_id, '_profit_margin', true );
return $margin > 10; // Exclude products with margin below 10%
} );
}
add_filter( 'wpify_woo_discounts_targets_prepared', 'exclude_low_margin_products', 10, 2 );

This filter allows you to add custom metadata to subrules (discount tiers).

apply_filters( 'wpify_woo_discounts_subrule_metadata', $metadata, $subrule, $rule );
ParameterTypeDescription
$metadataarraySubrule metadata
$subrulearraySubrule data
$rulearrayRule settings
/**
* Add custom metadata for tracking
*
* @param array $metadata Metadata
* @param array $subrule Subrule
* @param array $rule Rule
*
* @return array
*/
function add_tracking_metadata( $metadata, $subrule, $rule ): array {
$metadata['campaign_id'] = $rule['campaign_id'] ?? '';
$metadata['applied_at'] = current_time( 'mysql' );
return $metadata;
}
add_filter( 'wpify_woo_discounts_subrule_metadata', 'add_tracking_metadata', 10, 3 );

This filter allows you to modify the maximum quantity for a discount.

apply_filters( 'wpify_woo_discounts_quantity_limit', $limit, $product, $rule );
ParameterTypeDescription
$limitintQuantity limit
$productWC_ProductProduct
$rulearrayRule settings
/**
* Limit quantity for wholesale customers
*
* @param int $limit Limit
* @param WC_Product $product Product
* @param array $rule Rule
*
* @return int
*/
function limit_wholesale_quantity( $limit, $product, $rule ): int {
if ( current_user_can( 'wholesale_customer' ) ) {
return min( $limit, 100 ); // Max 100 pcs for wholesale
}
return $limit;
}
add_filter( 'wpify_woo_discounts_quantity_limit', 'limit_wholesale_quantity', 10, 3 );

This filter allows you to modify the fee/discount application type in the cart.

apply_filters( 'wpify_woo_discounts_fee_application_type', $type, $rule );
ParameterTypeDescription
$typestringApplication type (fixed, percent)
$rulearrayRule settings
/**
* Change application type for specific rules
*
* @param string $type Application type
* @param array $rule Rule
*
* @return string
*/
function change_fee_application_type( $type, $rule ): string {
// Use percentage for rules with the high_value tag
if ( in_array( 'high_value', $rule['tags'] ?? [], true ) ) {
return 'percent';
}
return $type;
}
add_filter( 'wpify_woo_discounts_fee_application_type', 'change_fee_application_type', 10, 2 );

This filter returns the price of a concrete bundle product selection — useful for custom selectors, blocks or templates that need to display the bundle price before the products are added to the cart. The filter matches the provided products against active Bundle rules and returns the discounted total of the first matching variant.

apply_filters( 'wpify_woo_discounts_bundle_set_price', $result, $products, $context );
ParameterTypeDescription
$resultarray|nullPreviously computed payload (pass null as the initial value)
$productsWC_Product[]One product per Bundle slot; slot quantities are read from the rule
$contextarrayOptional. 'application_mode' => string|string[] limits matching to rules containing that mode (default: no filtering)

null when no Bundle variant matches the supplied products; otherwise an array with:

KeyTypeDescription
rule_idstringMatching rule identifier
variant_idstringMatching variant (filter group) identifier
regular_totalfloatSum of unit_price × slot_qty across all slots
discounted_totalfloatSum of discounted_unit_price × slot_qty across all slots
slotsarrayPer-slot breakdown: slot_id, product_id, product, slot_qty, unit_price, discounted_unit_price
htmlstringPre-rendered strikethrough HTML (wc_format_sale_price when discounted, wc_price otherwise)
/**
* Render bundle price in a custom selector template.
*
* @param WC_Product[] $selected_products Products the customer picked.
*/
function render_bundle_set_price( array $selected_products ): void {
$price = apply_filters(
'wpify_woo_discounts_bundle_set_price',
null,
$selected_products
);
if ( null === $price ) {
return; // no bundle matched
}
echo $price['html']; // or build your own markup from $price['regular_total'] / $price['discounted_total']
}

To limit matching to a specific application mode (e.g. only Auto Price bundles):

$price = apply_filters(
'wpify_woo_discounts_bundle_set_price',
null,
$selected_products,
[ 'application_mode' => 'auto_price' ]
);

You can add custom functions either in your child theme’s functions.php or using the Code Snippets plugin.