Apply a coupon programmatically in Woocommerce
First, create a discount coupon (via http://docs.woothemes.com/document/create-a-coupon-programatically/):
$coupon_code = 'UNIQUECODE'; // Code - perhaps generate this from the user ID + the order ID$amount = '10'; // Amount$discount_type = 'percent'; // Type: fixed_cart, percent, fixed_product, percent_product$coupon = array( 'post_title' => $coupon_code, 'post_content' => '', 'post_status' => 'publish', 'post_author' => 1, 'post_type' => 'shop_coupon'); $new_coupon_id = wp_insert_post( $coupon );// Add metaupdate_post_meta( $new_coupon_id, 'discount_type', $discount_type );update_post_meta( $new_coupon_id, 'coupon_amount', $amount );update_post_meta( $new_coupon_id, 'individual_use', 'no' );update_post_meta( $new_coupon_id, 'product_ids', '' );update_post_meta( $new_coupon_id, 'exclude_product_ids', '' );update_post_meta( $new_coupon_id, 'usage_limit', '1' );update_post_meta( $new_coupon_id, 'expiry_date', '' );update_post_meta( $new_coupon_id, 'apply_before_tax', 'yes' );update_post_meta( $new_coupon_id, 'free_shipping', 'no' );
Then apply that coupon to your order:
if (!$woocommerce->cart->add_discount( sanitize_text_field( $coupon_code ))) $woocommerce->show_messages();
That last function returns a BOOL value: TRUE if the discount was successful, FALSE if it fails for any one of a variety of reasons.
I used this solution, but it contains a bug as the OP wrote it. If the user skips previewing the cart and goes straight to the checkout form, it does not apply the coupon. Here was my solution.
// Independence day 2013 coupon auto add// Add coupon when user views cart before checkout (shipping calculation page).add_action('woocommerce_before_cart_table', 'add_independence_day_2013_coupon_automatically');// Add coupon when user views checkout page (would not be added otherwise, unless user views cart first).add_action('woocommerce_before_checkout_form', 'add_independence_day_2013_coupon_automatically');// Check if php function exists. If it doesn't, create it.if (!function_exists('add_independence_day_2013_coupon_automatically')) { function add_independence_day_2013_coupon_automatically() { global $woocommerce; $coupon_code = 'independencedaysale'; $bc_coupon_start_date = '2013-06-30 17:00:00'; $bc_coupon_end_date = '2013-07-08 06:59:59'; // Only apply coupon between 12:00am on 7/1/2013 and 11:59pm on 7/7/2013 PST. if ((time() >= strtotime($bc_coupon_start_date)) && (time() <= strtotime($bc_coupon_end_date))) { // If coupon has been already been added remove it. if ($woocommerce->cart->has_discount(sanitize_text_field($coupon_code))) { if (!$woocommerce->cart->remove_coupons(sanitize_text_field($coupon_code))) { $woocommerce->show_messages(); } } // Add coupon if (!$woocommerce->cart->add_discount(sanitize_text_field($coupon_code))) { $woocommerce->show_messages(); } else { $woocommerce->clear_messages(); $woocommerce->add_message('Independence day sale coupon (10%) automatically applied'); $woocommerce->show_messages(); } // Manually recalculate totals. If you do not do this, a refresh is required before user will see updated totals when discount is removed. $woocommerce->cart->calculate_totals(); } else { // Coupon is no longer valid, based on date. Remove it. if ($woocommerce->cart->has_discount(sanitize_text_field($coupon_code))) { if ($woocommerce->cart->remove_coupons(sanitize_text_field($coupon_code))) { $woocommerce->show_messages(); } // Manually recalculate totals. If you do not do this, a refresh is required before user will see updated totals when discount is removed. $woocommerce->cart->calculate_totals(); } } }}
2017 / 2019 since Woocommerce 3
The following compact code in one hooked function will add a discount (coupon) based on cart total weight displaying a custom message. If customer changes cart items the code will check cart items removing the discount (coupon) if the cart weight is under the defined weight.
The code:
add_action('woocommerce_before_calculate_totals', 'discount_based_on_weight_threshold');function discount_based_on_weight_threshold( $cart ) { if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return; if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return; // Your settings $coupon_code = 'onweight10'; // Coupon code $weight_threshold = 100; // Total weight threshold // Initializing variables $total_weight = $cart->get_cart_contents_weight(); $applied_coupons = $cart->get_applied_coupons(); $coupon_code = sanitize_text_field( $coupon_code ); // Applying coupon if( ! in_array($coupon_code, $applied_coupons) && $total_weight >= $weight_threshold ){ $cart->add_discount( $coupon_code ); wc_clear_notices(); wc_add_notice( sprintf( __("Your order is over %s so a %s Discount has been Applied! Your total order weight is %s.", "woocommerce"), wc_format_weight( $weight_threshold ), '10%', '<strong>' . wc_format_weight( $total_weight ) . '</strong>' ), "notice"); } // Removing coupon elseif( in_array($coupon_code, $applied_coupons) && $total_weight < $weight_threshold ){ $cart->remove_coupon( $coupon_code ); }}
Code goes in function.php file of your active child theme (or active theme). Tested and works.