Dec.21

Google Affiliate Network API PHP Script

Google Affiliate Network

I was excited when I read Google had released an API for their Affiliate Network, as I wanted to automate pulling sales data from GAN. But I quickly became disheartened when I realized how difficult it is to use.

Since this was for myself, and not a web-based service that would be used by others, the Simple API method of authorization was good enough. After getting it to work with Picasa, but not GAN, I asked for some help and was introduced to the OAuth 2.0 Playground. This showed me the headers I’d need to send, and how OAuth 2.0 works.

For the record: the Google Affiliate Network API does NOT support Simple API Access. You cannot access the service with an API Key and IP locking.

I now have a working script, and have written up step-by-step instructions on how you can pull orders from GAN automatically each day.

This script is not complete, as it’s up to you to do something with the data once you have it. You can also modify the final call to pull advertisers, instead of orders.

How To & Tips

Jun.30

Google Rank Checker API

I read the other day, It would make life so much easier if Google had a paid rank checker API. Google might not have one, but Raven Tools does

Dec-7 2012 Update

Raven Tools no longer supplies SERP rank data. Use Microsite Masters instead


It would make life so much easier if Google had a paid rank checker API
@mattuk
Matt Sawyer

Not many people know this, but Raven Tools has an API.

Resources

Mar.12

Mar.10

Using the Microsoft adCenter API to Automate Reporting in PHP

Microsoft Advertising LogoAfter nearly three months of on & off coding, banging my head against the table and thumbing through the most comprehensive yet incomprehensible documentation I’ve ever seen, I was finally able to automate downloading stats (impressions, clicks, cost) for my coupon site.

For the longest time, the adCenter API was very closed. However, it now seems they’re opening it up, and I quickly was able to get an API account, but wasn’t able to get anything to work. Even adCenter’s most simple script doesn’t work out of the box, and it’s nearly impossible to figure out what has to be done. Hopefully this post will help you.

How To & Tips

Oct.25

Using the LinkShare Merchandiser Web Service

Linkshare Web Services

Linkshare Web Services

Building a product site using the LinkShare Merchandiser Web Service is one of the easiest and most straight-forward tasks you can do. If you’re just getting started with datafeeds and product-based sites, you may want to start here.

If you don’t have an account, you first need to signup with LinkShare. Then login, and click on Web Services under Links. Next, generate or update your token. You’ll need this when making requests.

Resources

May.18

Adding CJ Products To Your Datafeed Website

I received an email last week asking how to add Commission Junction products to datafeed websites. To do this, take a look at the CJ Web Services. Particularly, the Product Catalog Search Service (REST).

What this does is allows you to specify a merchant’s ID, and get all of the product links for that merchant. In my example, I was grabbing the products from Mighty Leaf Tea.

So this PHP script will sit in your site’s root, like the other admin scripts, and when you pull it up, you’ll be presented with a form, asking for the Advertiser ID. That’s the AID when you’re pulling links, or advertiserId in the URL when you’re looking at the merchant in CJ. For Mighty Leaf Tea, it’s 2346375.

The biggest problem is that the CJ Web Service for Product Catalog Search doesn’t return the product ID! So I had to come up with a way to create an ID for a product. In this case, I based it on the SKU and ended up with:

list($nProductID, ) = explode('E', round(hexdec(md5($oCJProduct->{'sku'}))/1000000000000000000000000));
$nProductID = str_replace('.', '', $nProductID);
$nProductID = abs($nProductID);

If you don’t know what this is doing, don’t worry about it. If you’re bored, look-up those functions & let me know if you can think of a better way to make an ID from a sku. There has to be a simpler solution.

Anyway, because of this, I had to change the ProductID field to a bigint(20), since these item IDs were quite large (ex: 22229748315941). In addition, whenever the front-end scripts type-juggled the itemID into an (int), I had to remove that, as int’s are not big enough to hold this data (browse.php and item.php).

Getting back to the script, when I enter 2346375 in the form, then submit it, this script will query CJ for all product links for that merchant, insert or update them in the database, and finally remove any old products.

<?php
	include('./vars.php');
	include('./admin-password.php');
	$bNavHome = true;
	include('./header.php');

	$cTitle = 'Get CJ Datafeed';
?>
<div class="post">
<h2 class="title"><?= $cTitle ?></h2>
<div class="entry">
<?php

	if (!empty($_GET['advertiser-id'])) {
		$cDevKey = 'youridhere';
		$nWebSiteID = "yourwebsiteid";

		$nMax = 1;

		// We'll use this later to determine which products can be deleted
		mysql_query("update products set bActiveProduct=0 where MerchantID=" . (int)$_GET['advertiser-id'] . "");

		function addCJProduct($oCJProduct) {
			list($nProductID, ) = explode('E', round(hexdec(md5($oCJProduct->{'sku'}))/1000000000000000000000000));
			$nProductID = str_replace('.', '', $nProductID);
			$nProductID = abs($nProductID);

			if (mysql_num_rows(mysql_query("select * from products where ProductID=" . $nProductID . " and MerchantID=" . (int)$oCJProduct->{'advertiser-id'} . " limit 1")) == 0) {
				// This is a new record
				$cQuery = "insert into products (ProductID, Name, MerchantID, Merchant, Link, Thumbnail, BigImage, Price, RetailPrice, Description, Lastupdated, Manufacturer, PartNumber, ISBN, UPC, SKU, bActiveProduct) values (" . $nProductID . ", '" . myres($oCJProduct->{'name'}) . "', '" . myres($oCJProduct->{'advertiser-id'}) . "', '" . myres($oCJProduct->{'advertiser-name'}) . "', '" . myres($oCJProduct->{'buy-url'}) . "', '" . myres($oCJProduct->{'image-url'}) . "', '" . myres($oCJProduct->{'image-url'}) . "', '" . myres($oCJProduct->{'price'}) . "', '" . myres($oCJProduct->{'retail-price'}) . "', '" . myres($oCJProduct->{'description'}) . "', now(), '" . myres($oCJProduct->{'manufacturer-name'}) . "', '" . myres($oCJProduct->{'sku'}) . "', '" . myres($oCJProduct->{'isbn'}) . "', '" . myres($oCJProduct->{'upc'}) . "', '" . myres($oCJProduct->{'sku'}) . "', 1)";
			} // ends <insert new record>
			else {
				// This is an existing record
				$cQuery = "update products set Name='" . myres($oCJProduct->{'name'}) . "', MerchantID=" . (int)$oCJProduct->{'advertiser-id'} . ", Merchant='" . myres($oCJProduct->{'advertiser-name'}) . "', Link='" . myres($oCJProduct->{'buy-url'}) . "', Thumbnail='" . myres($oCJProduct->{'image-url'}) . "', BigImage='" . myres($oCJProduct->{'image-url'}) . "', Price='" . myres($oCJProduct->{'price'}) . "', RetailPrice='" . myres($oCJProduct->{'retail-price'}) . "', Description='" . myres($oCJProduct->{'description'}) . "', Lastupdated=now(), Manufacturer='" . myres($oCJProduct->{'manufacturer-name'}) . "', PartNumber='" . myres($oCJProduct->{'sku'}) . "', ISBN='" . myres($oCJProduct->{'isbn'}) . "', UPC='" . myres($oCJProduct->{'upc'}) . "', SKU='" . myres($oCJProduct->{'sku'}) . "', bActiveProduct=1 where ProductID=" . $nProductID . " and MerchantID=" . (int)$oCJProduct->{'advertiser-id'} . " limit 1";
			} // ends else from <updating existing record>
			mysql_query($cQuery);
			if (mysql_error()) {
				echo("<p><b>MySQL Error: " . mysql_error() . "<br />\n");
				echo("$cQuery</p>");
				echo("<pre>");
				print_r($rsItem);
				echo("</pre>");
				exit();
			} // ends if (mysql_error())

		} // ends function addCJProduct($oCJProduct)

		for ($nPageNumber = 1; $nPageNumber <= $nMax; $nPageNumber++) {

			$cURL = 'https://product-search.api.cj.com/v2/product-search?';
			$cURL .= 'advertiser-ids=' . $_GET['advertiser-id'] . '&';
			$cURL .= 'records-per-page=1000&';
			$cURL .= 'page-number=' . $nPageNumber . '&';
			$cURL .= 'website-id=' . $nWebSiteID;

			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $cURL);
			curl_setopt($ch, CURLOPT_HTTPHEADER, array(
						  'Authorization: ' . $cDevKey,
						  'User-Agent: "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.15) Gecko/2009101601 Firefox/3.0.15 GTB6 (.NET CLR 3.5.30729)"'
						));

			curl_setopt($ch, CURLOPT_HEADER, false);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
			curl_setopt($ch, CURLOPT_TIMEOUT, 10);

			$cHTML = curl_exec($ch);
			if (curl_error($ch)) {
				echo "Curl error: " . curl_error($ch);
			} // ends if (curl_error($ch))
			else {
				$cXML = simplexml_load_string($cHTML);

				// Update nMax
				if ($cXML->products->attributes()->{'total-matched'} > 1000) {
					$nMax = floor($cXML->products->attributes()->{'total-matched'}/$cXML->products->attributes()->{'records-returned'});
				} // ends if ($cXML->products->attributes()->{'total-matched'} > 1000)

				for ($i = 0; $i < count($cXML->products->product); $i++) {
					addCJProduct($cXML->products->product[$i]);
				} // ends

			} // ends else from if (curl_error($ch))
		} // ends for loop

		mysql_query("delete from products where bActiveProduct=0 and MerchantID=" . (int)$_GET['advertiser-id'] . "");

		echo("<p><strong>" . $cXML->products->attributes()->{'total-matched'} . "</strong> products have been loaded.</p>");

		echo("<p>There are " . mysql_num_rows(mysql_query("select * from products where MerchantID=" . (int)$_GET['advertiser-id'] . "")) . " products.</p>");
	} // ends if (!empty($_GET['advertiser-id']))
	else {
		// Show form
		?>
		<form method="get" action="<?= $_SERVER['PHP_SELF'] ?>">
			CJ Advertiser ID: <input type="text" name="advertiser-id" /><br />
			<input type="submit" name="cAction" value="Get Datafeed" />
		</form>
		<?php
	} // ends else from if (!empty($_GET['advertiser-id']))
?>

</div>
</div><!-- ends class="post" -->

<p align="center">[ <a href="<?= $_SERVER['PHP_SELF'] ?>"><?= $cTitle  ?> Home</a> | <a href="./admin.php" target="_top">Admin Home</a> ]</p>
<?php
	include('./footer.php');
?>

Because CJ products do not include categories, I had to change my browse.php to search the product title for the category (green, black, white). To create a quality site, however, I recommend you put products in their appropriate category using the custom category script.

You’ll notice I output 2 numbers at the end – the first is how many products were returned, and the second is how many were loaded into the database. For Mighty Leaf Tea, it loaded 413 products, but only 410 were in the database when I was done. Either 3 products had duplicate SKUs, or 3 products ended up with duplicate ProductIDs after my process was done to generate a ProductID from SKU. I’m not sure, but 410/413 is pretty good, so I left it alone.

This script was really hacked together – let me know if you have questions / problems.

Resources

Jan.26

Tracking ShareASale Commissions with Prosper202

Prosper202 is a powerful affiliate marketing tracking software package, with the ability to give you your revenue, cost, and ROI on your PPC campaigns. But the true power is shown when you automate as much as possible.

I’ve previously written how to use the ShareASale API, but left it up to you to fill in the blanks. In this example, I’m going to show you how to automatically get your ShareASale revenues to post back to Prosper202.

Before we begin, make sure you’re read the first post I wrote, or else this one will go by quickly. What this script does is fetches your sales from the previous day and submits the values and SIDs to your Prosper202 installation.

$dYesterday = date("m/d/Y", time()-86400);

$cURL = 'https://shareasale.com/x.cfm?action=activity&affiliateId=yourid&token=yourtoken&dateStart=' . $dYesterday . '&dateEnd=' . $dYesterday . '&XMLFormat=0';
// $cURL = 'sas.csv';

$fp = fopen($cURL, "r");
if ($fp) {
	$rsMap = array();
	while (empty($rsMap) && (($rsHeadings = fgetcsv($fp, 1000, "|")) !== FALSE)) {
		if (md5(serialize($rsHeadings)) != '5b448a7bdbeea0be7d7f758f5f8ee90b') {
			// echo(md5(serialize($rsHeadings)) . "\n");
			// print_r($rsHeadings);

			while (list($nIndex, $cColumn) = each($rsHeadings)) {
				// echo("$cColumn => $nIndex<br />\n");

				$cColumn = ereg_replace("\(.+\)", "", $cColumn);

				$rsMap[$cColumn] = $nIndex;
			} // ends while (list($nIndex, $cColumn) = each($rsHeadings))
			// print_r($rsMap);
		} // ends
	}

	/*
	Array
	(
		[Trans ID] => 0
		[User ID] => 1
		[Merchant ID] => 2
		[Trans Date] => 3
		[Trans Amount] => 4
		[Commission] => 5
		[Comment] => 6
		[Voided] => 7
		[Pending Date] => 8
		[Locked] => 9
		[Aff Comment] => 10
		[Banner Page] => 11
		[Reversal Date] => 12
		[Click Date] => 13
		[Click Time] => 14
		[Banner Id] => 15
	)
	*/
	while (($rsStatData = fgetcsv($fp, 1000, "|")) !== FALSE) {
		// print_r($rsStatData);
		if (
			(md5(serialize($rsDeal)) != '5b448a7bdbeea0be7d7f758f5f8ee90b') &&
			($rsStatData[$rsMap['Commission']] > 0) &&
			!empty($rsStatData[$rsMap['Aff Comment']]) &&
			($rsStatData[$rsMap['Aff Comment']] != "none")
			) {

			$cPostback = 'http://yourdomain.com/tracking202/static/gpb.php?amount=' . urlencode($rsStatData[$rsMap['Commission']]) . '&subid=' . urlencode($rsStatData[$rsMap['Aff Comment']]);
			// echo("$cPostback\n");
			$fpPostback = @fopen($cPostback, "r");
			if ($fpPostback !== false) {
				fclose($fpPostback);
			} // ends

		} // ends if (!empty($rsDeal))

	} // ends while (($data = fgetcsv($fp, 1000, "|")) !== FALSE)
	fclose($fp);
} // ends if ($fp)

The script creates a URL to grab yesterday’s stats, creates an array of the column headers ($rsMap), loops through the rest of the stats and if:

  • the line isn’t empty
  • the Commission is greater than 0.00
  • there is a SID
  • the SID is not “none”

then it pings your Prosper202 installation and updates the database with the commission for that SID.

Most of the code in this script is just reading the CSV file and getting the column names in place. The real work is done in lines 55-60.

Remember you’re limited to 200 API calls per month with ShareASale, so schedule this script to run daily (via cron).

If you have any questions, please leave them in the comments. And if you haven’t done so already, signup with ShareASale!

How To & Tips

Dec.23

Getting Stats from Google AdWords using the API

Previously I have posted how to automatically get your revenue figures from ShareASale, Commission Junction, and PepperJam. Having that done, the next step to automate my P&L report was to get the advertising costs from Google using the AdWords API.

First, I had to set-up a MCC account and apply for a developer token. In about 24 hours, my developer token was approved and I was ready to roll.

At the time I’m writing this, the AdWords API is on v2009, but reporting is only available via v13. So my script is written for the v13 service.

AdWords, like some other APIs, transfer data via the SOAP protocol.

Start by downloading the AdWords API PHP Client Library, which includes the SOAP library file. I saved these in a folder called “lib”, knowing I may use the other files at a later time.

The script starts by defining SoapClientFactory:

class SoapClientFactory{
  public static function GetClient(
	  $endpoint, $wsdl = false, $proxyhost = false, $proxyport = false,
	  $proxyusername = false, $proxypassword = false, $timeout = 0,
	  $response_timeout = 30) {
	if (!extension_loaded('soap')) {
	  return new soapclientNusoap($endpoint, $wsdl, $proxyhost, $proxyport,
		$proxyusername, $proxypassword, $timeout, $response_timeout);
	} else {
	  return new soapclientNusoap($endpoint, $wsdl, $proxyhost, $proxyport,
		$proxyusername, $proxypassword, $timeout, $response_timeout);
	}
  }
}

Then define the SOAP headers and set them

$headers =
	'<email>' . $email . '</email>'.
	'<password>' . $password . '</password>' .
	'<clientEmail>' . $client_email . '</clientEmail>' .
	'<useragent>' . $useragent . '</useragent>' .
	'<developerToken>' . $developer_token . '</developerToken>' .
	'<applicationToken>' . $application_token . '</applicationToken>';

$namespace = 'https://adwords.google.com/api/adwords/v13';
$report_service = SoapClientFactory::GetClient($namespace . '/ReportService?wsdl', 'wsdl');
$report_service->setHeaders($headers);

Next, I had to define the job I wanted to run. Read through the Report Rules to figure out what you’re looking for; for this example, I did a summary by AdGroup in one Campaign. Note: “Validating a report job costs only 1 API unit, whereas a failed call toscheduleReportJob will cost 500 API units.”

$report_job =
	'<selectedReportType>AdGroup</selectedReportType>' .
	'<name>My Report Name Here</name>' .
	'<aggregationTypes>Summary</aggregationTypes>' .
	'<campaigns>8675309</campaigns>' .

	'<startDay>' . $dYesterday . '</startDay>' .
	'<endDay>' . $dYesterday . '</endDay>' .

	'<selectedColumns>Campaign</selectedColumns>' .
	'<selectedColumns>CampaignId</selectedColumns>' .
	'<selectedColumns>AdGroup</selectedColumns>' .
	'<selectedColumns>AdGroupId</selectedColumns>' .
	'<selectedColumns>AdGroupStatus</selectedColumns>' .

	'<selectedColumns>Impressions</selectedColumns>' .
	'<selectedColumns>Clicks</selectedColumns>' .
	'<selectedColumns>Cost</selectedColumns>' .

	'';

Of course, use your own Campaign ID – if you omit this, you will get all AdGroups from all Campaigns. My first report omitted this, just so I was able to see the proper value to set in there. In case you were wondering, $dYesterday was set earlier in my script as:

$dYesterday = date("Y-m-d", time()-86400);

The rest is some code I pulled from Google’s sample code, but I had to get it from a cached version of the page, so I’ll put it here, too, so everything’s in one place:

$request_xml =
	'<validateReportJob>' .
	'<job xmlns:impl="https://adwords.google.com/api/adwords/v13" ' .
	'xsi:type="impl:DefinedReportJob">' .
	$report_job .
	'</job>' .
	'</validateReportJob>';

# Validate report.
$report_service->call('validateReportJob', $request_xml);
if ($debug) show_xml($report_service);
if ($report_service->fault) show_fault($report_service);

# Schedule report.
$request_xml =
	'<scheduleReportJob>' .
	'<job xmlns:impl="https://adwords.google.com/api/adwords/v13" ' .
	'xsi:type="impl:DefinedReportJob">' .
$report_job .
	'</job>' .
	'</scheduleReportJob>';
$job_id = $report_service->call('scheduleReportJob', $request_xml);
$job_id = $job_id['scheduleReportJobReturn'];
if ($debug) show_xml($report_service);
if ($report_service->fault) show_fault($service);

# Wait for report to finish.
$request_xml =
	'<getReportJobStatus>' .
	'<reportJobId>' .
	$job_id .
	'</reportJobId>' .
	'</getReportJobStatus>';
$status = $report_service->call('getReportJobStatus', $request_xml);
$status = $status['getReportJobStatusReturn'];
if ($debug) show_xml($report_service);
if ($report_service->fault) show_fault($service);
while ($status != 'Completed' and $status != 'Failed') {
	// echo 'Report job status is "' . $status . '".' . "\n";
	sleep(30);
	$status = $report_service->call('getReportJobStatus', $request_xml);
	$status = $status['getReportJobStatusReturn'];
	if ($debug) show_xml($report_service);
	if ($report_service->fault) show_fault($service);
}

if ($status == 'Failed') {
	echo 'Report job generation failed.' . "\n";
	return;
}

# Download report.
$request_xml =
	'<getGzipReportDownloadUrl>' .
	'<reportJobId>' .
	$job_id .
	'</reportJobId>' .
	'</getGzipReportDownloadUrl>';
$report_url = $report_service->call('getGzipReportDownloadUrl', $request_xml);
$report_url = $report_url['getGzipReportDownloadUrlReturn'];
if ($debug) show_xml($report_service);
if ($report_service->fault) show_fault($service);
echo 'Report is available at "' . $report_url . '".' . "\n";

At this point, we have the URL where the report is available, so I use wget to download it, then extract it.

`/usr/bin/wget -q --output-document=temp/$dYesterday.xml.gz $report_url`;
`/bin/gunzip temp/$dYesterday.xml.gz`;

(Those are backticks, not single quotes) Then I use simplexml_load_file to load the report into a variable, which I can then work with:

$xml = simplexml_load_file('temp/' . $dYesterday . '.xml');
$namespaces = $xml->getNamespaces(true);

The script then loops through the rows of data, converts the data into an easily usable format, then it’s up to you do actually do something with it:

for ($i = 0; $i < count($xml->table->rows->row); $i++) {
	$rsData = array();
	foreach($xml->table->rows->row[$i]->attributes() as $var => $val) {
		$rsData[$var] = (string)$val;
	} // ends foreach($xml->table->rows->row[0]->attributes() as $var => $val)

	print_r($rsData);
	/*Array
	(
		[campaignid] => 8675309
		[campaign] => My Campaign Name
		[adgroupid] => 4815162342
		[adgroup] => First AdGroup Name
		[agStatus] => Enabled
		[imps] => 37
		[clicks] => 1
		[cost] => 300000
	)
	*/
} // ends for ($i = 0; $i < count($xml->table->rows->row); $i++)

If you notice, cost seems really high. I did not pay $300,000 for a single click – you have to massage that figure a bit:

(float)($rsData['cost']/1000000)

Finally, remove the XML file from the server

unlink('temp/' . $dYesterday . '.xml');

And stick some helper functions to the end

function show_xml($service) {
	echo $service->request;
	echo $service->response;
	echo "\n";
}

function show_fault($service) {
	// print_r($service);
	echo "\n";
	echo 'Fault: ' . $service->fault . "\n";
	echo 'Code: ' . $service->faultcode . "\n";
	echo 'String: ' . $service->faultstring . "\n";
	echo 'Detail: ' . $service->faultdetail . "\n";
	exit(0);
}

It looks like a lot of programming, but really, if you just put it all together, and adjust the variables as necessary, you’ll be able to automate the process of importing your AdWords advertising costs into whatever tracking system you use.

How To & Tips

Dec.16

Commission Junction Web Services

Next in my series of affiliate network API programming examples, I’ve decided to tackle Commission Junction. The goal was to get a list of my stats from yesterday for one website, and store them in my custom tracking script.

Commission JunctionCJ has two choices when it comes to how to interface with them: REST and SOAP. Since I already had some SOAP code laying around, I decided to go that route (using the Daily Publisher Commission Service) . However, after I had my script done (took about 20 minutes), I realized CJ was only giving back half of my sales! From their website, I saw 6 sales for this one website:

  • 971450243
  • 971361785
  • 971179939
  • 971144045
  • 971097290
  • 971086202

Yet the SOAP API only returned 3 of them:

  • 971086202
  • 971144045
  • 971450243

So I emailed CJ about this (have yet to hear back from them) but being the impatient person that I am, I checked out the REST API. To my surprise, there is no equivalent to their SOAP Daily Publisher Commission Service – the closest I saw was their Commission Detail Service (REST). However, this service doesn’t take in any date parameters – your choice is yesterday or nothing. For this project, that’s OK as that’s what I want.

So the first thing to do is build the query. Starting with the base URL, add on the parameters you’d like. In my case, it’s the date-type and website-ids.

$cURL = 'https://commission-detail.api.cj.com/v3/commissions?';
$cURL .= 'date-type=event&';
$cURL .= 'website-ids=' . $nWebSiteID;

Then, using curl, grab the results:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $cURL);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
			  'Authorization: ' . $cDevKey,
			  'User-Agent: "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.15) Gecko/2009101601 Firefox/3.0.15 GTB6 (.NET CLR 3.5.30729)"'
			));

curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);

$cHTML = curl_exec($ch);
if (curl_error($ch)) {
	echo "Curl error: " . curl_error($ch);
} // ends if (curl_error($ch))
else {
	$cXML = simplexml_load_string($cHTML);
	// var_dump($cXML);

	for ($i = 0; $i < count($cXML->commissions->commission); $i++) {
		addCJCommission($cXML->commissions->commission[$i]);
	} // ends for ($i = 0; $i < count($cXML->commissions->commission); $i++)
} // ends else from if (curl_error($ch))

What I have in the loop is a function, addCJCommission(), which takes the commission object as a parameter. This function will add the commission to my database. I’m not going to get into details about that (as it’s specific to my project, and not what you’re doing) but one thing I found out is that objects with a dash in the property name require some special coding. For example, getting the date from the event-date property:

Don’t do this:

$dDate = date("Y-m-d", strtotime($oCJCommission->event-date));

Do this:

$dDate = date("Y-m-d", strtotime($oCJCommission->{'event-date'}));

To get started with Commission Junction Web Services, visit http://help.cj.com/en/web_services/web_services.htm. Thanks to forums.digitalpoint.com for the discussion on setting the Authorization header with curl.

How To & Tips

Nov.06

Pepperjam Web Services

PepperjamHot on the heels of my post on how to use the ShareASale API, I recently wrote a quick script to get my Pepperjam stats & load them into my tracking database. Using the ShareASale script as a template, I was able to whip this up in about a half hour.

You can see an overview of Pepperjam’s Web Services at webservices.pepperjamnetwork.com . For my task, I wanted to get commissions from yesterday, so I went with the Transaction Details report (I would have used Transaction Summary, but that doesn’t give the program ID, only details does).

Unlike ShareASale, Pepperjam uses your web login (email address & password) to authenticate you to the web services. I don’t like this, because if you’re a marketer & outsource your API work to a programmer, you have to hand over your login information, whereas with ShareASale, you’re authenticated by IP address, affiliate ID & API token.

Pepperjam also does not return column headers in their csv file, so you’ll have to look them up in the online documentation before you finish your script.

Here’s a simple script which grabs yesterday’s stats and outputs them. Of course, this is just to get you started… you’ll want to modify this script to do something actually useful.

<?php
	set_time_limit(0);

	$dYesterday = date("Y-m-d", time()-86400);

	$cUsername = 'you@yourdomain.com';
	$cPassword = 'supersecretsssshhhhh';

	$cURL = 'https://webservices.pepperjamnetwork.com/?target=reports.transaction&startDate=' . $dYesterday . '&endDate=' . $dYesterday . '&username=' . urlencode($cUsername) . '&password=' . urlencode($cPassword) . '&format=csv';

	$fp = fopen($cURL, "r");
	if ($fp) {
		$rsMap = array();
		$nIndex = 0;
		$rsMap['Transaction ID'] = $nIndex++;
		$rsMap['Order ID'] = $nIndex++;
		$rsMap['SID'] = $nIndex++;
		$rsMap['Creative Type'] = $nIndex++;
		$rsMap['Commission Amount'] = $nIndex++;
		$rsMap['Order Amount'] = $nIndex++;
		$rsMap['Transaction Type'] = $nIndex++;
		$rsMap['Transaction Date'] = $nIndex++;
		$rsMap['Transaction Status'] = $nIndex++;
		$rsMap['Program Name'] = $nIndex++;
		$rsMap['Program ID'] = $nIndex++;

		while (($rsStatData = fgetcsv($fp, 1000, ",")) !== FALSE) {
			print_r($rsStatData);

			echo("You made \$" . number_format($rsStatData[$rsMap['Commission Amount']], 2) . " from " . $rsStatData[$rsMap['Program Name']] . "\n");
		} // ends while (($data = fgetcsv($fp, 1000, ",")) !== FALSE)
		fclose($fp);
	} // ends if ($fp)
?>

Let me know if you have questions! I’ll be working with Commission Junction next…

How To & Tips