Emailing Customers Product Cross Sells with Drupal Commerce

Emailing Customer Product Cross Sells with Drupal Commerce

Displaying cross sells on product and cart pages is a good way to increase your average order size and raise your conversion rate. This is well known. People in a buying mood appreciate being shown similar or related products. Netflix paid out a million dollar bounty to a team who helped improve their recommendation engine, because they know that accurate recommendations are critical to keeping and growing their customer base.

But you are probably missing out on one major opportunity involving prodcut cross-sells and upsells. Where else are your customers seeing your brand? Their personal email inbox. Every transactional email you send out is a potential goldmine of revenue. For people who have have a current/abandoned cart with you, or have recently purchased something, sending them cross-sells in your order update emails should be something you want to implement.

If you are using Drupal Commerce, this is not as hard as it sounds. The trademark flexibility is in full display, and I'll show you exactly how to do it. If you're not yet using Drupal Commerce to power your ecommerce efforts…what are you waiting for?

Generating Transactional Emails

For our purposes, we will assume you are using the Message module to generate your transactional emails. There are other methods you can use, but using Message offers the cleanest and most flexible choice. Commerce Kickstart comes boxed with it, or you can download the necessary modules yourself: Message, Message Notify, and Commerce Message.

Messages are entities, and so are fully fieldable. The default Message types that come packaged with Commerce Message have an entity reference field for referencing a Commerce order. This gives that Message type access to all of the data that is part of that order. You can view the default Message types by going to Structure->Message Types.

Message types added by Commerce Message

If you edit the Order Confirmation type, you'll see text boxes for the email subject and the email body. You can set these to anything you want. You might also notice some tokens in the text areas. These will be replaced based on the order that is attached to the Message.

To send one of these Message types via email, you need to manually code it in a module, or use Rules. For this article, we'll use Rules, because that's what Commerce Message does by default. Let's look at the order confirmation rule as an example.

Go to Configuration->Workflow->Rules and scroll down to the rule named "Commerce order message: order notification e-mail" and click "edit." This rule runs after someone has completed the checkout process. In the image below, you'll see the the Action area of this rule.

Actions for the order confirmation rule in Drupal Commerce

  • Since Messages are entities, we must first create an entity of that type, and select the correct Message type. In this case, the order confirmation Message type.
  • This Message type has a field that needs to reference an order, so the next step is to reference the order that was just completed. This is what the "Set a data value" action does.
  • Then we save the Message entity we created.
  • The last action is to actually send the email notification regarding that newly created entity.

So whenever someone completes an order, a Message gets created, and the owner of the order gets notified about it, all via this rule. Simple. Clear.

So how do we get actual cross sells into this message?

Displaying Cross Sells or Upsells

First, we need a way to actually determine cross sells for a product or a cart. In true Drupal fashion, there is about 17 different ways you can go about this. You can have the admin set the cross sells manually per product, have them pulled in based on similar categories, or maybe even recently added products.

For this article, we will determine cross sells from manual entry, but what we show you here will still work with other methods as long as you are using Views to display your cross sells. If you want granular control over what your customers see, then this is the way to go.

If you already have a View that displays products based on an argument you pass it, you can skip to the next step.

  • Create a View of products or product displays, whichever way your store is being managed. For this example, we will use actual products. 
  • Make sure the Display Format is set to Grid, as tables display pretty consistantly in all email clients.
  • Set it up with whatever fields you want. Make sure any links actually go to the Product Display of the product.
  • Create a Contextual Filter on the View for the Commerce Product: Product ID. Under "More," check the box next to "Allow mulitple values." It should look similar to the image below.
Settings for the Contextual Filter
 
And the full View will look similar to this:
 
Full Cross Sell View
 
Obviously, there can be some variation, but in this View, we have an image, the title, and the price of a product. We are doing some rewrites so the image and title link to the product's display node. We now have a View we can pass some product IDs to and it will display those products for us. This is all we need for the next step.  

Determining the Products and Placing the View in the Email

How do we place this View in the Message that gets sent to customers? And how do we determine which products get passed to the View?

First, you'll want to attach and Entity Reference field to your product type. Allow it to refer only to other products. This is where you will manually populate your cross sells. The name of our field is, simply, "field_cross_sells."

With a full order, however, how do you determine which cross sells to pull? Which product? This is where you have another decision to make. You can pull the cross sells from the last product that was added to the cart. Or you can combine all the cross sells from all the products in the order, and then randomly select a sample. Or perhaps you only want to pull cross sells from products of a certain category. The list goes on.

For this example, we will just pull the cross sells from the last product that was placed in the cart. At this point, you'll need to know how to write a custom module. Within this module, you will want to put the following function:

<?php
 
function mymodule_order_cross_sells($order) {
   
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
   
$product_ids = array(); // array keyed by the time the line item was updated, so we can sort based on time added to cart
   
$product_cross_sells = array(); // Will hold the ids of the products to display as cross sells
   
foreach ($order_wrapper->commerce_line_items as $line_item_wrapper) {
      if (
$line_item_wrapper->type->value() == 'product') {
       
$product_wrapper = $line_item_wrapper->commerce_product;
       
$cross_sells = $product_wrapper->field_cross_sells->value();
        if (!empty(
$cross_sells)) {
         
$product_ids[$line_item_wrapper->changed->value()] = $product_wrapper->product_id->value();
          foreach (
$cross_sells as $product) {
           
$product_cross_sells[$product_wrapper->product_id->value()][] = $product->product_id;
          }
        }
      }
    }
   
krsort($product_ids);
   
$product_id = array_shift($product_ids);
    if (!empty(
$product_cross_sells[$product_id])) {
      return
$product_cross_sells[$product_id];
    } else {
      return
FALSE;
    }
  }
?>

This function returns an array of product IDs, the cross sells from the last product that was added to the cart/order. You might need to make some tweaks based on the name of your field.

Next, we need to somehow get the View we created in the Message type, passing it the product IDs from this new function. We will again use the convention established by the Commerce Message module, and define a hook_message_presave(), along with a callback function. The code is below:

<?php
 
function mymodule_cross_sell_message_callback($message) {
   
$wrapper = entity_metadata_wrapper('message', $message);
   
$product_ids = mymodule_order_cross_sells($wrapper->message_commerce_order->value());
    return
views_embed_view('cross_sell_view_name', 'default', implode(',', $product_ids));
  }

  function
mymodule_message_presave($message) {
    if (!empty(
$message->mid) || $message->type != 'commerce_order_order_confirmation') {
      return;
    }
   
$message->arguments['!order-cross_sells'] = array(
     
'callback' => 'mymodule_cross_sell_message_callback',
     
'pass message' => TRUE,
    );
  }
?>

These functions provide a token you can place anywhere within the Message type. So go back and edit the Message at Structure->Message Types, and within the text area for the email body, place the "!order-cross_sells" token. It will automatically pull in the View, based on the order attached to the Message.

Message Email Body with Cross Sells Token

The hook only inserts the token for the Order Confirmation Email, so if you created a custom Message type, be sure and include the machine name in the first IF statement of the presave hook.

And we're done. You now have a way to put products inside transactional emails.

Other Options

The Order Confirmation email is obviously not the only type of email in which you can put cross sells or upsells. Be creative. If they are signing up for an account, you can ask a couple of questions, and send them products in the Account Welcome email, based on their answers. Or you can trigger certain messages based on products currently in their cart.

If you have a shipping notification alert set up, that is another email where you can insert products. 

Once you have this process down for one Message type, you can replicate it for other contexts and types as well. This flexibility and modularity is one of the main benefits of Drupal and Drupal Commerce, so take advantage of it.

Have you had any success with cross sells? Are you using transactional emails in creative ways to build more revenue?

logo_inverse

is loading the page...