Example for Amazon Shipping signature creation

This guide will provide an overview of signing the API request.

1. Use below HTTP headers while making call to Amazon Shipping APIs.

1.1 AccessToken (x-amz-access-token).

1.2 Amazon date (X-Amz-Date- pass amazon dateTime here). Please create separate function to create Amazon date and time.

1.3 Signature created (sign).

1.4 Content-Type – content-type to specify the request format.

$amz_date_time = getAmazonDateTime();
$sign = calcualteAwsSignatureAndReturnHeaders($api_token, $api_json_data, $amz_date_time);
    	$sign = implode(',',$sign);
    	$curl = curl_init();
    	curl_setopt_array($curl, array(
      	CURLOPT_URL => 'https://sellingpartnerapi-eu.amazon.com/shipping/v2/shipments/rates',
      	CURLOPT_RETURNTRANSFER => true,
      	CURLOPT_ENCODING => '',
      	CURLOPT_MAXREDIRS => 10,
      	CURLOPT_TIMEOUT => 0,
      	CURLOPT_FOLLOWLOCATION => true,
      	CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
      	CURLOPT_CUSTOMREQUEST => 'POST',
      	CURLOPT_POSTFIELDS => $api_json_data,
      	CURLOPT_HTTPHEADER => array(
        	'x-amz-access-token: '.$api_token,
//'X-Amz-Content-Sha256: beaead3198f7da1e70d03ab969765e0821b24fc913697e929e726aeaebf0eba3',
       	 'X-Amz-Date: '.$amz_date_time,
        	$sign,
        	'Content-Type: application/json' ),));

2. Create a function to calculate signature and assign the variables (host, accessKey, secretKey, region, service, requestUrl, uri, httpRequestMethod).

3. Assign the parameter which is needed for signature creation (terminationString, algorithm, canonicalURI, canonicalQueryString and signedHeaders)

3.1 TerminalString should be assigned with aws4_request value and this value is static and constant and doesn’t change.

3.2 Algorithm should be assigned as AWS4-HMAC-SHA256, this is a signature creation algorithm and value remain constant and doesn’t change.

3.3 canonicalURI should be assigned as shipping API uri (example for getRates API it should be ‘/shipping/v2/shipments/rates').

3.4 canonicalQueryString should be “” if there is no query string in the URL (for ex: getRates and PurchaseShipment doesn’t have quertstring so this value should be empty). For Tracking API this should has value (carrierId=AMZN_UK&trackingId=$trackingId).

3.5 SignedHeaders should only have ('content-type;host;x-amz-date').

function calcualteAwsSignatureAndReturnHeaders($reqToken, $data, $amz_date_time){
$host = "sellingpartnerapi-eu.amazon.com";
$accessKey = 'PUT YOUR ACCESS KEY';
$secretKey = 'PUT YOUR SECRET KEY';
$region = "eu-west-1";
$service = "execute-api";
$requestUrl = "https://sellingpartnerapi-eu.amazon.com/shipping/v2/shipments/rates";
$uri = '/shipping/v2/shipments/rates';
$httpRequestMethod = 'POST';
$debug= FALSE;
    	$terminationString = 'aws4_request';
    	$algorithm = 'AWS4-HMAC-SHA256';
   	$phpAlgorithm = 'sha256';
$canonicalURI = $uri;
    	$canonicalQueryString   = '';
    	$signedHeaders = 'content-type;host;x-amz-date';
    	$reqDate = getAmazonDate();
    	
$reqDateTime = $amz_date_time;

4. Create separate functions to create amazon date and time in UTC.

function getAmazonDate(){
    $currentDateTime = new DateTime('UTC');
    $reqDate = $currentDateTime->format('Ymd');
    return $reqDate;
}
function getAmazonDateTime(){    
    $currentDateTime = new DateTime('UTC');
    $reqDate = $currentDateTime->format('Ymd');
    $reqDateTime = $currentDateTime->format('Ymd\THis\Z');
    return $reqDateTime;
}

5. Create the following headers which is needed to create signature.

5.1 Create signing key – the signing key consists of secretKey (AWS SecretKey), Request date (Amazon date), region (eu-west-1 for EU, us-east-1 for NA, us-west-2 Far East (Japan)), service (should be hardcoded as “execute-api”) and terminationString (should be hardcoded as “aws4_request”).

// Create signing key
    $kSecret = $secretKey;
    $kDate = hash_hmac($phpAlgorithm, $reqDate, 'AWS4' . $kSecret, true);
    $kRegion = hash_hmac($phpAlgorithm, $region, $kDate, true);
    $kService = hash_hmac($phpAlgorithm, $service, $kRegion, true);
    $kSigning = hash_hmac($phpAlgorithm, $terminationString, $kService, true);

5.2 Create canonical headers is one of the important parts in signature creation, it consists of content-type header (for Amazon shipping APIs value of content-type should be “application/json”), host (host value of Amazon shipping API URL: sellingpartnerapi-eu.amazon.com (PROD), sandbox.sellingpartnerapi-eu.amazon.com(sandbox)), x-amz-date should have AmazonDatetime value, concatenate \n with all these values and save it in the variable ($canonicalHeadersStr = implode ("\n", $canonicalHeaders)).

// Create canonical headers
    $canonicalHeaders = array ();
    $canonicalHeaders[] = 'content-type:application/json';
    $canonicalHeaders[] = 'host:' . $host;
    $canonicalHeaders[] = 'x-amz-date:' . $reqDateTime;
    $canonicalHeadersStr = implode ("\n", $canonicalHeaders);

5.3 Create hash of the request payload – create hash for the input payload using sha256 Algorithm.

// Create request payload
    $requestHasedPayload = hash ($phpAlgorithm, $data);

5.4 Create canonical request, it consists of below fields:

5.4.1 httpRequestMethod – HTTP Method of the API. Example: POST for getRates, purchaseShipment API and its GET for tracking, cancelShipment and getShipmentDocument).
5.4.2 canonicalURI is the URI (path in the endpoint URL, it is /shipping/v2/shipments/rates for getRates API, /shipping/v2/shipments for purchaseShipment API.
5.4.3 canonicalQueryString is empty “” for POST method (getRates and purchaseShipment API) and has queryString in case of GET API (carrierId=AMZN_UK&trackingId=TBA303037991486 for getTracking API, packageClientReferenceId=9025968258&format=PDF for getShipmentDocument).
5.4.4 canonicalHeadersStr which is created in the previous step (step 2) while creating canonical header (canonicalHeadersStr).
5.4.5 signedHeaders which was declared in the previous step ($signedHeaders = 'content-type;host;x-amz-date').
5.4.6 requestHasedPayload which is the hash value of the requestPayload (created in step 3).
5.4.7 Concatinate all the above fields with \n and assign it to a variable ($requestCanonicalRequest = implode ("\n", $canonicalRequest)).
5.4.8 Create the hash of the above variable (concatenated value)- $requestHasedCanonicalRequest = hash ($phpAlgorithm, utf8_encode($requestCanonicalRequest));
// Create canonical request
$canonicalRequest = array ();
   		$canonicalRequest[] = $httpRequestMethod;
    	$canonicalRequest[] = $canonicalURI;
    	$canonicalRequest[] = $canonicalQueryString;
			$canonicalRequest[] = $canonicalHeadersStr . "\n";
    	$canonicalRequest[] = $signedHeaders;
    	$canonicalRequest[] = $requestHasedPayload;
$requestCanonicalRequest = implode ("\n", $canonicalRequest);	    
$requestHasedCanonicalRequest=hash($phpAlgorithm,utf8_encode($requestCanonical.Request));

5.5 Create Scope array and it consists of reqDate (which is AmazonDate in UTC, call the date only function which is defined in step 6 to get date value), region (eu-west-1 for EU, us-east-1 for NA, us-west-2 Far East (Japan)), service (should be hardcoded as “execute-api”) and terminationString (should be hardcoded as “aws4_request”).

// Create scope
$credentialScope = array ();
$credentialScope[] = $reqDate;
$credentialScope[] = $region;
$credentialScope[] = $service;
$credentialScope[] = $terminationString;
$credentialScopeStr = implode ('/', $credentialScope);

5.6 Create stringtoSign array, it consists of algorithm (value to be used is AWS4-HMAC-SHA256), reqDateTime (which is AmazonDateTime in UTC, call the date only function which is defined in step 6 to get date value), credentialScopeStr credential scope which was created in the previous step, requestHasedCanonicalRequest which is a hash of canonical array created in the previous step, concatenate \n and all these values and assign to a stringToSignStr value.

// Create string to signing
	$stringToSign = array ();
	$stringToSign[] = $algorithm;
	$stringToSign[] = $reqDateTime;
  $stringToSign[] = $credentialScopeStr;
  $stringToSign[] = $requestHasedCanonicalRequest;
  $stringToSignStr = implode ("\n", $stringToSign);

5.7 Create signature which is a hash of the signature and it takes $phpAlgorithm, $stringToSignStr, $kSigning as input variables.

// Create signature
   		 $signature = hash_hmac($phpAlgorithm, $stringToSignStr, $kSigning);

5.8 Create authorization header array which consists of Credential ('Credential=' . $accessKey . '/' . $credentialScopeStr), SignedHeaders ('SignedHeaders=' . $signedHeaders), Signature ('Signature=' . ($signature)) and algorithm (algorithm. ' ' . implode (', ', $authorizationHeader)).

// Create authorization header
   		$authorizationHeader = array ();
   	 	$authorizationHeader[] = 'Credential=' . $accessKey . '/' . $credentialScopeStr;
   	 	$authorizationHeader[] = 'SignedHeaders=' . $signedHeaders;
    	$authorizationHeader[] = 'Signature=' . ($signature);
   	 	$authorizationHeaderStr = $algorithm. ' ' . implode (', ', $authorizationHeader);

5.9 Create request headers array which consists of authorization (authorizationHeaderStr which is created in previous step), content-length, content-type, host (host of the Amazon shipping APIs), x-amz-date, x-amz-access-token (access token which is generated using getToken API) and x-amzn-shipping-business-id (for UK= AmazonShipping_UK, Italy = AmazonShipping_IT, France = AmazonShipping_FR, Spain = AmazonShipping_ES).

// Request headers 
    	$headers = array (); 
   			$headers[] = 'authorization: '.$authorizationHeaderStr; 
    		$headers[] = 'content-length='.strlen($data); 
    		$headers[] = 'content-type=application/json'; 
    		$headers[] = 'host='. $host; 
    		$headers[] = 'x-amz-date='. $reqDateTime; 
    		$headers[] = 'x-amz-access-token='. $reqToken;
    		$headers[] = 'x-amzn-shipping-business-id=AmazonShipping_IN';
return $headers;

Code examples:

PHP

<?php

//https://sellingpartnerapi-eu.amazon.com/shipping/v2/shipments/rates

$response = '{
    "access_token": "YOUR ACCESS TOKEN HERE.",
    "refresh_token": "YOUR REFRESH TOKEN HERE.",
    "token_type": "bearer",
    "expires_in": 3600
}';
    $response = json_decode($response, true);
    $api_token = $response['access_token'];

	/*processing token api ends here*/
	
	/*processing getRates api starts here*/
	
	$api_json_data = '{
      "shipTo": {
        "name": "Ship from Test",
        "addressLine1": "stringShipFrom Line 1",
        "addressLine2": "ShipFrom Line 2",
        "addressLine3": "ShipFrom Line 3",
        "companyName": "YashRetail",
        "stateOrRegion": "Haryana",
        "city": "Rewari",
        "countryCode": "IN",
        "postalCode": "560034",
        "email": "[email protected]",
        "phoneNumber": "9999999999"
      },
      "shipFrom": {
        "name": "ShipTo Test",
        "addressLine1": "ShipFrom Line 1",
        "addressLine2": "ShipFrom Line 2",
        "addressLine3": "ShipFrom Line 3",
        "stateOrRegion": "Haryana",
        "city": "Rewari",
        "countryCode": "IN",
        "postalCode": "500032",
        "email": "[email protected]",
        "phoneNumber": "9999999999"
      },
      "returnTo": {
        "name": "ReturnTo Test",
        "addressLine1": "Returnto Line 1",
        "addressLine2": "Returnto Line 2",
        "addressLine3": "Returnto Line 3",
        "companyName": "YashRetail",
        "stateOrRegion": "Haryana",
        "city": "Rewari",
        "countryCode": "IN",
        "postalCode": "600034",
        "email": "[email protected]",
        "phoneNumber": "9729991800"
      },
      "shipDate": "2022-09-28T09:27:05Z",
      "packages": [
        {
          "dimensions": {
            "length": 1,
            "width": 2,
            "height": 2,
            "unit": "INCH"
          },
          "weight": {
            "unit": "GRAM",
            "value": 250
          },
          "insuredValue": {
            "value": 10,
            "unit": "INR"
          },
          "isHazmat": false,
          "charges": [
            {
              "amount": {
                "value": 2,
                "unit": "INR"
              },
              "chargeType": "TAX"
            }
          ],
          "packageClientReferenceId": "fc",
          "items": [
            {
              "itemValue": {
                "value": 10,
                "unit": "INR"
              },
              "description": "Description of the item1.",
              "itemIdentifier": "ITEM-26495734098",
              "quantity": 1,
              "weight": {
                "unit": "GRAM",
                "value": 150
              },
              "isHazmat": false,
              "productType": "Health Care items",
              "invoiceDetails": {
                "invoiceNumber": "0092590411-IV-cancelling2",
                "invoiceDate": "2021-02-11T09:27:05Z"
              }
            }
          ]
        }
      ],
      "valueAddedServices": {
        "collectOnDelivery": {
          "amount": {
            "value": 10,
            "unit": "INR"
          }
        }
      },
      "taxDetails": [
      {
        "taxType": "GST",
        "taxRegistrationNumber": "06AABCY2351G1ZJ"
      }
     ],
      "channelDetails": {
        "channelType": "EXTERNAL"
      }
    }';

    $amz_date_time = getAmazonDateTime();
    $sign = calcualteAwsSignatureAndReturnHeaders($api_token, $api_json_data, $amz_date_time);
   
    $sign = implode(',',$sign);

    $curl = curl_init();
    
    curl_setopt_array($curl, array(
      CURLOPT_URL => 'https://sellingpartnerapi-eu.amazon.com/shipping/v2/shipments/rates',
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_ENCODING => '',
      CURLOPT_MAXREDIRS => 10,
      CURLOPT_TIMEOUT => 0,
      CURLOPT_FOLLOWLOCATION => true,
      CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
      CURLOPT_CUSTOMREQUEST => 'POST',
      CURLOPT_POSTFIELDS => $api_json_data,
      CURLOPT_HTTPHEADER => array(
        'x-amz-access-token: '.$api_token,
        //'X-Amz-Content-Sha256: beaead3198f7da1e70d03ab969765e0821b24fc913697e929e726aeaebf0eba3',
        'X-Amz-Date: '.$amz_date_time,
        $sign,
        'Content-Type: application/json'
      ),
    ));
    
    
    $response = curl_exec($curl);
    curl_close($curl);
    echo $response;



function calcualteAwsSignatureAndReturnHeaders($reqToken, $data, $amz_date_time){

$host               = "sellingpartnerapi-eu.amazon.com";
$accessKey          = 'PUT YOUR ACCESS KEY';
$secretKey          = 'PUT YOUR SECRET KEY';
$region             = "eu-west-1";
$service            = "execute-api";
$requestUrl         = "https://sellingpartnerapi-eu.amazon.com/shipping/v2/shipments/rates";
$uri                = '/shipping/v2/shipments/rates';
$httpRequestMethod  = 'POST';
$debug              = FALSE;

    $terminationString  = 'aws4_request';
    $algorithm      = 'AWS4-HMAC-SHA256';
    $phpAlgorithm       = 'sha256';
    $canonicalURI       = $uri;
    $canonicalQueryString   = '';
    $signedHeaders = 'content-type;host;x-amz-date';

    $reqDate = getAmazonDate();
    $reqDateTime = $amz_date_time;

    // Create signing key
    $kSecret = $secretKey;
    $kDate = hash_hmac($phpAlgorithm, $reqDate, 'AWS4' . $kSecret, true);
    $kRegion = hash_hmac($phpAlgorithm, $region, $kDate, true);
    $kService = hash_hmac($phpAlgorithm, $service, $kRegion, true);
    $kSigning = hash_hmac($phpAlgorithm, $terminationString, $kService, true);

    // Create canonical headers
    $canonicalHeaders = array();
    $canonicalHeaders[] = 'content-type:application/json';
    $canonicalHeaders[] = 'host:' . $host;
    $canonicalHeaders[] = 'x-amz-date:' . $reqDateTime;
    $canonicalHeadersStr = implode("\n", $canonicalHeaders);

    // Create request payload
    $requestHasedPayload = hash($phpAlgorithm, $data);
    //$requestHasedPayload = Hex(SHA256Hash($data));

    // Create canonical request
    $canonicalRequest = array();
    $canonicalRequest[] = $httpRequestMethod;
    $canonicalRequest[] = $canonicalURI;
    $canonicalRequest[] = $canonicalQueryString;
    $canonicalRequest[] = $canonicalHeadersStr . "\n";
    $canonicalRequest[] = $signedHeaders;
    $canonicalRequest[] = $requestHasedPayload;
    $requestCanonicalRequest = implode("\n", $canonicalRequest);
    $requestHasedCanonicalRequest = hash($phpAlgorithm, utf8_encode($requestCanonicalRequest));
    if($debug){
        /*echo "<h5>Canonical to string</h5>";
        echo "<pre>";
        echo $requestCanonicalRequest;
        echo "</pre>";*/
    }
    
    // Create scope
    $credentialScope = array();
    $credentialScope[] = $reqDate;
    $credentialScope[] = $region;
    $credentialScope[] = $service;
    $credentialScope[] = $terminationString;
    $credentialScopeStr = implode('/', $credentialScope);

    // Create string to signing
    $stringToSign = array();
    $stringToSign[] = $algorithm;
    $stringToSign[] = $reqDateTime;
    $stringToSign[] = $credentialScopeStr;
    $stringToSign[] = $requestHasedCanonicalRequest;
    $stringToSignStr = implode("\n", $stringToSign);
    if($debug){
        /*echo "<h5>String to Sign</h5>";
        echo "<pre>";
        echo $stringToSignStr;
        echo "</pre>";*/
    }

    // Create signature
    $signature = hash_hmac($phpAlgorithm, $stringToSignStr, $kSigning);

 	// Create authorization header
    $authorizationHeader = array();
    $authorizationHeader[] = 'Credential=' . $accessKey . '/' . $credentialScopeStr;
    $authorizationHeader[] = 'SignedHeaders=' . $signedHeaders;
    $authorizationHeader[] = 'Signature=' . ($signature);
    $authorizationHeaderStr = $algorithm . ' ' . implode(', ', $authorizationHeader);


    // Request headers 
    $headers = array(); 
    $headers[] = 'authorization: '.$authorizationHeaderStr; 
    //$headers[] = 'X-Amz-Content-Sha256='.$requestHasedPayload; 
    $headers[] = 'content-length='.strlen($data); 
    $headers[] = 'content-type=application/json'; 
    $headers[] = 'host='. $host; 
    $headers[] = 'x-amz-date='. $reqDateTime; 
    $headers[] = 'x-amz-access-token='. $reqToken;
    $headers[] = 'x-amzn-shipping-business-id=AmazonShipping_IN';


    return $headers;
}
function getAmazonDate(){
    
    $currentDateTime = new DateTime('UTC');
    $reqDate = $currentDateTime->format('Ymd');
    return $reqDate;
}
function getAmazonDateTime(){
    
    $currentDateTime = new DateTime('UTC');
    $reqDate = $currentDateTime->format('Ymd');
    $reqDateTime = $currentDateTime->format('Ymd\THis\Z');
    return $reqDateTime;
}
?>