The Amazon Associates program, in conjunction with Amazon Web Services (AWS), offers developers a powerful means to search the Amazon Store's catalog using a RESTful API and integrate data for virtually any product listing into a web page. If you are a programmer with a business mindset and looking for ways to monetize your site, you can sign up to be an Amazon affiliate and earn a commission every time a user clicks on your product link and completes a purchase. (And as far as affiliate revenue programs go, Amazon's commissions are among the most generous.)
For those of you who are new to the idea of affiliate marketing, let's suppose you are running a blog or website featuring movie reviews. For every movie review that is posted, you can benefit by including a link for readers to buy the movie somewhere near your article. To make it more attractive to click, the link can include a thumbnail image of the DVD, the full movie name, the lowest price available, and a "Buy" button.
In this tutorial, I will show you how to create a dedicated, reusable class in PHP that connects to the Product Merchandising API and fetches XML data based on the name of the product and the shopping category (in this case "Movies"). At the end, we will populate that data into a small, fully customizable HTML template so that we end up with a working demo of a mini affiliate module. Let's get started.
[Please note before accessing the example demo that if you opt to purchase or rent the DVD from the link provided, it will result in a commission payment to Envato Tuts+.]
Registering for an Affiliate ID and Getting Credentials
First, sign up for an account at affiliate-program.amazon.com. It's free, and you just need to follow the steps to create a profile for your website. You'll be asked to verify your identity at the end of the process by entering your phone number, receiving an automated call, and entering the passcode that you are given.
Once you've completed the registration, you will be given an Amazon Affiliate ID (in this example, it is "envtuts-20"). Log in to your main account area with your username and password and click on the Product Merchandising API tab. There, click on the link to get started using the API.
You will need to sign up for Amazon Web Services (aws.amazon.com), if you have not done so already. In the AWS console, under Security Credentials, you can generate an access key and secret key for making API requests. (Note: it is important to keep these credentials private and secure. Never expose them publicly on GitHub or anywhere else online.)
Setting Up Our PHP Helper Class
For the purposes of this example and to keep things relatively simple, we'll implement the Singleton design pattern to create a static helper class called AmazonAssocHelper
. It will include a public method that connects to the API, searches the catalog, and returns product data using certain parameters that we pass. Here is how I set it up:
<?php namespace App\Services; use App\lib\vendor\aws\AWSSignedRequest; class AmazonAssocHelper { //AMAZON AFFILIATE PROGRAM ACCESS -- sign into affiliate-program.amazon.com const AMZ_ASSOC_TAG = 'your-affiliate-id'; //AWS credentials -- sign into aws.amazon.com const AMZ_ASSOC_ACCESSKEY = 'YOUR_ACCESS_KEY'; const AMZ_ASSOC_SECRETKEY = 'YOUR_SECRET_KEY'; //Set the values for some of the search parameters private static $operation = "ItemSearch"; private static $version = "2013-08-01"; private static $response_group = "Small,Images,OfferSummary"; protected function __construct() { } /** * Fetches relevant product data in product API from keyphrase and category * returns: array of data from the top result node */ public static function fetch_product_data($keyphrase, $category) { $result_xml = self::get_search_results($keyphrase, $category); //return an array containing the item name, link, image, and price return self::get_top_result_data($result_xml); } }
The first couple of lines declare a namespace for our class and include a third-party class that we'll need to make a signed request to the API with our credentials. The three constants I've defined are our all-important affiliate ID, which we obtained after signing up, and the AWS access key and secret key. When we make our API requests, the affiliate ID automatically gets inserted into the product link we retrieve so that purchases can be appropriately traced to our account and we get paid! Be sure to fill in all three of these values for yourself.
The three private variables refer to search parameters that we'll supply for every request. I am using "ItemSearch" as our operation choice to look up results. "2013-08-01" is the date of the latest API version, which we also need to supply to our signed request, and lastly, we must designate which response groups we would like returned. There are 55 choices altogether, and they vary in size and type of data returned. For our purposes, we are looking for the full title of the movie and the full URL for the detail page, both of which are in the "Small" response group. We also need a thumbnail image from the "Images" response group, and the lowest new price from the "OfferSummary" group.
The fetch_product_data()
function is our publicly exposed method that takes two parameters:
- a specific key-phrase referring to the name of the product we want to search for
- an accepted Amazon product category
It will ultimately return an array of data pertaining to the top search result parsed from an XML response. (In many cases, the first result will be the product we want, assuming that the key-phrase we supply is specific enough. As an alternative, you may decide to modify this example to search by the specific ASIN or ID number of the product instead, but there are pros and cons to this method. If you have a large database of article pages for different movies, using a name-based approach will most likely prove more efficient, as it will allow you to automatically generate product links for all of your articles instantly, and save you the extra time and effort of having to manually look up and insert IDs for each one.)
Generating a Signed Request
The next step is to add a helper class that will make the signed request to the product API. I used a third-party function written by Ulrich Mierendorff and wrapped it in its own static class called AWSSignedRequest
. It takes several parameters, including the region (for the U.S. store, we use "com"), an array of request parameters, our AWS keys, response groups, and the API version that we've already defined. The get_signed_request()
method essentially builds and returns a full URL request string from all of these pieces.
The final signed request URL will look like this:
http://webservices.amazon.com/onca/xml? AWSAccessKeyId=AKIAJILHUTAJ5MMQRJWA &AssociateTag=envtut-20 &Operation=ItemSearch &ResponseGroup=Small%2CImages%2COfferSummary &SearchIndex=Movies &Service=AWSECommerceService &Timestamp=2015-11-06T20%3A34%3A52Z &Title=Jurassic%20World &Version=2013-08-01 &Signature=nwkfvvLCvl11Cunuthz6orXCbBMnBc1Z7S3HSfYW%2B14%3D
In the AmazonAssocHelper
class, place the call to get_signed_request()
in a method called get_search_results()
and pass in an array containing four search parameters ($params
) along with the other arguments as shown below:
/** * Runs search with signed request on product API using keyphrase and category name * returns: XML object */ private static function get_search_results($keyphrase, $search_index) { //Define the request $params = array("SearchIndex"=>$search_index, //the category "Title"=>$keyphrase, "Operation"=>self::$operation, "ResponseGroup"=>self::$response_group); $request = AWSSignedRequest::get_signed_request('com', $params, self::AMZ_ASSOC_ACCESSKEY, self::AMZ_ASSOC_SECRETKEY, self::AMZ_ASSOC_TAG, self::$version); $response = file_get_contents($request); //for testing /*header('Content-type: application/xml'); echo $response; exit;*/ return simplexml_load_string($response); }
Our $params
array includes the operation and the response that we've already defined, as well as values for 'Title' and 'SearchIndex'. In this case, the SearchIndex is simply the product category we are searching on. Now, we can invoke PHP's built-in file_get_contents()
function to generate a response from the request, and since the response is an XML document, we use another built-in function, simplexml_load_string()
, to load it as an object that we can parse.
Testing the Response and Previewing XML Data
At this point, we'll want to be able to try out our API request with some real product values so we can see an actual example of what our returned XML data looks like before parsing it. As shown above, add a few test lines to set the header to application/XML and print out the $response
before it gets returned.
Now, create a file that will serve as the main page where the product link will be shown. I have decided to call it index.php. At the top, add the following bit of code:
<?php namespace App\Services; require_once getcwd() . '/App/app_global.php'; /** * Call the Product API by passing in the name of the product and the shopping category * ("Books", "Movies", and "VideoGames" are examples -- see API documentation for full list) */ $product_data = AmazonAssocHelper::fetch_product_data("Jurassic World", "Movies"); ?>
The first two lines declare our namespace and include a global function that allows our class files to be auto-loaded, since we are not using a PHP framework. The next line is the important one. We call the fetch_product_data()
public method on our AmazonAssocHelper
class and assign it to an array variable called $product_data
, passing in a specific movie name as the key-phrase (I chose "Jurassic World" from the current list of best sellers) and "Movies" as the category.
When you load your page, the beginning of your XML response should look like this:
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2013-08-01"> <OperationRequest> <RequestId>807ede04-f196-44a0-99ff-ff1bb1c12fee</RequestId> <Arguments> <Argument Name="AWSAccessKeyId" Value="AWS_ACCESS_KEY"/> <Argument Name="AssociateTag" Value="envtut-20"/> <Argument Name="Operation" Value="ItemSearch"/> <Argument Name="ResponseGroup" Value="Small,Images,OfferSummary"/> <Argument Name="SearchIndex" Value="Movies"/> <Argument Name="Service" Value="AWSECommerceService"/> <Argument Name="Timestamp" Value="2015-11-03T21:32:57Z"/> <Argument Name="Title" Value="Jurassic World"/> <Argument Name="Version" Value="2013-08-01"/> <Argument Name="Signature" Value="QhUpga/f2MReU4xpdzCr432hMvxeA72N+v+G0hVi17M="/> </Arguments> <RequestProcessingTime>0.0887890000000000</RequestProcessingTime> </OperationRequest> <Items> <Request>...</Request> <TotalResults>37</TotalResults> <TotalPages>4</TotalPages> <MoreSearchResultsUrl>...</MoreSearchResultsUrl> <Item> <ASIN>B016W01EXI</ASIN> <DetailPageURL> http://www.amazon.com/Jurassic-World-Chris-Pratt/dp/B016W01EXI%3FSubscriptionId%3DAKIAJILHUTAJ5MMQRJWA%26tag%3Denvtut-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB016W01EXI </DetailPageURL> <ItemLinks> <ItemLink> <Description>Technical Details</Description> <URL> http://www.amazon.com/Jurassic-World-Chris-Pratt/dp/tech-data/B016W01EXI%3FSubscriptionId%3DAKIAJILHUTAJ5MMQRJWA%26tag%3Denvtut-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB016W01EXI </URL> </ItemLink>
If you scroll and scan the output, you should be able to see all of the data that will be useful to our demo, including the <DetailPageURL>
, <Title>
, <LargeImage>
, and <LowestNewPrice>
. There are the nodes we will focus on parsing.
Parsing the Response Object
In the AmazonAssocHelper
class, create another method called get_top_result_data()
that takes our XML response object as the parameter. We can now start reading the nodes that contain the data that we want to capture, but before we go there, let's plan on incorporating some error checking. I've added a couple lines at the top of our function to invoke a method that will take care of parsing Error nodes for us, and I'll go over that in more detail toward the end of this tutorial.
/** * Parses top result node, and its attributes, from XML object * returns: array of product data */ private static function get_top_result_data($xml) { if (!empty(self::handle_errors($xml))) { return array('error'=>self::handle_errors($xml)); } //get the first result node $first_item = $xml->Items[0]->Item; $item_title = $first_item->ItemAttributes->Title; $item_link = $first_item->DetailPageURL; $item_image = $first_item->LargeImage->URL; $item_price = $first_item->OfferSummary->LowestNewPrice->FormattedPrice; return array( 'title'=>(string)$item_title, 'link'=>(string)$item_link, 'image'=>(string)$item_image, 'price'=>(string)$item_price ); }
As you can see in the code above, we start by retrieving the first item node (Item[0]
) from the XML object and storing it in a variable ($first_item
). Then, we operate on each of its children to collect the pieces of information we want ($item_title
, $item_link
, $item_image
, $item_price
). Finally, we package it all into an array with named attributes and return the array.
Handling Errors
Create a method called handle_errors()
and pass the same XML object. Based on the way this API is set up, multiple error nodes with specific codes and messages are set to appear in either the <OperationRequest>
node or the Items <Request>
node. You can test for them by setting an invalid search index or response group. The following code loops through each of the nodes and pushes the error messages into an array that we return:
/** * Checks for errors in the request/ result * returns: array with message(s) describing the "error" */ private static function handle_errors($xml) { $errors_arr = array(); //process errors in request foreach ($xml->OperationRequest->Errors->Error as $error) { error_log("Error code: " . $error->Code . "\r\n"); error_log($error->Message . "\r\n"); error_log("\r\n"); array_push($errors_arr, (string)$error->Message); } //check for invalid category, no matches, or other search error foreach ($xml->Items->Request->Errors->Error as $error) { error_log("Error code: " . $error->Code . "\r\n"); error_log($error->Message . "\r\n"); error_log("\r\n"); array_push($errors_arr, (string)$error->Message); } return $errors_arr; }
Populating the Data in an HTML Template
At this point, you can remove the XML testing calls as we are ready to look at the final data output. In index.php, print the returned $product_data array
and load the page. You should see something like this:
Array ( [title] => Jurassic World [link] => http://www.amazon.com/Jurassic-World-Chris-Pratt/dp/B016W01EXI%3FSubscriptionId%3DAKIAJILHUTAJ5MMQRJWA%26tag%3Denvtut-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB016W01EXI [image] => http://ecx.images-amazon.com/images/I/51ePeFXU2ML.jpg [price] => $5.99 )
Once you've verified that the data looks correct for each of the four properties we are interested in, you can try running the call on other movies or products for comparison. Note that the affiliate ID (in this case "envtut-20") has automatically been inserted into the 'link' as we should expect.
As a final step, let's code a simple block of HTML to display our movie data and make it link to the purchase page. In index.php, add the following underneath the PHP block:
<!doctype html> <html> <head> <title>Tuts+ Demo: Amazon Product API</title> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"> <link rel="stylesheet" href="css/reset.css" /> <link rel="stylesheet" href="css/style.css" /> </head> <body> <?php if (!empty($product_data) && array_key_exists("error", $product_data) != 1): ?> <div class="affiliate_wrap"> <a href="<?php echo $product_data['link']; ?>" target="_blank"> <img src="<?php echo $product_data['image']; ?>" alt="<?php echo $product_data['title']; ?>" /> </a> <h2> <a href="<?php echo $product_data['link']; ?>" target="_blank"><?php echo $product_data['title']; ?></a> </h2> <p><em><?php echo $product_data['price']; ?></em> on Amazon</p> <a class="button" href="<?php echo $product_data['link']; ?>" target="_blank">Buy Now</a> </div> <?php endif ?> </body> </html>
Note that in the event of an error being returned, I've opted to hide the display altogether, but alternatively, you could display the appropriate error message in its place.
Now, with CSS styles added, the final output would look something like this:
And there you have it! Once you've launched your affiliate module into the world, you can track all of the purchases and earnings by signing in to your account.
Conclusion
In this tutorial, we covered all of the steps required for a PHP programmer to build a scalable code solution that instantly generates a custom affiliate link for virtually any product in the Amazon Store. Based on your needs or preferences, you can decide to change or extend this example with different parameters.
You may also explore the API documentation in more detail on your own to discover even more of what is possible. The real power in using the Product Merchandising API lies in the flexibility it provides for arranging and displaying a full range of real-time information about a product or list of products. With Amazon's default link generator (no coding knowledge required), you do not get nearly the same amount of control, and your options are limited.
The API really shines on web pages that are generated dynamically and on pages where you need a higher level of control over the appearance of your links, so if you are working on a website or blog along these lines and looking to incorporate monetization, I hope that you have found this tutorial useful.
Comments