Woocommerce: How to Programmatically Create a Coupon

The Problem

Although WooCommerce provides an admin interface to add coupons, sometimes we might want to create a coupon on the fly in code rather than adding one via the admin interface.

In this article we’ll look at how to create WooCommerce coupon programmatically, this will include a reasonably in-depth look at the various options available to us when creating a coupon.

Learn How To Change Your Store Without Hiring a Coder
Click here to enroll in our Free WooCommerce Coding Course

We’ll also look at some code that applies a programmatically created coupon to a user’s basket.

Creating a Coupon Programmatically

The function below will create a coupon

function generate_coupon($coupon_code) {

    $coupon = new WC_Coupon();

    $coupon->set_code($coupon_code);

    //the coupon discount type can be 'fixed_cart', 'percent' or 'fixed_product', defaults to 'fixed_cart'
    $coupon->set_discount_type('fixed_cart');

    //the discount amount, defaults to zero
    $coupon->set_amount(20);

    //the coupon's expiration date defaults to null
    $coupon->set_date_expires(null);

    //determines if the coupon can only be used by an individual, defaults to false
    $coupon->set_individual_use(false);

    //the individual prodcuts that the disciunt will apply to, default to an empty array
    $coupon->set_product_ids(array());

    //the individual products that are excluded from the discount, default to an empty array
    $coupon->set_excluded_product_ids(array());

    //the times the coupon can be used, defaults to zero
    $coupon->set_usage_limit(0);

    //the times the coupon can be used per user, defaults to zero
    $coupon->set_usage_limit_per_user(0);

    //whether the coupon awards free shipping, defaults to false
    $coupon->set_free_shipping(false);

    //the product categories included in the promotion, defaults to an empty array
    $coupon->set_product_categories(array());

    //the product categories excluded from the promotion, defaults to an empty array
    $coupon->set_excluded_product_categories(array());

    //whether sale items are excluded from the coupon, defaults to false
    $coupon->set_exclude_sale_items(false);

    //the minimum amount of spend required to make the coupon active, defaults to an empty string
    $coupon->set_minimum_amount('');

    //the maximum amount of spend required to make the coupon active, defaults to an empty string
    $coupon->set_maximum_amount('');

    //a list of email addresses, the coupon will only be applied if the customer is linked to one of the listed emails, defaults to an empty array
    $coupon->set_email_restrictions(array());

    //save the coupon
    $coupon->save();

    return $coupon_code;
}

You’ll notice that I’ve included a lot of calls to “set_” functions, and in many cases, I’ve passed the default value to the function so the call has no real effect, I’ve only added the calls to illustrate what fields you can set if you need to.

You could create a coupon using the much shorter function below

function generate_coupon($coupon_code) {

    $coupon = new WC_Coupon();

    $coupon->set_code($coupon_code);

    //the coupon discount type can be 'fixed_cart', 'percent' or 'fixed_product', defaults to 'fixed_cart'
    $coupon->set_discount_type('fixed_cart');

    //the discount amount, defaults to zero
    $coupon->set_amount(20);

    //save the coupon
    $coupon->save();

    return $coupon_code;
}

and then just add in any “set_” functions that you need to use.

Adding the Programmatically Created Coupon to a User’s Basket

Here’s the code

add_action('woocommerce_before_calculate_totals', 'hwn_add_programmatically_created_coupon_to_basket');
function hwn_add_programmatically_created_coupon_to_basket( $cart ) {
   if ( is_admin() && ! defined( 'DOING_AJAX' ) )
      return;

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

   $coupon_code  = 'hwn_generated_coupon';

   if (!coupon_exists($coupon_code)) {
      generate_coupon($coupon_code);
   }

   $applied_coupons  = $cart->get_applied_coupons();

   if( ! in_array($coupon_code, $applied_coupons) && cart_contains_items('beanie' )){
      $cart->add_discount( $coupon_code );
      wc_clear_notices();
      wc_add_notice(__("Your order contains a beanie so a discount has been applied!", "woocommerce"), "notice");
   }
   elseif( in_array($coupon_code, $applied_coupons) && !cart_contains_items('beanie' )){
      $cart->remove_coupon( $coupon_code );
   }
}

In this code, we create a function and hook it into the “woocommerce_before_calculate_totals” action. Let’s take a look at what the function does.

  • We firstly add some code to exit the function if it has been called via the admin area or if an AJAX action is taking place
  • We then ensure that the “woocommerce_before_calculate_totals” action has been called no more than two times previously using the WordPress did_action function
  • Now we create a name for our coupon and store it in the $coupon_code variable
  • We now need to check if our coupon has already been created, wed do this using a custom function named  coupon_exists we’ll take a look at this function in more detail shortly.
  • If a coupon with the same name as our $coupon_code variable then we don’t create a new coupon, if there is no coupon already in distance then we call the generate_coupon  we defined earlier. If your logic is different, e.g. you need to create a coupon for every customer, then you could change this logic to suit your needs
  • Now we have a coupon ready to use we get the coupons already applied to the user’s basket, we do this by using the WooCommerce built-in $cart->get_applied_coupons() function, we write this information to an array named $applied_coupon
  • We now exercise some logic to see if we should apply the coupon to the basket, when you’re implementing the code in your own stores you may just want to apply the coupon to every basket or set-up the qualifying logic in the coupon itself, but for this example, I’ve introduced some if logic just to show what happens when the coupon is or isn’t applied to the basket.
  • In order to provide the if logic I’ve added a custom function named cart_contains_items that returns true if a product is in the basket, specifically we fire the coupon add logic if the basket contains a product with a product slug of “beanie”.
  • If the cart does include a beanie then we call the $cart->add_discount( $coupon_code ) to add the coupon to the basket, we then clear the notices and add a new notice informing the customer that the discount has been applied

  • Finally, the elseif logic at the end of the function handles the case where a user doesn’t have a beanie in their basket, here we call the $cart->remove_coupon( $coupon_code )   to remove the coupon from the user’s basket. This code deals with the scenario where the user has had a beanie in their basket but has removed it, calling remove_coupon  ensures that a user is no longer awarded the coupon after the Beanie had been removed.

The coupon_exists and cart_contains_items

When we looked at the code we mentioned a couple of custom functions that we used, we’ll take a look at these functions now. We’ll start with the  coupon_exists function

function coupon_exists($coupon_code) {
    global $wpdb;

    $sql = $wpdb->prepare( "SELECT post_name FROM $wpdb->posts WHERE post_type = 'shop_coupon' AND post_name = '%s'", $coupon_code );

    $coupon_codes = $wpdb->get_results($sql);

    if (count($coupon_codes)> 0) {
        return true;
    }
    else {
        return false;
    }
}

In this function, we use the $wpdb global object to query the WordPress database to see if we already have a coupon code with the coupon code we wish to use.

Let’s step through the code

  • We firstly create a SQL statement that queries the databases to see if it can find a record in the posts table with a post_type of “shop_coupon” and the name that we supply.
  • Note how we pass the SQL statement to the  $wpdb->prepare function to protect ourselves from a SQL injection attack.
  • We then use the $wpdb->get_results function to return an array of the results returned by the SQL query
  • If the array is empty then we return false (indicating the coupon does not already exist), if the array contains any results then we return true

We can now use this function to determine if a coupon exists, and only create a new coupon if we do not find one in the database.

Finally, let’s take a look at the cart_contains_items function

function cart_contains_items( $search_products ) {
    $searching_for_single_product = !is_array($search_products);

    if ( ! WC()->cart->is_empty() ) {
        // Loop though cart items
        foreach(WC()->cart->get_cart() as $cart_item ) {
            // Handling also variable products and their products variations
            $products_ids_array[] = $cart_item['product_id'];
            $products_ids_array[] = $cart_item['variation_id'];
            $products_ids_array[] = $cart_item['data']->get_slug();
        }
    }

    if ($searching_for_single_product) {
        return in_array($search_products, $products_ids_array);
    }
    else {
        return !array_diff($search_products, $products_ids_array);
    }
}

I’m not going to go through this line by line as I’m planning to write a post in the near future that will look at functions that calculate how many products of a certain type are in a user’s basket.

I still wanted to put the code in this article though so you could see how it works.

Conclusion

There are many different options available when creating a coupon in WooCommerce and it is possible to specify all of these options when creating a coupon programmatically.

We also looked at how to apply a coupon to a user’s basket after creating it programmatically.

Learn the basics of WooCommerce Development, in a friendly and accessible way
Click here for more details about the "Learning WooCommerce Development By Example" book

If you have questions or queries about the code then please don’t hesitate to let me know in the comments.


Warning: Undefined array key "format" in H:\root\home\bravenewworld101-001\www\hardworkingnerd\wp-content\plugins\convertkit\includes\class-convertkit-resource-forms.php on line 330

Leave a Comment