Autoship CloudAutoship Cloud CustomizationsTutorials

Step by Step Guide: How to Build a Meal Subscription Box Site with WordPress, WooCommerce, & Autoship Cloud

Did you know? Creating a powerful, flexible, and dynamic eCommerce Meal Subscription site like HelloFresh, Blue Apron, Daily Harvest and Freshly is easier than you think.

We’re going to walk you through the code and steps to create a site just as dynamic, if not more, as those already out there using the flexibility of WordPress, power of WooCommerce’s checkout, and automation of Autoship Cloud!

What We’re Creating

As a Cloud Based Engine for all things Scheduled, QPilot has seen a lot of different types of shipped products being turned into Subscriptions, from CBD and Hemp products, to Vitamins, Pet Food and Healthcare products. However, we get asked quite often how ( and is it possible ) to create a meal subscription site like HelloFresh, Blue Apron, Daily Harvest or Freshly. Not only is it possible but below we’ll walk you though the code, steps and pieces needed to do it, and not only that you can demo the site we’ll create together and see for your self.

Follow our tutorial and allow your customers to fill a box with tasty meals and schedule that box to be auto shipped monthly, weekly, or even daily ( Wow talk about fresh! ). We’ll be covering the following main key areas, diving into depth within each and walking through the details with the end result being your own Meal Subscription Site.


Subscription Plans & Schedules

Creating delicious food that drives customers to pay for a Weekly subscription isn’t easy, neither are the logistics to shipping those meals in an efficient and manageable way. This is where the plan / box size and schedule comes into play. We’ll need to be able to offer, assign and manage various box sizes ( i.e. Subscription Plans ), the affect they have on the cart, and the schedule the box should ship.

Customer Journey

One thing that’s evident with any Meal Subscription company currently out there is the walk through, widgetized approach to the customer journey. Here at Patterns in the Cloud we never do anything half hearted, we’ll be walking our customer through the steps needed to complete their box, from registration, plan selection, meal selection, scheduling, and eventually to checking out. It’ll not only be important to help visually walk our customer through the process but also automatically re-direct them to the appropriate step when they try to jump ahead.

Cart Requirements

This isn’t your standard eCommerce site but a Subscription Box site and one which needs to allow our customers the flexibility to select the meals they want, while at the same time limit how many meals they can add to their cart based on their selected plan, adjusting their cart contents when they change their plan, and controlling when they can checkout based on if the cart is valid.

Creating the Scheduled Order

A Subscription Box isn’t a typical eCommerce order, but a Scheduled Order. A Scheduled Order is an order that Ships repeatedly on a set schedule ( i.e. Monthly, Weekly, Daily, etc. ) and for which the customer is billed each time the order ships. The good news is that Autoship Cloud powered by QPilot come pre-built to create Scheduled Orders at checkout and offer a way for customers to manage their orders after the fact.


Did You Know?

Scheduled Orders are almost always for physical items that need to be shipped on a regular basis, ranging from Pet Products like with Petco, to Health and Beauty Products like philosophy, and even home goods like with Amazon’s Subscribe and Save. Unlike a typical subscription ( and even most Scheduled Order services ), Scheduled Orders created with Autoship Cloud powered by QPilot are similar to a cart in that they can be edited on the fly by the customer ( adding items, removing items, changing the order’s schedule, etc. ) before each scheduled shipment. The Best Part…it’s all built in with our pre-built customer centric Scheduled Order management system.


Pre-Requirements

Here are several components that you’ll need to install / setup before starting this project.


Subscription Plans & Schedules

We’re going to start our tutorial by defining the basics of our meal subscription site, and include some tools that will help us manage and use them throughout our site. In a Meal Subscription site there are two main components ( other than the actual food ), the Box Size and the Shipping Schedule.

Subscription Plans & Schedules

Managing the Customers Plan / Selected Box Size

As with all Meal Subscription services there’s a Box size, meaning a number of meals that will be included in each shipment, and a Schedule, how often will the shipment be shipped. We’re going to offer three different Plans or Box Sizes in our demo site, a 2 Meal Fresh Box, a 3 Meal Fresh Box, and our default 5 Meal Fresh Box. This means that based on the Plan the customer chooses, they must have 2, 3, or 5 Meals in their cart ( different or the same item ) before they can checkout.

Since we now have our Plan sizes specified we’ll need to store the assigned plan somewhere so that we can retrieve it when running our cart validations later on. We’re going to store our Plan value in a custom xx_fresh_box_plan User Metadata record.

Managing the Customers Plan Step 1

Retrieve Available Plans

The first function we’ll need to create is one that tells us the available plans to pick from. Create a new xx_get_user_plans() function which returns an array of available plans. We’re going to start with three plans ( through you could start with however many & sizes that fit your business ), a 2 Meal Fresh Box, 3 Meal Fresh Box, and a 5 Meal Fresh Box.

  • 2 Meal Fresh Box
  • 3 Meal Fresh Box
  • 5 Meal Fresh Box

Managing the Customers Plan Step 2

Assign a Plan to a Customer

The second function we’re going to create is the xx_add_plan_to_user() function, which I’m sure you can guess will be used to add / assign a plan to a customer. The new function should take two optional integer parameters, ( $user_id ) and ( $plan ). The first step in the function is to check if the user id was supplied and if not use the WordPress get_current_user_id() function to retrieve the id of the currently logged in user.

The second part of the function will be to update the user’s custom xx_fresh_box_plan metadata record with the supplied plan value using the WordPress update_user_meta() function.

Managing the Customers Plan Step 3

Retrieve a Customer’s Plan

The last function we’re going to create is a xx_get_user_plan() function which will return the current plan assigned to a customer. The new function should take one optional integer parameter ( $user_id ) and return an integer. Just as we did with the first function, if a user id isn’t supplied, the user id will be retrieved using the get_current_user_id() function. Then use the WordPress get_user_meta() function along with the user id to retrieve the plan assigned to the user. In this case we’re going to default the plan to the plan we prefer our customers select when they haven’t selected a plan yet, our 5 Meal Fresh Box.

NOTE While we’ve added functions to set and get the plan assigned to a user, we’ll be adding the form our customers will use to select their plan later on.

We’ve added ways to assign a plan to a customer, and retrieve the plan, but what about the cart? Our plan does equate to box size and box size determines how many meals our customers can add to their carts. We’ll get into the cart validation a bit later, however, we’re going to create a cart utility function ( while plans are fresh in our minds ) to help make our lives a bit easier going forward and to help save some keystrokes. The function pairs the xx_get_user_plan() function from above directly to the cart size.

Customer Plan Utility Function

Compare Selected Plan to Current Cart Quantity

Create a new xx_get_cart_diff() function which will take an optional $quantity parameter and returns the difference between the current cart count ( using the WC_Cart::get_cart_contents_count() method ) and the current user’s plan returned from the xx_get_user_plan() function above.

Subscription Plans & Schedules

Managing a Customers Schedule

We’ve laid out what subscription box plans will be available to our customers and the functions we’ll use to manage those. The next big piece to any meal subscription service is the Schedule. Some services don’t offer choices and ship every week, while others offer options for Weekly or Monthly. We’re going to eventually offer multiple schedule options ( who doesn’t like flexibility? ) and just like those meal subscription sites we’ll need a way to save and retrieve the schedule a customer chooses.

Before getting into the functions we’ll need to manage a schedule, we’ll need to define what a schedule is and how it’s stored. Luckily, Autoship Cloud does this for us so no need to rack our brains coming up with a complex definition.

Autoship Cloud defines a schedule as a configured frequency type ( i.e. time – Months, Weeks, Days, etc. ) and frequency ( i.e. interval – 1, 2, 12, 60 etc. ) that a Scheduled Order is repeated.

Makes sense right? Given this we’re going to store our schedule as a string as {time}-{interval}, where the time portion of the string is the frequency_type ( i.e. Months, Weeks, Days, etc. ) and the interval portion of the string is the frequency.

Managing the Customers Schedule Step 1

Assign a Schedule to a Customer

The first thing we’ll take care of is assigning a schedule to a customer. Create a new xx_update_fresh_box_schedule() function, which takes an optional integer ( $user_id ) and an optional string ( $schedule ). Just like we did with the Plan functions, the first thing we’ll do is to check if the user id was supplied and if not use the WordPress get_current_user_id() function to retrieve the id of the currently logged in user.

The second part of the function ( just like we did earlier with our plan function ) will be to update the user’s custom xx_fresh_box_schedule metadata record with the supplied schedule ( the frequency and frequeny_type string ) using the WordPress update_user_meta() function.

Managing the Customers Schedule Step 2

Retrieve a Customer’s Schedule

The second function we’re going to create is the xx_get_fresh_box_schedule() function, which will be used to retrieve a customer’s schedule. The new function should take two optional parameters, an integer ( $user_id ), and a boolean ( $components ). Once again, Just as we did with the other functions, if a user id isn’t supplied, the user id will be retrieved using the get_current_user_id() function. Then use the WordPress get_user_meta()function along with the user id to retrieve the schedule assigned to the user. The last part is where the Boolean comes into play. As we mentioned earlier the schedule is stored as a string {frequency_type}-{frequency}, though in a future step we will need to get each individual component separately so that we’ll be able to assign them to the cart items. If the optional Boolean parameter is set to true, use the PHP explode() function to return the value as an array, with two keys frequency_type and frequency.

NOTE Just like with our plan functions, while we’ve added functions to set and get the schedule for a user, we’ll be adding the form our customers will use to select their schedule later on.

The Customer Journey

As with any eCommerce site, it’s especially important to create a customer journey that makes ordering easy and conversion turnkey. There are quite a few different approaches to this and here at QPilot we’ve seen quite a few. Here are some of the current Meal Subscription sites and how they approach the customer journey…

Card image cap
  • Step 1: Select Box Size
  • Step 2: Always Weekly
  • Step 3: Register
  • Step 4: Checkout
  • Step 5: Select Meals
Card image cap
  • Step 1: Register
  • Step 2: Select Box Size
  • Step 3: Always Weekly
  • Step 4: Choose Delivery Date
  • Step 5: Select Meals
  • Step 6: Checkout
Card image cap
  • Step 1: Register
  • Step 2: Select Box Size
  • Step 3: Weekly or Monthly
  • Step 4: Select Meals
  • Step 5: Checkout
Card image cap
  • Step 1: Register
  • Step 2: Select Box Size
  • Step 3: Always Weekly
  • Step 4: Checkout
  • Step 5: Select Meals

While these journeys are similar, they do vary in a few ways, like when they capture the customer’s email address, if the customer can choose different schedules, and at what point the customer actually goes through the checkout.

Our Customer Journey

We understand the importance of being able to retarget potential customers that don’t convert so we’re going to be sure to capture our potential customers emails first. In addition, we’re going to give our customers the ability to choose between several different weekly schedules ( though you may decide logistically you only allow one schedule ). With these points in mind we’re going to use the following journey:

Card image cap
  • Step 1: Register
  • Step 2: Select Box Size
  • Step 3: Select Meals
  • Step 4: Select Schedule
  • Step 5: Checkout

Our Customer Journey

Setup Pages, Templates, & Content

Now that we have an outline of what we want our customer journey to be we’ll need to setup the pages that correspond to each step in the journey and the content that we’re going to display on each page.

Get started by creating a Welcome Parent Page, this page will be both the first and second steps in our journey ( Register & Select your Plan ). Next create three child pages to our Welcome page, a Select Your Meals, Select Your Schedule, and Order page. Creating our steps as child pages will help us keep the steps organized and will help in controlling our customers journey ( more on this later ). Now set the menu order on each page to the step in the journey:

Our Customer Journey Step 1

Register & Select Plan

The Welcome Page, the page on which our customer will register for an account and select / change their plan and will be the first step in the journey. Menu Order 1.

Our Customer Journey Step 2

Select Meals

The Welcome / Select Your Meals Child Page, the page on which our customer will select the meals for their box and the second step in our journey. Menu Order 2.

Our Customer Journey Step 3

Select Your Schedule

The Welcome / Select Your Schedule Child Page, the page on which our customer will select how often they want their meal subscription box shipped and the third step in our journey. Menu Order 3.

Our Customer Journey Step 4

Checkout

The Welcome / Checkout Child Page, the final page on our customers journey and the step in which our customer enters their shipping, billing, payment info, and from where we will create their scheduled order ( i.e. meal subscription box ). This is the fourth step in our journey. Menu Order 4.

However, before adding content to each of our pages we’re going to add a couple more helper functions that will help us to understand if the customer is on one of our steps, and to push a customer to a specific step if needed.

Our Customer Journey Utility Function

Prev, Current, and Next Step URLs

First, lets create a new xx_journey_steps() function that takes an optional string ( $step ) as it’s only parameter. This function will return an array of all steps along with each step’s associated URLs or a single step’s URLs ( if the optional step name is supplied ). The URLs included for each step will include a previous record ( contains the URL to the previous steps page ), current record ( contains the URL to the current steps page ), and a next record ( contains the URL to the next steps page ). While we could hard code the URLs and steps, there’s no need to since we setup our steps as child pages to our Welcome page and used menu order numbers to help specify the order.

In the new xx_journey_steps() function first get the post object for the first page ( i.e. main parent page ) of the journey using WordPress’ WP_Query::get_posts() method, with the query post_type argument set to page, post_status to publish, and the menu_order argument set to 1. This will return an array of pages ( WP_Post objects ) with a menu order number 1. Since we only have one we’re going to get the first WP_Post from the array and set up our first step values using the get_permalink() WordPress function.

IMPORTANT! If you are already using the order number field and could have multiple pages with order number 1 it might be better to use another method to return the first page in our journey ( like get_page_by_path() ).

The next component to add to our utility function will be to get all the child pages to our first step page, ordered by menu order number. The resulting array will have each step in our journey in order, which means the first WP_Post object is actually our second step in the journey so lets also add the next record to our first step.

Last step in creating our new xx_journey_steps() function is adding a for loop, within which we set the previous, current, and next records. Note, rather than using a foreach loop, we’re using a for loop along with the next() PHP function for moving the internal array pointer forward by one.


Our Customer Journey Utility Function

Check if Customer is on Journey Page

The second utility function we’re going to create is a new xx_is_customer_journey() function which takes an optional step slug as a parameter and returns a page id ( of the journey page the customer is on or if the slug supplied matches the page the customer is on ) or 0 if the customer is not on a journey page or the page doesn’t match the supplied slug. This will help us guide our customers through the journey by knowing which step they are on or if they are even on our journey.

In our new function, start by retrieving the current global $post object. Assuming we’re not on a special WooCommerce page ( like the shop, checkout, order received or cart pages – more on this later ) then the global $post object should contain the current page’s WP_Post object. We’re going to track the current post / page id in a $page_id variable so lets set that variable to the current Page’s id using $post->ID to retrieve the ID property of the WP_Post class.

The second section of our function is going to check if we’re on the WooCommerce Shop, Checkout, Order Received or Cart pages. These are unique pages in that they aren’t always “real” pages so the global $post object doesn’t necessarily match up to the slug being supplied. In this case we’re going to check for each of these pages using the WooCommerce conditional functions, is_shop(), is_checkout(), is_order_recieved_page() and is_cart(). Then we’ll use the wc_get_page_id() function along with the option name ( i.e. “shop”, “checkout”, or “cart” ) to retrieve the respective page id. When it comes to the Order Received page we’re going to simply return 0 since even though it’s under the Order / Checkout Page we’re not considering it a part of our journey.

The last step to our function is to retrieve all our journey step pages using our xx_journey_steps() function. Remember this function returns an array of our custom journey steps where the keys are the page slugs. We’re going to check to see if the slug for the current page exists in our journey steps by using the get_post_field() function to retrieve the ‘post_name’ field value for our current $page_id. If the current page is not one of our journey pages then we return 0 otherwise the current page id to signify that it is a journey page. If an optional step parameter (i.e. slug ) was supplied we also check to see if it matches the slug for the current page, again if it doesn’t then we also return a 0.

Our Customer Journey Utility Function

Redirect Customer to Journey Page

A third utility function we’re going to create related to the journey is a new xx_send_to_journey_step() function which takes three arguments, a String ( $step ), an Array ( $args ), and a Boolean ( $redirect ). The function will either return a URL or redirect the customer using the wp_safe_redirect() function depending on the $redirect parameter. The URL returned/redirected to will be the page URL based on the slug supplied and if arguments are also supplied in the $args parameter, they will be appended to the URL.

Our Customer Journey Step 1

Welcome Register & Plan Page

We have our utilities and now it’s time to setup our pages. The first step in our journey, and the step that starts the customer in our funnel, is to get the customer registered and capture their email. This will be done on our Welcome Page. While we could use the build in WooCommerce registration form we’re going to create a custom form instead, one that’s more streamlined ( requiring less information ) and built for better conversion. This entails adjusting the WooCommerce account setup options for username and passwords, creating a custom form, and lastly redirecting our customer to the next step in the journey.


Our Customer Journey Step 1a

WooCommerce Account Settings

First, we’re going to set our WooCommerce Account Settings so that Passwords and Usernames are automatically generated for new customers, which will allow us to create a new customer account using only an email. To do this ensure that the following two options are checked under the WP Admin > WooCommerce > Settings > Account & Privacy ( tab ):


Our Customer Journey Step 1b

Sign-In Form Creation

Our second step is to create a custom registration form. Your form is going to have two parts ( at least ), the first is the registration form and the second is the form for selecting / changing plans. To make this easy first copy the registration form from the WooCommerce login form from woocommerce/templates/myaccount/form-login.php and delete all the components we’re not going to use ( all paragraph html elements except those that contain the email field and the registration button ).


Our Customer Journey Step 1c

Select Plan Form Creation

Next, create a form our customer can use to select the fresh plan. We’re going to use the xx_get_user_plans() function we created earlier to retrieve a list of available plans to populate our select drop down list. We’re also going to use the xx_get_user_plan() function to retrieve the current plan ( if there is one ) assigned to the user and then use the WordPress selected() function to add a selected attribute to the select option that matches the customers current plan.


Our Customer Journey Step 1c

Select Plan Form Action & Security

We’re going to need to process the form when the customer submits the plan and in order to know when this form is submitted we’re going to assign it a custom action. Add a new input field with the name set to “action” and the value set to “xx_assign_meal_plan“. This will be the value we’re going to look for in our function server side that will be processing the form submission in the next step. Now lets add a security nonce using the wp_nonce_field() function with the action set to xx-assign-meal-plan and the name set to xx-assign-meal-plan-nonce.


Our Customer Journey Step 1c

Process the Form Submission

Now that we’ve created the Select Plan Form HTML our customer can use to adjust their plan, we need to add a function to process the form when submitted to the server. Create a new xx_autoship_apply_plan_handler() function ( which doesn’t take any arguments ) and add a new add_action() call with the action set to “wp“, our new function as the hooked function, and a priority of 99. Why the “wp” action? It’s a hook recommended by WordPress as a good place to perform high level validation before template redirects…

This hook is one effective place to perform any high-level filtering or validation, following queries, but before WordPress does any routing, processing, or handling….

WordPress Reference Docs

We’re going to add four checks, the first is to check for the xx_assign_meal_plan action we added as a hidden field, this will tell us if the post being submitted is our plan submission form and that we should process it. Assuming this is our form ( i.e. the action is there and it’s our action ), then the second is to verify that the customer is still logged in by using the WordPress is_user_logged_in() function. If not lets add a WooCommerce notice and return.

Note while the user no longer being logged in will most likely invalidate the nonce, we want to distinguish between the two so that we can customize our notification.

The third check is to add a wp_verify_nonce() function call to confirm that the nonce we added to the form is still valid. If the nonce fails, lets add a WooCommerce notice and return.

The last check we’re going to add is to validate the contents of the xx-plan-option form field. We’re going to validate the plan option submitted using our xx_get_user_plans() function and make sure the value submitted exists. If the value isn’t valid, add a notice like with the other checks and return.


Our Customer Journey Step 1c

Assign Submitted Plan

After checking that the form being submitted is the one we want, and the submitted value is valid, we now need to assign the new plan to the customer. We do this using our xx_add_plan_to_user() function. Pass the function the current user’s id, and the new plan value. Once assigned, we add our success message to the WooCommerce notice queue using the WooCommerce wc_add_notice() function and redirect the customer to the next step in the journey, the Select Your Meals page ( i.e. our Shop Page ), using our xx_send_to_journey_step() function and the Select Your Meals page slug, ‘meals’.


Our Customer Journey Step 1d

Sign-In Form

Great!….but wait, what happens if our customer already has an account? or what will they do if they have an account and lost their password? The new form will work to register a customer and for them to select / change their plan but doesn’t help them login if they already have an account or even recover their password if they forgot it.

We’re going to add two more else / if blocks to our code snippet. The first is going to be our login section which will be displayed if the current URL has an action parameter set to sign-in. Just like we did for our registration form, we’re going to port the WooCommerce login form from woocommerce/templates/myaccount/form-login.php, strip some of the un-needed html, adjust the classes and such to match our theme, and finally wrap it in the else if statement.


Our Customer Journey Step 1e

Password Recovery Form

The second else / if block we’re going to add is going to be our password recovery section which will be displayed if the current URL has an action parameter set to lost-login. Just like we did for our other forms, we’re going to port the WooCommerce lost password form from woocommerce/templates/myaccount/form-lost-password.php, strip out some of the un-needed html, adjust the classes and such to match our theme, and finally wrap it in the else if statement.


The final touch will be to add some navigation links to each form so that our customers can toggle between the views. Since we’re using an action parameter to determine which view should be shown, this is as simple as adding a link with a URL including the action set to the view ( i.e. /?action=sign-in ).


Our Customer Journey Step 1f

Creating The Shortcode

Our plan will be to make all our content as shortcodes, since we may want to try different customer journeys or even include our registration form on different landing pages ( hey, we’re all marketers at heart right?! ) we’re going to wrap this form ( and the next form ) in a function and attach that function to a custom shortcode using the WordPress add_shortcode() function call. This way including our registration form is as simple as including the shortcode on any page we want.>

NOTE Remember to return the content of the form in the shortcode function and not print it, hence our use of the ob_start() and ob_get_clean() buffer functions

Our Customer Journey Step 2

Select Your Meals Page

The second step in our customers journey is to have our newly registered customer select the meals they want to be included in their meal subscription box ( what we’re calling their fresh box! ). Since we’re using wonderful WooCommerce we setup this page by pointing the WooCommerce Shop page to our new Select Your Meals page. In WP Admin > WooCommerce > Settings > Products ( tab ) select the Shop Page drop down and choose our new Select Your Meals page.


Our Customer Journey Step 3

Select Your Schedule Page

After our Customer’s have selected the meals they want included in their box they need to select a schedule for their meal subscription scheduled order…do they want their Fresh Box shipped Daily? Weekly? Monthly? a specific day of the Month? The good news is that Autoship Cloud powered by QPilot is built for this and allows us to use the out of the box functionality to assign a schedule to products and a cart, using default Frequency and Frequency Types or customized schedules.

Since we’re using WooCommerce we’re using the cart to hold the contents of our Fresh Box and since the customers need to assign a schedule to their box we’re going to do this by assigning a schedule to the cart items. This schedule will be used later by Autoship Cloud to automatically generate a Scheduled Order for the meal subscription box in QPilot at checkout.

Interesting Fact! Autoship Cloud allows for each cart item to have a different schedule ( awesome right?! ). At checkout Autoship Cloud will automatically group items by their assigned schedule and generate a separate Scheduled Order for each group of items. A truly powerful and flexible way to create recurring orders and allowing for almost endless possibilities. However, for our purposes we’re shipping all the items in the same Fresh Box so they all get the same schedule.


Autoship Cloud Functions

In our Customer Journey Step 3 we’re going to start using several different off the shelf functions from the Autoship Cloud powered by QPilot plugin library for creating our Select Your Schedule Page functionality. Autoship Cloud is build as a library of functions that can be used to customize customer experiences and create, update, and manage Scheduled Orders from WooCommerce.


autoship_set_full_cart_schedule()

The first Autoship Cloud function we’re going to use it the autoship_set_full_cart_schedule() function. This function can be used to assign a single schedule ( i.e. Once a Months, Every Two Weeks, daily, etc. ) to all the items currently in the cart. It takes two parameters, an integer for the frequency and a string for the frequency type ( valid values are Months, Weeks, Days, DayOfTheWeek, or DayOfTheMonth ).

found in Autoship Cloud/src/cart.php file

autoship_get_full_cart_schedule()

The second Autoship Cloud library function we’re going to use is the autoship_get_full_cart_schedule() function. As the name implies, the function will return an array of all unique Schedules assigned to the cart and the total count of items with that schedule. As we mentioned previously, a cart could have multiple Scheduled products with different Schedules so the result from the function call could be an array with multiple records. However, again we’re using a single schedule for our entire Fresh Box ( i.e. WC Cart ) so we’re not concerned with multiple schedules.

found in Autoship Cloud/src/cart.php file

autoship_default_frequency_options()

The third Autoship Cloud library function we’re going to use is the autoship_default_frequency_options() function. This function will returns an array of available schedule options ( frequency and frequency types ), and the associated display name to use as the label. The options returned by this function include 1 – 5 Months by default but can be adjusted to match you’re desired schedule options using the autoship-default-frequency-options filter.

found in Autoship Cloud/src/products.php file

autoship_get_frequency_display_name()

The last Autoship Cloud library function we’re going to use is the autoship_get_frequency_display_name() function. This function will returns a user friendly display label based on the supplied frequency and frequency type. We’ll be using this function when we customize our schedule Options.

found in Autoship Cloud/src/utilities.php file


Our Customer Journey Step 3a

Custom Schedule Options

As our site name ( Weekly Fresh ) implies we’re going to adjust the Autoship Cloud default frequency options to Weekly options by creating a new xx_populate_schedule_options() function, hook it to the autoship-default-frequency-options filter using the add_filter() function call, and finally populating the options array with our weekly options.

Within our new hooked function we’ll be using the autoship_get_frequency_display_name() library function to generate our schedule Option display names as well as a counter to generate weekly options from every week to every 4 weeks.

  • Every 1 Weeks
  • Every 2 Weeks
  • Every 3 Weeks
  • Every 4 Weeks

Our Customer Journey Step 3b

Select Schedule Form

Above we reviewed the functions that we can use to assign and retrieve the schedule from a WC Cart and we’ve adjusted the schedule options we’ll be making available to our customers. Now we need to create the form that will be displayed on our Select a Schedule page which customers can use to choose the schedule they want.

First, create a new HTML form with a Select drop down and a Submit button. To populate the Select drop down use the autoship_default_frequency_options() function we mentioned above to get the values, then iterate through the values to populate the select drop down’s option using the key as the value and the associated display_name value as the label.

We also need to make sure we show the customer the current schedule assigned to the cart. To do this use the Autoship Cloud library autoship_get_full_cart_schedule() function ( mentioned earlier ) to retrieve the current WC Carts schedule. Then while iterating through the frequency options returned by the autoship_default_frequency_options() function check if the frequency and frequency_type matches and if so add the selected value to the Select option using the WordPress selected() function.


Our Customer Journey Step 3c

Select Schedule Form Action & Security

Just like we did for our Select Your Plan form we’re going to add a custom action ( in this case lets use “xx_assign_box_schedule“), and a security nonce ( let’s use xx-assign-schedule as the action and xx-assign-schedule-nonce as the name ) to our form.

Finally, because our goal is to make our form as usable as possible, let’s also wrap this form in a shortcode call using the add_shortcode() function.


Our Customer Journey Step 3d

Process the Form Submission

Now that we’ve created the HTML form the customer will use to submit the selected schedule, we need to add a function to process the form submission. Create a new xx_autoship_apply_box_schedule_handler() function ( which doesn’t take any arguments ) and add a new add_action() call with the action set to “wp” and our new function as the hooked function.

As we did with the handler for our Select Your Plan form, we’re going to add the same four validation checks. First checking that the action being submitted matches our custom action ( i.e. xx_assign_box_schedule ), then add our check to see if the user is logged and once those are in place lets add the security nonce check. In the last two checks if they fail let’s add notices to the WooCommerce Notice queue and return the user.

The last check we’re going to add, just like we did with the Select Your Plan form, is to validate the contents of the cart-schedule-option form field. In this case we’re going to validate the actual schedule option submitted using Autoship Cloud’s autoship_default_frequency_options() function. Remember this function returns an array of valid schedule options, so we only process this form if the value submitted exists in the results.


Our Customer Journey Step 3e

Assign Submitted Schedule

After checking that the form being submitted is the one we want, and the submitted value is valid, we now need to assign the schedule to the cart and to the customer. We do this using the Autoship Cloud autoship_set_full_cart_schedule() function referenced above and our custom xx_update_fresh_box_schedule() function. Once assigned, we add our success message to the WooCommerce notice queue using wc_add_notice and redirect the customer to the next step in the journey, Order ( our Checkout Page ), using our xx_send_to_journey_step() function passing it the slug for our order page, “order”.


Our Customer Journey Step 4

Checkout

The last step in our customers journey is to have them checkout and place their order. Again, since we’re using WooCommerce we setup this page by pointing the WooCommerce Checkout page to our new Checkout page. In WP Admin > WooCommerce > Settings > Advanced ( tab ) select the Checkout Page drop down, under Page Setup, and choose our new Checkout page. Choose the Save Settings option to save our selection.


We’ve created our customer journey, including getting them registered, adding a way for them to add meals to their Fresh Box, a way to Schedule their Fresh Box, and eventually checkout. However, there’s one more piece to our journey, assisting our customer through it and ensuring that each step is completed before the next step can be taken.

Journey Check Points

Controlling our Customers’ Journey

We’ve created our pages ( from our Welcome Page to our Checkout Page ) yet we should add a way to control when our customer can move on to the next step. They shouldn’t be able to select a schedule without selecting their meals, right? They shouldn’t be able to checkout without selecting a schedule, right?… these are the safeguards we’re going to put in place with our new xx_redirect_router(). Get started by creating a new xx_redirect_router() function and hook it to the template_redirect action using an add_action() function call with a priority of 8. We’re using a priority of 8 so that we can hook in before WooCommerce and catch our customers before any WooCOmmerce validation/functionality is performed.


Journey Check Point 1

Not Logged In

The first check we’re going to add to our new router function is to check if the user is not logged in, and if they aren’t logged in and they are on any page other than our Welcome page, we should send them to our Welcome page so that they can sign in or register. We’re going to use the WordPress is_user_logged_in() function to determine if the current customer is logged in and we’re going to use our xx_is_customer_journey() utility function to check if they are on our welcome page by passing it the slug to our Welcome page, “welcome”. Then, if the customer isn’t logged in and not on our Welcome page we’re going to use our xx_send_to_journey_step() function to redirect them to the welcome page / first step in the journey by passing it the slug to our Welcome page.

  • Not Signed In
  • Not on Welcome Page / First Step
  • Send them to Welcome Page

Journey Check Point 2

Logged In & On a Non-Restricted Page

The second check we’re going to add to our new router function is to check if the user is logged in, and if they are and they are on the Welcome Page, Select Your Meals ( i.e. Shop ) Page, page or Box ( i.e. Cart ) Page, don’t restrict them. Again, we’re going to use the WordPress is_user_logged_in() function to determine if the current customer is logged in, and our xx_is_customer_journey() utility function to check if they are on our welcome page, Select Your Meals page ( i.e. the shop ), or the Your Fresh Box page ( i.e. cart ) ( which we’ve renamed to Your Fresh Box with box as the slug, remember the utility function takes the page slug ). Then, if the customer is logged in and on one of these pages / steps we’re going to let them browse.

  • Signed In
  • On Welcome Page
  • Or on Select Your Meals ( i.e. Shop ) Page
  • Or on Your Fresh Box ( i.e. Cart ) Page
  • Don’t Do Anything

Journey Check Point 3

Full Fresh Box ( i.e. Cart )

The third check we’re going to add to our new router function is to check if the user is not on the Welcome page, Select Your Meals ( i.e. Shop ) Page, or Box ( i.e. Cart ) Page and they don’t have a full Box. Remember, our customer already chose a plan size ( number of meals ) and before they can move on they need to have selected the right number of meals for their plan. Luckily, we have a handy cart utility function, xx_get_cart_diff(), that can tell us if the Cart Quantity matches the customers plan.

  • Signed In
  • Not on Welcome Page
  • Not on Select Your Meals ( i.e. Shop ) Page
  • Not on Your Fresh Box ( i.e. Cart ) Page
  • Box / Cart is not Full
  • Send them to Select Your Meals Page

Journey Check Point 4

Cart is Full But Not Scheduled

The last check we’re going to add to our router function is to check if the user is not on the Welcome page, Select Your Meals ( i.e. Shop ) Page, Box ( i.e. Cart ) Page, or Select Your Schedule page and they have a full Box but no schedule assigned. This means that are customer selected their plan, filled their box, but never selected a schedule.

  • Signed In
  • Not on Welcome Page
  • Not on Select Your Meals ( i.e. Shop ) Page
  • Not on Your Fresh Box ( i.e. Cart ) Page
  • Not on Select Your Schedule Page
  • Box / Cart is Full
  • Schedule is not assigned
  • Send them to Select Your Schedule Page

Cart Requirements

One of the areas that seems to give Merchants and Developers the most headaches when attempting to create a subscription box site and customer journey is the WooCommerce Shopping Cart, adding the necessary requirements for customers to fulfill before they can checkout. As is the case with most meal subscription box companies, we’re going to require a specific number of meals ( based on their plan ) to be selected by our customer in order for the customer to checkout. Our cart validations will include the following steps:

Cart Validation Step 1

Check if our Cart is already full

Add Validation when a customer tries to add a product to the cart and it’s full.

Cart Validation Step 2

Check if the selected Quantity being added is valid

Add Validation when a customer tries to add N number of a product to the cart and it would be more then our allowed total cart quantity ( i.e. Box Size )

Cart Validation Step 3

Only add the number of meals that will fit in our cart, not more

Add Validation that adjusts the quantity being added to the cart to fit the available space only.

Cart Validation Step 4

Validate the Cart on Update Cart action in the Cart

Add Validation when a customer tries to update the cart to more then our allowed quantity ( i.e. Box Size )

Cart Validation Step 5

Validate the Cart when the Box Size / Plan is Changed

Add Validation that adjusts the carts contents when the customer changes plans.

Cart Validation Step 6

Disabled the Checkout button(s) if the Cart isn’t full

Add Validation that disables the checkout button(s) if the cart isn’t full.

Cart Validation Step 1

Check if our Cart is already full

The first step is to add validation for when a customer is trying to add an item to the cart and it’s already full. To do this we’re going to use the woocommerce_add_to_cart_validation filter. The filter passes the current valid Boolean, the id of the product being added, and the quantity being added.

Create a new xx_cart_is_valid_size() function that takes three parameters, a boolean ( $passed ), and two integers ( $added_product_id and $quantity ). We’re then going to use an add_filter() function call to hook this function to the woocommerce_add_to_cart_validation filter. The first check we’ll add to our new xx_cart_is_valid_size( ) function will be to check if the cart ( our Fresh Box ) is currently full. If it’s full, we add an error to the WooCommerce notice queue and return false letting WooCommerce know that the product should not be added and the validation failed.

To check if our cart is full we use the xx_get_cart_diff() function we created above to get the current difference between the box size and cart. If the difference is 0 then we know the cart is full. If it is full we use the wc_add_notice() function to add an error to the WooCommerce notice queue and return false.

Cart Validation Step 2

Check if the selected Quantity being added is valid

The second validation step we’re going to add to the xx_cart_is_valid_size() function we created in Step 1 above is to add a check to see if the quantity being added to our cart makes the cart total exceed our box size. If it does we’re going to display an error notice letting the customer know that not all the items were able to be added.

Important! In this step we aren’t going to invalidate the cart, we’re only going to add a notice since we’re going to adjust the quantity being added in the next step.

We check if the quantity being added would cause our cart to exceed our box size ( or plan size ) is to subtract the quantity being added from our current available room ( i.e. the $diff value we retrieved using our xx_get_cart_diff() function ). If the result is less than zero it means the quantity being added is to much for our cart size.

Cart Validation Step 3

Only add the number of meals that will fit in our cart, not more.

When it comes to cart validation, while we could prevent any quantity from being added to the cart that would exceed our cart size, why would we? Instead, we’re going to proactively adjust the quantity being added to fit our remaining space in the cart if our cart has room and the quantity of meals the customer is trying to add would be to much for our Box Size.

To do this we’re going to use the woocommerce_add_to_cart_quantity filter. Create a new xx_limit_quantity_added_to_cart() function which takes three parameters, two integers, the new quantity for the item ( $quantity ) and an optional original quantity ( $original ), as well as a boolean flag used to specify if the call is being made after or before the cart has been updated ( hence the optional original quantity parameter ).

Remember that our xx_get_cart_diff() function allows for an optional parameter, which if supplied will adjust the cart quantity by that value. We’re going to use the same xx_get_cart_diff() function to get the total allowed quantity. However, we’re going to pass the xx_get_cart_diff() function the difference between the quantity being added and the original quantity if this is post cart update, otherwise we pass it 0 ( meaning telling it not to adjust the cart difference ).

If the current cart difference tells us the cart is full, then no new quantities can be added so we return the original quantity ( no change ). However, if the cart was not full before adding this quantity and adding the quantity puts the cart over the max, determine the amount of the item that can be added and return the new total quantity.

Cart Validation Step 4

Validate the Cart on Update Cart action

Now that we’ve added our custom validation which runs when the add to cart button is clicked, and our function for limiting the quantity of items added to the cart so that it doesn’t exceed our Box Size, we want to add the same checks when the customer adjusts the quantity in the cart form. While we could use the woocommerce_update_cart_validation filter, instead we’re going to use the woocommerce_after_cart_item_quantity_update action. The reason is we want to adjust the quantity ( not just reject the entire amount ) and allow our customers to be able to add as many items as possible but just not more than our max ( remember our helpful new xx_limit_quantity_added_to_cart() function we created above? ). The woocommerce_after_cart_item_quantity_update filter fires after the quantity has already been updated in the cart. Given this, we’re going to back calculate the original quantity in the cart, then determine how many of the newly added items can be included and adjust the cart quantity to include those only if needed.

First create a new xx_validate_update_cart() function that takes the four parameters from the woocommerce_after_cart_item_quantity_update filter. The parameters include a string which is the cart item key ( $cart_item_key ), an integer which is the new quantity ( $quantity ), another integer which is the old quantity ( $old_quantity ), and finally the WC_Cart object itself ( $cart ). Use the add_action call to hook this new function to the update action. Rather than recreate the wheel we’re going to re-use our xx_limit_quantity_added_to_cart() function ( surprise! ), sending it the new quantity, old quantity, and true to specify that this call is happening post cart update.

If the new calculated quantity ( quantity returned from our xx_limit_quantity_added_to_cart() function ) is not equal to the quantity originally supplied to our function by the hook ( $quantity ) then the quantity had to be changed by our function. Also, we only need to worry about running any adjustments if the quantity supplied by the hook is more than the old quantity, no need to worry if items are being removed. However, if the new quantity returned does not match the total quantity the cart was suppose to be updated to, then we need to adjust the quantity.

Since there was a change in how much we could add out of the new total quantity, we’re going to set the cart items total quantity to the new amount, add a notice using the wc_add_notice function, and lastly update the cart session data using the WC()->cart->set_session() method.

Cart Validation Step 5

Validate the Cart when the Box Size / Plan is Changed

The last cart validation step is to validate and adjust the cart contents when the customer changes their plan. We created our plan form above, as we added the functionality to update and retrieve our customers selected plan. However, remember that the plan size equates to cart size and the total number of meals that can be and must be in the cart before our customer can move on. This is the validation we’re now going to add.

Start by creating a new xx_adjust_cart_size() function for this which takes a single integer ( $size ) parameter, which is the size the cart should be adjusted to fit ( i.e. the new plan size once selected by the customer ). The first component to this function will be to get the current cart count using the WC()->cart->get_cart_contents_count() method and compare it to the supplied size, if the supplied size is bigger or equal to the current count we’re good, and don’t need to do anything. However, if the cart count is bigger than the supplied size then we need to adjust the cart.

To remove excess items we’re essentially going to re-fill the cart until we hit our new maximum quantity. Start by creating a new empty cart array, a counter ( set to 0 ) and grab the current cart item array using the WC()->cart->cart_contents. Next, iterate over the current array of items, each time checking if the quantity of items we’ve added ( i.e. the counter ) to our new cart array is equal to our new size. If it is we break the foreach loop since we’ve keep as many as we could. If it’s not then we add the item to our new array, only keeping the quantity of the item that wouldn’t push us over our max size.

The last part of the new function is to update the WC()->cart->cart_contents to our newly populated items array and set the cart session using the WC()->cart->set_session() method so our new cart contents get applied.

We’ve now covered the first two main components, the Subscription Plan & Box Size and the Cart Requirements. The two we have left are the Customer Journey and actually creating the Scheduled Order.

Cart Validation Step 6

Disabled the Checkout button(s) if the Cart isn’t full

We’ve taken care of all the difficult cart validations when it comes to quantity vs Box / Plan Size, now the last step is to take care of any checkout buttons on the mini-cart or in the cart page. While we’ve already added our xx_redirect_router() function and checks for ensuring the steps that need to be completed before checking out are completed, we always want to be as user friendly and proactive as possible, not waiting for the user to encounter an error. In order to prevent our user from trying to checkout before they are ready we’re going to override the woocommerce_widget_shopping_cart_proceed_to_checkout() and woocommerce_button_proceed_to_checkout() pluggable functions ( A pluggable function is a function that can be overridden by either defining it in your theme or plugin, read more here ).


Cart Validation Step 6a

Mini-Cart Checkout Button Override

Create a new woocommerce_widget_shopping_cart_proceed_to_checkout() pluggable function of your own. In the function we’re going to use our xx_get_cart_diff() and xx_cart_has_schedule() utility functions to check if the cart is full and that there’s a schedule assigned to it. If both are true we’re going to display the standard WooCommerce Checkout button. However, if the cart isn’t full we’re going to echo our own disabled version of the checkout button with the URL set to “#” and an added disabled class. This way if the customer clicks the button nothing happens and visually the button will look and act disabled. If the cart is full then we know that it needs a schedule so we’re going to output a button to assign a schedule and set the URL to our Select Your Schedule page using our xx_send_to_journey_step() function.

NOTE on the disabled class : If the theme you’re using doesn’t include the disabled class you’ll want to add it to your CSS and could implement something like .disabled { opacity: .5!important; cursor: not-allowed; }


Cart Validation Step 5b

Cart Checkout Button Override

Just like with the mini-cart’s function, now create a new woocommerce_button_proceed_to_checkout() pluggable function to disable / override the checkout button on the cart page. In the function we’re going to use the same xx_get_cart_diff() and xx_cart_has_schedule() utility functions as with the mini-cart to check if the cart is full and that’s its been assigned a schedule. If the cart count and box size matches and the cart has an assigned Schedule, display the standard WooCommerce cart/proceed-to-checkout-button.php template. Otherwise we’re going to display a “Continue to Next Step” button that either points back to our Select Your Meals page or Select Your Schedule page. To retrieve the URLs for those pages we’re going to use our xx_send_to_journey_step() function and pass it either our Select Your Meals page slug “meals” or our Select Your Schedule page slug “schedule”.

Creating the Scheduled Order

We’ve covered all the pieces to create a meal subscription site like the big boys on the block…. almost. The biggest piece and the most important piece is actually creating the Scheduled Order. Creating a recurring order that will be processed on a scheduled bases ( otherwise why make our customers choose a schedule? ).

Autoship Cloud automatically takes care of this. It’s. That. Simple.


Sure, what Autoship Cloud powered by QPilot does behind the scenes isn’t simple, but it is simple from the site owner perspective. Activate the Autoship Cloud plugin, register for a QPilot account ( QPilot is the cloud based engine that manages all the Scheduled Orders in the cloud and allows the magic to happen ) , connect the two via WP-Admin and done. That easy, the Autoship Cloud plugin takes care of the rest. It will automatically monitor checkout orders for any items with an associated schedules, group those items and generate a Scheduled Order ( or in our case a meal subscription ) after the customer checks out. In addition, if an order has scheduled items, the plugin will automatically force the payment method to be saved and send that info with the Scheduled Order so that payment processing is uninterrupted. Wow right?!?

Last but not least, Autoship Cloud comes with a customer facing Scheduled Order management dashboard baked right in. Unlike any other solution out there, after customers sign up and created their meal box subscription, they can login, go to the My Account > Scheduled Orders ( or whatever you’ve decided to label it ) and add more meals ( if you let them ), create more scheduled orders or boxes ( again if you let them ) and a host of other features…things not even the big named meal subscription companies out there can do.


Creating the Scheduled Order

My Account Redirect for Customers with Subscriptions

The one adjustment we’re going to add here will be to automatically redirect our customers to their scheduled Meal Subscription in My Account when they log in if they have one already. If they login and they don’t already have a subscription then lets send them to our Welcome Page!

We’re going to use the WooCommerce woocommerce_registration_redirect and woocommerce_login_redirect hooks for this. Create a new xx_login_register_redirect_to_get_started() function that takes two parameters, a String ( $redirect ) and a WP_User|WP_Error ( $user ), and add two new add_filter() calls with the filter names set to woocommerce_registration_redirect and woocommerce_login_redirect, our new function as the hooked function, a priority of 10, and 2 accepted arguments.

The first thing we’re going to do in the function is to check if the user has scheduled orders already. Luckily, Autoship Cloud gives us a function for this already, the autoship_customer_has_scheduled_order() function which takes a user id as an argument and returns true if the user has one or more scheduled orders, otherwise it returns false.

Creating the Scheduled Order

My Account Redirect for Customers with Subscriptions

The second piece to our new function will be to actually adjust the redirect URL based on if the user has a Meal Subscription or not. Just as we did in the first part of this function we’re going to use another Autoship Cloud function, autoship_get_scheduled_orders_url(), which returns the URL for the My Account > Scheduled Orders ( or Fresh Boxes in our site ) page. If the user has a meal subscription then return the URL from autoship_get_scheduled_orders_url(), otherwise return the URL to the Welcome page using our xx_send_to_journey_step() function.

Final Touches

Stylize, New Mini Cart, and Other polishing touches

As with all projects, the last step before stamping the creation completed is to apply the final polished touches. This project is no different, while we didn’t go through all the polishing touches ( hey we all have our own opinions about UI/UX right? ) we will briefly touch on what we’ve decided to add to give that final presentation. All of which is included in the demo plugin download.

Final Touches

WooCommerce Shop Page



  • New Header Navigation
  • Active Page Highlight
  • Centered Headers & Text
  • Removed Search
  • Removed Breadcrumbs
  • Adjusted Button Sizes
  • Adjusted Button Text
  • Removed View Cart Link
  • Added Remove from Cart Button
  • Larger Font Sizes
  • New Color Pallet
  • Removed Sidebar
  • Renamed Page
  • New Page Intro
  • Removed In-Page Sort
  • Mobile First
Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Shop
Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Original Shop

Final Touches

WooCommerce Cart



  • New Tile Layout
  • Mobile First
  • Navigation
  • Centered Headers & Text
  • Removed Search
  • Removed Breadcrumbs
  • Adjusted Button Text
  • Removal Icon on Image Hover
  • Full Width Buttons
  • Larger Font Sizes
  • New Color Pallet
  • Removed Sidebar
  • Renamed Page
  • New Page Divider Icon
  • Full Width Coupon Block
  • Full Width Checkout Button
Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Cart Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Mobile Cart
Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Original Cart Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Mobile Original Cart

Final Touches

WooCommerce Mini-Cart



  • New Tile Layout
  • Mobile First
  • Centered Headers & Text
  • Adjusted Button Text
  • Removal Icon on Image Hover
  • Full Width Buttons
  • Larger Font Sizes
  • New Color Pallet
  • Larger Images
  • Drop Shadow and New Style
  • Expanded & Contracted Mini-Cart Views
  • Count Badge
  • Box Counter
  • Next Step Button
  • New Tile Layout
  • Centered Headers & Text
  • Adjusted Button Text
  • Removal Icon on Image Hover
  • Full Width Buttons
  • Larger Font Sizes
  • New Color Pallet
  • Larger Images
  • Drop Shadow and New Style
Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Completely Custom Mini-Cart Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Mobile Completely Custom Mini-Cart
Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Mini-Cart Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Mobile Mini-Cart
Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Original Mini-Cart Autoship Cloud WooCommerce Storefront eCommerce Meal Subscription Mobile Original Mini-Cart

Final Thoughts

Conclusion

We’ve now walked through how to take WordPress, WooCommerce, and Autoship Cloud and create a Meal Subscription site. We’ve covered everything from Plan Creation and Tracking, to Cart Validation, Scheduling, Customer Journey Control, and even Scheduled Order creation at Checkout.

What’s Next?

If you’re looking to create a Meal Subscription site that requires an order to be created at checkout ( i.e. you fulfill the first meal box right away ) then you should be all set. However, say you don’t ship the first box for a while and thus would rather have the WooCommerce order for the first box be created when it’s actually time… We can do that too!

Autoship Cloud powered by QPilot not only lets you create Scheduled Orders at checkout but even without a checkout. Want to learn how to create a Meal Subscription site that allows you to capture a customers address, save a payment method and generate a Scheduled Order all without a checkout? Click the link below and see how!

Post navigation