• Server-side Google Analytics by Ast

    Usually, people integrate Google Analytics into their shops via simple pre-generated client-side javascript snippet. It's pretty straightforward, easy to use and works most of the time correctly from the box. So why would anyone want to use a server-side integration? There could be a helluva lot of reasons for that. For example, you may want to track some events from your server-side workers — it would be ugly and unreliable to involve a browser here. Or you may want to implement a complicated funnel for your sales, mixing client-side events with your CRM events. So, is it possible? Sure, it is, and we'll use Measurements Protocol for this. At the time, it's a most modern way to save raw analytics data to Google Analytics from whatever environment you want. I'll use PHP with Guzzle for code examples, but you surely can use any language and any library: there are no differences in principle at all. And let's assume that you already have the perfectly working client-side integration.

    Identifying the user

    It's important to understand who made an action such as a pageview or a transaction on your website. If you are able to identify a user, you can build a funnel, look at a conversion and a traffic source. Otherwise, you won't be able to understand how events are interconnected at all. Google Analytics stores the user's identifier in a specific cookie named "_ga" (please, note, its name can be changed, so check your concrete client integration options). It looks like this: "_ga=1.2.286403989.1366364567", the last two numbers form a clientId, used by Google Analytics to identify a user. Let's create a function to parse it. If there is no cookie named "_ga", we'll generate our own identifier:

    function getClientId() {
        $cookie = $this->request->cookies->get('_ga');
        if ($cookie) {
            $cv = explode('.', $cookie);
            return $cv[2] . '.' . $cv[3];
        }
        return uniqid();
    }

    We also may want to gather context of the request:

    function getContext() {
        $context = [];
        if ($utmSource = $this->request->query->get('utm_source')) {
            $context['utm_source'] = $utmSource;
        }
        if ($utmMedium = $this->request->query->get('urm_medium')) {
            $context['utm_medium'] = $utmMedium;
        }
        if ($utmCampaign = $this->request->query->get('utm_campaign')) {
            $context['utm_campaign'] = $utmCampaign;
        }
        if ($utmTerm = $this->request->query->get('utm_term')) {
            $context['utm_term'] => $utmTerm;
        }
        if ($utmContent = $this->request->query->get('utm_content')) {
            $context['utm_content'] = $utmContent;
        }
        return $context;
    }

    You need to store such data somewhere in your database if you want to be able to report any events for this user later. You can't do it at a runtime as there could be no cookies or GET variables at the time.

    Tracking transactions

    The most important things in e-commerce analytics are transactions. Google Analytics treats every transaction as an immutable record to its database, so if it's sent to their server, it's all over: you can't change your revenue, you can't remove the transaction, you can't do anything. That's why it's very important to understand your funnel and determine when it's good to save a transaction. 

    function saveTransaction($clientId, $transactionId, $revenue, array $context) {
        $transactionParams = [
            'v' => 1, // protocol version
            't' => 'transaction', // event type
            'tid' => $this->identifier, // your UA-XXXXX-1 identifier
            'cid' => $clientId, // client identifier
            'ti' => $transactionId, // transaction unique identifier
            'tr' => $revenue, // transaction revenue
            'cu' => 'USD', // currency
        ];
        // merge utm context from "getContext()"
        $formParams = array_merge($transactionParams, $context);
        // remember to set "base_uri" to "http://www.google-analytics.com/"
        $this->client->request('POST', 'collect', ['form_params' => $transactionParams]);
    }

    We also want to save data about items:

    function saveItem($clientId, $transactionId, $revenue, array $items) {
        $itemParams = [
            'v' => 1, // protocol version
            't' => 'item', // event type
            'tid' => $this->identifier, // your UA-XXXXX-1 identifier 
            'cid' => $clientId, // client identifier, same as above
            'ti' => $transactionId, // transaction unique identifier, same as above
            'in' => $item['name'], // for example "blue pants"
            'ip' => $item['price'], // price of the specific item
            'iq' => $item['quantity'], // quantity of the specific item
            'ic' => $item['article'], // code of the specific item, for example "pants-001"
            'iv' => $item['category'], // category of item, for example "pants"
            'cu' => 'USD'
        ];
        
        $this->client->request('POST', 'collect', ['form_params' => $itemParams]);
    }

    That's it.

comments powered by Disqus