How to Integrate Universal Analytics with Salesforce

How to Integrate Universal Analytics with Salesforce

Salesforce is one of the most popular customer relationship management systems (CRM) around and right now thousands of businesses rely on it to put exact figures on their marketing return on investment (ROI). To achieve the full picture of ROI on marketing, it can be as simple as tracking submissions on a PPC-only or email-only landing page, but what about web leads that come from multiple channels and are all forced  through the same set of contact forms?

Integrating your marketing analytics with your CRM is a popular topic and many assume it’s as straight-forward as downloading a plugin and popping in your account number. However, this is not the case. It was much easier with Classic analytics, using the values available in the _utmz tracking cookie. However, since the release of Universal Analytics, and the use of the less accessible _ga cookie, the disconnect between marketing attribution data and the Salesforce system seems to have become greater, but it still follows the basic steps:

  • Create the fields in Salesforce
  • Generate HTML for your field IDs
  • Modify and install your tracking code
  • Add the IDs to hidden fields in your forms
  • Push the values to Salesforce

There is no native Analytics integration for Salesforce, despite hordes of forum users requesting it for the last six or seven years. After searching for integration methods, we have found that most resources were only capable of last-click attribution (not good enough for us!), and none have been updated for Universal Analytics.

The method below is based on the Professional edition of Salesforce and we’re using Contact Form 7 as this is an extremely  popular plugin.

To see Universal Analytics data in your Salesforce leads, follow these steps:

1)    Create fields for your Salesforce records

Create the following fields in your lead records in Salesforce:

  1. GA Medium
  2. GA Source
  3. GA Campaign
  4. GA Content
  5. GA Term

Our Salesforce consultant helped us set this up so that the fields appear under ‘Google Analytics’ for each lead: this helps us to break all leads down by the same source/medium as we would do in Analytics. You can do this for any fields you require to be sent to the CRM along with the lead. We’ve included the content and term here.

ga-leads-records

2)    Generate web-to-lead HTML

Salesforce uses a series of ID numbers to map incoming values onto these fields so your code and forms need to reference these IDs when they are submitted.

The quickest way to access all the IDs is to use  Salesforce’s web-to-lead generator which will contain the necessary IDs to send the form values to Salesforce – make sure you include your GA fields as you’ll need all of these for the next steps.

3)    Install tracking code

Install the following code on every page of your website (at least every page where there’s a form). The code we used here is based on the getSetReff code provided by innertrends on github, which emulates the values Universal Analytics uses and makes them available for us to use. It’s optimised for Tag Manager, so this is what we’re firing on every page (you’ll have to wrap it in a script tag to get it to work in GTM):



  function getSetReff()
{

    if (!Array.prototype.filter) //Array.filter() isn't included in IE until version 9.
    {
      Array.prototype.filter = function(fun /*, thisArg */)
      {
        "use strict";

        if (this === void 0 || this === null)
          throw new TypeError();

        var t = Object(this);
        var len = t.length >>> 0;
        if (typeof fun !== "function")
          throw new TypeError();

        var res = [];
        var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
        for (var i = 0; i < len; i++)
        {
          if (i in t)
          {
            var val = t[i];

            // NOTE: Technically this should Object.defineProperty at
            //       the next index, as push can be affected by
            //       properties on Object.prototype and Array.prototype.
            //       But that method's new, and collisions should be
            //       rare, so use the more-compatible alternative.
            if (fun.call(thisArg, val, i, t))
              res.push(val);
          }
        }

        return res;
      };
    }

    var _reff=[];
    _reff = dataLayer.filter(function (value) {if (value.setDomain)  return value; });
    if (_reff.length === 0) _reff[0]={'setDomain':document.location.hostname};
    
    function rC(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}
    function sC(n,v,d) { dd = new Date(); dd.setTime(dd.getTime() + (d*24*60*60*1000)) ;e = typeof d != "undefined" ? ";expires="+dd.toGMTString() : ""; document.cookie = n+"="+v+e+";domain="+_reff[0].setDomain+";path=/"; }
    function gcP(p) { if (document.location.search.indexOf(p) != -1) { return (""+document.location.search.split(p+"=")[1]).split("&")[0]; } else { return "not-set"; }  }
    
    //every pageview
    var __asc = (typeof rC("__sreff") != "undefined" ? rC("__sreff") : "");
    var __apc = (typeof rC("__reff") != "undefined" ? rC("__reff") : "");
    var __tsc = t__apc = [];
    var __rf = __tpc = res__apc = __gsr = __nwss = "";
    var __cmp = "utm_campaign";
    var __mdm = "utm_medium";
    var __srcs = "utm_source";
    var __flag = 0; 

    //referrer or params?
    if (document.location.search.indexOf(__cmp) != -1 || document.location.search.indexOf(__mdm) != -1 || document.location.search.indexOf(__srcs) != -1)
    {
        __gsr = "//campaign::c:["+gcP(__cmp)+"]m:["+gcP(__mdm)+"]s:["+gcP(__srcs)+"]";
    }
    else { __gsr = document.referrer; flag=1; }
    //console.log(__gsr);
    //get referrer domain & verify adwords
    __gsr = ((document.location.search.indexOf("gclid") != -1) ? "//campaign::[adwords]" : __gsr); 
    __gsr = ((typeof __gsr == "undefined" || __gsr == "" || __gsr.indexOf(_reff[0].setDomain)!=-1) ? "(direct)" : __gsr.split('/')[2]);

    if (__asc)
    {
        __tsc = __asc.split(".");
        __tsc[1] = new Date().getTime();
        __tsc[2]++;
        __asc = __tsc.join(".");
        sC("__sreff",__asc);
    }
    else
    {
        __tsc[0] = __tsc[1] = new Date().getTime(); //start time = current time
        __tsc[2]=1; //first pageview
        __asc = __tsc.join(".");
        __nwss = 1;
        sC("__sreff",__asc);
    }
    


    //if new session
    if (__nwss == 1)
    {
        res__apc = (__apc != "" ? __apc+"|" : ""); 
        sC("__reff",res__apc+__gsr+"&"+__asc,730);
    }
    else
    {
        t__apc = __apc.split("|");
        __tpc = t__apc[t__apc.length-1];
        __tpc=__tpc.split("&")[0] != "" ? __tpc.split("&")[0] : __gsr;
        res__apc = (t__apc.length == 1 ? "" : (t__apc.slice(0,-1).join("|")+"|"));

        if (__gsr == "(direct)" || __gsr.indexOf(__tpc.split("&")[0])!=-1) 
        {
            sC("__reff",res__apc+__tpc+"&"+__asc,730);
        }
        else
        {
            sC("__reff",__apc+"|"+__gsr+"&"+__asc,730);
        } 
    } 
    document.getElementById("ID FOR MEDIUM").value = gcP(__mdm); /* Campaign_Medium */
	document.getElementById("ID FOR CAMPAIGN FIELD").value =gcP(__cmp); /* Campaign_CampaignName */ 
    if (__gsr == "(direct)"){
      
    document.getElementById("ID FOR SOURCE FIELD").value = __gsr; /* Campaign_Source */ }
else if (__gsr == "" || gcP(__srcs) == "not-set")
{
  
  document.getElementById("ID FOR SOURCE FIELD").value = document.referrer; /* Campaign_Source */ 
}       
  
else {
 
      document.getElementById("ID FOR SOURCE FIELD").value =gcP(__srcs); /* Campaign_Source */ 
    
    }
    
  
  
    return rC("__reff");
}

getSetReff();


The final section of the tracking code is what populates the hidden fields: you’ll need to customise to match your field IDs that you exported in step 2, for example:

document.getElementById(“ID FOR MEDIUM“).value = gcP(__mdm); /* Campaign_Medium */

This code can be modified to identify any of the values in the _ga cookie. We’re most interested in source, medium and campaign, but you could do the same thing for term and content for PPC traffic.

4)    Add the IDs to your form

Step 1, 2 and 3 are the pre-requisites to accessing and reporting on the data – now we’ve got to bridge the gap between the data we’ve captured on your website and the final destination in the CRM. We do this by injecting the values into hidden fields and pushing them to Salesforce via the form.

First, create hidden fields that are able to contain the GA values. You’ll need to use the IDs you’ve exported in step 2 and attached them to the HTML of your forms, it should look something like this:

html-forms

5)    Push the values to Salesforce

 

We’re nearly done! Now all you have to do is push the values to Salesforce using a PHP function. Remember to read carefully and input your own Organization ID, etc. to get it to work for you.


                
  $submission = WPCF7_Submission::get_instance();
  $data = $submission->get_posted_data();

  $first_name = $data['your-firstname'];
  $last_name = $data['your-lastname'];
  $company = $data['company'].$data['_post_url'];
  $email = $data['your-email'];
  $phone = $data['phone'];
  $website = $data['your-website'];
  $seobudget = $data['seobudget'];
  $ppcbudget = $data['ppcbudget']; 
  $memberStatus = $data['memberStatus'];
  $serviceInterests = $data['serviceInterest'];
  $GaMedium = $data['N0Y00000ABxTe'];
  $GaSource = $data['N0Y00000ABxTj'];
  $GaCampaign = $data['N0Y00000ABxTo'];
  $GaContent= $data['N0Y00000ABxTk'];
  $GaTerm = $data['N0Y00000ABxU3'];
  
  $post_items[] = 'oid={YOUR_ORGANIZATION_ID}; // Replace this by your OID value
  $post_items[] = 'first_name=' . $first_name;
  $post_items[] = 'last_name=' . $last_name;
  $post_items[] = 'email=' . $email;
  $post_items[] = 'company=' . $company;
  $post_items[] = 'URL=' . $website;
  
  $post_items[] = 'phone=' . $phone;
  $post_items[] = 'Campaign_ID=' . $campaignID;
  $post_items[] = 'member_status=' . $memberStatus;
  $post_items[] = 'lead_source=' . $leadSource; 
  
  
  $post_items[] = '00N0Y00000ABxTe=' . $GaMedium;  
  
 
  if(!empty($first_name) && !empty($last_name) && !empty($email)) // Replace _WOCF7 ID with form that you want to use. Remove it completely for all forms
  {
    $post_string = implode ('&', $post_items);
    // Create a new cURL resource
    $ch = curl_init();
    if (curl_error($ch) != "")
    {
     // error handling
      file_put_contents('cf7data.txt', 'Curl Error: ' . curl_error($ch));
    }
    $con_url = 'https://www.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8';
    curl_setopt($ch, CURLOPT_URL, $con_url);
    // Set the method to POST
    curl_setopt($ch, CURLOPT_POST, 1);
    // Pass POST data
    curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_string);
    curl_exec($ch); // Post to Salesforce
    curl_close($ch); // close cURL resource
  } 
}


Now, when a form is submitted, the hidden fields will populate the relevant fields in the lead record in Salesforce. And we’re done!

Feel free to take this code and run with it – or if you need help integrating Universal Analytics with Salesforce, contact Receptional.

 

For more information about Salesforce get in touch with Ben Coleman:

bcoleman@performa-it.co.uk

+44 (0) 117 230 2390

www.performa-it.co.uk

Share this Post: Facebook Twitter LinkedIn Google Plus StumbleUpon Reddit RSS Email