Steffan
Steffan

Reputation: 743

Amazon MWS feeds to php cURL request - update price

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

Answers (1)

Steffan
Steffan

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

Related Questions