Using Amazon E-Commerce Services With PEAR SOAP

Integrate Amazon.com's ECS service into your own Web site.

If Wishes Were Horses...

Unless you've been snoozing in a cave for the last few years, you know what Amazon.com is - quite simply, the pioneer of Internet retailing, with a catalog spanning books, DVDs, electronics, home furnishings, cars, cutlery, shoes...the list is endless. In fact, if you can't find it on Amazon.com, chances are good it doesn't exist!

One of Amazon's cooler initiatives in recent years has been its E-Commerce Service (ECS), formerly called the Amazon Web Service (AWS). This service is essentially a set of APIs designed to let users interact with the Amazon database using the SOAP protocol. The idea behind this is simple: to allow independent users to easily create online stores, supported by Amazon's extensive product catalog and transaction back-end. Oh yeah...and it's free!

If this sounds like a sweet deal, it gets sweeter: your favourite language and mine, PHP, can easily be set up to work with the SOAP protocol, either through the external PEAR SOAP class or using the built-in SOAP client that ships with PHP 5.x. The only problem? Not too many people know how to do it.

That's where this tutorial comes in. Over the next few pages, I'll be demonstrating how you can integrate the Amazon ECS service into your own Web site using PHP and the PEAR SOAP class. If you've always wanted your own online book store, pet store or shoe store (or maybe all three), then keep reading - I'm about to make your dream come true!

SOAP-ing Up

In order to understand ECS, you need to first understand SOAP. First and most importantly, it's not something you wash your hands with. Instead, SOAP is an acronyms for the Simple Object Access Protocol, an XML-based method for exchanging information over HTTP.

According to the official definition available on the W3C's site (http://www.w3.org/2000/xp/Group/), SOAP is a "lightweight protocol intended for exchanging structured information in a decentralized, distributed environment. It uses XML technologies to define an extensible messaging framework providing a message construct that can be exchanged over a variety of underlying protocols. The framework has been designed to be independent of any particular programming model and other implementation specific semantics."

Yes, "duh" was my reaction too...

Translated from native Geek, SOAP is basically a way for a client to talk to a server, ask it questions and receive responses. SOAP uses XML and HTTP; it used XML to encode questions (and decode responses) into a format suitable for transmission across a network, and HTTP to actually perform the transmission.

In a typical SOAP transaction, a SOAP client encodes an API call as a SOAP request and transmits it to the SOAP server. The SOAP server receives this request, decodes it, invokes the corresponding function and packages the output as a SOAP response packet that gets transmitted back to the client. The client can then decode the response and use the results of the API call in whatever manner it chooses. The entire process is fairly streamlined and, because of its reliance on existing standards, easy to understand and use.

What does this have to do with ECS, you ask? Quite a lot, actually. In the ECS universe, your PHP application functions as the SOAP client and Amazon's ECS server functions as...well, the SOAP server. Every time you query the Amazon database for a particular product, you're actually performing a SOAP request and receiving back data encoded as SOAP response packets.

There's a lot more to the technical SOAP specification than the brief outline above and if you have trouble falling asleep at night, I'd recommend you read it all. You can find the complete SOAP specification at http://www.w3.org/2000/xp/Group/

In order to get started using PHP, SOAP and ECS (aka AWS 4.0), there are a couple of things you'll need:

· First, you need the PEAR SOAP class, which you can download from http://pear.php.net/package/SOAP. Install it using the instructions provided on the PEAR Web site, at http://pear.php.net/

· Second, you need a ticket to the Amazon.com gravy train. Drop by http://webservices.amazon.com/, register with Amazon.com, and pick up your free developer token. This developer token will be needed to gain access to the ECS service, so hang on to it tight.

· Third, you need a copy of the ECS Developer Guide, freely available from Amazon.com at http://webservices.amazon.com/ . This guide is your roadmap to working with ECS - it contains detailed information on available API calls, input parameters, error codes and return values, together with information on how to integrate and use ECS on different platforms. You'll be referring to it frequently throughout this tutorial.

All set up? Let's rock and roll.

Cracking The Code

I'll begin with a simple example that demonstrates the power of ECS - looking up a book on Amazon.com. Here's the code:

<?php
// include SOAP class
include("SOAP/Client.php");

// initialize SOAP client
$soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

// set data for SOAP request
$params = array('SubscriptionId' => 'YOUR-ID-HERE',
                'ItemId' => '0385504209');

// call an ECS method
$result = $soapclient->call("ItemLookup", $params);

// check for errors
if (PEAR::isError($result)) {
    die("Something went wrong...");
}
// else print ECS return values
print_r($result);
?>

The first order of business is to include the SOAP class file, which contains all the methods needed to access SOAP services, and create an instance of the client. The client is initialized with the URL to Amazon's ECS service (in case you're wondering where I got this URL from, it's listed in the ECS developer kit). Once the client is initialized, its call() method is used to create and transmit a SOAP request to the ECS server; the server's response is then printed to the output device.

Every item in the Amazon.com product catalog is tagged with a unique identifier. This identifier is called an ASIN, or Amazon Stock Identification Number. To look up a specific item in the catalog, ECS offers the ItemLookup() method, which accepts one or more ASINs as input and returns product information for each.

In this script, I've used this ItemLookup() method with ASIN 0385504209, which happens to refer to Dan Brown's "The Da Vinci Code". Here's the response from Amazon.com:

Array
(
    [OperationRequest] => stdClass Object
        (
            [HTTPHeaders] => stdClass Object
                (
                    [Header] =>
                )

            [RequestId] => 1J06J96NXDPE2HVB596S
            [Arguments] => stdClass Object
                (
                    [Argument] =>
                )

            [RequestProcessingTime] => 0.0101399421691895
        )

    [Items] => stdClass Object
        (
            [Request] => stdClass Object
                (
                    [IsValid] => True
                )

            [Item] => stdClass Object
                (
                    [ASIN] => 0385504209
                    [DetailPageURL] => http://www.amazon.com/exec/obidos/redirect?tag=ws%26link_code=sp1%26camp=2025%26creative=165953%26path=http://www.amazon.com/gp/redirect.html%253fASIN=0385504209%2526tag=ws%2526lcode=sp1%2526cID=2025%2526ccmID=165953%2526location=/o/ASIN/0385504209%25253FSubscriptionId=YOUR-ID-HERE
                    [ItemAttributes] => stdClass Object
                        (
                            [Author] => Dan Brown
                            [ProductGroup] => Book
                            [Title] => The Da Vinci Code
                        )

                )

        )

)

Is that appealing, or just appalling? You decide...

A close look at the output above will reveal that no, it's not a bad hair day, but an array of nested objects containing information on the product category (books), together with the book's title, author, ASIN and Amazon.com page URL. It's not particularly readable at the moment, so let's pretty it up a bit before proceeding with the explanation:

<html>
<head><basefont face="Arial"></head>
<body>
<?php
// include SOAP class
include("SOAP/Client.php");

// initialize SOAP client
$soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

// set data for SOAP request
$params = array('SubscriptionId' => 'YOUR-ID-HERE',
                'ItemId' => '0385504209');

// get item information
$result = $soapclient->call("ItemLookup", $params);

// check for errors
if (PEAR::isError($result)) {
    die("Something went wrong...");
}

// else print ECS return values
$data = $result['Items']->Item;
?>
<h2><?php echo $data->ItemAttributes->Title; ?></h2>
<?php echo $data->ItemAttributes->Author; ?>
<p />
<a href="<?php echo $data->DetailPageURL; ?>">Amazon.com Product Page...</a>
</body>
</html>

Here's what it looks like now:

The sharper-eyed amongst you will have noticed that the call to ItemLookup() in the listings above actually contains two parameters. The "SubscriptionId" parameter contains your ECS developer token, while the "ItemId" parameter contains the ASIN to search for. Different ECS method calls require different parameters - look up the developer guide to see a list - but the "SubscriptionId" parameter is mandatory for all of them.

In case you're wondering how I got the ASIN in the first place, it's pretty simple: just look in the URL of the corresponding product page on Amazon.com. For example, if you browse to Amazon.com's product page for "The Da Vinci Code", you'll see that the URL looks like this:

http://www.amazon.com/exec/obidos/ASIN/0385504209/

The product ASIN is the identifier following the ASIN string in the URL. You can use this method to locate ASINs for any product on Amazon.com. Note, however, that the values returned by ItemLookup() differ from product category to product category; a book lookup operation will return author and title information, while a DVD lookup operation will return cast, director and genre information. You can look in the ECS developer kit to understand the expected return values for each Amazon.com product category.

Wanting More...

Now, while the previous listings were certainly illustrative, they weren't very practical. After all, if you want your customers to purchase a book from your online store, you'll have to give them some more information about it - a description, a cover image, maybe some reviews. Heck, at the very least, you'll need to tell them the price (which, if you go back and look closely at the output of the previous script, wasn't actually included in the ECS response packet).

It's precisely to satisfy this need for more information that ECS includes "response groups". A response group is basically a filter that you attach to your ECS request; this filter lets you control the type (and amount) of information returned by ECS. Let's look at one of them in particular, the "Large" response group, which should provide sufficient information to create a detailed product page:

<html>
<head><basefont face="Arial"></head>
<body>
<?php
// include SOAP class
include("SOAP/Client.php");

// initialize SOAP client
$soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

// set data for SOAP request
// ask for "Large" data set
$params = array('SubscriptionId' => 'YOUR-ID-HERE',
                'ItemId' => '0385504209',
                'ResponseGroup' => 'Large');

// get item information
$result = $soapclient->call("ItemLookup", $params);

if (PEAR::isError($result)) {
    die("Something went wrong...");
}

// format and print results
$data = $result['Items']->Item;
?>
    <table cellpadding="10">
    <tr>
    <!-- book cover image -->
    <td>
<?php
if (isset($data->MediumImage->URL)) {
?>
    <img src="<?php echo $data->MediumImage->URL; ?>" align="left">
<?php
}
?>
    </td>
    <td>
        <!-- book title -->
        <b><?php echo $data->ItemAttributes->Title; ?></b>
        <br />
        <!-- author -->
        <?php echo $data->ItemAttributes->Author; ?>
        <p />
        <!-- publisher -->
        Publisher: <?php echo $data->ItemAttributes->Publisher; ?>
        <br />
        <!-- binding -->
        Binding: <?php echo $data->ItemAttributes->Binding; ?>
        <br />
        <!-- pages -->
        Pages: <?php echo $data->ItemAttributes->NumberOfPages; ?>
        <p />
        <!-- price -->
        <b>Amazon.com Price: <?php echo $data->ItemAttributes->ListPrice->FormattedPrice; ?></b>
    </td>
    </tr>
    </table>
    <p />
<?php
// check for editorial review
// print if available
if (isset($data->EditorialReviews)) {
?>
    <b>Book description</b>
    <br />
    <?php echo $data->EditorialReviews[0]->Content; ?>
    <p />
<?php
}
?>
<?php
// check for customer reviews
// print if available
if (isset($data->CustomerReviews->Review)) {
?>
    <b>Customer reviews</b>
    <br />
<?php
    $x = 0; // counter for reviews array
    while ($review = $data->CustomerReviews->Review[$x]) {
        echo "<u>" . $review->Summary . "</u> <br />";
        echo $review->Content . "<p />&nbsp;<p />";
        $x++;
    }
}
?>
    <p />
    <!-- amazon.com URL for this product -->
    <a href="<?php echo $data->DetailPageURL; ?>">Go to Amazon.com Product Page...</a>
</body>
</html>

Here's what the output looks like:

ECS comes with a bunch of pre-defined response groups, each designed for a specific purpose. The one I've used above, "Large", produces extremely detailed product information, including product reviews, images, pricing, availability, seller offers and more.

In addition to "Large", ECS has 35 (yup, 35!) other response groups for specific types of data - images, product attributes and accessories, customer reviews, editorial reviews and similar products. For an exhaustive listing and usage examples, look in the ECS developer kit.

Interestingly, you can combine response groups to customize the ECS return value even further, simply by using a comma-separated list of response group names. The following brief example illustrates:

<?php
// include SOAP class
include("SOAP/Client.php");

// initialize SOAP client
$soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

// set data for SOAP request
// ask for custom data set
$params = array('SubscriptionId' => 'YOUR-ID-HERE',
                'ItemId' => '0385504209',
                'ResponseGroup' => 'Images,ItemAttributes,SalesRank,Similarities');

// get item information
$result = $soapclient->call("ItemLookup", $params);

if (PEAR::isError($result)) {
    die("Something went wrong...");
}
print_r($result);
?>

Searching For Godot

In the real world, it's quite likely that customers won't know the ASIN for the product they're looking for. That's why ECS also includes the ability to search the Amazon.com catalog, via its ItemSearch() method call.

You can search against most product attributes, including keyword, author, title, artist, manufacturer, brand, composer, condition, price and many more. Obviously, the attributes against which the search can be performed differ from product category to product category, and you're expected to apply some common sense when performing a search - it wouldn't be logical to search the Electronics category by Author, for example. The ECS developer guide contains detailed information on which attributes are available in each product category.

Here's an example that illustrates the ItemSearch() method:

<html>
<head><basefont face="Arial"></head>
<body>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
Search Amazon.com Books by
<select name="c">
    <option>Author</option>
    <option>Title</option>
    <option>Keywords</option>
</select>
for
<input type="text" name="q">
<input type="submit" name="submit">
</form>
<?php
if ($_POST['q']) {
    // include SOAP class
    include("SOAP/Client.php");

    // initialize SOAP client
    $soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

    // set data for SOAP request
    $params = array('SubscriptionId' => 'YOUR-ID-HERE',
                    'SearchIndex' => 'Books',
                    $_POST['c'] => $_POST['q']);

    // get item information
    $result = $soapclient->call("ItemSearch", $params);

    if (PEAR::isError($result)) {
        die("Something went wrong...");
    }

    // get number of results
    $numResults = $result['Items']->TotalResults;

    // format and display results
    $data = $result['Items']->Item;
    echo "<h2>Search Results</h2>";
    echo "$numResults result(s) found <p />";
    // if more than one item
    // iterate over item array
    if (is_array($data)) {
        foreach ($data as $item) {
            showProductInfo($item);
        }
    // if only one item
    } else {
        showProductInfo($data);
    }
}

// function to display product information
function showProductInfo($item) {
    if (isset($item->ItemAttributes->Title) && isset($item->ItemAttributes->Author)) {
        echo "<b><a href=" . $item->DetailPageURL . ">" . $item->ItemAttributes->Title . "</b></a> <br />";
        echo $item->ItemAttributes->Author . " <p />";
    } else {
        echo "<a href=" . $item->DetailPageURL . ">[ASIN: " . $item->ASIN . "]</a>  <br />Cannot find title/author information for this product <p />";
    }
}
?>
</body>
</html>

Here's what it looks like:

This listing contains a query form for Amazon.com's Books section. It allows you to search the book catalog by title, author or keyword, and contains an input field for one or more query terms. Once submitted, a SOAP request for the ItemSearch() method is created and transmitted to the SOAP server.

There are two arguments that must be passed to the ItemSearch() method. The first argument, "SearchIndex", specifies the Amazon.com store to search (in this case, "Books", although you can also use "Apparel", "Baby", "Music", "HomeGarden", "Jewelry" and others - consult the ECS documentation for a complete list), while the second specifies the attribute to search against (in this case, "Title", "Author" or "Keywords", although again you can get a complete list from the ECS developer guide) with the user's search term.

The SOAP response to an ItemSearch() request usually contains the total number of matches found, the number of pages (more on this shortly) and details on each matching item. It's fairly easy to process this data into a search results page, complete with links to the corresponding product detail page on Amazon.com. Remember that you can attach a "ResponseGroup" argument to the request as well, to control the amount of information retrieved for each product.

Flipping Pages

There's one problem with the previous listing: no matter how many products match your query term, it will only display the first 10. This is because the default number of results returned by ItemSearch() is 10.

Before you break down and start sobbing, I should tell you that the guys at Amazon HQ anticipated this problem, and built a solution into ECS. It's possible to access results beyond the first ten matches by adding the special "ItemPage" parameter to your ECS request. This parameter tells ECS which "page" of results to return - for example, to access results 11-20, you would ask for page #2.

Here's the modified version of the previous script, this time with "next page" and "previous page" links:

<html>
<head><basefont face="Arial"></head>
<body>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
Search Amazon.com Books by
<select name="c">
    <option>Author</option>
    <option>Title</option>
    <option>Keywords</option>
</select>
for
<input type="text" name="q">
<input type="submit" name="submit">
</form>
<?php
if ($_GET['q']) {
    // include SOAP class
    include("SOAP/Client.php");

    // initialize SOAP client
    $soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

    // page number to display
    $pageNum = (($_GET['page'] > 0) && trim($_GET['page']) != "") ? $_GET['page'] : 1;

    $query = urldecode($_GET['q']);

    // set data for SOAP request
    $params = array('SubscriptionId' => 'YOUR-ID-HERE',
                    'SearchIndex' => 'Books',
                    'ItemPage' => $pageNum,
                    $_GET['c'] => $query);

    // get item information
    $result = $soapclient->call("ItemSearch", $params);

    if (PEAR::isError($result)) {
        die("Something went wrong...");
    }

    // get number of results
    $numResults = $result['Items']->TotalResults;
    $data = $result['Items']->Item;
    echo "<h2>Search Results</h2>";
    echo "$numResults result(s) found <p />";

    // display search results
    // if more than one item
    // for each item, print author, title and Amazon URL
    if (is_array($data)) {
        foreach ($data as $item) {
            showProductInfo($item);
        }
    // if only one item
    } else {
        showProductInfo($data);
    }

    // display prev/next page links
    // previous page link
    if ($pageNum > 1) {
        echo "<a href='c=" . $_GET['c'] . "&q=" . urlencode($_GET['q']) . "&page=" . ($pageNum-1) . "'>...Previous page</a>";
    }
    // spacer
    echo "&nbsp;&nbsp;&nbsp;";
    // next page link
    if ($pageNum < $result['Items']->TotalPages) {
        echo "<a href='?c=" . $_GET['c'] . "&q=" . urlencode($_GET['q']) . "&page=" . ($pageNum+1) . "'>Next page...</a>";
    }
    echo "<p />";

}

// function to display product information
function showProductInfo($item) {
    if (isset($item->ItemAttributes->Title) && isset($item->ItemAttributes->Author)) {
        echo "<b><a href=" . $item->DetailPageURL . ">" . $item->ItemAttributes->Title . "</b></a> <br />";
        echo $item->ItemAttributes->Author . " <p />";
    } else {
        echo "<a href=" . $item->DetailPageURL . ">[ASIN: " . $item->ASIN . "]</a>  <br />Cannot find title/author information for this product <p />";
    }
}
?>
</body>
</html>

You'll notice the additional "ItemPage" argument to ItemSearch(), and the fact that the "next page" and "previous page" links now pass the script a page number parameter. This page number is used by ItemSearch() to retrieve the next or previous page of results.

You'll also notice that the search form in this script uses the GET method to process data (the previous version used POST). The reason for this is closely linked to the pagination problem. Each time ItemSearch() is called with a new page number, it is necessary to also provide it the original query term and search attribute. The simplest way to do this is to attach both to the "next page" and "previous page" URLs, and process them using GET. A close look at the page links generated by the script above will reveal how this is done.

So now you know how to search Amazon.com with ECS. You're not just a geek any more - you're a geek with something to talk about at your next party. Whoopee!

Window Shopping

Searching for a specific title is one way to shop...but it's not necessarily the most fun way. That's why ECS includes what I like to call "aimless window shopper" functions, that make it possible to browse the Amazon.com catalog by category and locate products that way.

The ECS method for this is called BrowseNodeLookup(), and it works by accepting a category ID (in ECS parlance, a "browse node ID") and returning all the sub-categories of that ID. Here's an example:

<?php
// include SOAP class
include("SOAP/Client.php");

// initialize SOAP client
$soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

// set data for SOAP request
// get categories in DVD section
$params = array('SubscriptionId' => 'YOUR-ID-HERE',
                'ResponseGroup' => 'BrowseNodeInfo',
                'BrowseNodeId' => '130');

// get node information
$result = $soapclient->call("BrowseNodeLookup", $params);
if (PEAR::isError($result)) {
    die("Something went wrong...");
}

// print ECS return values
print_r($result);
?>

Here's the output:

Array
(
    [OperationRequest] => stdClass Object
        (
            [HTTPHeaders] => stdClass Object
                (
                    [Header] =>
                )

            [RequestId] => 15DDEZ4N04766FPSXBY6
            [Arguments] => stdClass Object
                (
                    [Argument] =>
                )

            [RequestProcessingTime] => 0.0105631351470947
        )

    [BrowseNodes] => stdClass Object
        (
            [Request] => stdClass Object
                (
                    [IsValid] => True
                )

            [BrowseNode] => stdClass Object
                (
                    [BrowseNodeId] => 130
                    [Name] => DVD
                    [Children] => Array
                        (
                            [0] => stdClass Object
                                (
                                    [BrowseNodeId] => 404278
                                    [Name] => Actors & Actresses
                                )

                            [1] => stdClass Object
                                (
                                    [BrowseNodeId] => 403502
                                    [Name] => Directors
                                )

                            [2] => stdClass Object
                                (
                                    [BrowseNodeId] => 694466
                                    [Name] => Formats
                                )

                            [3] => stdClass Object
                                (
                                    [BrowseNodeId] => 404276
                                    [Name] => Genres
                                )

                            [4] => stdClass Object
                                (
                                    [BrowseNodeId] => 408328
                                    [Name] => Special Features
                                )

                            [5] => stdClass Object
                                (
                                    [BrowseNodeId] => 498862
                                    [Name] => Specialty Stores
                                )

                        )

                )

        )

)

Sure, it's ugly, but no one said this was gonna be a beach party...

As the script and its output illustrate, the BrowseNodeLookup() call accepts a browse node ID (product category) and returns all the child nodes (sub-categories) of that ID. It's fairly easy to massage this data into a more usable category list with a foreach() loop. Here's how:

<html>
<head><basefont face="Arial"></head>
<body>
<?php
// include SOAP class
include("SOAP/Client.php");

// initialize SOAP client
$soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

// set data for SOAP request
// get categories in DVD section
$params = array('SubscriptionId' => 'YOUR-ID-HERE',
                'ResponseGroup' => 'BrowseNodeInfo',
                'BrowseNodeId' => '130');

// get node information
$result = $soapclient->call("BrowseNodeLookup", $params);
if (PEAR::isError($result)) {
    die("Something went wrong...");
}

// print node list
if (is_array($result['BrowseNodes']->BrowseNode->Children)) {
    echo $result['BrowseNodes']->BrowseNode->Name;
    echo "<ul>";
    foreach ($result['BrowseNodes']->BrowseNode->Children as $child) {
        echo "<li>" . $child->Name . "</li>";
    }
    echo "</ul>";
} else {
    echo "No more categories available";
}
?>
</body>
</html>

Here's what it looks like:

I'm sure you're wondering how I managed to get the browse node ID in the first place. Like the ASIN, it's pretty simple - simply browse to the appropriate category on Amazon.com and look in the URL. For example, if you browse to Amazon.com's Music Styles section, you'll see that the URL looks like this:

http://www.amazon.com/exec/obidos/tg/browse/-/301668/

The browse node ID is the identifier following the "browse" string in the URL. You can use this method to locate browse node IDs for any product category on Amazon.com.

With a little ingenuity, it's fairly easy to create an Amazon.com catalog browser using the BrowseNodeLookup() function in combination with the ItemSearch() function. Take a look:

<html>
<head><basefont face="Arial"></head>
<body>
<table border="1" cellpadding="5" cellspacing="0">
<tr>
<?php
// include SOAP class
include("SOAP/Client.php");

// initialize SOAP client
$soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

// set a root node
// defaults to 130 (root for DVD section)
$rootNode = (isset($_GET['b']) && trim($_GET['b']) != "") ? $_GET['b'] : 130;

// set data for SOAP request
$params = array('SubscriptionId' => 'YOUR-ID-HERE',
                'ResponseGroup' => 'BrowseNodeInfo',
                'BrowseNodeId' => $rootNode);

// get node information
$result = $soapclient->call("BrowseNodeLookup", $params);
if (PEAR::isError($result)) {
    die("Something went wrong...");
}

// if child nodes exist
// print them
echo "<td valign=top>";
if (is_array($result['BrowseNodes']->BrowseNode->Children)) {
    echo $result['BrowseNodes']->BrowseNode->Name;
    echo "<ul>";
    foreach ($result['BrowseNodes']->BrowseNode->Children as $child) {
        echo "<li><a href='?b=" . $child->BrowseNodeId . "'>" . $child->Name . "</a>";
    }
    echo "</ul>";
} else {
    echo "No more categories available";
}
echo "</td>";

// get products listed under this node
// set data for SOAP request
$params = array('SubscriptionId' => 'YOUR-ID-HERE',
                'SearchIndex' => 'DVD',
                'Sort' => 'salesrank',
                'ResponseGroup' => 'Small',
                'BrowseNode' => $rootNode);

// get item information
$result = $soapclient->call("ItemSearch", $params);
if (PEAR::isError($result)) {
    die("Something went wrong...");
}
echo "<td valign=top>";
echo "Bestselling products in this category:<p />";
// format and display results
for ($x=0; $x<5; $x++) {
    $item = $result['Items']->Item[$x];
    echo "<b><a href=" . $item->DetailPageURL . ">" . $item->ItemAttributes->Title . "</a></b><br />";
    echo $item->ItemAttributes->Artist . "<p />";
}
echo "</td>";
?>
</tr>
</table>
</body>
</html>

Here's what it looks like:

The script accepts a browse node ID using the GET method, and then calls BrowseNodeLookup() to get a list of its child nodes (a default node ID, in this case pointing to the DVD section, is set for first-time use). Clicking a sub-category link calls the script again with the sub-category's browse node ID, resulting in the next level of links being displayed. This process continues until no further child nodes exist.

The browse node ID is also used to great effect in the call to ItemSearch(). You see, just as you can call ItemSearch() with a product attribute like "Title" or "Manufacturer", so too can you call it with a "BrowseNodeId". When called in this manner, ItemSearch() restricts its search to the corresponding category.

You're probably also wondering how I managed to get only the best-selling products in each category. There's a trick to that too - the additional "Sort" parameter to ItemSearch() makes it possible to sort the final result set alphabetically, by sales rank, by price, by customer rating and so on. A complete list of possible sort criteria can be found in the ECS developer guide; the one I've used here, "salesrank", sorts products in ascending order by their sales rank, thereby producing an ordered list of items with the best-selling ones at the top.

Music Match

One of Amazon.com's innovations is "similarity searches", wherein the service attempts to recommend titles similar to the one you're currently perusing. These recommendations are made after aggregating the data from previous customer transactions - thousands upon thousands of them - to create a very precise set of recommendation maps. ECS makes this data available to you free of charge, with its SimilarityLookup() method. "Cool!" is probably an understatement here...

To perform a similarity search, call the SimilarityLookup() method with one or more ASINs, as in the next example:

<html>
<head><basefont face="Arial"></head>
<body>
<?php
// include SOAP class
include("SOAP/Client.php");

// initialize SOAP client
$soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

// set data for SOAP request
// ask Amazon Music for items
// similar to Abba and Rod Stewart
$params = array('SubscriptionId' => 'YOUR-ID-HERE',
                'ResponseGroup' => 'Small',
                'SimilarityType' => 'Intersection',
                'ItemId' => 'B000001DZO,B00005R1Q9',
                'MerchantId' => 'Amazon');

// get node information
$result = $soapclient->call("SimilarityLookup", $params);
if (PEAR::isError($result)) {
    die("Something went wrong...");
}

// get returned data
$data = $result['Items']->Item;
if (is_array($data)) {
    echo "You might also enjoy the following similar items: <p />";
    foreach ($data as $item) {
        if (isset($item->ItemAttributes->Title) && isset($item->ItemAttributes->Artist)) {
            echo "<b><a href=" . $item->DetailPageURL . ">" . $item->ItemAttributes->Title . "</b></a> <br />";
            echo $item->ItemAttributes->Artist . " <p />";
        } else {
        echo "<a href=" . $item->DetailPageURL . ">[ASIN: " . $item->ASIN . "]</a>  <br />Cannot find title/artist information for this product <p />";
        }
    }
} else {
    echo "No similarities found";
}
?>
</body>
</html>

In this example, I've provided ASINs for an "ABBA: Greatest Hits" CD and a "The Best of Rod Stewart" CD (oh, quit pulling faces, you rap/hip-hop/metal/ska/alternative deadheads!). ECS goes to work looking for similar titles I might like, and comes up with Elton John and Barry Manilow. Take a look:

In addition to the mandatory "SubscriptionId" parameter, the call to SimilarityLookup() in this script takes some extra parameters: the "ItemId" parameter is a comma-separated list of ASINs to use as the base for the search, the "SimilarityType" parameter tells ECS to find a result set intersecting all the provided ASINs (an alternative to "Intersection" is "Random", in which ECS will return items similar to any of the provided ASINs), and the "MerchantId" parameter restricts the search to Amazon.com merchandise only (for a wider search, use the value "All" to include other vendors).

Reading Lists

Just as you can search for similar titles, so too can you search for Amazon.com wishlists, wedding registries and user lists. The method call to use here is the ListLookup() call, which - like ItemLookup() - expects a list ID. Here's an example of how it can be used:

<html>
<head><basefont face="Arial"></head>
<body>
<?php
// include SOAP class
include("SOAP/Client.php");

// initialize SOAP client
$soapclient = new SOAP_Client("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService");

// set data for SOAP request
// ask Amazon Books for the user-created list
// of Scarlet Pimpernel books
$params = array('SubscriptionId' => 'YOUR-ID-HERE',
                'ListType' => 'Listmania',
                'ListId' => '25CPRT9427XD8',
                'ResponseGroup' => 'Small');

// get node information
$result = $soapclient->call("ListLookup", $params);
if (PEAR::isError($result)) {
    die("Something went wrong...");
}

// get returned data
$data = $result['Lists']->List;
//print_r($result);
if (is_array($data)) {
    echo "The Scarlet Pimpernel Books: <p />";
    foreach ($data as $item) {
        if (isset($item->Item->ItemAttributes->Title) && isset($item->Item->ItemAttributes->Author)) {
            echo "<b><a href=" . $item->Item->DetailPageURL . ">" . $item->Item->ItemAttributes->Title . "</b></a> <br />";
            echo $item->Item->ItemAttributes->Author . " <p />";
        } else {
        echo "<a href=" . $item->Item->DetailPageURL . ">[ASIN: " . $item->Item->ASIN . "]</a> <br /> Cannot find title/author information for this product <p />";
        }
    }
} else {
    echo "No list found";
}
?>
</body>
</html>

Notice the additional "ListType" parameter, which can be set to any one of "Listmania", "WeddingRegistry" or "WishList", depending on the type of list you're attempting to retrieve.

And here's the output:

As always, you can obtain a list ID from the URL of its Amazon.com page. Alternatively, consider using the ECS ListSearch() method, which lets you look up a list ID by user name or user email address.

And that just about wraps this tutorial up. Over the last few pages, I introduced you to the PEAR SOAP class and demonstrated how you can use it to interact with remote Web Services, notably the one provided by Amazon.com. I then took you on a tour of some of the available method calls in the ECS API, demonstrating how to browse the Amazon catalog, search for products, obtain detailed product information, and retrieve similar products and lists.

Surprisingly, everything you've just read is only the tip of the iceberg - ECS includes functions to create and manage Amazon.com shopping carts, access customer transactions, look up third-party sellers and product listings, and perform a few other fairly sophisticated tasks. It isn't possible to cover them all in a single tutorial; instead, you should feel free to experiment with the available functions and customize the service to your own needs.

Until next time...happy coding!

Disclaimer: Product data and images are copyright and proprietary to Amazon.com, and are only shown here for illustrative purposes.

This article was first published on23 Aug 2006.