Integrating without SDK

Here we have shared code samples to call SearchItems operation using HTTP client.

Product Advertising API has several parameters/headers which depend on the target Amazon Locale. These common parameters are listed here: Common Request Parameters. The examples provided in this section are for US locale and hence all locale specific values like host, region, etc. are for US locale.

Java

In this section, we will share a simple Java program to create and send request for SearchItems operation. You can create a simple Java project or Maven project in your preferred IDE.

  • For a Java project, download the following artifacts and add them to your projects.

classpath:

commons-logging-1.2.jar

httpcore-4.4.9.jar

httpclient-4.5.5.jar

json-20180130.jar

  • For a Maven project, add the following dependencies to the pom.xml file.
<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20180130</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.5</version>
</dependency>
  • Copy the following classes to your Java development environment.
package com.amazon.paapi.test;

/*
 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

import java.util.Map;
import java.util.TreeMap;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONObject;

public class PaapiClient {

    public static void main(String[] args) throws Exception {

        String HOST = "webservices.amazon.com";
        String region = "us-east-1";
        String path = "/paapi5/searchitems";

        // Sample SearchItems request. You can also use scratchpad tool (UI) to test a request and then use JSON Payload value here. Scratchpad link: https://webservices.amazon.com/paapi5/scratchpad/index.html
        // Put your Partner tag (Store/Tracking id) in place of Partner tag
        String requestPayload = "{\"Keywords\":\"harry\","
                + "\"PartnerTag\":\"PartnerTag\",\"PartnerType\":\"Associates\","
                + "\"SearchIndex\":\"All\","
                +"\"Resources\": [\"Images.Primary.Small\",\"ItemInfo.Title\",\"Offers.Listings.Price\"]}";

        TreeMap<String, String> headers = new TreeMap<String, String>();
        headers.put("host", HOST);
        headers.put("content-type", "application/json; charset=utf-8");
        // x-amz-target is value specific to your version and operation. For version 1's SearchItems it'll be com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems
        headers.put("x-amz-target", "com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems");
        headers.put("content-encoding", "amz-1.0");

        // Put your Access Key in place of <ACCESS_KEY> and Secret Key in place of <SECRET_KEY> in double quotes
        AWSV4Auth awsv4Auth = new AWSV4Auth.Builder(<ACCESS_KEY>, <SECRET_KEY>)
            .path(path)
            .region(region)
            .service("ProductAdvertisingAPI")
            .httpMethodName("POST")
            .headers(headers)
            .payload(requestPayload)
            .build();

        HttpClient client = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost("https://" + HOST + path);
        httpPost.setEntity(new StringEntity(requestPayload));
        // Signing
        Map<String, String> header = awsv4Auth.getHeaders();
        for (Map.Entry<String, String> entrySet : header.entrySet()) {
            httpPost.addHeader(entrySet.getKey(), entrySet.getValue());
            // Print headers by un-commenting following line
            //System.out.println("Key: " + entrySet.getKey() + " Value: " + entrySet.getValue());
        }

        HttpResponse response = client.execute(httpPost);
        HttpEntity entity = response.getEntity();
        String jsonResponse = EntityUtils.toString(entity, "UTF-8");
        int statusCode = response.getStatusLine().getStatusCode();
        System.out.println(jsonResponse);
        if(statusCode == 200) {
            System.out.println("Successfully received response from Product Advertising API.");
        } else {
            JSONObject json = new JSONObject(jsonResponse);
            if(json.has("Errors")) {
                JSONArray errorArray = json.getJSONArray("Errors");
                for(int i = 0; i < errorArray.length(); i++) {
                    JSONObject e = errorArray.getJSONObject(i);
                    System.out.println("Error Code: "+e.get("Code")+", Message: "+e.get("Message"));
                }
            } else {
                System.out.println("Error Code: InternalFailure, Message: The request processing has failed because of an unknown error, exception or failure. Please retry again.");
            }
        }
    }
}
  • Use the following AWS Signature Version 4 helper to sign your request to AWS.
package com.amazon.paapi.test;

/*
 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class AWSV4Auth {

    private AWSV4Auth() {
    }

    public static class Builder {
        private String awsAccessKey;
        private String awsSecretKey;
        private String path;
        private String region;
        private String service;
        private String httpMethodName;
        private TreeMap<String, String> headers;
        private String payload;

        public Builder(String awsAccessKey, String awsSecretKey) {
            this.awsAccessKey = awsAccessKey;
            this.awsSecretKey = awsSecretKey;
        }

        public Builder path(String path) {
            this.path = path;
            return this;
        }

        public Builder region(String region) {
            this.region = region;
            return this;
        }

        public Builder service(String service) {
            this.service = service;
            return this;
        }

        public Builder httpMethodName(String httpMethodName) {
            this.httpMethodName = httpMethodName;
            return this;
        }

        public Builder headers(TreeMap<String, String> headers) {
            this.headers = headers;
            return this;
        }

        public Builder payload(String payload) {
            this.payload = payload;
            return this;
        }

        public AWSV4Auth build() {
            return new AWSV4Auth(this);
        }
    }

    private String awsAccessKey;
    private String awsSecretKey;
    private String path;
    private String region;
    private String service;
    private String httpMethodName;
    private TreeMap<String, String> headers;
    private String payload;
    private final String hmacAlgorithm = "AWS4-HMAC-SHA256";
    private final String aws4Request = "aws4_request";
    private String signedHeaders;
    private String xAmzDate;
    private String currentDate;

    private AWSV4Auth(Builder builder) {
        awsAccessKey = builder.awsAccessKey;
        awsSecretKey = builder.awsSecretKey;
        path = builder.path;
        region = builder.region;
        service = builder.service;
        httpMethodName = builder.httpMethodName;
        headers = builder.headers;
        payload = builder.payload;
        xAmzDate = getTimeStamp();
        currentDate = getDate();
    }

    public Map<String, String> getHeaders() {
        headers.put("x-amz-date", xAmzDate);

        // Step 1: CREATE A CANONICAL REQUEST
        String canonicalURL = prepareCanonicalRequest();

        // Step 2: CREATE THE STRING TO SIGN
        String stringToSign = prepareStringToSign(canonicalURL);

        // Step 3: CALCULATE THE SIGNATURE
        String signature = calculateSignature(stringToSign);

        // Step 4: CALCULATE AUTHORIZATION HEADER
        if (signature != null) {
            headers.put("Authorization", buildAuthorizationString(signature));
            return headers;
        } else {
            return null;
        }
    }

    private String prepareCanonicalRequest() {
        StringBuilder canonicalUrl = new StringBuilder();

        canonicalUrl.append(httpMethodName).append("\n");

        canonicalUrl.append(path).append("\n").append("\n");

        StringBuilder signedHeaderBuilder = new StringBuilder();
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> entrySet : headers.entrySet()) {
                String key = entrySet.getKey();
                String value = entrySet.getValue();
                signedHeaderBuilder.append(key).append(";");
                canonicalUrl.append(key).append(":").append(value).append("\n");
            }
            canonicalUrl.append("\n");
        } else {
            canonicalUrl.append("\n");
        }

        signedHeaders = signedHeaderBuilder.substring(0, signedHeaderBuilder.length() - 1);
        canonicalUrl.append(signedHeaders).append("\n");

        payload = payload == null ? "" : payload;
        canonicalUrl.append(toHex(payload));

        return canonicalUrl.toString();
    }

    private String prepareStringToSign(String canonicalUrl) {
        String stringToSign = "";
        stringToSign = hmacAlgorithm + "\n";
        stringToSign += xAmzDate + "\n";
        stringToSign += currentDate + "/" + region + "/" + service + "/" + aws4Request + "\n";
        stringToSign += toHex(canonicalUrl);
        return stringToSign;
    }

    private String calculateSignature(String stringToSign) {
        try {
            byte[] signatureKey = getSignatureKey(awsSecretKey, currentDate, region, service);
            byte[] signature = hmacSha256(signatureKey, stringToSign);
            return bytesToHex(signature);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    private String buildAuthorizationString(String signature) {
        return hmacAlgorithm + " "
                + "Credential=" + awsAccessKey + "/" + getDate() + "/" + region + "/" + service + "/" + aws4Request + ","
                + "SignedHeaders=" + signedHeaders + ","
                + "Signature=" + signature;
    }

    private String toHex(String data) {
        MessageDigest messageDigest;
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(data.getBytes("UTF-8"));
            byte[] digest = messageDigest.digest();
            return String.format("%064x", new java.math.BigInteger(1, digest));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    private byte[] hmacSha256(byte[] key, String data) throws Exception {
        String algorithm = "HmacSHA256";
        Mac mac = Mac.getInstance(algorithm);
        mac.init(new SecretKeySpec(key, algorithm));
        return mac.doFinal(data.getBytes("UTF8"));
    }

    private byte[] getSignatureKey(String key, String date, String regionName, String serviceName) throws Exception {
        byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
        byte[] kDate = hmacSha256(kSecret, date);
        byte[] kRegion = hmacSha256(kDate, regionName);
        byte[] kService = hmacSha256(kRegion, serviceName);
        byte[] kSigning = hmacSha256(kService, aws4Request);
        return kSigning;
    }

    private final char[] hexArray = "0123456789ABCDEF".toCharArray();

    private String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars).toLowerCase();
    }

    private String getTimeStamp() {
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        return dateFormat.format(new Date());
    }

    private String getDate() {
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        return dateFormat.format(new Date());
    }
}

PHP

In this section, we will share a simple PHP program to create and send request for SearchItems operation. You can create a PHP project in your preferred IDE.

  • Copy the following code to your PHP development environment.
<?php

/**
 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

$searchItemRequest = new SearchItemsRequest ();
$searchItemRequest->PartnerType = "Associates";
// Put your Partner tag (Store/Tracking id) in place of Partner tag
$searchItemRequest->PartnerTag = <PARTNER_TAG>;
$searchItemRequest->Keywords = "Harry";
$searchItemRequest->SearchIndex = "All";
$searchItemRequest->Resources = ["Images.Primary.Small","ItemInfo.Title","Offers.Listings.Price"];
$host = "webservices.amazon.com";
$path = "/paapi5/searchitems";
$payload = json_encode ($searchItemRequest);
//Put your Access Key in place of <ACCESS_KEY> and Secret Key in place of <SECRET_KEY> in double quotes
$awsv4 = new AwsV4 (<ACCESS_KEY>, <SECRET_KEY>);
$awsv4->setRegionName("us-east-1");
$awsv4->setServiceName("ProductAdvertisingAPI");
$awsv4->setPath ($path);
$awsv4->setPayload ($payload);
$awsv4->setRequestMethod ("POST");
$awsv4->addHeader ('content-encoding', 'amz-1.0');
$awsv4->addHeader ('content-type', 'application/json; charset=utf-8');
$awsv4->addHeader ('host', $host);
$awsv4->addHeader ('x-amz-target', 'com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems');
$headers = $awsv4->getHeaders ();
$headerString = "";
foreach ( $headers as $key => $value ) {
    $headerString .= $key . ': ' . $value . "\r\n";
}
$params = array (
        'http' => array (
            'header' => $headerString,
            'method' => 'POST',
            'content' => $payload
        )
    );
$stream = stream_context_create ( $params );

$fp = @fopen ( 'https://'.$host.$path, 'rb', false, $stream );

if (! $fp) {
    throw new Exception ( "Exception Occured" );
}
$response = @stream_get_contents ( $fp );
if ($response === false) {
    throw new Exception ( "Exception Occured" );
}
echo $response;

class SearchItemsRequest {
    public $PartnerType;
    public $PartnerTag;
    public $Keywords;
    public $SearchIndex;
    public $Resources;
}
?>
  • Use the following AWS Signature Version 4 code to sign your request to AWS.
<?php

/**
 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

class AwsV4 {

    private $accessKeyID = null;
    private $secretAccessKey = null;
    private $path = null;
    private $regionName = null;
    private $serviceName = null;
    private $httpMethodName = null;
    private $queryParametes = array ();
    private $awsHeaders = array ();
    private $payload = "";

    private $HMACAlgorithm = "AWS4-HMAC-SHA256";
    private $aws4Request = "aws4_request";
    private $strSignedHeader = null;
    private $xAmzDate = null;
    private $currentDate = null;

    public function __construct($accessKeyID, $secretAccessKey) {
        $this->accessKeyID = $accessKeyID;
        $this->secretAccessKey = $secretAccessKey;
        $this->xAmzDate = $this->getTimeStamp ();
        $this->currentDate = $this->getDate ();
    }

    function setPath($path) {
        $this->path = $path;
    }

    function setServiceName($serviceName) {
        $this->serviceName = $serviceName;
    }

    function setRegionName($regionName) {
        $this->regionName = $regionName;
    }

    function setPayload($payload) {
        $this->payload = $payload;
    }

    function setRequestMethod($method) {
        $this->httpMethodName = $method;
    }

    function addHeader($headerName, $headerValue) {
        $this->awsHeaders [$headerName] = $headerValue;
    }

    private function prepareCanonicalRequest() {
        $canonicalURL = "";
        $canonicalURL .= $this->httpMethodName . "\n";
        $canonicalURL .= $this->path . "\n" . "\n";
        $signedHeaders = '';
        foreach ( $this->awsHeaders as $key => $value ) {
            $signedHeaders .= $key . ";";
            $canonicalURL .= $key . ":" . $value . "\n";
        }
        $canonicalURL .= "\n";
        $this->strSignedHeader = substr ( $signedHeaders, 0, - 1 );
        $canonicalURL .= $this->strSignedHeader . "\n";
        $canonicalURL .= $this->generateHex ( $this->payload );
        return $canonicalURL;
    }

    private function prepareStringToSign($canonicalURL) {
        $stringToSign = '';
        $stringToSign .= $this->HMACAlgorithm . "\n";
        $stringToSign .= $this->xAmzDate . "\n";
        $stringToSign .= $this->currentDate . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "\n";
        $stringToSign .= $this->generateHex ( $canonicalURL );
        return $stringToSign;
    }

    private function calculateSignature($stringToSign) {
        $signatureKey = $this->getSignatureKey ( $this->secretAccessKey, $this->currentDate, $this->regionName, $this->serviceName );
        $signature = hash_hmac ( "sha256", $stringToSign, $signatureKey, true );
        $strHexSignature = strtolower ( bin2hex ( $signature ) );
        return $strHexSignature;
    }

    public function getHeaders() {
        $this->awsHeaders ['x-amz-date'] = $this->xAmzDate;
        ksort ( $this->awsHeaders );
        $canonicalURL = $this->prepareCanonicalRequest ();
        $stringToSign = $this->prepareStringToSign ( $canonicalURL );
        $signature = $this->calculateSignature ( $stringToSign );
        if ($signature) {
            $this->awsHeaders ['Authorization'] = $this->buildAuthorizationString ( $signature );
            return $this->awsHeaders;
        }
    }

    private function buildAuthorizationString($strSignature) {
        return $this->HMACAlgorithm . " " . "Credential=" . $this->accessKeyID . "/" . $this->getDate () . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "," . "SignedHeaders=" . $this->strSignedHeader . "," . "Signature=" . $strSignature;
    }

    private function generateHex($data) {
        return strtolower ( bin2hex ( hash ( "sha256", $data, true ) ) );
    }

    private function getSignatureKey($key, $date, $regionName, $serviceName) {
        $kSecret = "AWS4" . $key;
        $kDate = hash_hmac ( "sha256", $date, $kSecret, true );
        $kRegion = hash_hmac ( "sha256", $regionName, $kDate, true );
        $kService = hash_hmac ( "sha256", $serviceName, $kRegion, true );
        $kSigning = hash_hmac ( "sha256", $this->aws4Request, $kService, true );

        return $kSigning;
    }

    private function getTimeStamp() {
        return gmdate ( "Ymd\THis\Z" );
    }

    private function getDate() {
        return gmdate ( "Ymd" );
    }
}
?>

results matching ""

    No results matching ""