Sunday, August 29, 2010

Amazon Online Shopping with Web Service Framework for PHP

Introduction

phpIf you have done any on-line shopping, then you probably don't need an introduction to Amazon.com. But have you ever thought of writing your own application to make purchases on Amazon? In fact, you can do just that with the Amazon Web Services API. This tutorial guides you through writing a simple PHP application to access Amazon E-commerce Web services to do on-line shopping, using the Web Services Framework for PHP.

Table of Contents

 

Designing the PHP Class Library for Amazon

When writing a Web service application, it is a good practice to keep all of the Web service invocation logic in a separate layer and build the dependent logic on top of that. So, in this case we will create a class named 'AmazonClient' and contain all the Web services requests to the Amazon Web service in it.

First of all, create the skeleton of the class which would be filled by the operations as we go on through the tutorial.

<?php
class AmazonClient extends WSClient
{
const AMAZON_ENDPOINT = "http://webservices.amazon.com/onca/xml";

private $amazon_key;
private $amazon_service;


public function __construct($amazon_key)
{
parent::__construct(array(

"to"=>self::AMAZON_ENDPOINT ));
$this->amazon_key = $amazon_key;
$this->amazon_service = "AWSECommerceService";
}


/* the service operations to be implemented */

}
?>

So whoever uses this PHP class will only need to write the following bit of code. For example, to do an ItemSearch:

$amzon_client = new AmazonClient("your amazon key");
$amazon_client->ItemSearch();

Next, we will look at how to fill the class body with service operation invocations..


 


Using The SOAP API


As always, we will start our on-line shopping task by searching. For that, you can use Amazon's ItemSearch service operation.


ItemSearch operation requires a keyword and a Search Index as input parameters. In fact, the keyword will be your search query. Search Index is the item category you want to search, and hence needs be one of the values defined by Amazon. Some of the possible values are Books, Music, DVD, Software, Software Video Games and etc..


Here is our version of ItemSearch which just wraps the Amazon request for ItemSearch:

/**
* Search Web
* @param $query query string
* @param $catagory catagory to search
* @param $catagory page to search
* @return associate array consist of the response parameters
*/

public function ItemSearch($query, $catagory, $page = 1)

{
$req_payload = <<<XML
<ItemSearch xmlns="http://webservices.amazon.com/AWSECommerceService/2007-10-29">
<AWSAccessKeyId>19VBVF8C8HQQ0HTH7PR2</AWSAccessKeyId>
<Request>
<ResponseGroup>Medium</ResponseGroup>
<ItemPage>{$page}</ItemPage>
<Keywords>{$query}</Keywords>
<SearchIndex>{$catagory}</SearchIndex>
</Request>
</ItemSearch>
XML;
/* call the request method with the request payload */
$ret_message = $this->request($req_payload);
$simplexml = new SimpleXMLElement($ret_message->str);


$res = $simplexml->Items;

return $res;
}

You can test your code very easily with the following bit of code:

<?php
require_once("AmazonClient.php");
$amazon_client = new AmazonClient("your amazon key");


$items = $amazon_client->ItemSearch("Web Services", "Books");
foreach($items->Item as $item)

{
echo $item->ASIN.". ";
echo $item->ItemAttributes->Title. " - ";
echo $item->ItemAttributes->Author. "\n";

}
?>

You will find the detailed result se, that contains the information of the Books you searched for, like Book Title, Authors etc. Amazon returns many other fields that you may be interested in like the DetailPageURL ($item->DetailPageURL) of the item.


You may have noticed in the above code I have been echoing the "ASIN" of the item ($item->ASIN). ASIN is the unique identifier of the item used by Amazon. This is required to be remembered, since you will need it when you are adding items to the shopping cart.


After you've found the item you've been looking for, you may do a simple lookup on that particular item. For that, Amazon provides the ItemLookup operation. Here, by setting the request parameter 'ResponseGroup' to 'Large'. you will be able to access more information about the book, like people's reviews, impressions and so on.

/**
* ItemLookup
* @param $ASIN Amaxon Item Id
* @return associate array consist of the response parameters
*/

public function ItemLookup($ASIN)
{


$req_payload = <<<XML
<ItemLookup xmlns="http://webservices.amazon.com/AWSECommerceService/2007-10-29">
<AWSAccessKeyId>{$this->amazon_key}</AWSAccessKeyId>
<Request>
<ResponseGroup>Large</ResponseGroup>
<ItemId>{$ASIN}</ItemId>
<ReviewPage>1</ReviewPage>
</Request>
</ItemLookup>
XML;

$ret_message = $this->request($req_payload);
$simplexml = new SimpleXMLElement($ret_message->str);


$res = $simplexml->Items;

return $res;
}

You will be able to access this operation using the following lines of code:

/* we can use the same $amazon_client we created for the ItemSearch */
$lookups = $amazon_client->ItemLookup("0131488740");

echo $lookups->Item->ASIN.". ";

echo $lookups->Item->ItemAttributes->Title. " - ";
echo $lookups->Item->ItemAttributes->Author. "\n";

 


This way you will be able to retrieve a lot of interesting details of the item you want to buy, from your applicaiton. But if you feel that you may have missed something, you can simply put a link to the Item web page in Amazon.com by setting a hyperlink to the $lookups->Item-> DetailPageURL value inside the above code. After these operations, you will probably have enough information about the Item and may decide that you really want to buy this Item. In fact, you can put that Item to the virtual shopping cart through the Amazon 'CartCreate' operation. With this operation you can give one or more items you wish to add to the cart.

/**
* CartCreate
* @param $items array of items you wish to add to the new cart
* sould be a hash of "Product ASIN"=> "Quantity"
* @return associate array consist of the response parameters
*/

public function CartCreate(Array $items)

{

$items_xml = "";
foreach ($items as $asin => $quantity)

{
$items_xml .= <<<XML
<Item>
<ASIN>{$asin}</ASIN>
<Quantity>{$quantity}</Quantity>
</Item>

XML;
}
/* Create the request payload */
$req_payload = <<<XML
<CartCreate xmlns="http://webservices.amazon.com/AWSECommerceService/2007-10-29">

<AWSAccessKeyId>{$this->amazon_key}</AWSAccessKeyId>
<Request>
<Items>
$items_xml
</Items>
</Request>
</CartCreate>
XML;
/* call the request method with the request payload */
  $ret_message = $this->request($req_payload);
$simplexml = new SimpleXMLElement($ret_message->str);


$res = $simplexml-> Cart;

return $res;
}

Note that you are giving the items you want to buy in a hashmap with the mapping "Product ASIN"=> "Quantity". So the following code shows how you can create a cart and check the total price.

$cart = $amazon_client->CartCreate(array("0131488740" => 5));

echo $cart-> CartId." - ";
echo $cart->CartItems->SubTotal->FormattedPrice."\n";

echo $cart->PurchaseURL."\n";

From this you can retrieve the following information:




















Cart Id$cart-> CartIdThe Id of the cart you just created
HMAC$cart-> HMACSecurity Token which would be required in order to change the cart later
Purchase URL$cart->PurchaseURLYou can navigate to PurchaseURL to continue the rest of the transaction through the Amazon website..
CartItemId$cart->CartItems->CartItem[x]->CartItemIdThis exists for each cart item and is different from ASIN


Here you will need to keep the Cart Id, HMAC and CartItemId for each Item in order to modify or clear items in the cart. I will show you how you can implement it in your application with Amazon's CartModify and CartClear operations.

/**
* CartModify
* @param $cart_id id of the cart to modify..
* @param $HMAC security token for authentication..
* @param $items array of items you wish to change
* sould be a hash of "Cart Item Id"=> "Quantity"
* @return associate array consist of the response parameters
*/

public function CartModify($cart_id, $HMAC, Array $items)

{

$items_xml = "";
foreach ($items as $cart_item_id => $quantity)

{
$items_xml .= <<<XML
<Item>
<CartItemId>{$cart_item_id}</CartItemId>
<Quantity>{$quantity}</Quantity>
</Item>

XML;
}

/* create the request payload */
$req_payload = <<<XML
<CartModify xmlns="http://webservices.amazon.com/AWSECommerceService/2007-10-29">
<AWSAccessKeyId>{$this->amazon_key}</AWSAccessKeyId>
<Request>
<CartId>$cart_id</CartId>
<HMAC>$HMAC</HMAC>
<Items>
$items_xml
</Items>
</Request>
</CartModify>
XML;
/* call the request method with the request payload */
$ret_message = $this->request($req_payload);
$simplexml = new SimpleXMLElement($ret_message->str);


$res = $simplexml-> Cart;

return $res;
}
/**
* CartClear
* @param $cart_id id of the cart to modify..
* @param $HMAC security token for authentication..
* @return associate array consists of the response parameters
*/

public function CartClear($cart_id, $HMAC)
{


$req_payload = <<<XML
<CartClear xmlns="http://webservices.amazon.com/AWSECommerceService/2007-10-29">
<AWSAccessKeyId>{$this->amazon_key}</AWSAccessKeyId>
<Request>
<CartId>$cart_id</CartId>
<HMAC>$HMAC</HMAC>
</Request>
</CartClear>

XML;
/* call the request method with the request payload */   
$ret_message = $this->request($req_payload);
$simplexml = new SimpleXMLElement($ret_message->str);


$res = $simplexml;

return $res;
}

You will be able to modify or clear the cart you created earlier, using above operations in few lines of codes like the following:

$modified_cart_info = $amazon_client->CartModify("102-0208736-3675339", "lXOKkAyAVl6+/+D+KA3GvJRF4P0=", array("U181IPR3ATNL7R" => 3));


$cleared_cart_info = $amazon_client->CartClear("102-0208736-3675339", "lXOKkAyAVl6+/+D+KA3GvJRF4P0=");

Here I have put some fake values for HMACs, Card Ids and CartItemIds. Please use your own values here. (That is, you will be using the values retrieved from the CartCreate operation)


So that is all you need to write your own online shopping application. The next part of the tutorial is going to explain how to do the same thing in another way.


 


Using The REST API


The amazing thing about Amazon.com is that it provides Web services for both SOAP and REST consumers. And if you are using Web Service Framework for PHP you will be able to convert the SOAP consumer to a REST consumer with a very little change of code. But unfortunately with Amazon you may need to change all the request messages too, since their API for REST and SOAP are different. Now I will walk you through the same set of steps demonstrated above, but this time it will be using "REST" and not "SOAP".


The first and most important thing is to change the options of the WSClient constructor[4] which would be called inside the AmazonClient.

const AMAZON_ENDPOINT = "http://webservices.amazon.com/onca/xml";
parent::__construct(array(
"to"=>self::AMAZON_ENDPOINT,
"HTTPMethod"=>GET,
"useSOAP"=>FALSE));

Here, note the options given to the WSClient.












"to"This is the REST service endpoint of the Amazon ECommerce web service. This is different to the soap endpoint
"useSOAP" =>FALSEThis is where we tell the WSClient we are not interested in going with SOAP.
"HTTPMethod"=> "GET"We will use 'Get' method to do the queries

So that is the basic configuration you need to change to convert your SOAP consumer to a REST consumer. In addition to that you will need to change your request xml. For example, for the Item Search Operation your new (for REST) request will be like this.

<ItemSearch>
<Service>AWSECommerceService</Service>
<AWSAccessKeyId>{$this->amazon_key}</AWSAccessKeyId>

<Operation>ItemSearch</Operation>
<Keywords>{$query}</Keywords>
<SearchIndex>{$catagory}</SearchIndex>

<ItemPage>{$page}</ItemPage>
</ItemSearch>

As you can see in the 'REST' request all the XML elements can go only one level deeper. So the elements like Keywords, SearchIndex and ItemPage are taken away from "Request" element (which is where they were in the SOAP request) and put as immediate child of the ItemSearch. So the WSF/PHP engine will convert this in to the following HTTP request..

GET /onca/xml?Service=AWSECommerceService&AWSAccessKeyId='Your_amazon_key'&Operation=ItemSearch&Keywords='Your-Query'&SearchIndex=Books&ItemPage=1 HTTP/1.1

You will probably be thinking how can you give an array of elements to the 'REST' request. This kind of request messages are present in the Amazon E-Commerce service as well. (e.g. CartCreate, CartModify)


So next we will see how to invoke the 'CartCreate' operation with REST.

$i = 0;
$items_xml = "";
foreach ($items as $asin => $quantity)

{
$i ++;
$items_xml .= <<<XML
<Item.$i.ASIN>$asin</Item.$i.ASIN>
<Item.$i.Quantity>$quantity</Item.$i.Quantity>

XML;
}
/* call the request method with the request payload */       
$req_payload = <<<XML

<CartCreate>
<Service>AWSECommerceService</Service>
<AWSAccessKeyId>{$this->amazon_key}</AWSAccessKeyId>
<Operation>CartCreate</Operation>
$items_xml
</CartCreate>

XML;

So for a call like the following,

$cart = $amazon_client->CartCreate(array("0131488740" => 5, "0234323234" => 3));
the generated request message will be like,
         <CartCreate>
<Service>AWSECommerceService</Service>
<AWSAccessKeyId>your_amazon_key</AWSAccessKeyId>
<Operation>CartCreate</Operation>

<Item.1.ASIN>0131488740</Item.1.ASIN>
<Item.1.Quantity>5</Item.1.Quantity>

<Item.2.ASIN>0234323234</Item.2.ASIN>
<Item.2.Quantity>3</Item.2.Quantity>

</CartCreate>

which would be ultimately converted to a HTTP GET request with the following format.

GET /onca/xml?Service=AWSECommerceService&AWSAccessKeyId=your_amazon_key&Operation=CartCreate&Item.1.ASIN=0131488740&Item.1.Quantity=5&Item.2.ASIN=0234323234&Item.2.Quantity=3 HTTP/1.1

You may be wondering now how you should handle the 'REST' Reponse, and if that would be different from the way you handle the 'SOAP' Response? Fortunately, it is not. Both the payload of the SOAP response and that of the REST response have the same format. That means, you can use the same piece of code to handle the response in both 'SOAP' and 'REST' forms when you are using WSClient.


If you followed the tutorial fully, you will now have a PHP class which helps you to access Amazon Web services either in REST or SOAP form. If you want to improve the flexibility of your class by giving the class user an option to set which request format (from 'REST' and 'SOAP') they prefer, what you need to do is to simply add another parameter to your class constructor that specifies whether to use 'REST' or 'SOAP. Based on this constructor parameter, you can select the logic to be used when sending the request. So users of the class do not need to worry about the underlying message format after they initially construct the class.


 


Conclusion


This tutorial guided you to write a simple web service consumer for amazon.com services using WSO2 Web Service Framework for PHP (WSF/PHP). By writing a consumer for a popular public Web service, the simplicity of the use of WSF/PHP consumer API and it s ability to call both 'REST' and 'SOAP' services with a minimum amount of work is highlighted.


Resources



Author


Dimuthu Gamage is a Software Engineer at WSO2. He is a commiter of the Apache Web Service project and a developer of the WSF/PHP and WSF/Ruby projects.


dimuthu at wso2 dot com.


I wrote an article on "Amazon Online Shopping with Web Service Framework for PHP" for Oxygen Tank.
There I'm demonstrating how to write a simple client library to access Amazon Web Services and how to use that library to write your own application to do online shopping with Amazon.com
There I attached the Client library code and two sample applications, one very simple php command line application and the other much improved web application. Hope this will be useful to people who are interested in working with WSF/PHP.

No comments:

Post a Comment