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 meta
update_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();
}
}
}
}