Unique URL for Shopping Cart Confirmation Page

Jun 20, 2011 5:49 pm

Currently, Freedom doesn't offer a native way to bring users to a unique URL when they finish the checkout process with the Commerce module, and that can be a problem for clients who want to, for example, use Google Analytics to track conversions. I know others have requested this feature, and I hope it will appear in the future. For the time being, I've recently written a short snippet of code that will make this happen. Unfortunately, it will require that PHP hooks are enabled on the server where you use it.

In the PHP hooks section of Green's layouts "module," enter the following code as the pre-processing hook:

<?php
/* Unique Shopping Cart Confirmation pages
 * Author: Thomas Hopkins [thomas.hopkins@accrinet.com] */
require_once(LIBRARY_ROOT . '/class/FreedomHook.php');
ob_start();
if ($_REQUEST['result'] === "commerce_finished" &&
    !empty($_SESSION['content'])) {
    
    if (empty($_SERVER['HTTPS'])) {
        header("Location: https://{$GLOBALS['site_host_ssl']}/index.php?result=commerce_finished");
        ob_end_flush();
    }

    echo $_SESSION['content'];
    unset($_SESSION['content']);
    ob_end_flush();
    die();
}
?>

In the post-processing hook, put the following code:

<?php
if (FreedomHook::isCommerceFinished()) {
    $_SESSION['content'] = ob_get_contents();
    header("Location: https://{$GLOBALS['site_host_ssl']}/index.php?result=commerce_finished");
}
ob_end_flush();
?>

When a user submits a shopping cart transaction, this code will redirect them to index.php?result=commerce_finished, showing them their purchase summary on a page with a unique URL. If you need to use Google Analytics' conversion tracking, you could use the PHP DOM extension like so to append the correct code in the pre-processing hook:

<?php
/* Unique Shopping Cart Confirmation pages
 * Author: Thomas Hopkins [thomas.hopkins@accrinet.com] */
require_once(LIBRARY_ROOT . '/class/FreedomHook.php');
ob_start();
if ($_REQUEST['result'] === "commerce_finished" &&
    !empty($_SESSION['content'])) {
    
    if (empty($_SERVER['HTTPS'])) {
        header("Location: https://{$GLOBALS['site_host_ssl']}/index.php?result=commerce_finished");
        ob_end_flush();
    }
    $page = new DOMDocument();
    $page->loadHTML($_SESSION['content']);
    $analy_code = $page->createDocumentFragment();
    $analy_code->appendXML("ADD GOOGLE ANALYTICS SCRIPT HERE");
    $page->getElementsByTagName('body')->item(0)->appendChild($analy_code);
    echo $page->saveHTML();
    unset($_SESSION['content']);
    ob_end_flush();
    die();
}
?>

Of course, use this at your own risk. This code will fail if the site in question does not have a secure URL set, and I have not tested it extensively. But it seems to work well where we need it to work.

thanks and a question

Oct 16, 2012 9:37 am

If we using the second pre-processing hook, how do we pull the database values that Google needs, ie:

<script type="text/javascript">

var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-X']);
_gaq.push(['_trackPageview']);
_gaq.push(['_addTrans',
'1234', // order ID - required
'Acme Clothing', // affiliation or store name
'11.99', // total - required
'1.29', // tax
'5', // shipping
'San Jose', // city
'California', // state or province
'USA' // country
]);

// add item might be called for every item in the shopping cart
// where your ecommerce engine loops through each item in the cart and
// prints out _addItem for each
_gaq.push(['_addItem',
'1234', // order ID - required
'DD44', // SKU/code - required
'T-Shirt', // product name
'Green Medium', // category or variation
'11.99', // unit price - required
'1' // quantity - required
]);
_gaq.push(['_trackTrans']); //submits transaction to the Analytics servers

(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();

</script>

Thanks in advance - trying to solve this issue today

Oct 17, 2012 2:52 pm

In that case you could try something like this. Unfortunately, I cannot test this now, but this should give you the output you need, and PHP's linter doesn't find any syntax errors with it.

In your pre-processing hook section, you could put this:

?php
/* Unique Shopping Cart Confirmation pages
 * Author: Thomas Hopkins [thomas.hopkins@accrinet.com] */
require_once(LIBRARY_ROOT . '/class/FreedomHook.php');
ob_start();
if ($_REQUEST['result'] === "commerce_finished" &&
    !empty($_SESSION['content'])) {
       
    if (empty($_SERVER['HTTPS'])) {
        header("Location: https://{$GLOBALS['site_host_ssl']}/index.php?result=commerce_finished");
        ob_end_flush();
    }

    $finalCommerceArray = $_SESSION['final_commerce_data'];

    $page = new DOMDocument();
    $page->loadHTML($_SESSION['content']);
    $analy_code = $page->createDocumentFragment();

    $appendHTML = "
        <script type=\"text/javascript\">

var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-X']);
_gaq.push(['_trackPageview']);
_gaq.push(['_addTrans',
'{$finalCommerceArray['row_id']}', // order ID - required
'Acme Clothing', // affiliation or store name
'{$finalCommerceArray['total']}', // total - required
'', // tax
'', // shipping
'{$finalCommerceArray['city']}', // city
'{$finalCommerceArray['state']}', // state or province
'{$finalCommerceArray['country']}' // country
]);

";

foreach ($finalCommerceArray['items'] as $itemArray) {
    $appendHTML .= "
        _gaq.push(['_addItem',
        '{$finalCommerceArray['row_id']}', // order ID - required
        '{$itemArray['sku']}', // SKU/code - required
        '{$itemArray['name']}, // product name
        '', // category or variation
        '{$itemArray['price']}', // unit price - required
        '{$itemArray['qty']}' // quantity - required
        ]);
    ";
}
$appendHTML .= "
        _gaq.push(['_trackTrans']); //submits transaction to the Analytics servers

        (function() {
        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
        })();

    </script>


";

    $analy_code->appendXML($appendHTML);
    $page->getElementsByTagName('body')->item(0)->appendChild($analy_code);
    echo $page->saveHTML();
    unset($_SESSION['content']);
    unset($_SESSION['final_commerce_data'])
    ob_end_flush();
    die();
}
?>

In the post-processing PHP hook, then, put this:

<?php
require_once(LIBRARY_ROOT . '/class/FreedomHook.php');
if (FreedomHook::isCommerceFinished()) {
    $_SESSION['final_commerce_data'] = FreedomHook::getCommerceFinalData();
    $_SESSION['content'] = ob_get_contents();
    header("Location: https://{$GLOBALS['site_host_ssl']}/index.php?result=commerce_finished");
}
ob_end_flush();
?>

That should spit out the Javascript that Google expects for tracking, though I don't like to use PHP to generate Javascript like this. Still, it should work.

Oct 17, 2012 2:56 pm

Of course, you MUST replace "UA-XXXXX-X" in the pre-hook with your own Google Analytics UA ID, or Google won't know what to do.