Reputation: 743
I'm trying to convert an amazon MWS scratchpad feeds query over to php cURL request (update price), but get the following error:
the Content-MD5 HTTP header you passed for your feed did not match the Content-MD5 we calculated for your feed
The problem is that I dont know how to include the xml data ($feed) into the curl request.
The code works for other requests where no feed is needed, but how can I include the feed in the request?
$feed = <<< EOD
<?xml version="1.0" encoding="utf-8"?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
<Header>
<DocumentVersion>1.01</DocumentVersion>
<MerchantIdentifier>$merchant_token</MerchantIdentifier>
</Header>
<MessageType>Price</MessageType>
<Message>
<MessageID>$i</MessageID>
<Price>
<SKU>$sku</SKU>
<StandardPrice currency="$currency">$new_price</StandardPrice>
</Price>
</Message>
</AmazonEnvelope>
EOD;
$feedHandle = @fopen('php://temp', 'rw+');
fwrite($feedHandle, $feed);
rewind($feedHandle);
$params = array(
'AWSAccessKeyId' => $this->awsAccessKeyId,
'Action' => "SubmitFeed",
'Merchant' => $this->sellerId,
'SignatureMethod' => "HmacSHA256",
'SignatureVersion' => "2",
'Timestamp'=> gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time()),
'Version'=> "2009-01-01",
'MarketplaceIdList.Id.1' => $this->markedplaceId,
'FeedType' => '_POST_PRODUCT_PRICING_DATA_',
//'FeedContent' => $feedHandle,
'PurgeAndReplace' => 'false', //Leave this PurgeAndReplace to false so that it want replace whole product in amazon inventory
//'Content-Md5' => base64_encode(md5(stream_get_contents($feedHandle), true))
);
$md5 = base64_encode(md5(stream_get_contents($feedHandle), true));
// Sort the URL parameters
$url_parts = array();
foreach(array_keys($params) as $key)
$url_parts[] = $key . "=" . str_replace('%7E', '~', rawurlencode($params[$key]));
sort($url_parts);
// Construct the string to sign
$url_string = implode("&", $url_parts);
$string_to_sign = "GET\nmws.amazonservices.de\n/Feeds/2009-01-01\n" . $url_string;
// Sign the request
$signature = hash_hmac("sha256", $string_to_sign, $this->awsSecretAccessKey, TRUE);
// Base64 encode the signature and make it URL safe
$signature = urlencode(base64_encode($signature));
$url = "https://mws.amazonservices.de/Feeds/2009-01-01" . '?' . $url_string . "&Signature=" . $signature;
//var_dump($url);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: text/xml',
'Content-MD5: '.$md5,
'FeedContent: '.$feedHandle
)
);
$response = curl_exec($ch);
$parsed_xml = simplexml_load_string($response);
return $parsed_xml;
Upvotes: 1
Views: 1843
Reputation: 743
Ok I finally got it to work. The problem was in the string to sign, it should be POST like this:
$string_to_sign = "POST\nmws.amazonservices.de\n/Feeds/2009-01-01\n" . $url_string;
And another problem was that I needed to trim the $feed. Here is the working code:
$feed = <<< EOD
<?xml version="1.0" encoding="utf-8"?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
<Header>
<DocumentVersion>1.01</DocumentVersion>
<MerchantIdentifier>$merchant_token</MerchantIdentifier>
</Header>
<MessageType>Price</MessageType>
<Message>
<MessageID>$i</MessageID>
<Price>
<SKU>$sku</SKU>
<StandardPrice currency="$currency">$new_price</StandardPrice>
</Price>
</Message>
</AmazonEnvelope>
EOD;
$feedHandle = @fopen('php://temp', 'rw+');
fwrite($feedHandle, trim($feed));
rewind($feedHandle);
$params = array(
'AWSAccessKeyId' => $this->awsAccessKeyId,
'Action' => "SubmitFeed",
'Merchant' => $this->sellerId,
'SignatureVersion' => "2",
'Timestamp'=> gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time()),
'Version'=> "2009-01-01",
'SignatureMethod' => "HmacSHA256",
'FeedType' => '_POST_PRODUCT_PRICING_DATA_',
'MarketplaceIdList.Id.1' => $this->markedplaceId,
'PurgeAndReplace' => 'false'
);
// Sort the URL parameters
$url_parts = array();
foreach(array_keys($params) as $key)
$url_parts[] = $key . "=" . str_replace('%7E', '~', rawurlencode($params[$key]));
sort($url_parts);
// Construct the string to sign
$url_string = implode("&", $url_parts);
$string_to_sign = "POST\nmws.amazonservices.de\n/Feeds/2009-01-01\n" . $url_string;
// Sign the request
$signature = hash_hmac("sha256", $string_to_sign, $this->awsSecretAccessKey, TRUE);
// Base64 encode the signature and make it URL safe
$signature = urlencode(base64_encode($signature));
$url = "https://mws.amazonservices.de/Feeds/2009-01-01" . '?' . $url_string . "&Signature=" . $signature;
$md5 = base64_encode(md5(trim($feed), true));
$httpHeader=array();
$httpHeader[]='Transfer-Encoding: chunked';
$httpHeader[]='Content-Type: text/xml';
$httpHeader[]='Content-MD5: ' . $md5;
$httpHeader[]='Expect:';
$httpHeader[]='Accept:';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_INFILE, $feedHandle);
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeader);
curl_setopt($ch, CURLOPT_POST, true);
$response = curl_exec($ch);
if(curl_errno($ch))
{
var_dump(curl_error($ch));
}
$parsed_xml = simplexml_load_string($response);
return $parsed_xml;
Upvotes: 4