Magento TutorialCreating An Affiliate Tracking Module In Magento

Advertisement

In this tutorial, we will create a Magento module that will capture an affiliate referral from a third-party source (e.g. an external website or newsletter) and include a HTML script on the checkout success page once this referral has been converted.

We will be covering the following topics:

  • Capturing URI request data,
  • Saving to and retrieving values from cookies,
  • Adding custom content to the checkout success page,
  • Adding module configuration to the Magento admin panel,
  • Creating a community module with no hard-coded settings.

As always, this module will be written in such a way that no core files are modified, making it portable and Magento-upgrade friendly.

Before We Start

This tutorial assumes you are already familiar with the basics of creating a Magento module, and builds upon the points covered in our previous article on the Magento Layout, so it might be worth brushing up on both if you are not familiar with either.

We will be adding any website-specific settings as configurable options in the Magento admin panel, and as a result, our module is a “community module” whose code belongs in app/code/community, the idea being that we can drop this community module onto any Magento instance, and no code modification will be required to get it working.

To begin with, create the following file structure with empty files ready to be populated later.

app
  - code
      - community
          - SmashingMagazine
              - Affiliate
                  - Block
                      - Conversion.php
                  - Model
                      - Observer.php
                  - etc
                      - config.xml
  - design
      - frontend
          - base
              - default
                  - layout
                      - smashingmagazine_affiliate.xml
                  - template
                      - smashingmagazine_affiliate
                          - conversion.phtml

  - etc
      - modules
          - SmashingMagazine_Affiliate.xml

We can already complete the content of SmashingMagazine_Affiliate.xml:

<&quest;xml version="1.0" encoding="UTF-8"&quest;>
<config>
    <modules>
        <SmashingMagazine_Affiliate>
            <active>true</active>
            <codePool>community</codePool>
        </SmashingMagazine_Affiliate>
    </modules>
</config>

Capturing The Affiliate Referral

The first task of an affiliate-tracking process is to capture the referral, which is generally provided as a $_GET parameter in a URI from a third-party website or newsletter. For example smashingmagazine.com/&quest;utm_source=some_affiliate_id where some_affiliate is the ID of the affiliate we will reward if our customer completes a purchase.

Defining An Event Observer

We want our affiliates to be able to link to any page of our website, so that our customers can click directly to the product or service of interest. Therefore, we need a method of checking for this $_GET parameter on every page. As always, we don’t want to modify any Magento core code since we want our module to be as portable and upgrade-friendly as possible, so we will be using an event observer.

One event that is dispatched on every page is controller_front_init_before, so let’s create our config.xml with an observer for this event.

<&quest;xml version="1.0" encoding="UTF-8"&quest;>
<config>
    <modules>
        <SmashingMagazine_Affiliate>
            <version>0.0.1</version>
        </SmashingMagazine_Affiliate>
    </modules>
    <global>
        <models>
            <smashingmagazine_affiliate>
                <class>SmashingMagazine_Affiliate_Model</class>
            </smashingmagazine_affiliate>
        </models>
        <events>
            <controller_front_init_before>
                <observers>
                    <smashingmagazine_affiliate>
                        <class>smashingmagazine_affiliate/observer</class>
                        <method>captureReferral</method>
                        <type>singleton</type>
                    </smashingmagazine_affiliate >
                </observers>
            </controller_front_init_before>
        </events>
    </global>
</config>

Now, on every page load, our method SmashingMagazine_Affiliate_Model_Observer::captureReferral() will be called, so let’s add our Observer.php content:

<&quest;php
class SmashingMagazine_Affiliate_Model_Observer
{
    public function captureReferral(Varien_Event_Observer $observer)
    {
        // here we add the logic to capture the referring affiliate ID
    }
}

Capturing the Affiliate ID

The event controller_front_init_before is dispatched from the Magento core method Mage_Core_Controller_Varien_Front::init(), and includes a reference to the same class in the dispatched event, as per the following code snippet from app/code/core/Mage/Core/Controller/Varien/Front.php:

Mage::dispatchEvent(
    'controller_front_init_before',
    array('front' => $this)
);

We can therefore gain access to this instance of Mage_Core_Controller_Varien_Front by including the following code in our event observer:

<&quest;php
class SmashingMagazine_Affiliate_Model_Observer
{
    public function captureReferral(Varien_Event_Observer $observer)
    {
        $frontController = $observer->getEvent()->getFront();
    }
}

Since the controller is responsible for the parsing of URI’s, we now have access to the appropriate object we need to determine whether our $_GET parameter exists:

<&quest;php
class SmashingMagazine_Affiliate_Model_Observer
{
    public function captureReferral(Varien_Event_Observer $observer)
    {
        $frontController = $observer->getEvent()->getFront();

        $utmSource = $frontController->getRequest()
            ->getParam('utm_source', false);

        if ($utmSource) {
            // here we will save the referrer affiliate ID
        }
    }
}

In the above code, we are calling getRequest() to retrieve the Mage_Core_Controller_Request_Http instance from the controller, which will contain all of the information we need about the current URI request, including any $_POST or $_GET parameters. We are interested in the $_GET parameter utm_source, and we can retrieve its value with the getParam() method.

If present in the current URI, the variable $utmSource will now contain our referring affiliate’s ID.

Saving the Affiliate ID

Now we need to save the $utmSource value so that we can reward this affiliate should our customer proceed to make a purchase. There are a number of ways to do this, but for this tutorial we will simply use a $_COOKIE. Magento has core functionality for dealing with cookies, so this is very straightforward:

<&quest;php
class SmashingMagazine_Affiliate_Model_Observer
{
    const COOKIE_KEY_SOURCE = 'smashingmagazine_affiliate_source';

    public function captureReferral(Varien_Event_Observer $observer)
    {
        $frontController = $observer->getEvent()->getFront();

        $utmSource = $frontController->getRequest()
            ->getParam('utm_source', false);

        if ($utmSource) {
            Mage::getModel('core/cookie')->set(
                self::COOKIE_KEY_SOURCE,
                $utmSource,
                $this->_getCookieLifetime()
            );
        }
    }

    protected function _getCookieLifetime()
    {
        $days = 30;

        // convert to seconds
        return (int)86400 * $days;
    }
}

We have defined a new constant, COOKIE_KEY_SOURCE, because we will be referencing this same value from a different class. When referencing a value like this from multiple locations, it is good practice to use a constant. This way, we keep refactoring work to a minimum should the value ever need to change. We have also defined a new protected method, _getCookieLifetime(), for retrieving the cookie lifetime, which we have hard coded to 30 days from now.

Notifying The Affiliate Upon Conversion

At this point, we now have an affiliate ID stored in a cookie, and let’s assume our referred customer has added some products to their basket, completed the checkout process and is now on the checkout success page. This is where most affiliate schemes will require some notification that a referral has been converted to an order, usually by way of including a snippet of JavaScript or a tiny image at the bottom of the page.

There are, of course, other methods of updating affiliates, for example via server-side API calls which can be achieved with further event observers, but for this tutorial we will utilize the Magento layout to introduce some custom HTML at the bottom of the order success page.

We need to update our config.xml to cater to blocks and layout updates:

<&quest;xml version="1.0" encoding="UTF-8"&quest;>
<config>
    <modules>
        <SmashingMagazine_Affiliate>
            <version>0.0.1</version>
        </SmashingMagazine_Affiliate>
    </modules>
    <global>
        <blocks>
            <smashingmagazine_affiliate>
                <class>SmashingMagazine_Affiliate_Block</class>
            </smashingmagazine_affiliate>
        </blocks>
        <models>
            <smashingmagazine_affiliate>
                <class>SmashingMagazine_Affiliate_Model</class>
            </smashingmagazine_affiliate>
        </models>
        <events>
            <controller_front_init_before>
                <observers>
                    <smashingmagazine_affiliate>
                        <class>smashingmagazine_affiliate/observer</class>
                        <method>captureReferral</method>
                        <type>singleton</type>
                    </smashingmagazine_affiliate >
                </observers>
            </controller_front_init_before>
        </events>
    </global>
    <frontend>
        <layout>
            <updates>
                <smashingmagazine_affiliate
                         module="SmashingMagazine_Affiliate">
                    <file>smashingmagazine_affiliate.xml</file>
                </smashingmagazine_affiliate>
            </updates>
        </layout>
    </frontend>
</config>

Adding a New Layout File

The layout handle we are interested in is onepage_checkout_success, since this is the handle for the checkout success page that our customer has arrived at. Let’s update our layout file, smashingmagazine_affiliate.xml:

<&quest;xml version="1.0" encoding="UTF-8"&quest;>
<layout>
    <checkout_onepage_success>
        <reference name="before_body_end">
            <block type="smashingmagazine_affiliate/conversion"
              name="smashingmagazine_affiliate_conversion"
              template="smashingmagazine_affiliate/conversion.phtml" />
        </reference>
    </checkout_onepage_success>
</layout>

Adding A New Template File

The content of conversion.phtml will depend on the requirements of the specific affiliate we are building the module for, so for now we will use a generic format that includes a one pixel image with a dynamic merchant and affiliate ID:

<img
    src="http://media.smashingmagazine.com/themes/smashingv4/images/logo.png
    	&quest;merchant_id=<&quest;php echo $this->getMerchantId() &quest;>
    	&affiliate_id=<&quest;php echo $this->getAffiliateId() &quest;>"
    width="1"
    height="1"
/>

Creating a Custom Block

Now we need to create a new block for populating the dynamic data in our template:

<&quest;php
class SmashingMagazine_Affiliate_Block_Conversion
    extends Mage_Core_Block_Template
{
    public function getMerchantId()
    {
        return '12345';
    }

    public function getAffiliateId()
    {
        return Mage::getModel('core/cookie')->get(
            SmashingMagazine_Affiliate_Model_Observer::COOKIE_KEY_SOURCE
        );
    }
}

Adding Items To The Magento Admin Panel

At this point, we have a working module. All of our functionality is in place, and our hard-coded values will work for a single instance of Magento. We have the equivalent of a local module. The next step is to move these hard-coded values into the Magento admin panel, so they can be configured on multiple Magento instances without any code modification required.

It is quite straightforward to add items to the Magento admin panel system configuration, and it’s ideal for saving website-specific credentials or settings, like in our case the merchant ID and cookie timeout value.

Now, let’s log in to the Magento admin panel and navigate to SystemConfiguration using the main menu. We can see a number of tabs on the left-hand side for configuring the various elements of our Magento instance, such as “General,” “Web,” “Design,” etc. We are now going to add a new tab for our module’s configuration items.

Adding New System Configuration Tab

Let’s create a new XML file called app/code/community/SmashingMagazine/Affiliate/etc/system.xml and add to it the following content:

<&quest;xml version="1.0" encoding="UTF-8"&quest;>
<config>

    <!-- we are defining a new tab -->
    <tabs>

        <!-- our tab unique short name -->
        <smashingmagazine>

            <!-- the title of our tab in the admin panel sidebar -->
            <label>Smashing Magazine</label>

            <!-- the order our tab should appear on the sidebar -->
            <sort_order>100</sort_order>

        </smashingmagazine>

    </tabs>

</config>

Configuring ACL Settings

Since we have added a new tab, we need to add a new entry to the Magento ACL (Access Control List) to allow access to this tab. Let’s create a new file called app/code/community/SmashingMagazine/Affiliate/etc/adminhtml.xml, with the following content:

<&quest;xml version="1.0" encoding="UTF-8"&quest;>
<adminhtml>
    <acl>
        <resources>
            <admin>
                <children>
                    <system>
                        <children>
                            <config>
                                <children>
                                    <smashingmagazine_affiliate>
                                        <title>Smashing Magazine
                                               Affiliate</title>
                                    </smashingmagazine_affiliate>
                                </children>
                            </config>
                        </children>
                    </system>
                </children>
            </admin>
        </resources>
    </acl>
</adminhtml>

Note: When making changes to the ACL, if you are already logged in to Magento you will have to log out and back in again for the new permissions to take effect. You may find you get a “404 page not found” when you try to access a newly-added tab that you do not have ACL access to.

Adding New System Configuration Items

Next we will update our system.xml to add the items that we want to be configurable:

<&quest;xml version="1.0" encoding="UTF-8"&quest;>
<config>
    <tabs>
        <smashingmagazine>
            <label>Smashing Magazine</label>
            <sort_order>100</sort_order>
        </smashingmagazine>
    </tabs>

    <!-- we are adding a new section to our tab -->
    <sections>

        <!-- unique shortname for our section -->
        <smashingmagazine_affiliate>

            <!-- the title of our section in the sidebar -->
            <label>Affiliate Tracking</label>

            <!-- the tab under which we want our section to appear -->
            <tab>smashingmagazine</tab>

            <!-- order of section relative to our tab -->
            <sort_order>10</sort_order>

            <!-- visibility of our section -->
            <show_in_default>1</show_in_default>
            <show_in_website>1</show_in_website>
            <show_in_store>1</show_in_store>

            <!-- we are adding some new groups to our section -->
            <groups>

                <!-- the unique short code for our group -->
                <general>

                    <!-- the title of our group -->
                    <label>General Settings</label>

                    <!-- order of group relative to the section -->
                    <sort_order>10</sort_order>

                    <!-- visibility of our group -->
                    <show_in_default>1</show_in_default>
                    <show_in_website>1</show_in_website>
                    <show_in_store>1</show_in_store>

                    <!-- we are adding some new fields to our group -->
                    <fields>

                        <!-- the unique short code for our field -->
                        <status>

                            <!-- the label of our field -->
                            <label>Enabled</label>

                            <!-- the type of our field -->
                            <frontend_type>select</frontend_type>

                            <!-- the source of our 'select' type -->
                            <source_model>
                                adminhtml/system_config_source_enabledisable
                            </source_model>

                            <!-- order relative to the group -->
                            <sort_order>10</sort_order>

                            <!-- visibility of our field -->
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>0</show_in_store>
                        </status>
                        <merchant_id>
                            <label>Merchant ID</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>20</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </merchant_id>
                    </fields>
                </general>
                <cookie>
                    <label>Cookie Settings</label>
                    <sort_order>20</sort_order>
                    <show_in_default>1</show_in_default>
                    <show_in_website>1</show_in_website>
                    <show_in_store>1</show_in_store>
                    <fields>
                        <timeout>
                            <label>Cookie Timeout</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>10</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>

                            <!--
                                we can add a comment that
                                will appear below the field
                            -->
                            <comment><![CDATA[
                                This is the amount of time
                                an affiliate cookie will last, in days
                            ]]></comment>
                        </timeout>
                    </fields>
                </cookie>
            </groups>
        </smashingmagazine_affiliate>
    </sections>
</config>

Defining Default Admin Panel Settings

Sometimes it is convenient to provide default values for your admin panel configuration settings. For example, it would be a good idea to provide a default value for the cookie timeout value. However, there may be no benefit in providing a default value for merchant_id, since it will always be different. Let’s update our config.xml with a default cookie timeout value:

<&quest;xml version="1.0" encoding="UTF-8"&quest;>
<config>
    <modules>
        <SmashingMagazine_Affiliate>
            <version>0.0.1</version>
        </SmashingMagazine_Affiliate>
    </modules>
    <global>
        <blocks>
            <smashingmagazine_affiliate>
                <class>SmashingMagazine_Affiliate_Block</class>
            </smashingmagazine_affiliate>
        </blocks>
        <models>
            <smashingmagazine_affiliate>
                <class>SmashingMagazine_Affiliate_Model</class>
            </smashingmagazine_affiliate>
        </models>
        <events>
            <controller_front_init_before>
                <observers>
                    <smashingmagazine_affiliate>
                        <class>smashingmagazine_affiliate/observer</class>
                        <method>captureReferral</method>
                        <type>singleton</type>
                    </smashingmagazine_affiliate >
                </observers>
            </controller_front_init_before>
        </events>
    </global>
    <frontend>
        <layout>
            <updates>
                <smashingmagazine_affiliate
                         module="SmashingMagazine_Affiliate">
                    <file>smashingmagazine_affiliate.xml</file>
                </smashingmagazine_affiliate>
            </updates>
        </layout>
    </frontend>

    <!-- we are setting the default value -->
    <default>

        <!-- this is the section short name -->
        <smashingmagazine_affiliate>

            <!-- this is the group short name -->
            <cookie>

                <!-- this is the field short name -->
                <timeout>30</timeout>

            </cookie>

        </smashingmagazine_affiliate>

    </default>

</config>

Accessing The System Configuration Values

Once all of the above is configured, and we have our default values in place, or have saved our settings through the admin panel, we can easily access these values with the following snippet:

<&quest;php $value = Mage::getStoreConfig('system/config/path'); &quest;>

The system/config/path should be replaced with the configuration path to the particular setting you are interested in. For example, the path to our merchant_id setting would be smashingmagazine_affiliate/general/merchant_id.

Retrieving Our Cookie Timeout From Admin Panel

We previously had the cookie lifetime hard-coded to 30 days, which we can now replace with our Magento admin panel system configuration value:

<&quest;php
class SmashingMagazine_Affiliate_Model_Observer
{
    const COOKIE_KEY_SOURCE = 'smashingmagazine_affiliate_source';

    public function captureReferral(Varien_Event_Observer $observer)
    {
        $frontController = $observer->getEvent()->getFront();

        $utmSource = $frontController->getRequest()
            ->getParam('utm_source', false);

        if ($utmSource) {
            Mage::getModel('core/cookie')->set(
                self::COOKIE_KEY_SOURCE,
                $utmSource,
                $this->_getCookieLifetime()
            );
        }
    }

    protected function _getCookieLifetime()
    {
        $days = Mage::getStoreConfig(
            'smashingmagazine_affiliate/cookie/timeout'
        );

        // convert to seconds
        return (int)86400 * $days;
    }
}

Retrieving Our Merchant ID From Admin Panel

We can now complete our block by replacing the hard-coded values with calls to the Magento admin panel system configuration. We will also add a new method for retrieving whether the module should be “active” or not:

<&quest;php
class SmashingMagazine_Affiliate_Block_Conversion
    extends Mage_Core_Block_Template
{
    public function getIsActive()
    {
        return Mage::getStoreConfig(
            'smashingmagazine_affiliate/general/status'
        ) &quest; true : false;
    }

    public function getMerchantId()
    {
        return Mage::getStoreConfig(
            'smashingmagazine_affiliate/general/merchant_id'
        );
    }

    public function getAffiliateId()
    {
        return Mage::getModel('core/cookie')->get(
            SmashingMagazine_Affiliate_Model_Observer::COOKIE_KEY_SOURCE
        );
    }
}

Enabling Or Disabling Our Module Through The Admin Panel

With this simple modification to our template, conversion.phtml, we can prevent the image from appearing on the checkout success page if the module status is disabled in the Magento admin panel.

<&quest;php if ($this->getIsActive()): &quest;>
<img
    src="http://media.smashingmagazine.com/themes/smashingv4/images/logo.png
             &quest;merchant_id=<&quest;php echo $this->getMerchantId() &quest;>
             &affiliate_id=<&quest;php echo $this->getAffiliateId() &quest;>"
    width="1"
    height="1"
/>
<&quest;php endif; &quest;>

Similar if statements can be added in Observer.php to prevent the affiliate cookie being checked for and saved.

Summary

By carefully considering the way we structure our code, and utilizing the Magento admin panel, we can create community modules that are portable enough to simply drop on to any Magento website and they will work out of the box. They can then be customized and fine tuned as required to suit the needs of each website.

Feel free to view or download the source code (Github), and, as always, I welcome any questions and would love to hear any feedback in the comments area below.

(cp)

↑ Back to top

  1. 1

    It appeared exactly when I’m working on something similar. Will go through it and keep posting.
    Thanks for your article!!!

    0
  2. 2

    This is a great article man! Very well explained – one of the best extension tutorials I’ve read.

    Nice work!

    0
  3. 3

    Is it something like fetching amazon products to any magento website and sell ..is this script works that way …can some one guide me on the same

    Regards
    Pankaj

    0
  4. 4

    Hi Joseph, Affiliate tracking module will assists affiliate campaign by allowing the generation of a product feed in the Affiliate Window XML format. Affiliate Window is the leading performance marketing network in the world, and this module allows you to easily keep track of things from your Magento site. Offering easy implementation and efficient optimization you can make the most of your campaign with a network that’s known for producing results.
    Regards
    Paul D. Mitchell

    0
  5. 5

    This is extremely helpful. Thanks so much!

    0
  6. 6

    excellent tutorial, exactly what I needed. Thank you.

    0
  7. 7

    I am looking to start an affiliate program for my website, i am guessing that this modules will allow me to capture the referral for my product. i mean the affiliate from whom the client came? Is it?
    Thakn you

    0
  8. 8

    Thankyou for sharing this tutorial.

    I wanted to ask if I could use this for something similar whereby I offer a subscription product from the checkout success page?

    I will need to keep a record as to whether the subscription was successful or not inside Magento…

    Any thoughts would very appreciated.

    0
  9. 9

    I forgot to mention that the subscription product is through a third party…

    0
  10. 10

    useless article, magento 2 is coming out in a few months and you have to learn how to make module from scratch..

    0
  11. 11

    how to use step by step please tell me

    0
  12. 12

    HI !

    This is one of nice tutorial which have a pertinent code work flow you have mentioned here every line in more logical way, which are really understandable .
    I like it very much.

    Thanks

    0
  13. 13

    Perfecte v-hals T-shirts

    March 9, 2014 11:38 am

    Very nice and thanks for sharing

    This code is probably based on 1.7.0.2 – any chance to get this working on 1.8.1? of is this extension available somewhere, like on github?

    many thanks, Sean

    0

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top