--- title: "Stop WooCommerce coupons from selling below product cost: a plugin you can build today" description: Coupon stacking can push orders below cost on tight-margin products. Here is a small plugin that enforces a per-product cost floor at checkout. source: https://steem.dev/blog/woocommerce-block-coupons-below-cost published: 2026-09-16 updated: 2026-09-16 alternative_to: Advanced Coupons site: Steem — AI WordPress plugin generator --- # Stop WooCommerce coupons from selling below product cost: a plugin you can build today WooCommerce's coupon system lets you restrict combinations, exclude products, and cap discount amounts. What it cannot do is look at a product's actual cost and refuse a combination that would make the sale unprofitable. A 30% sitewide coupon applied on top of a 20% sale on a product with a 40% markup leaves almost nothing. WooCommerce does not know that. The usual workaround is adding exclusion lists to each coupon. That keeps working until your catalogue changes, and then the lists are stale, and someone places an order on a tight-margin product with two discounts stacked. Maintaining per-coupon exclusions is work that multiplies every time you add a discount code or a new product line. The plugin here adds a cost field to each product. At checkout, if any item would sell below that cost after all applied coupons are factored in, the coupons are removed and the customer sees why. No exclusion lists to maintain. The rule is global and applies to every coupon automatically. ## What one build covers - Product cost field in the pricing tab for simple and variable products - Per-variation cost entry for variable products - Checkout validation that removes all coupons if any item's effective unit price falls below its stored cost - Cart notice naming the specific product that triggered the removal ## What it does not cover - Shipping and gateway fees are not included in the margin calculation - Products without a cost entered are not protected until a cost is added - Immediate coupon validation on the cart page before the customer reaches checkout - Removing only the offending coupon while preserving others that would not cause a violation - Automatic sync from external cost sources such as supplier feeds or accounting software ## The prompt ```text Build a WordPress plugin called WP Margin Guard. Plugin Name: WP Margin Guard. Version: 1.0.0. Description: Block WooCommerce coupons that would discount any item below its entered product cost. Wrap all code in a class WP_Margin_Guard with a public init() method; instantiate the class and call init() on plugins_loaded. Product cost field: hook woocommerce_product_options_pricing. Call woocommerce_wp_text_input with array: id '_product_cost', label 'Product cost', description 'Coupons are removed at checkout if this item would sell below this price after discounts. Leave blank to skip validation for this product.', type 'number', custom_attributes array('min'=>'0','step'=>'0.01'). Hook woocommerce_process_product_meta with $post_id argument. Read $_POST['_product_cost'], sanitise with wc_format_decimal(). If the result is a non-empty string, update_post_meta($post_id,'_product_cost',$value); otherwise delete_post_meta($post_id,'_product_cost'). Variation cost field: hook woocommerce_variation_options_pricing with three arguments ($loop, $variation_data, $variation). After the variation sale price, output a paragraph with a label 'Variation cost' and a number input: name 'variable_cost_price[{$loop}]', value esc_attr(get_post_meta($variation->ID,'_product_cost',true)), class 'wc_input_price', min '0', step '0.01'. Hook woocommerce_save_product_variation with two arguments ($variation_id, $i). If isset($_POST['variable_cost_price'][$i]), sanitise with wc_format_decimal(), and if the result is a non-empty numeric string call update_post_meta($variation_id,'_product_cost',$value); else delete_post_meta($variation_id,'_product_cost'). Cart validation: hook woocommerce_check_cart_items. If WC()->cart->get_applied_coupons() is empty, return. Loop through WC()->cart->get_cart() as $cart_item_key => $item. For each item: $cost_id = !empty($item['variation_id']) ? $item['variation_id'] : $item['product_id']; $cost = get_post_meta($cost_id,'_product_cost',true). If empty($cost) or !is_numeric($cost), continue to next item. $effective_price = floatval($item['line_total']) / max(1, $item['quantity']). If $effective_price < floatval($cost): $name = esc_html($item['data']->get_name()); foreach WC()->cart->get_applied_coupons() as $code call WC()->cart->remove_coupon($code); wc_add_notice(sprintf('Your coupons have been removed. %s would sell below its cost price after the applied discounts. You can re-apply a coupon that keeps all items above cost.',$name),'notice'); return. No settings page, no stylesheet, no script. ``` ## What WooCommerce coupon controls already do Three native settings address stacking directly. "Individual use only" prevents any other coupon being applied in the same order. "Cannot be used with" lets you name specific coupon codes that cannot be combined with this one. "Exclude products" and "Exclude categories" prevent a coupon from applying to particular items. Used carefully, these cover many cases. The problem is maintenance: each coupon carries its own exclusion list, and when your catalogue changes those lists need to change with it. A store running welcome popups, affiliate codes, and seasonal sales simultaneously ends up with a matrix of per-coupon rules that nobody fully understands. Advanced Coupons and Flycart Discount Rules both extend this model. You can build condition groups, set maximum discount amounts, and restrict stacking by coupon type. These are the right answer if your stacking problem is about coupon combinations. They are not the right answer if your problem is that the store does not know your cost. ## How the plugin enforces the margin floor The plugin adds a "Product cost" number field to the product pricing tab in the WordPress admin, positioned alongside the regular and sale price fields. For variable products, each variation gets its own cost field. Leave the field blank and the product behaves exactly as before. Enter a number and that number becomes the checkout floor. At checkout, the plugin divides each cart item's after-discount line total by its quantity to get the effective unit price. If that price is below the stored cost for any item, all applied coupons are removed from the cart. The customer sees a notice naming the specific product that triggered the removal and explaining that their coupons have been taken off. The coupon codes remain visible on the cart page so the customer can read them. The validation fires during WooCommerce's checkout verification step. A customer can add a coupon to the cart before proceeding without seeing an error immediately: the check runs when they try to pay, not on the cart page. If you want immediate feedback when a coupon is typed in, the follow-up prompt in the next section adds the same check to the cart update step. ## What the cost floor does not cover Shipping costs and payment gateway fees are not part of the calculation. The plugin compares effective item price to product cost only. A sale at exactly cost still loses money once the payment processor takes its fee and the courier takes the rest. If those costs matter to your margin floor, enter a cost that is higher than your actual purchase price to build in a buffer. Products without a cost field filled in are not validated. The plugin only enforces the floor on items where you have explicitly entered a cost. Products catalogued before the plugin was installed are unprotected until you add a cost value to each one. When a violation is detected, all coupons in the cart are removed, not only the one that caused the problem. If a customer has a valid 5% loyalty code and a problematic 60% flash sale code stacked, both are removed. The follow-up message below produces a version that removes only the offending coupon while preserving others. ## When to use a coupon rules plugin instead If your stacking problem is about preventing specific coupon types from combining, rather than enforcing a cost floor, Advanced Coupons or Flycart Discount Rules are better fits. You can build logic like "no percentage coupon can be combined with another percentage coupon" or "maximum total discount of 20 euros per order" without storing any cost data at all. If your cost data already lives somewhere else, such as a supplier feed, a WMS, or accounting software, the plugin's manual cost entry will drift as costs change. The right long-term answer in that case is a sync from your source of truth into the _product_cost meta key, rather than relying on someone remembering to update it in the product admin. Paste the prompt below into Steem to build the plugin. To extend it so that only the offending coupon is removed rather than all coupons, the follow-up is: 'Modify WP Margin Guard so that when a below-cost item is detected, the plugin identifies which applied coupon caused the violation and removes only that one. Test by iterating through WC()->cart->get_applied_coupons() in reverse order, temporarily removing each and recalculating line totals, until the violation clears. Restore coupons that are not responsible.' ## Questions ### What happens if I have not entered a cost for a product? The plugin only validates items where a cost has been entered. If the cost field is blank, the product behaves exactly as before. You can roll out cost data gradually and the protection activates product by product as you fill in the fields. ### Does it work with variable products? Yes. Each variation has its own cost field in the variation pricing section. If a variation has no cost entered, it is not validated, even if other variations in the same product do have costs set. ### My customer had a valid loyalty coupon and an invalid flash sale coupon. Both were removed. Is that intentional? Yes, the initial build removes all coupons when any violation is found. This is the conservative default. The follow-up prompt in the guide produces a version that removes only the offending coupon while keeping valid ones active. ### Does this catch coupons applied automatically via URL? Yes. The validation runs during WooCommerce's checkout verification step, which fires regardless of how a coupon was added to the cart. URL-applied coupons and manually entered codes go through the same check. ### My supplier costs change regularly. Will the plugin stay accurate? The plugin stores cost as product meta. Update the cost field in the product admin and the new floor takes effect on the next order. If costs change frequently, consider writing a small script that syncs from your purchasing system into the _product_cost meta key rather than updating manually each time. --- Source: https://steem.dev/blog/woocommerce-block-coupons-below-cost Build this plugin: https://steem.dev/app