HowTo: Paypal tracking via IPN script

Disclaimer: I am not a professional programmer. There may be better ways to go about this, but the following solution has worked for me.

Piwik 1.3 and up now provides the ability to force the visitorID and attributionInfo when using the tracking API. This is particularly exciting for those of us using Paypal as the payment gateway for an ecommerce site. Google Analytics doesn’t play nice with Paypal - their “solution” for ecommerce tracking is to place their tracking code in your paypal landing page, turn on auto-return and PDT. Unfortunately, most people don’t bother to continue to your landing page after completing their purchase on Paypal’s site. Which is why IPN is the only reliable way to get payment info from paypal to your site’s backend processes. Since javascript will not be executed in an IPN script, Piwik’s php tracking API now comes to the rescue!

Implementing e-commerce tracking for paypal users is a two step process:

Step 1: Modify the Piwik javascript tracking code on your order page. This is the last page on your site before you send users to paypal. We are going to retrieve the visitorID and attributionInfo from Piwik, and put it into paypal’s custom variable for later use.

1a: modify your paypal buy now form as follows:

  • If you haven’t already, give the form a name.
- Pass the following hidden input. See next step if you were already using the custom var.

1b: In my scenario, I was already using the custom var. So, I need to pass the php var I was using for this to javascript, where I will append the piwik information to it in step 1c.

// my existing code... then...
// Pass $custom to javascript (for piwik tracking later)
<script type="text/javascript">
		jsCustom = "<?php echo $custom; ?>";

//... the rest of my existing php code...

1c: Modify the standard Piwik JS tracking code to also grab the visitorID, etc. from Piwik, and forward it via paypal.

<!-- Piwik tracker customized for this page only -->
<script type="text/javascript">
var pkBaseURL = (("https:" == document.location.protocol) ? "" : "");
document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
</script><script type="text/javascript">
try {
var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 1);
[b]visitorId = piwikTracker.getVisitorId();
attributionInfo = JSON2.stringify(piwikTracker.getAttributionInfo());
jsCustom = jsCustom + "&" + visitorId + "&" + attributionInfo;[/b]
document.ppbuynow.custom.value = jsCustom;
} catch( err ) {}
</script><noscript><p><img src="" style="border:0" alt="" /></p></noscript>
<!-- End Piwik Tracking Code -->

Notice that I’ve taken my existing data that I was already passing via Paypal’s custom var, and appended the piwik data, using ampersands as a delimiter.

Next post will show Step 2: modifying your IPN script.

Step 2: Modify your IPN script.

2a: Retrieve the custom var from paypal, and parse it:

$temp = $_POST['custom'];
// <snip> & <snip> & ip & visitorId & attributionInfo

list($<snip>, $<snip>, $custIP, $visitorId, $attributionInfo) = explode("&", $temp, 5);

Note: it’s very important to limit the explode with the optional Limit parameter (5 in my case). This is because you cannot guarantee that the attribution info won’t contain your delimiter character. Since you are placing the attributionInfo last, explode will grab the first 4 items (in my example) by respecting the delimiter, then whatever is left will get assigned to $attributionInfo even if it contains multiple ampersands. If your custom data (if you have any) might contain ampersands, then pick a different delimiter character throughout this howto guide (including in step 1).

2b: Run a bit of cleanup, just in case your custom variable exceeded paypal’s 255 character limit:

// Did attributionInfo get truncated by paypal?
if (substr($attributionInfo,-1) != ']')
	if (substr($attributionInfo,-1) != '"')
	        $attributionInfo .= '"]';
	        $attributionInfo .= ']';

2c: In the part of your IPN script that runs if the transaction is verified, after you’ve done any additional order checking and you have vetted this order as valid, add the following:

//Piwik Analytics
$piwikTracker = new PiwikTracker( $idSite = 1 );

$piwikTracker->setTokenAuth("your Token - click API at the top of your Piwik screen to find");


$pwkResult = $piwikTracker->doTrackGoal($idGoal = 1, $revenue = $cartSub);

$piwikTracker->doTrackPageView('IPN Script');

When debugging, it is helpful to enable tracker debugging:
In the file path/to/piwik/piwik.php, you can set $GLOBALS[‘PIWIK_TRACKER_DEBUG’] = true;

Then, have your IPN script email you after completing a goal, making the body of the email be the variable $pwkResult:

mail("","Debug info", $pwkResult, $headers);

I hope this was helpful. Again, I’m sure there are cooler, tighter, better ways to accomplish some of what I’ve done in this example, but this works for my site. If any of you PHP guru’s out there spot any issues, especially any potential security holes, please do post.

Thank you for the post…I have been looking to use the IPN Script for tracking my PayPal. I love this forum.

I’m trying to modify this for the post WorldPay callback scripts. Not got as far as the goal triggering yet! Just trying to get the Checkout --> WorldPay and WorldPay --> “callback script on our site” to tie up into a single visit. The Checkout --> WorldPay is seen as visit 1 and this has a valid IP address but the WorldPay --> callback script is seen as a second, and separate, visit and has the IP address of our own server! I assume this is a result of the way WorldPay calls the callback script, but I’m not 100% sure!

Can anyone tell me how important it is to set the Visitor ID and the Attribution Info parameters. I don’t actually know what the Attribution Info is!


Attribution info = referrer information, used by Piwik to attribute a Goal conversion to a particular Keyword/website URL used to find your website in the first place (optional but highly recommended)

visitor ID = used to make sure you record the page view / goal conversion / whatever you record to the same visitor, and avoid creating a new visit/visitor when you later on record a goal conversion.

I had just reached this conclusion Matt :slight_smile: In answer to my earlier post and your response I know realise that this is what the Visitor ID does. Using the setTokenAuth(…), setIp(…), setUrl(…), setUrlReferrer(…) sequence above the goals are now being assigned to the correct visitor but they are still being split apart from the visitors original visit block. So the next step is to try passing the Visitor ID to WorldPay and try extracting that for Piwik. Hopefully everything including the post WorldPay return visit will then appear all in the same block.

Setting the Visitor ID does indeed tie the two ends up, so now we can see, all in one block, a visit terminating in a conversion and the goal being triggered :)-D So this method also works for WorldPay.

Now to look into how to extend this to track: affiliate site 1 --> our site 2 --> our shop site 3 --> WorldPay site 4 --> post WP on our site 5!!! Then I shall retire :smiley:

Would it be useful to post what I have to get WorldPay working? In a new How-To or in this one?

I have a solution that is very similar to this in my IPN script. After upgrading to 1.8.3 I now get an error returned from
$track_response = $t->doTrackGoal($idGoal = 2, $revenue = $sale_amount);
and the goal is not tracked.

The response is: The parameter ‘ua’ isn’t set in the Request, and a default value wasn’t provided.
This is purposeful, because I do not want the user-agent for the visit changed by this behind the scene script, there is no User-Agent being specified.

Why/when did this become required, and where can I find it to turn it off?

Thanks for any help,


just set &ua=test for example

Does this still work. I am using the piwik version 1.12. I’ve implemented 1a, 1b and 1c of the first part of the instructions with relative ease. However the second part was confusing. What is the location of the IPN script (php)? Is it a native file in the piwik setup or does it need to be created from scratch? (I have created it from scratch putting in the pieces of code you provided) but nothing happened. Also I wasn’t sure if the Token Auth in the following statement $piwikTracker->setTokenAuth(“your Token - click API at the top of your Piwik screen to find”);
should be entered as the entire string? (ex: “&token_auth=XXXXXXXXxxxxxXXXXXXXXXXxxXXXxX”) or as only the number (ex: XXXXXXXXxxxxxXXXXXXXXXXxxXXXxX)?

I’m shocked there isn’t an easier way to track PayPal ecommerce. :frowning: However, I’ll keep working on this.


Sorry for the bump, but where is the PiwikTracker class located? I’ve searched google etc. but I cannot find it…

My Piwik install and my IPN script are on completely different cPanel accounts.


See it here: GitHub - matomo-org/piwik-php-tracker: PHP Client for Piwik Analytics Tracking API

Thank you SO much for this post! Just what I needed!