phpbase64md5amazon-mwsamazon-marketplace

Issue calculating MD5 hash of Amazon Marketplace feed


I’m trying to submit a SubmitFeed request to Amazon Marketplace, but when I submit the request I 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

So I tested the request on the Amazon Marketplace Scratchpad. I add my XML to the body, and the headers, and it generates the following MD5 hash:

1db3b177e743dc8c0df4dc9eb5c1cbcf

But there’s also a Content-MD5 (Base64) header, with this value:

HbOxd+dD3IwN9NyetcHLzw==

And it appears to be that value that’s actually sent to Amazon MWS as the Content-MD5 HTTP header, not the raw MD5 hash.

I’ve checked my PHP script and it’s generating the raw MD5 hash correctly, as when I wrap my XML string in the md5 function (md5($xml)) I get the same raw MD5 hash that Amazon generates. But if I then wrap that in the base64_encode function, I get a totally different value as to what Amazon lists for the Content-MD5 (Base64) value.

So far, I’ve tried wrapping the following in the base64_encode function:

But none yield the same value as Amazon’s Content-MD5 (Base64) value.

So what exactly is Amazon Base64-encoding to get that value? I’ve tried decoding the value, but just got a load of random characters that appears to be an encoding issue, so I can’t see the raw string that Amazon’s encoding to point me in the right direction.

Any guidance on this would be appreciated.


Solution

  • Found the solution. I decided to look at the documentation for the md5 function and found there was a second parameter to get the raw output of the function, which is false by default. So, I decided to set that flag to true instead and Base64-encode the result of that call.

    Voilà! I got the same Base64 value as Amazon!

    Using Guzzle, this is what I’m sending to Amazon and I’m now getting successful responses:

    $xml = trim($xml);
    
    // For some reason, the time my PHP script is sending is about 20 minutes out
    // from my system time. This fixes that.
    $timestamp = gmdate('c', time() + 1200);
    
    $url = 'https://mws.amazonservices.co.uk/';
    
    $parameters = [
        'Action' => 'SubmitFeed',
        'AWSAccessKeyId' => '#MY_ACCESS_KEY_ID#',
        'FeedType' => '_POST_PRODUCT_DATA_',
        'MarketplaceIdList.Id.1' => 'A1F83G8C2ARO7P', # UK marketplace ID
        'Merchant' => '#MY_SELLER_ID#',
        'PurgeAndReplace' => 'false',
        'SignatureMethod' => 'HmacSHA256',
        'SignatureVersion' => '2',
        'Timestamp' => $timestamp,
        'Version' => '2009-01-01',
    ];
    
    /**
     * Custom class that generates signature for request.
     *
     * @see http://stackoverflow.com/a/29724063/102205
     */
    $signature = new Signature($url, $parameters, '#MY_SECRET_ACCESS_KEY#');
    
    $parameters['Signature'] = (string) $signature;
    
    try {
        $response = $this->client->post($url, [
            'headers' => [
                'Content-MD5' => base64_encode(md5($xml, true)),
                'User-Agent' => '#MY_USER_AGENT_STRING#',
            ],
            'query' => $parameters,
            'body' => $xml,
        ]);
    } catch (\GuzzleHttp\Exception\ClientException $e) {
        $response = $e->getResponse();
    }
    
    return $response->xml();
    

    Hope this helps someone else!