Apply a Coupon Programmatically in Woocommerce

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.

Programmatically apply a coupon in WooCommerce 3.0+ for first order made by a customer

The code you are using no longer works in current WooCommere versions because it contains outdated code, shortcomings and some superfluous steps:

  • It is not necessary to use get_posts() with multiple arguments, use wc_get_customer_order_count() instead
  • $tmp_order->ID changed to $tmp_order->get_id() since WooCommerce 3
  • Your code does not take guest users into account, so they can manually apply the coupon
  • The coupon is not automatically awarded, your code assumes that the customers have apply this coupon
  • The woocommerce_after_checkout_validation hook contains 2 parameters
  • You don't need to return true\false as it's an action hook not a filter.

This updated version takes into account:

  • If the coupon been applied, but the customer is NOT logged in, coupon will be removed
  • If the coupon been applied, the customer is logged in, but NOT the first order, the coupon will be removed
  • If the coupon been applied, the customer is logged in and it is the first order, the coupon remains
  • If the coupon NOT been applied, the customer is logged in and it is the first order, the coupon is applied

So you get:

function action_woocommerce_after_checkout_validation( $data, $error ) {
// Change the name to your coupon
$new_cust_coupon_code = 'coupon1';

// Initialize
$coupon_been_applied = false;
$remove_coupon = false;

// Get applied coupons
$applied_coupons = WC()->cart->get_applied_coupons();

// Has coupon already been applied by the customer
if ( in_array( $new_cust_coupon_code, $applied_coupons ) ) {
$coupon_been_applied = true;

// Coupon has been applied, but customer is a guest user
if ( ! is_user_logged_in() ) {
$remove_coupon = true;
}
}

// Customer is logged in
if ( is_user_logged_in() ) {
// Check if the customer has bought before
$has_bought_before = wc_get_customer_order_count( get_current_user_id() ) >= 1 ? true : false;

// Coupon been applied, but customer has bought before
if ( $coupon_been_applied && $has_bought_before ) {
$remove_coupon = true;
// NOT been applied AND NOT has bought before
} elseif ( ! $coupon_been_applied && ! $has_bought_before ) {
// Apply coupon
WC()->cart->apply_coupon( $new_cust_coupon_code );
}
}

// When true
if ( $remove_coupon ) {
// Remove coupon
WC()->cart->remove_coupon( $new_cust_coupon_code );

// Show message
$error->add( 'validation', sprintf( __( 'Coupon code: "%s" is only applicable for logged in new customers. So the coupon has been removed', 'woocommerce' ), $new_cust_coupon_code ) );
}
}
add_action( 'woocommerce_after_checkout_validation', 'action_woocommerce_after_checkout_validation', 10, 2 );

Applying coupon to WooCommerce order programmatically

Here you can add the coupon by this way please change the content according to your need

add_action( 'woocommerce_before_cart', 'apply_coupons' );

function apply_coupons() {

$coupon_code = 'freeweek';

if ( WC()->cart->has_discount( $coupon_code ) ) return;

foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {

// this is your product ID
$autocoupon = array( 745 );

if( in_array( $cart_item['product_id'], $autocoupon ) ) {
WC()->cart->add_discount( $coupon_code );
wc_print_notices();
}

}

}

Create a coupon programmatically since WooCommerce 3+

Since WooCommerce 3, your code is a bit outdated as for example apply_before_tax is not used anymore. You should better use all available WC_Coupon setter methods, for coupon creation.

In the code below I just use the necessary setter methods (related to your code):

// Get an empty instance of the WC_Coupon Object
$coupon = new WC_Coupon();

// Set the necessary coupon data (since WC 3+)
$coupon->set_code( $coupon_code ); // (string)
// $coupon->set_description( $description ); // (string)
$coupon->set_discount_type( $discount_type ); // (string)
$coupon->set_amount( $coupon_amount ); // (float)
// $coupon->set_date_expires( $date_expires ); // (string|integer|null)
// $coupon->set_date_created( $date_created ); // (string|integer|null)
// $coupon->set_date_modified( $date_created ); // (string|integer|null)
// $coupon->set_usage_count( $usage_count ); // (integer)
$coupon->set_individual_use( true ); // (boolean)
// $coupon->set_product_ids( $product_ids ); // (array)
// $coupon->set_excluded_product_ids( $excl_product_ids ); // (array)
$coupon->set_usage_limit( 0 ); // (integer)
// $coupon->set_usage_limit_per_user( $usage_limit_per_user ); // (integer)
// $coupon->set_limit_usage_to_x_items( $limit_usage_to_x_items ); // (integer|null)
// $coupon->set_free_shipping( $free_shipping ); // (boolean) | default: false
// $coupon->set_product_categories( $product_categories ); // (array)
// $coupon->set_excluded_product_categories( $excl_product_categories ); // (array)
// $coupon->set_exclude_sale_items( $excl_sale_items ); // (boolean)
// $coupon->set_minimum_amount( $minimum_amount ); // (float)
// $coupon->set_maximum_amount( $maximum_amount ); // (float)
// $coupon->set_email_restrictions( $email_restrictions ); // (array)
// $coupon->set_used_by( $used_by ); // (array)
// $coupon->set_virtual( $is_virtual ); // (array)

// Create, publish and save coupon (data)
$coupon->save();

Now to update coupons you can use the following hooked function with any setter method like:

add_action( 'woocommerce_coupon_options_save', 'action_coupon_options_save', 10, 2 );
function action_coupon_options_save( $post_id, $coupon ) {
$coupon->set_usage_limit( 0 );
$coupon->save();
}

Code goes in functions.php file of the active child theme (or active theme).

Applying programmatically a coupon to an Order in WooCommerce3

OK, so I played about a little longer and it looks like in V3 things are a little more manual.

Adding a WC_Order_Item_Coupon item to a woo order does simply that. It adds the coupon object to the order object. No calculations are made and the product line items remain unchanged. You have to iterate over the product items manually and apply the coupon yourself by calculating the line item totals and subtotals. calculate_totals() then does as expected.

// Create the coupon
global $woocommerce;
$coupon = new WC_Coupon($coupon_code);

// Get the coupon discount amount (My coupon is a fixed value off)
$discount_total = $coupon->get_amount();

// Loop through products and apply the coupon discount
foreach($order->get_items() as $order_item){
$product_id = $order_item->get_product_id();

if($this->coupon_applies_to_product($coupon, $product_id)){
$total = $order_item->get_total();
$order_item->set_subtotal($total);
$order_item->set_total($total - $discount_total);
$order_item->save();
}
}
$order->save();

I wrote a helper function to make sure the coupon applies to the product in question coupon_applies_to_product(). Strictly not needed given I'm creating the order entirely in code.. but I use it it other places so added it.

// Add the coupon to the order
$item = new WC_Order_Item_Coupon();
$item->set_props(array('code' => $coupon_code, 'discount' => $discount_total, 'discount_tax' => 0));
$order->add_item($item);
$order->save();

You now get the nicely formatted order in wp-admin with line items showing the specific discount + the coupon code etc.

Check programmatically if a coupon is valid for a user in WooCommerce

The WC_Coupon method is_valid() does everything itself, as it uses WC_Discount method is_coupon_valid() that checks if coupon is valid for the user.

So you doesn't need anything more than you have already done in your code.

Add free product when a certain coupon is applied in WooCommerce

Your current code contains some mistakes or could be optimized:

  • global $woocommerce can be replaced by WC()
  • $woocommerce->cart->get_applied_coupons() is not necessary, as the coupon that have been applied is passed to the callback function.

Instead, use the last available argument in WC_Cart::add_to_cart() method, which will allow you to add any custom cart item data. Then you will be able to get that data easily from the cart object.

So you get:

function action_woocommerce_applied_coupon( $coupon_code ) {
// Settings
$product_id = 131468;
$quantity = 1;
$free_price = 0;
$coupon_codes = array( 'coupon1', 'mybday' );

// Compare
if ( in_array( $coupon_code, $coupon_codes ) ) {
// Add product to cart
WC()->cart->add_to_cart( $product_id, $quantity, 0, array(), array( 'free_price' => $free_price ) );
}
}
add_action( 'woocommerce_applied_coupon', 'action_woocommerce_applied_coupon', 10, 1 );

// Set free price from custom cart item data
function action_woocommerce_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;

if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return;

// Loop through cart contents
foreach ( $cart->get_cart_contents() as $cart_item ) {
if ( isset( $cart_item['free_price'] ) ) {
$cart_item['data']->set_price( $cart_item['free_price'] );
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );

Note: in addition to using woocommerce_applied_coupon, you must also use woocommerce_removed_coupon for when the coupon is removed, the product is removed as well

function action_woocommerce_removed_coupon( $coupon_code ) {
// Settings
$product_id = 131468;
$coupon_codes = array( 'coupon1', 'mybday' );

// Compare
if ( in_array( $coupon_code, $coupon_codes ) ) {
// Loop through cart contents
foreach ( WC()->cart->get_cart_contents() as $cart_item_key => $cart_item ) {
// When product in cart
if ( $cart_item['product_id'] == $product_id ) {
// Remove cart item
WC()->cart->remove_cart_item( $cart_item_key );
break;
}
}
}
}
add_action( 'woocommerce_removed_coupon', 'action_woocommerce_removed_coupon', 10, 1 );

woocommerce apply coupon programmatically bug

Based on your explanation, it sounds like you should be using the woocommerce_add_to_cart hook, which is run when a product is successfully added to the cart. I also don't think you should be using is_admin(), since that just checks if you're on an admin page...not if the current user is an admin.

I would do something like the following:

add_action( 'woocommerce_add_to_cart', 'apply_matched_coupons' );

function apply_matched_coupons() {
// If the current user is a shop admin
if ( current_user_can( 'manage_woocommerce' ) ) return;
// If the user is on the cart or checkout page
if ( is_cart() || is_checkout() ) return;

$coupon_code = 'somecodehere';

if ( WC()->cart->has_discount( $coupon_code ) ) return;

WC()->cart->add_discount( $coupon_code );
}


Related Topics



Leave a reply



Submit