如何使用PHP调用Amazon MWS订单?

4
我遇到了很多麻烦和挫折,试图建立与Amazon MWS中的“订单”部分相关的API调用。当我使用Amazon MWS Scratchpad时,我能够得到成功的响应,但是当我自己进行调用时,会出现错误:
The request signature we calculated does not match the signature you provided.
Check your AWS Secret Access Key and signing method.
Consult the service documentation for details.

我可能错了,但我认为我的调用方式可能有问题,因为即使我使用Scratchpad创建的确切调用,我仍然收到相同的错误。
无论如何,这是我试图构建请求的代码:
<?php

$secretKey = '<MY SECRET KEY>';

$parameters = array();

// required parameters
$parameters['Action']               = 'ListOrders';
$parameters['AWSAccessKeyId']       = '<MY ACCESS KEY>';
$parameters['MWSAuthToken']         = '<MY AUTH TOKEN>';
$parameters['SellerId']             = '<MY SELLER ID>';
$parameters['SignatureMethod']      = 'HmacSHA256';
$parameters['SignatureVersion']     = '2';
$parameters['Timestamp']            = gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time());
$parameters['Version']              = '2013-09-01';

// optional parameters
$parameters['MarketplaceId.Id.1']   = '<MARKETPLACE ID>';
$parameters['CreatedAfter']         = '2015-10-04T04%3A00%3A00Z';

/**
 * Calculate String to Sign
 * 
 * @param array $parameters request parameters
 * @return String to Sign
 */
function _calculateStringToSign(array $parameters) {
    $data = "POST\n";
    $data .= "mws.amazonservices.com\n";
    $data .= "/Orders/2013-09-01\n";        
    $data .= _getParametersAsString($parameters);
    return $data;
}


/**
 * Convert paremeters to Url encoded query string
 */
function _getParametersAsString(array $parameters)
{
    uksort($parameters, 'strcmp');
    $queryParameters = array();
    foreach ($parameters as $key => $value) {
        $queryParameters[] = $key . '=' . _urlencode($value);
    }
    return implode('&', $queryParameters);
}

function _urlencode($value) {
    return str_replace('%7E', '~', rawurlencode($value));
}

/**
 * Computes RFC 2104-compliant HMAC signature.
 */
function _sign($stringToSign, $secretKey)
{
    $hash = 'sha256';

    return urlencode(base64_encode(
        hash_hmac($hash, $stringToSign, $secretKey, true)
    ));
}

/**
 * Builds up the request.
 */
function buildRequest(array $parameters, $secretKey) {
    $endpoint = 'https://mws.amazonservices.com/Orders/2013-09-01';

    $signature = _sign(_calculateStringToSign($parameters), $secretKey);
    $parameters['Signature'] = $signature;

    uksort($parameters, 'strcmp');
    return $endpoint . '?' . _getParametersAsString($parameters);
}

这里是实际进行调用的代码:

$request = buildRequest($parameters, $secretKey);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) chrome/39.0.2171.71 Safari/537.36');
$page = curl_exec($ch);
curl_close($ch);
var_dump($page);

我需要帮助确定这个脚本出了什么问题,为什么我无法得到成功的响应。


你解决了你的问题吗?我认为我正在面临同样的问题(https://dev59.com/2Jzha4cB1Zd3GeqPMe2K) - Martina
@A.. 这是很久以前的事情,我几乎记不清了。我认为这里的答案很有帮助,我还必须改变参数的顺序。如果你到那时还没有得到它,我可以稍后尝试找到我使用的代码。 - tam5
稍后我会更改参数的顺序...但如果你能给我展示你的代码,我将不胜感激! - Martina
@A.. 我刚刚将我正在使用的代码复制到了gist上。我没有时间测试它或者仔细检查它,因为它是 Laravel 项目的一部分。不确定代码的质量或其他任何事情,因为这是一段时间以前的事情,但你应该能够使用它来获得成功的响应。等我有更多时间时,我会回来看看你是否已经解决了问题。 - tam5
非常感谢,今晚我会尝试一下!如果它有效,请回复我的问题,我会给你正确的答案。 - Martina
我试了你的代码!它可以工作!如果你愿意,你可以回答我的问题。非常感谢!! - Martina
2个回答

4

解决方案:

$secretKey = 'ENTERVALUE';

$parameters = array();

// required parameters
$parameters['AWSAccessKeyId']       = 'ENTERVALUE';
$parameters['Action']               = 'GetOrder';
$parameters['AmazonOrderId.Id.1']   = 'ENTERVALUE';
$parameters['SellerId']             = 'ENTERVALUE';
$parameters['SignatureMethod']      = 'HmacSHA256';
$parameters['SignatureVersion']     = '2';
$parameters['Timestamp']            = gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time());
$parameters['Version']              = '2013-09-01';




/**
 * Calculate String to Sign
 * 
 * @param array $parameters request parameters
 * @return String to Sign
 */
function _calculateStringToSign(array $parameters) {
    $data = "POST\n";
    $data .= "mws.amazonservices.com\n";
    $data .= "/Orders/2013-09-01\n";        
    $data .= _getParametersAsString($parameters);
    return $data;
}


/**
 * Convert paremeters to Url encoded query string
 */
function _getParametersAsString(array $parameters)
{
    uksort($parameters, 'strcmp');
    $queryParameters = array();
    foreach ($parameters as $key => $value) {
        $queryParameters[] = $key . '=' . _urlencode($value);
    }
    return implode('&', $queryParameters);
}

function _urlencode($value) {
    return str_replace('%7E', '~', rawurlencode($value));
}

/**
 * Computes RFC 2104-compliant HMAC signature.
 */
function _sign($stringToSign, $secretKey)
{
    //HmacSHA1
    $hash = 'sha256';

    return base64_encode(
        hash_hmac($hash, $stringToSign, $secretKey, true)
    );
}

/**
 * Builds up the request.
 */
function buildRequest(array $parameters, $secretKey) {
    //$endpoint = 'https://mws.amazonservices.com/Orders/2013-09-01';

    $signature = _sign(_calculateStringToSign($parameters), $secretKey);
    $parameters['Signature'] = $signature;

    uksort($parameters, 'strcmp');
    return _getParametersAsString($parameters);
}

$request = buildRequest($parameters, $secretKey);



$allHeaders = array();
    $allHeaders['Content-Type'] = "application/x-www-form-urlencoded; charset=utf-8"; // We need to make sure to set utf-8 encoding here
    $allHeaders['Expect'] = null; // Don't expect 100 Continue
    $allHeadersStr = array();
    foreach($allHeaders as $name => $val) {
        $str = $name . ": ";
        if(isset($val)) {
            $str = $str . $val;
        }
        $allHeadersStr[] = $str;
    }


//complete string
//echo $endpoint . '?' .$request;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://mws.amazonservices.com/Orders/2013-09-01');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
curl_setopt($ch, CURLOPT_HTTPHEADER, $allHeadersStr);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) chrome/39.0.2171.71 Safari/537.36');
$response = curl_exec($ch);
echo $response;
$xml = simplexml_load_string( $response );
echo curl_error($ch);
curl_close($ch);

虽然这段代码片段可能解决了问题,但包括解释真的有助于提高您的帖子质量。请记住,您正在为未来的读者回答问题,而这些人可能不知道您的代码建议原因。 - Derek Brown

2
这可能听起来很愚蠢,但是根据您目前的代码,您正在进行一个GET请求,但您将其标记为POST。将签名的第一行从POST更改为GET,则该签名应能够完美通过。

@tam - 好的,如果是POST请求,添加curl_setopt($ch, CURLOPT_POST, true);。它将发送一个空的postbody请求。然而,我对此持怀疑态度,因为v2签名仅适用于GET请求。此外,您用于排序参数的比较是错误的-它不是str比较而是byte比较。将参数连接起来,然后进行“排序”。 - Sébastien Renauld
你大部分是对的。事实上,我确实需要进行GET请求而不是POST,所以谢谢你并点赞。然而还有另一个问题,就是由于某种原因,Signature必须放在查询的末尾,这样做让它对我起作用了。不知道为什么,特别是不知道为什么没有在任何地方提到,但我已经让它工作了。我将更新我的问题,并附上最终有效的代码。 - tam5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接