SOAP


eBay's SOAP interface is simply massive. The WSDL document alone weighs in at a hefty 1.4MB, and the documentation PDF weighs in at a terrifying 1,209 pages! It's a lot to get your head around. Luckily, the requests examined here are not too complex, and should serve as a great foundation for your applications. Unfortunately, eBay and NuSOAP do not presently work well together (beyond the HTTPS requirements), so requests will be generated manually, rather than using the NuSOAP framework.

Unlike the majority of SOAP APIs, eBay requires information to be sent in the HTTP header as well as within the request itself. The presence of these headers allows eBay to route requests internally to different servers depending on the type of request.

Hello World

The eBay SOAP equivalent of the Hello World program is obtaining official eBay time. This request isn't entirely trivial because all SOAP requests are sent over HTTPS, and as such, cURL will need to be used to perform the request. Additionally, there are a number of required HTTP headers for eBay SOAP requests.

General SOAP Caller

 function calleBay($callName, $request, $returnRAW = FALSE) {   global $appID, $version, $endPoint;   $requestURL = "$endPoint?callname=$callName&appid=$appID     &version=$version&routing=default";   $length = strlen($request);   $headers = array();   $headers[] = 'SOAPAction: ""';   $headers[] = "Content-Type: text/xml";   $headers[] = "Content-Length: $length"; 

As mentioned earlier, several of the request parameters will be passed in the GET line, and the endpoint for the request is generated, including the name request being made and some specifics about the application making the request.

   $ch = curl_init();   curl_setopt($ch, CURLOPT_URL, $requestURL);   curl_setopt($ch, CURLOPT_HEADER, false);   curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);   curl_setopt($ch, CURLOPT_POST, true);   curl_setopt($ch, CURLOPT_POSTFIELDS, $request);   curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);   curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);   curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);   $data = curl_exec($ch);   curl_close($ch);   if ($returnRAW == TRUE)   {     return $data;   }else   {     $xml = simplexml_load_string($data);     $newXML = $xml->children('http://schemas.xmlsoap.org/soap/envelope/')->       children('urn:ebay:apis:eBLBaseComponents');     return $newXML;   } } 

The function will optionally return the raw response in plain text (very useful when making a new call for the first time) or the relevant portion of the response as a SimpleXML object (the namespaces are discussed in the next section).

The cURL options used in the request are as follows:

  • CURLOPT_URL — This sets the endpoint for the call.

  • CURLOPT_HEADER — Binary value, indicating you want the headers from the response returned (true), or indicating you do not (false).

  • CURLOPT_HTTPHEADER — Allows specific HTTP headers to be set using an array.

  • CURLOPT_POST — Binary value, indicating whether POST values will be sent (true) or will not be present (false).

  • CURLOPT_POSTFEILDS — Allows the POST string to be set.

  • CURLOPT_RETURNTRANSFER — Binary value, indicating whether the response should be returned (true) or sent directly to the browser (false).

  • CURLOPT_SSL_VERIFYHOST — Instructs libcurl not to verify the host.

  • CURLOPT_SSL_VERIFYPEER — Instructs libcurl not to verify the certificate's authentication chain.

Hard-Coded getTime Request

A lot was introduced with the generic caller, so in an attempt to keep this "simple" example simple, a hard-coded request will be used:

 function getSimpleTime() {   global $version, $devID, $appID, $cert, $token;   $call = "GeteBayOfficialTime";   $message = <<< XMLBLOCK <?xml version="1.0" encoding="utf-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"     xmlns:xsd="http://www.w3.org/2001/XMLSchema"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   <soapenv:Header>     <RequesterCredentials soapenv:mustUnderstand="0"          xmlns="urn:ebay:apis:eBLBaseComponents">        <eBayAuthToken>$token</eBayAuthToken>        <ns:Credentials xmlns:ns="urn:ebay:apis:eBLBaseComponents">         <ns:DevId>$devID</ns:DevId>         <ns:AppId>$appID</ns:AppId>         <ns:AuthCert>$cert</ns:AuthCert>        </ns:Credentials>     </RequesterCredentials>   </soapenv:Header>   <soapenv:Body>   <GeteBayOfficialTimeRequest xmlns="urn:ebay:apis:eBLBaseComponents">   <ns1:Version xmlns:ns1="urn:ebay:apis:eBLBaseComponents">$version</ns1:Version>   </GeteBayOfficialTimeRequest>   </soapenv:Body> </soapenv:Envelope> XMLBLOCK;   $RAWxml = calleBay($call, $message, TRUE);   $xml = simplexml_load_string($RAWxml);   print_r($xml);   echo "Time: ". $xml->children('http://schemas.xmlsoap.org/soap/envelope/')     ->children('urn:ebay:apis:eBLBaseComponents')->GeteBayOfficialTimeResponse     ->Timestamp . "\n"; } 

The output should look something like this:

 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"   xmlns:xsd="http://www.w3.org/2001/XMLSchema"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  <soapenv:Body>   <GeteBayOfficialTimeResponse xmlns="urn:ebay:apis:eBLBaseComponents">    <Timestamp>2005-11-02T03:57:39.309Z</Timestamp>    <Ack>Success</Ack>    <Version>429</Version>    <Build>e429_intl_Bundled_1949355_R1</Build>   </GeteBayOfficialTimeResponse>  </soapenv:Body> </soapenv:Envelope> SimpleXMLElement Object (   [Body] => SimpleXMLElement Object   (     [GeteBayOfficialTimeResponse] => SimpleXMLElement Object     (       [Timestamp] => 2005-11-02T03:54:05.481Z       [Ack] => Success       [Version] => 429       [Build] => e429_intl_Bundled_1949355_R1     )   ) ) Time: 2005-11-02T03:54:05.481Z 

I've decided to list all three versions of the output here (raw, SimpleXML, and desired) to point out that namespaces is one of those places where SimpleXML turns out to be not quite so simple. The basic SimpleXML tree shown matches the raw XML rather nicely (ignoring the namespaces), but once it becomes time to access those elements, you need to start worrying about those namespaces again. It's important to remember that when dealing with namespaces, SimpleXML needs the URI for the namespace, not the name used.

Simplifying Requests

A pair of basic functions can be used to automate request generation and save you the problems of manually coding each new request. Generating the body of the request separately from the envelope portion is done for two reasons: simplicity, and to allow for manual generation of either should the situation warrant it.

Generating the Body of a Request

 function generateBody($callName, $attributes) {   $body = "<soapenv:Body>\n";   $body .= "<{$callName}Request xmlns=\" urn:ebay:apis:eBLBaseComponents\">\n";   foreach ($attributes AS $key => $value)   {     $body .= "<ns1:$key xmlns:ns1=\"urn:ebay:apis:eBLBaseComponents\">       $value</ns1:$key>\n";   }   $body .= "</{$callName}Request>\n";   $body .= "</soapenv:Body>";   return $body; } 

The name of the call is suffixed with Request to satisfy the formatting requirements of the SOAP document. The suffix is added here rather than simply being required in the $callName variable to allow the same variable to be used here and as a GET parameter. Attributes is an associative array containing all of the parameters the request requires; it is iterated through and added to the body of the request.

Completing the Request

 function generateRequest($body) {   global $version, $endPoint, $devID, $appID, $cert, $token;    $request = <<< XMLBLOCK <?xml version="1.0" encoding="utf-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"   xmlns:xsd="http://www.w3.org/2001/XMLSchema"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   <soapenv:Header>     <RequesterCredentials soapenv:mustUnderstand="0"       xmlns="urn:ebay:apis:eBLBaseComponents">       <eBayAuthToken>$token</eBayAuthToken>         <ns:Credentials xmlns:ns=" urn:ebay:apis:eBLBaseComponents">         <ns:DevId>$devID</ns:DevId>         <ns:AppId>$appID</ns:AppId>         <ns:AuthCert>$cert</ns:AuthCert>       </ns:Credentials>     </RequesterCredentials>   </soapenv:Header>   $body </soapenv:Envelope> XMLBLOCK;   return $request; } 

Simplified getTime Request

Using the newly created functions, you can now request the time from eBay very easily:

 function newGetTime() {   $call = "GeteBayOfficialTime";   $queryInfo = array();   $queryInfo["Version"] = 425;   $myRequest = generateBody($call, $queryInfo);   $message = generateRequest($myRequest);   $xml =  calleBay($call, $message, FALSE);   echo "Time: " . $xml->GeteBayOfficialTimeResponse->Timestamp . "\n"; } 

Browsing the eBay Categories

For easy browsing, eBay's extensive auctions are divided into numerous categories and subcategories, and sub-subcategories to the nth degree. This makes things really easy when using the regular eBay websites, but it can make things a bit more difficult when interacting with the API. In order to properly access the eBay category listings, a few more options for the API need to be introduced.

Category Organization

Categories are organized in a basic tree; there are a few root elements (such as Antiques or Cars) that can contain any number of subcategories (which can contain subcategories of their own, and so on). When retrieving a record for a specific category, you receive only a limited amount of information:

 <Category>  <AutoPayEnabled>true</AutoPayEnabled>  <CategoryID>63561</CategoryID>  <CategoryLevel>3</CategoryLevel>  <CategoryName>Cabinets, Armoires, Cupboards</CategoryName>  <CategoryParentID>20091</CategoryParentID>  <Expired>false</Expired>  <IntlAutosFixedCat>false</IntlAutosFixedCat>  <LeafCategory>false</LeafCategory>  <Virtual>false</Virtual>  <LSD>false</LSD>  <ORPA>false</ORPA>  <BestOfferEnabled>true</BestOfferEnabled> </Category> 

In terms of organization, there are five relevant fields: CategoryID, CategoryLevel, CategoryParentID, and LeafCategory. CategoryID, and CategoryName are the unique identifiers for the category, and every category has one. CategoryLevel indicates the depth of the category in the tree. CategoryParentID is (surprisingly enough) the ID of the category's parent. Finally, LeafCategory is a boolean value indicating whether this category is a leaf (leafs have no children).

When making a getCategory request, you can optionally specify the maximum depth (LevelLimit) you want to retrieve categories from. This is always relative to the root of the hierarchy, not the category you are requesting information for.

Note 

For categories with a Level of 1, CategoryID will equal CategoryParentID. The root level is not considered a parent.

Detail Level

Many eBay requests allow you to specify a detail level with the request. This value can be used to either request all available information pertaining to your request, or only a limited subset of that information. This is quite useful when (for example) attempting to determine if the local cached copy of eBay's category hierarchy is up to date. By specifying a low detail level (or specifying no detail level at all) in the request, eBay will return only basic version information about the current category hierarchy. If that information matches the local cache, it can be used safely. If not, the same request will be repeated, changing only the detail level to request all available information.

This may sound silly; just keep in mind how massive some of the responses eBay may return are. The full detail response to a request for the full category hierarchy is (at print time) 8.7MB. Requesting that much information on every page load as a user navigates eBay's categories is not only a horrible waste, but a huge performance hit.

Site ID

This refers to a specific site within the eBay network. This information is only required when making a request applicable to a site other than the one the request is actually being made against. For example, if you are using the US API endpoint, and want the category information for the Canadian site, you would need to specify a site ID.

Your options are as follows:

  • 0—US

  • 2—Canada

  • 3—United Kingdom

  • 15—Australia

  • 16—Austria

  • 71—France

  • 77—Germany

  • 101—Italy

  • 104—Japan

  • 146—Netherlands

  • 193—Switzerland

  • 196—Taiwan

  • 123—Belgium NL

  • 23—Belgium FR

Retrieving the Categories

Retrieving the categories using the code presented previously is pretty easy; however, because the full response for the getCategories request is so long, some caching will need to be implemented. The function will optionally accept a single parameter indicating the category for which the children should be retrieved, or just the root of the listings. The getCategories request returns by default only basic information about the hierarchy. You need to include a DetailLevel attribute in your request (with a value such as ReturnAll) to obtain the desired information.

 function getCategories($parent = -1) {   $call = "GetCategories";   $attributes = array();   $attributes['Version'] = 425;   $attributes['CategorySiteID'] = 0;   if ($parent != -1)   {     $attributes['CategoryParent'] = $parent;   }else   {     $attributes['LevelLimit'] = 1;   } 

If a specific category was requested, the CategoryParent element is added to the request; otherwise, LevelLimit is capped at 1 to keep the request size down. Notice that for all other categories the full depth will be returned.

   $myRequest = generateBody($call, $attributes);   $message = generateRequest($myRequest);   $breifXML = simplexml_load_string(calleBay($call, $message, TRUE));   $lastUpdated = $parent . "." . $breifXML->     children('http://schemas.xmlsoap.org/soap/envelope/')->     children('urn:ebay:apis:eBLBaseComponents')->GetCategoriesResponse->     UpdateTime . ".xml"; 

A request is made without specifying a detail level, so eBay returns only very basic information about the current category hierarchy (version, timestamp for last update). This timestamp is used to generate a filename for caching purposes.

   if (file_exists("/eBayCatCache/$lastUpdated"))   {     echo "<!-- CACHE -->\n";     $xml = simplexml_load_file("/tmp/$lastUpdated"); 

If the file exists, load it into a SimpleXML object and continue.

   }else   {     echo "<!-- NEW -->\n";     $attributes['DetailLevel'] = 'ReturnAll';     $myRequest = generateBody($call, $attributes);     $message = generateRequest($myRequest);     $xml = calleBay($call, $message, TRUE);     file_put_contents("/tmp/$lastUpdated", $xml);     $xml = simplexml_load_string($xml);   } 

Otherwise, add the DetailLevel attribute to the request to obtain full category information. Note that the calleBay() function is called requesting raw output (a string containing the response, rather than a SimpleXML object). This is done so that the entire request may be saved to disk.

   $xml = $xml->children('http://schemas.xmlsoap.org/soap/envelope/')- >children('urn:ebay:apis:eBLBaseComponents');   return $xml; } 

Finally, the relevant portion of the SimpleXML object is returned. The object returned when passed 37903 as parent is shown here:

 SimpleXMLElement Object (   [Timestamp] => 2005-11-07T04:26:24.166Z   [Ack] => Success   [Version] => 429   [Build] => e429_intl_Bundled_1949355_R1   [CategoryArray] => SimpleXMLElement Object   (     [Category] => Array     (       [0] => SimpleXMLElement Object       (         [AutoPayEnabled] => true         [CategoryID] => 37903         [CategoryLevel] => 2         [CategoryName] => Antiquities (Classical, Amer.)         [CategoryParentID] => 20081         [Expired] => false         [IntlAutosFixedCat] => false         [LeafCategory] => false         [Virtual] => false         [LSD] => false         [ORPA] => false         [BestOfferEnabled] => true       )       ...       [5] => SimpleXMLElement Object       (         [AutoPayEnabled] => true         [CategoryID] => 73464         [CategoryLevel] => 3         [CategoryName] => Other         [CategoryParentID] => 37903         [Expired] => false         [IntlAutosFixedCat] => false         [LeafCategory] => true         [Virtual] => false         [LSD] => false         [ORPA] => false         [BestOfferEnabled] => true       )     )   )   [CategoryCount] => 6   [UpdateTime] => 2005-10-11T02:59:22.000Z   [CategoryVersion] => 56   [ReservePriceInclusive] => false   [MinimumReservePrice] => 0.0   [ReduceReserveInclusive] => true ) 

Notice that category 37903 is included in the response; eBay includes the category you request along with its children as part of the response.

Displaying the Categories

Using the previous function, the categories can be displayed to the screen in an easily navigated format:

 function displayCategoryListings($parent = -1) {  $xml = getCategories($parent);  foreach($xml->GetCategoriesResponse->CategoryArray->Category AS $category)  {    if ($category->CategoryID == $parent)    {      if ($category->CategoryLevel == 1)      {       echo "<h3>{$category->CategoryName}</h3>";       echo "(<a href=\"?\">Return to parent</a>)\n\n";      }else      {        echo "<h3>{$category->CategoryName}</h3>";        echo "(<a href=\"?loadCat={$category->CategoryParentID}\">Return to parent          </a>)\n";      } 

After obtaining the relevant XML using the getCategories() function presented earlier, the resulting categories are iterated through. Because the response includes the category in question, it must be treated as a special case. If this category is a top-tier category, the link to return to the parent will not specify a parent (to obtain the root level items); otherwise the link will go to the specified parent.

    }else    {      if ($category->CategoryParentID == $parent || $parent == -1)      {        if ($category->LeafCategory == "true")        {          echo "<span class=\" catTitle\">{$category->CategoryName}</span>            (<a href=\"?listCategory={$category->CategoryID}\"            class=\" viewItems\">view items</a>)\n";        }else        {          echo "<a href=\"?loadCat={$category->CategoryID}\" class=\" catTitle\">         {$category->CategoryName}</a> (<a href=\"?listCategory=         {$category->CategoryID}\" class=\" viewItems\">view items</a>) \n";        }      }    }  } } 

For the remaining categories, there are two options: either the category is a leaf (has no children), in which case it will not be presented as a link, or a link is provided to view that category's children. In either case, a link is provided to view listings in that category (the code for which is presented next).

Figure 9-2 shows Category listings for eBay.

image from book
Figure 9-2

Retrieving Items for Sale in a Category

Unlike the category hierarchy, the items for sale in a specific category are constantly changing, more so on the live system than the sandbox, but while it's a large amount of data, it's not a good candidate for caching. As such, a second function to handle caching isn't needed, there's enough. abstraction with the functions that are needed already.

 function getCategoryListings($category) {   $call = "GetCategoryListings";   $attributes = array();   $attributes['Version'] = 425;   $attributes['CategoryID'] = $category;   $myRequest = generateBody($call, $attributes);   $message = generateRequest($myRequest);   $xml = calleBay($call, $message, FALSE);   echo "<h3>Listings in {$xml->GetCategoryListingsResponse->Category->     CategoryName}</h3>\n";   $parentID = getCategoryID($xml->GetCategoryListingsResponse->Category->     CategoryID);   echo "Return to <a href=\"?loadCat={$parentID}\">Categories</a> Listing\n"; 

Generate the request and print the basic category information.

    if (isset($xml->GetCategoryListingsResponse->ItemArray->Item))    {      foreach($xml->GetCategoryListingsResponse->ItemArray->Item AS $item)      {        echo "<a href=\"?{$item->ItemID}\">{$item->Title}</a> currently has a high          bid of $ {$item->SellingStatus->CurrentPrice} after a total of {$item->          SellingStatus->BidCount} bids\n";      }    }else    {      echo "No listings, Sorry\n";    } 

Some items (particularly in the sandbox environment) don't have any listings, so this is handled gracefully. Otherwise the items available are iterated through with basic information.

    if (isset($xml->GetCategoryListingsResponse->SubCategories->Category))    {      echo "<h3>Sub-Categories</h3>";      foreach($xml->GetCategoryListingsResponse->SubCategories->Category AS        $category)      {         echo "<a href=\"?listCategory={$category->CategoryID}\">{$category->         CategoryName}</a> has {$category->NumOfItems} items listed\n";      }    } } 

Finally, with the GetCategoryListings call, subcategories are returned with basic information about the listings available within. If these subcategories are present within the response, they are iterated through, with links provided. Figure 9-3 shows Listings within a category.

image from book
Figure 9-3

Searching eBay

Browsing categories is all well and good, but with the depth of categories and sheer number of listings on the live sites, searching will likely become a necessity.

Basic Search

Using the basic query interface with the functions already defined is a simple matter of substitution:

 function doBasicSearch($query) {   $call = "GetSearchResults";   $attributes = array();   $attributes['Version'] = 425;   $attributes['Query'] = $query;   $myRequest = generateBody($call, $attributes);   $message = generateRequest($myRequest);   $xml = calleBay($call, $message, FALSE); 

Here the basics of the call are set up, and the call itself is generated. As you can see, the name of the call is GetSearchResults, and it requires a single attribute Query specifying the user's search query.

   echo "<div class=\"resultHeading\">";   echo "<span class=\"resultHeader\">Search Results for: $query</span>";   echo "</div>";   if ($xml->GetSearchResultsResponse->PaginationResult->TotalNumberOfEntries == 0)   {     echo "Sorry, there are no results to display";   }else   { 

The heading information is displayed, and the zero result case is handled.

     foreach($xml->GetSearchResultsResponse->SearchResultItemArray->SearchResultItem     AS $searchResult)     {       $results = array();       foreach($xml->GetSearchResultsResponse->SearchResultItemArray->         SearchResultItem AS $searchResult)       {         $results[] = $searchResult->Item;       }       displayItems($results);     }   } } 

Unfortunately, things don't go quite as smoothly here as one might hope. The problem is how eBay encapsulates its return information for different calls. When you perform a GetCategoryListings request, the items are available in $xml->GetCategoryListingsResponse->ItemArray->Item, which is easy to handle. However, the GetSearchResults request presents the array at $xml- >GetSearchResultsResponse->SearchResultItemArray->SearchResultItem, and each item is then stored within an Item element. This leaves two choices: Either the display function must be written to handle a variety of inputs, or a temporary variable needs to be used (in this case, GetCategoryListings is fine) to present information to the display function in a consistent manner. I've chosen the latter, because it allows me to keep the display functions cleaner, hopefully allowing for a cleaner separation of business and presentation logic.

Figure 9-4 shows a Basic Search for DVD.

image from book
Figure 9-4

Searching Within a Category

From a coding standpoint, searching within a category is trivial; you merely add an additional parameter to the request, and display results in much the same manner. The difficulty stems from presenting this to the user in a coherent manner. Simply presenting a drop-down containing all available categories to the user and asking them to select one would be pointless, because there's simply too many for that to be useful. The obvious method, which is the method presented here and is similar to the one used on eBay itself, is to allow users to search within a category once they have navigated there via the category listings. Though this is slightly less powerful than some alternatives, it is very user friendly.

In order to implement this searching method, a small addition must be made to the Category listing function to allow the user to enter a search query:

 function displayCategoryListings($parent = -1) {  $xml = getCategories($parent);  if ($parent != -1)  {    echo "<div class=\"resultHeading\">";    echo '<span class=\"resultHeader\">Search this Category: <form method="get">      <input type="hidden" name="categoryname" value=""><input type="text"      name="query"><input type="submit"></span>';    echo "</div>";  }  foreach($xml->GetCategoriesResponse->CategoryArray->Category AS $category)  { 

This is just a basic form. The comparison to $parent is done to avoid displaying this limited search while the user is viewing the root category listings (which would be redundant and confusing).

The search function itself is quite similar to the function seen previously:

 function doCategorySearch($query, $category) {   $call = "GetSearchResults";   $attributes = array();   $attributes['Version'] = 425;   $attributes['Query'] = $query;   $attributes['CategoryID'] = $category;   $myRequest = generateBody($call, $attributes);   $message = generateRequest($myRequest);   $xml = calleBay($call, $message, FALSE);   echo "<div class=\" resultHeading\">";   echo "<span class=\" resultHeader\">Search Results for: $query</span>";   echo "</div>";   if ($xml->GetSearchResultsResponse->PaginationResult->TotalNumberOfEntries == 0)   {     echo "Sorry, there are no results to display, <a href=\"?query=$query\">Search       all of eBay</a>";   }else   {     $results = array();     foreach($xml->GetSearchResultsResponse->SearchResultItemArray->SearchResultItem       AS $searchResult)     {       $results[] = $searchResult->Item;     }     displayItems($results);   } } 

As you can see, the only changes are the addition of the CategoryID attribute and the addition of the user-friendly option to extend the search when no results are obtained.

Figure 9-5 shows a category search with results.

image from book
Figure 9-5

Figure 9-6 shows a category search with zero results.

image from book
Figure 9-6

Putting Items Up for Auction

Listing items on eBay requires making a call more complex than the ones introduced thus far; multiple levels of nested elements are required to make even the most basic listing. As such, generateBody() will finally receive some improvements. Also, remember that you must use a Sellers account when listing items; otherwise the API will return an error.

Improving generateBody()

To handle nested elements, the function will need to be refactored to function recursively.

In order to pass elements that will need to be nested, the calling function will declare them, something like this:

   $queryInfo = array();   $queryInfo["Version"] = 425;   $queryInfo["Item"] = array();   $queryInfo["Item"]["Country"] = "CA";   $queryInfo["Item"]["PrimaryCategory"] = array();   $queryInfo["Item"]["PrimaryCategory"]["CategoryID"] = "23";   $queryInfo["Item"]["Quantity"] = 1; 

As you can see, upper-level elements are still defined as normal. When nesting must occur, the item is declared as an array, and elements are added. This example demonstrates several levels of nesting.

For the function itself, I've introduced an additional parameter, $depth. This serves two purposes: First, it allows the function to determine if it is generating the outermost portions of the request (and encapsulate accordingly), and second, it allows the code to indent the request properly. The function also adds an is_array() check to the loop, making the recursive call when an array is located.

 function advGenerateBody($callName, $attributes, $depth = 0) {   $body = "";   $prefix = str_repeat("\t", $depth);   if ($depth == 0)   {     $body .= "<soapenv:Body>\n";     $body .= "<{$callName}Request xmlns=\"urn:ebay:apis:eBLBaseComponents\">\n";   }   foreach ($attributes AS $key => $value)   {     if (is_array($value))     {       $body .=$prefix."<ns1:$key xmlns:ns1=\"urn:ebay:apis:eBLBaseComponents\">\n";       $body .= $prefix . advGenerateBody($callName, $value, ($depth + 1));       $body .= $prefix . "</ns1:$key>\n"; 

When an array element is located, it indicates nesting, and the recursive call is made. Depth is incremented both to avoid duplicating the outer elements and to ensure proper indentation.

     }else     {       $body .=$prefix."<ns1:$key xmlns:ns1=\"urn:ebay:apis:eBLBaseComponents\">         $value</ns1:$key>\n";     }   }   if ($depth == 0)   {     $body .= "</{$callName}Request>\n";     $body .= "</soapenv:Body>";   }   return $body; } 

For reference, the output of the example elements referred to in the preceding code would be as follows:

 <soapenv:Body> <AddItemRequest xmlns="urn:ebay:apis:eBLBaseComponents"> <ns1:Version xmlns:ns1="urn:ebay:apis:eBLBaseComponents">425</ns1:Version> <ns1:Item xmlns:ns1="urn:ebay:apis:eBLBaseComponents">   <ns1:Country xmlns:ns1="urn:ebay:apis:eBLBaseComponents">CA</ns1:Country>   <ns1:PrimaryCategory xmlns:ns1="urn:ebay:apis:eBLBaseComponents">     <ns1:CategoryID xmlns:ns1="urn:ebay:apis:eBLBaseComponents">23</ns1:CategoryID>   </ns1:PrimaryCategory>   <ns1:Quantity xmlns:ns1="urn:ebay:apis:eBLBaseComponents">1</ns1:Quantity> </ns1:Item> </AddItemRequest> </soapenv:Body> 

With this improved function in hand, you're able to make an addItem request.

Adding Your First Item

As mentioned previously, listing an item with eBay requires quite a few parameters to be passed along. This is a bare-bones request, containing just enough information to list the item. This call will be hard-coded; subsequent ones will make use of user input. The following table lists the parameters being sent with this request. For more information on specific elements, please see the API documentation.

Name

Format

Description

Version

int

Version of the API being referenced.

Item

Container

Contains all information regarding the item being listed.

Country

String

Two-letter ISO 3166 country code for the current location of the item.

Currency

String

Three-letter representation of the currency of the auction. For example, USD and CAD (US Dollar and Canadian Dollar, respectively).

Title

String

Title of the listing as it will appear in search results and at the top of the listing. Plain text.

Description

String

A detailed description of the item. If you wish to use HTML, encapsulate the content within CDATA.

ListingDuration

Token

One of the following: Days_1, Days_3, Days_5, Days_7, Days_10, Days_21, Days_30, Days_60, Days_90, Days_120, GTC. A seller must have a positive feedback of 10 or higher to make a 1-day listing.

Location

String

Indicates the current location of the item, including country. Maximum length 45 characters.

PaymentMethods

Token

A token indicating accepted payment methods. Acceptable values include PayPal, AmEx, VisaMC.

PayPalEmailAddress

String

Email address of the seller's PayPal account (required if PayPal is an accepted payment type).

PrimaryCategory

Container

Container for the CategoryID.

CategoryID

Int

Contained by PrimaryCategory, indicates the primary category for the listing.

Quantity

Int

The number of items being sold.

StartPrice

Double

The minimum starting bid for the item.

With that information in mind, the call can actually be made:

 $call = "AddItem"; $queryInfo = array(); $queryInfo["Version"] = 425; $queryInfo["Item"] = array(); $queryInfo["Item"]["Country"] = "CA"; $queryInfo["Item"]["Currency"] = "USD"; $queryInfo["Item"]["Title"] = "Simple Test Auction"; $queryInfo["Item"]["Description"] = "Professional Web APIs, The Movie!"; $queryInfo["Item"]["ListingDuration"] = "Days_3"; $queryInfo["Item"]["Location"] = "Windsor, Ontario, Canada"; $queryInfo["Item"]["PaymentMethods"] = "PayPal"; $queryInfo["Item"]["PayPalEmailAddress"] = "reinhei@uwindsor.ca"; $queryInfo["Item"]["PrimaryCategory"] = array(); $queryInfo["Item"]["PrimaryCategory"]["CategoryID"] = "617"; $queryInfo["Item"]["Quantity"] = 1; $queryInfo["Item"]["StartPrice"] = "9.99"; $myRequest = advGenerateBody($call, $queryInfo); $message = generateRequest($myRequest); $xml = calleBay($call, $message,TRUE); 

The full request for this call as sent to eBay is (token trimmed):

 <?xml version="1.0" encoding=" utf-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"    xmlns:xsd="http://www.w3.org/2001/XMLSchema"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   <soapenv:Header>     <RequesterCredentials soapenv:mustUnderstand="0"          xmlns="urn:ebay:apis:eBLBaseComponents">        <eBayAuthToken>...</eBayAuthToken>        <ns:Credentials xmlns:ns="urn:ebay:apis:eBLBaseComponents">         <ns:DevId>C481VH1E49RGK2J21C5H96R143H641</ns:DevId>         <ns:AppId>WROXP91H134FC2I9R276TB6J6945CT</ns:AppId>         <ns:AuthCert>P76S15991C6$HFI91JM21-Y7F7V7RD</ns:AuthCert>        </ns:Credentials>     </RequesterCredentials>   </soapenv:Header>   <soapenv:Body> <AddItemRequest xmlns="urn:ebay:apis:eBLBaseComponents"> <ns1:Version xmlns:ns1="urn:ebay:apis:eBLBaseComponents">425</ns1:Version> <ns1:Item xmlns:ns1="urn:ebay:apis:eBLBaseComponents">   <ns1:Country xmlns:ns1="urn:ebay:apis:eBLBaseComponents">CA</ns1:Country>   <ns1:Currency xmlns:ns1="urn:ebay:apis:eBLBaseComponents">USD</ns1:Currency>   <ns1:Title xmlns:ns1="urn:ebay:apis:eBLBaseComponents">Simple Test    Auction</ns1:Title>   <ns1:Description xmlns:ns1="urn:ebay:apis:eBLBaseComponents"> Professional Web    APIs, The Movie!</ns1:Description>   <ns1:ListingDuration    xmlns:ns1="urn:ebay:apis:eBLBaseComponents">Days_3</ns1:ListingDuration>   <ns1:Location xmlns:ns1="urn:ebay:apis:eBLBaseComponents">Windsor, Ontario,    Canada</ns1:Location>   <ns1:PaymentMethods    xmlns:ns1="urn:ebay:apis:eBLBaseComponents">PayPal</ns1:PaymentMethods>   <ns1:PayPalEmailAddress xmlns:ns1="urn:ebay:apis:eBLBaseComponents">    example@example.com</ns1:PayPalEmailAddress>   <ns1:PrimaryCategory xmlns:ns1="urn:ebay:apis:eBLBaseComponents">     <ns1:CategoryID xmlns:ns1="urn:ebay:apis:eBLBaseComponents">617</ns1:CategoryID>   </ns1:PrimaryCategory>   <ns1:Quantity xmlns:ns1="urn:ebay:apis:eBLBaseComponents">1</ns1:Quantity>   <ns1:StartPrice xmlns:ns1="urn:ebay:apis:eBLBaseComponents">9.99</ns1:StartPrice> </ns1:Item> </AddItemRequest> </soapenv:Body> </soapenv:Envelope> 

The associated response is quite lengthy:

 <?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  <soapenv:Body>   <AddItemResponse xmlns="urn:ebay:apis:eBLBaseComponents">    <Timestamp>2006-01-11T05:10:04.066Z</Timestamp>    <Ack>Success</Ack>    <Version>439</Version>   <Build>e439_intl_Bundled_2267338_R1</Build>    <ItemID>4504419255</ItemID>    <StartTime>2006-01-11T05:10:02.644Z</StartTime>    <EndTime>2006-01-14T05:10:02.644Z</EndTime>    <Fees>     <Fee>      <Name>AuctionLengthFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>BoldFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>BuyItNowFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>CategoryFeaturedFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>FeaturedFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>FeaturedGalleryFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>FixedPriceDurationFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>GalleryFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>GiftIconFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>HighLightFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>InsertionFee</Name>      <Fee currency>0.35</Fee>     </Fee>     <Fee>      <Name>InternationalInsertionFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>ListingDesignerFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>ListingFee</Name>      <Fee currency>0.35</Fee>     </Fee>     <Fee>      <Name>PhotoDisplayFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>PhotoFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>ReserveFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>SchedulingFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>SubtitleFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>BorderFee</Name>      <Fee currency>0.0</Fee>     </Fee>     <Fee>      <Name>ProPackBundleFee</Name>      <Fee currency>0.0</Fee>     </Fee>    </Fees>   </AddItemResponse>  </soapenv:Body> </soapenv:Envelope> 

The key pieces of information in that response are the ACK, ItemID, StartTime, EndTime, and the nonzero fees. The ACK is important because it identifies that the listing was successful, and the item has been listed with eBay with an ID of ItemID. StartTime and EndTime should likely be presented to the users for their records. Finally, the fees — first, it's important to note that these are not the final fees associated with listing an item; eBay charges a fee in relation to the final selling price of the item, which can't be determined at list time. Second, because of how many different fees there are, I would recommend simply iterating through them, and presenting the end users with an itemized (and totaled) list of the fees associated with their listing.

If you want to present the user with estimated listing fees before actually listing the item, a VerifyAddItem call can be made instead of an AddItem call. The parameters are identical (in the case of this sample code, just change the first line to $call = "VerifyAddItem"), but the item is not listed, just checked for validity and estimated costs are returned.

Note 

Note that there is a delay between submitting an item and it being available on the site via a keyword search, or via category browsing. In my experience, this delay runs anywhere between 15 and 60 seconds. The item is, however, immediately visible by doing a search for its listing ID.

Adding an Image to the Item Description

eBay doesn't provide a mechanism within its API to upload pictures to its servers, and as such, the eBay Picture Services system is unavailable to us (developers using the Java SDK or the Windows SDK, however, can hit those services). As such, to include an image with an eBay listing, it must first be hosted on a web server. The mechanisms involved in allowing a user to upload an image are rather trivial, and as such, are not covered here, but keep in mind a few considerations:

  • Restrict the types of files users can upload to images; don't rely on form-side checking; repeat it all server side. You can use local image libraries to ensure it is in fact an image.

  • If you generally do referrer checking to avoid problems with hot-linking, lax the rules for the appropriate folders. Images will likely also be downloaded via email clients (when people email descriptions to each other) or other API-based clients.

  • Watermarking images with the item number or seller's name can be a good idea; many eBayers have in the past reported problems with other users "stealing" their images for their own sales. PHP's image libraries make this an easy value-add for your customers.

Including an HTML image tag in the item description is easy and quick once you have the image on your server. The previous example could be modified to include an image like this:

 $queryInfo["Item"]["Description"] = '<![CDATA[Professional Web APIs, The Movie! <img src="http://www.example.com/images/dvd_cover.jpg">]]>'; 

Simply adding the image to the item description will have the desired effect of showing the image to the customer; it will not, however, activate the small camera icon used in search result or category listings to indicate that the listing has a picture. Because the API has no specific method to add the icon to the listing, it must be approached in a backwards manner. The API will add the icon whenever a valid image is present URL is presented in one of the Picture branches. That knowledge, combined with a specified null image URL, will allow you to toggle the camera icon when images are included in the description:

 $queryInfo["Item"]["VendorHostedPicture"] = array(); $queryInfo["Item"]["VendorHostedPicture"]["SelfHostedURL"] =   "http://pics.ebay.com/aw/pics/dot_clear.gif"; 




Professional Web APIs with PHP. eBay, Google, PayPal, Amazon, FedEx, Plus Web Feeds
Professional Web APIs with PHP. eBay, Google, PayPal, Amazon, FedEx, Plus Web Feeds
ISBN: 764589547
EAN: N/A
Year: 2006
Pages: 130

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net