do it simply - Help and Advice

How to create recurring bookings with Woocommerce

If you are looking for a Recurring Plugin it’s here.

The plugin is available here for $39.  View the demos too.

Lot’s of people love what Woocommerce Bookings can do, and many buy into the pretty expensive plugin before they realise some of limitations. I was one of them.

I had to learn pretty quickly that 2 of the most important features that my client wanted weren’t available out of the box with Bookings. The first was allowing customers to select the date first before seeing what they are able to book. You can see my write up to that selecting the date first, here.  The other big one is…

how to create a recurring booking

Woocommerce Bookings allows you to manually create a booking and this is where you start to create a recurring one.  From their website here is the basic code to create a booking programmically.

The create_wc_booking function 
The function takes the following arguments:

create_wc_booking( $product_id, $new_booking_data = array(), $status = 'confirmed', $exact = false )

Product ID: The id of the bookable product which you are creating a new booking for.
New Booking data: Array of booking data. See “booking data array” below.
Status: Status of the new booking. Valid statuses include: ‘unpaid’, ‘pending’, ‘confirmed’, ‘cancelled’, ‘complete’
Exact: true or false – If false, the function will look for the next available slot after your start date, if the date you tried to book is unavailable.

Booking Data Array 
The $new_booking_data argument is passed to your new booking. By default, it consists of the following:

$defaults = array(
		'product_id'  => $product_id, // Booking ID
		'start_date'  => '',
		'end_date'    => '',
		'resource_id' => '',

Now for many, this may just be enough to get you on your way, but some of us need a little more.  Below I will map out what I have done for my client.

I have modified the plugin to create 5, 6, or 7 week recurring bookings, this depends on the time of year.  These make up a course, I’m assuming same as what you are trying to do.  There is quite a lot of variability in the dates and resources for my version so I haven’t automated a huge amount yet (I am planning on it for a plugin).

    I'll send you the plugin when it's ready

    Please note: we do not share your information with anyone else. We will only use it to share helpful information regarding our plugin development.

    My other main variable is that my system allows you to have different resource capacity on each of those recurring dates.  For instance Mar 18th might have 24 places and Mar 25th may have 32.  This was a specific client request, and makes what I’ve done a little more complicated.

    Here are the basics.

    1. Create a product that describes the course/classes (as I’ve done here).  No dates or anything are needed in the product itself, you will do this manually.  I suppose there is no reason you can’t supply a calendar here and use the selected date as the start of your bookings.  It wasn’t necessary for my version.

    2. Create a bespoke plugin to work along side Woo Bookings. – You can do this in function.php but it can become really messy really soon.

    3. Create a function to do the work that hooks into  the action ‘woocommerce_payment_complete’

    4. The function will loop through the items in your completed order, if the item matches the course (product) id, then you will manually create your recurring bookings.

    5.  Creating the manually bookings needs the following parameters, the product_id you are booking against, the start date, the number of weeks, and the resource_id (if used).

    So, here is my seemingly complicated function, but if you read through it, it’s not too difficult to figure out.

    // create new booking term 5th Nov to 11th Feb 2 Terms 1 level
    function pedal_woocommerce_payment_complete_winter( $order_id ) {
    	global $post;
    	//check for term product id
    	$term_order = new WC_Order( $order_id );
    	$term_items = $term_order->get_items();
    	// get the order_item_id for bookings
    	// get order_id for items not race day (or any other product type in future)
    	while (list($var, $term_item) = each($term_items)) {    
    		// an array of included products
    		$included = array(2656);
    		if(in_array($term_item['product_id'], $included)) {  
    	        $order_item_id= $var;
    	        $sh_product_id = $term_item['product_id'];		    
    			    if($sh_product_id == 2656) {
    				    $product_variation_id = $term_item['variation_id'];
    				    //term 8 - 5 weeks
    				    if($product_variation_id ==2659) {
    					    $time_start = '1478338200';
    						$time_end ='1478345400';
    						$resource = 2662;
    						// this the booking product to schedule against
    						$b_product = 2661;			    	    
    					   // term 9 - 6 wks
    					   elseif ($product_variation_id == 2660) {
    						    $time_start = '1483781400';
    							$time_end ='1483788600';							
    							// this the booking product to schedule against							
    							$resource = 2667;	
    							$b_product = 2661;			    			     
    					   else {
    					 //create arguments array for first booking date
    					create_wc_booking($b_product, $new_booking_data = array(
    					//'product_id'  => $product_id, // Booking ID
    					'start_date'  => $time_start,
    					'end_date'    => $time_end,
    					'order_item_id' => $order_item_id,
    					'resource_id' => $resource,
    					), $status = 'confirmed', $exact = true );
    					//now create follow up booking 1 week on
    					//get latest booking post id
    					global $wpdb;
    					$get_book = $wpdb->get_var( "SELECT ID FROM $wpdb->posts WHERE post_type='wc_booking' ORDER BY ID DESC
    					LIMIT 1" );
    					//now create booking with parent id 2 times	
    					if ($product_variation_id == 2660) {		
    						$week2 = array(
    						'start_date'  => strtotime('+7 day', $time_start),
    						'end_date'    => strtotime('+7 day', $time_end),				
    						'parent_id'   => $get_book,
    						'resource_id' => $resource+1,
    						create_wc_booking($b_product, $week2, $status = 'confirmed', $exact = true );
    						$week3 = array(
    						'start_date'  => strtotime('+14 day', $time_start),
    						'end_date'    => strtotime('+14 day', $time_end),				
    						'parent_id'   => $get_book,
    						'resource_id' => $resource+2,
    						create_wc_booking($b_product, $week3, $status = 'confirmed', $exact = true );
    						$week4 = array(
    						'start_date'  => strtotime('+21 day', $time_start),
    						'end_date'    => strtotime('+21 day', $time_end),				
    						'parent_id'   => $get_book,
    						'resource_id' => $resource+3,
    						create_wc_booking($b_product, $week4, $status = 'confirmed', $exact = true );
    						$week5 = array(
    						'start_date'  => strtotime('+28 day', $time_start),
    						'end_date'    => strtotime('+28 day', $time_end),				
    						'parent_id'   => $get_book,
    						'resource_id' => $resource+4,
    						create_wc_booking(  $b_product, $week5, $status = 'confirmed', $exact = true );						
    						if ($product_variation_id == 2660) {
    							$week6 = array(
    							'start_date'  => strtotime('+35 day', $time_start),
    							'end_date'    => strtotime('+35 day', $time_end),						
    							'parent_id'   => $get_book,
    							'resource_id' => $resource+5,
    							create_wc_booking($b_product, $week6, $status = 'confirmed', $exact = true );
    				else {
    		// now check for membership purchase and create discount code
    		// an array of membership
    		$mem = array(1091);
    		if(in_array($term_item['product_id'], $mem)) {  
    			$create_code = substr($term_order->billing_last_name, 0, 3).$order_id;
    			// add the code programically to woo
    			$coupon_code = $create_code; // Code
    			 $to = array('', $term_order->billing_email);
               $subject = 'Your Membership Discount Code';
               $message = '
    			  <title>Little Fodder\'s Membership</title>
    			<h3>Hi '.$term_order->billing_first_name.' '.$term_order->billing_last_name.', thanks for the order of a Little Fodder\'s Membership</h3>
    			  <p>Your discount code for the year is '.$create_code.'  Please keep it safe.</p>
    			  <p>This code can be used in your shopping basket with every order and entitles you to 20% off.</p>
    			  <p>Thank you,</p>
    			  <p>The Pedal a Bike Away Team</p>
                    woocommerce_mail( $to, $subject, $message, $headers = "Content-Type: text/htmlrn", $attachments = "" );
    			$amount = '20'; // 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', '' );
    			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' );			
    add_action( 'woocommerce_payment_complete','pedal_woocommerce_payment_complete_winter' );

    Some things to note on my function.

    a. I am manually entering the product and resource ids because these are constantly changing for my client.  You could create (and I will eventually) an admin bit to allow you to set these in the CMS.
    b. Normally the resource id wouldn’t have to change per week you could use the same, again this is bespoke for my client.
    c. My client has variations too, sometimes this is course level, sometimes it’s course length, so I have to check for this in my function too.
    d. I have also added a check in this function to see if they have purchased a membership, if they have I add a coupon to their basket which gives them 20% off of their booking.

    As I’ve previously mentioned, more of this can be automated or looped… I’m working on it.. 🙂

    I’d like to hear what you’ve tried to do or got stuck on.  My ultimate goal is to write this into a plugin to help others.  To do this I would like more use cases.  So please send me a note.

    Update: First plugin version available.

    You can visit my Github repository for the first version of the recurring bookings plugin.  Please feedback how you get on.

      I'll send you the plugin when it's ready

      Please note: we do not share your information with anyone else. We will only use it to share helpful information regarding our plugin development.

      Good luck!


      Posted in Developers, For Business OwnersTagged , ,

      6 responses to “How to create recurring bookings with Woocommerce”

      1. beth says:

        Hi Guillermo. I have a version of it, but it’s not ready for general release. I’m hoping to work on it in April.

      2. beth says:

        Thanks Guillermo, I will send you an email. Beth

      3. Pradeep M. says:

        Hi Beth,

        Great article and the snippets! Sorry this is not directly related to the topic discussed in here but is there a way for us to create new ‘booking statuses’ for WC Bookings (not order statuses)?


        • beth says:

          I’m sorry Pradeep. I’m not sure what you are asking here. What type of statuses would you want for bookings? There are already pending, confirmed, etc. Depending on your set-up. I’m sure there is a hook to add your own specific ones.

      4. Nicholas McLean says:

        Hello! I was just brainstorming yesterday on how to use product add in’s to allow customers to automatically let bookings recur weekly!

        I’ll let you know how it goes!

      Leave a Reply

      Your email address will not be published. Required fields are marked *