Multiple Checkout Items

In the previous guide, we showed the basic SDK code to send product and payment confirmation events on a single product.

In reality, users usually purchase multiple products, or multiple items of the same product.

There are two distinct use-cases for multiple checkout items.

  1. A user purchased several identical items. For example, 5 iPhone X covers.
  2. A user purchased several items of different kinds. For example, 2 iPhone X covers, and 3 iPhone X chargers.

In both use-cases, you should first decide whether each sold item should be considered as an individual conversion, or should any number of identical item sales are considered a single conversion.

Important

Each call to QL.sendConfirmationEvent creates a single conversion event in QL's BI database. If you don't send the number of sold units in the units field, then each event is treated as a single unit of sale.

Conversion events are used to calculate conversion rate and, while units are used to aggregated number of sales across QL's UI.

Multiple checkout items - Distinct conversion events

When a user purchased several items and we want to count each item as a distinct conversion event, we create an array of sold products, and add each sale to that array, along with its sale price.

In this example, we do not specify the number of sold units, as each conversion event is treated as a single unit of sale.

app/controllers/checkout_controller.rb

class CheckoutController < ApplicationController
 
  # GET /checkout/finish - renders successful checkout
  def finish
    checkoutProducts = session[:checkoutProducts] # read checked out products from session
    @soldProducts = []
    checkoutProducts.each do |product|
      # product is a hash table: {id: productId, salePrice: salePrice}
      productFromDB = Product.find(product[:id])
      productId = productFromDB.ean # use product ean as productId
      # push product to soldProducts      
      @soldProducts.push({
        id: productId,
        salePrice: product[:salePrice]
      })
    end
    @clientKey = 'myvirtualstore'
    
    render('finish') # explicit call to render the payment confirmation view
  end
 
end

Sale price

In reality sale prices can be different event when the same item is sold multiple times. For example Buy 2 and get the 3rd one free. You should set the sale price of each item to the actual price it was purchased in.

Multiple checkout items - Summed conversion events with sold units

When a user purchased several items and we want to count all items of the same kind (product ID) as a single conversion event, we create an array of sold products, and add an entry for each product kind (product ID), along with its sold units amount, and its sale price multiplied by the number of sold items.

app/controllers/checkout_controller.rb

class CheckoutController < ApplicationController
 
  # GET /checkout/finish - renders successful checkout
  def finish
    checkoutProducts = session[:checkoutProducts] # read checked out products from session
    @soldProducts = []
    checkoutProducts.each do |product|
      # product is a hash table: {id: productId, salePrice: salePrice, soldItems: soldItems}
      productFromDB = Product.find(product[:id])
      productId = productFromDB.ean # use product ean as productId
      # push product to soldProducts      
      @soldProducts.push({
        id: productId,
        salePrice: product[:salePrice] * product[:soldItems],
        units: product[:soldItems]
      })
    end
    @clientKey = 'myvirtualstore'
    
    render('finish') # explicit call to render the payment confirmation view
  end
 
end

Note

As with the previous example, we save the sale information in the user's session for simplicity. In reality you might save this information in your DB or a cache layer like Redis.

Sending events with the SDK

With our controller code in place, we can now modify our payment confirmation view to send multiple items to QL using the SDK.

Note that the @soldProducts array is serialized into JSON, and then iterated over, calling the QL.sendConfirmationEvent method for each product in the array.

app/views/checkout/finish.erb

<div>
  <p>Your payment has been received!</p>
  <!-- rest of HTML is redacted for simplicity --> 
</div>
<!-- ADD QL's SDK at the bottom of the page template -->
<script>
  window.QLAsync = function(QL){
    var clientKey  = '<%= @clientKey %>',
        soldProducts  = JSON.parse('<%= @soldProducts.to_json %>');
    QL.init(clientKey);
    
    // iterate over soldProducts and send each one to QL using the SDK
    soldProducts.forEach(function(product) {
      // try to grab the units field from the products object
      // and default to 1 if missing
      var units = product.units || 1;
      QL.sendConfirmationEvent(product.productId, {
        price: parseFloat(product.salePrice, 10),
        units: parseInt(units, 10)
      });
    });    
  }
</script>
<script src="//d3jdlwnuo8nsnr.cloudfront.net/sdk/v3.0/ql.js"></script>

Google Tag Manager

The above example can be modified to work on Google Tag Manager by pushing the @soldProducts array into GTM's dataLayer.

app/views/checkout/finish.erb

<div>
  <p>Your payment has been received!</p>
  <!-- rest of HTML is redacted for simplicity --> 
</div>
<!-- initialize GTM dataLayer -->
<script>
  window.dataLayer = window.dataLayer || [];
  dataLayer.push({
    'clientKey':  '<%= @clientKey %>',
    'soldProducts': '<%= @soldProducts.to_json %>'
  }]);
</script>

Modify your GTM embed code for the Payment Confirmation Page as follows:

<script>
  window.QLAsync = function(QL){
    var clientKey  = '{{ clientKey }}',
        soldProducts  = JSON.parse('{{ soldProducts }}');
    QL.init(clientKey);
    
    // iterate over soldProducts and send each one to QL using the SDK
    soldProducts.forEach(function(product) {
      // try to grab the units field from the products object
      // and default to 1 if missing
      var units = product.units || 1;
      QL.sendConfirmationEvent(product.productId, {
        price: parseFloat(product.salePrice, 10),
        units: parseInt(units, 10)
      });
    });    
  }
</script>
<script src="//d3jdlwnuo8nsnr.cloudfront.net/sdk/v3.0/ql.js"></script>

Things to consider

Single vs. Multiple confirmation events

Sending multiple distinct confirmation events to QL might create some additional network load in your user's browser. QL recommends that you try to minimize the number of sent events to avoid any negative effect on user experience.

To that end, whenever possible, QL recommends that you send a single confirmation event, with total sale price (per productId), and amount of sold units.

Conversion vs. Sold units

QL counts the number of confirmation events and uses it to calculate % Conversion in the QL UI. When deciding how to send events to QL, you should consider what constitutes as a conversion in your business model. For example, if a user viewed a single iPhone cover (1 page view), but purchased 3 units, does that constitute as 100% conversion rate (1 view and 1 purchase), or 300% conversion rate (1 view and 3 purchases) ?

In all likelihood, a sale of 3 units of the same item is considered as a single conversion, in which case, you should simply add the total amount of sold units and summed sale price to the confirmation event as explained above. This is the way QL recommends you use our SDK.