调用亚马逊SP-API PUT Item接口遇400/403错误,请求排查
NetSuite调用亚马逊SP-API PUT Item接口的问题
授权获取AccessToken(此步骤正常)
初始化参数:
var amz_url = "https://api.amazon.com/auth/o2/token", grant_type = "refresh_token", refresh_token = "{refreshtoken}", client_id = "amzn1.application-oa2-client.e87c20f1bc464aad8652******", client_secret = "46b8fbaef6285e8245b3a99d5a15ac8773e9d775bca44cb2220738a5f1*****";
授权请求代码:
let headers = ({ 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache', 'Host': 'api.amazon.com' }); let response = https.post({ url: amz_url, body: { grant_type: '' + grant_type + '', refresh_token: '' + refresh_token + '', client_id: '' + client_id + '', client_secret: '' + client_secret + '' }, headers: headers }); log.debug("resp-code", response.code); log.debug("resp-body", JSON.parse(response.body).refresh_token); var accessToken = JSON.parse(response.body).access_token;
STS令牌获取与请求体构建
// Getting access token - END // Getting STS token var awsMainAccessKey = "AKIA4OOZPXxxxxxxx", awsMainSecretKey = "QbcC9g9ylrEGoy4aEUJ0uMkyq6xxxxxxx"; var stsResponse = stsRequest(awsMainAccessKey,awsMainSecretKey); //Calculating timestamp in ISO 8601 format var today = new Date(); var ISO8601Date = getXAmzDate(); log.debug('ISO 8601 date format ', ISO8601Date); var bodyObj = {}, condArray = [], itemArray = [], attributesObj = {}, marketPlaceId = "A1F83G8C2ARO7P", sellerId = "A3F8NJ8FFYFH4O", productSKU = "TKR45UVDS";
请求体构建(生成的Payload Hash与亚马逊预期不符):
var bodyObj_final = { "productType": "LUGGAGE", "requirements": "LISTING", "attributes": { "condition_type": [ { "value": "new_new", "marketplace_id": marketPlaceId } ], "item_name": [ { "value": "AmazonBasics 16 Underseat Spinner Carry-On", "language_tag": "en_US", "marketplace_id": marketPlaceId } ] } } bodyObj.productType = "LUGGAGE"; condArray.push({ "value": "new_new", "marketplace_id": marketPlaceId }); itemArray.push({ "value": "DC Basics 16 Inches rterdfertr", "language_tag": "en_UK", "marketplace_id": marketPlaceId }); attributesObj.condition_type = condArray; attributesObj.item_name = itemArray; bodyObj.attributes = attributesObj; var secToken = stsResponse.Credentials.SessionToken; method = 'PUT'; var contenthash = "beaead3198f7da1e70d03ab969765e0821b24fc913697e929e726aeaebf0eba3";
标准化URI、签名构建与请求发送
// 标准化URI和查询字符串 canonical_uri = encodeURI('/listings/2021-08-01/items/' + sellerId +'/'+productSKU); canonical_querystring = encodeURI('marketplaceIds')+'='+encodeURI('A1F83G8C2ARO7P'); var hmacsha256Data = CryptoJS.SHA256(JSON.stringify(bodyObj_final)); var payload_hash = CryptoJS.enc.Hex.stringify(hmacsha256Data); canonical_headers = 'host:' + 'sellingpartnerapi-eu.amazon.com' + '\n' + 'x-amz-content-sha256:' + contenthash + '\n'+ 'x-amz-date:' + ISO8601Date + '\n'+ 'x-amz-security-token:' + secToken + '\n'; signed_headers = "host;x-amz-content-sha256;x-amz-date;x-amz-security-token"; log.debug('Payload payload_hash ', payload_hash); // 硬编码的正确Hash,替换后请求可正常工作 var paylhash = "31ff10db41d2c210f7beb1adcd5b71c5ab2ec7ed13ad3972be88ff06db177aed"; canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + paylhash; log.debug('URI String ', canonical_request); /*************** TASK 2: CREATE THE STRING TO SIGN************* */ var monthStr = (today.getUTCMonth()+1), dayStr = today.getUTCDate(); var datestamp = today.getUTCFullYear() + '' + (monthStr>9?monthStr:('0'+monthStr)) + '' + (dayStr>9?dayStr:('0'+dayStr)); region = 'eu-west-1'; service = 'execute-api'; algorithm = 'AWS4-HMAC-SHA256'; var canonicalsha256Data = CryptoJS.SHA256(canonical_request); var canonical_hash = CryptoJS.enc.Hex.stringify(canonicalsha256Data); log.debug('canonical_hash is ', canonical_hash); credential_scope = datestamp + '/' + region + '/' + 'execute-api' + '/' + 'aws4_request'; string_to_sign = algorithm + '\n' + ISO8601Date + '\n' + credential_scope + '\n' + canonical_hash; log.debug('String to Sign ', string_to_sign); /************* TASK 3: CALCULATE THE SIGNATURE *************/ secret_key = stsResponse.Credentials.SecretAccessKey; access_key = stsResponse.Credentials.AccessKeyId; signing_key = getSignatureKey(secret_key, datestamp, region, service); var signingsha256Data = CryptoJS.HmacSHA256(string_to_sign, signing_key); var signature = CryptoJS.enc.Hex.stringify(signingsha256Data); log.debug('Final signature is ', signature); /************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************/ authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature; log.debug('authorization_header is ', authorization_header); headers = { 'host': 'sellingpartnerapi-eu.amazon.com', 'x-amz-access-token': accessToken, 'X-Amz-Security-Token': secToken, 'x-Amz-Content-Sha256': contenthash, 'x-amz-date': ISO8601Date, 'Authorization': authorization_header, 'Content-Type': 'application/json', 'ACCEPT': '*/*' } /************** SEND THE REQUEST **************/ request_url = 'https://sellingpartnerapi-eu.amazon.com'+canonical_uri+'?' + canonical_querystring; log.debug('Request URL is ', request_url); var response_end = https.put({ url: request_url, headers: headers, body : bodyObj }); log.debug('Response Code is ', response_end.code); log.debug('Response is ', response_end.body);
遇到的问题
- 自行生成的Payload Hash与亚马逊预期不符,只有硬编码正确的
paylhash时请求才能正常执行,否则返回403错误 - 使用自定义Hash时,请求返回400错误:
{ "errors": [ { "code": "InvalidInput", "message": "Invalid Input", "details": "" } ] }
排查建议
- 请求体一致性验证:计算Hash用的是
bodyObj_final,但实际发送的是bodyObj,两者结构不一致(比如bodyObj_final有requirements字段,bodyObj没有),这会导致Hash和实际请求体不匹配。必须保证计算Hash的对象与发送的请求体完全相同。 - JSON序列化细节:
JSON.stringify的输出要严格符合亚马逊的要求,比如键的顺序、是否有多余空格。可以手动序列化请求体,用在线SHA256工具计算Hash,和代码生成的对比,确认是否一致。 - 请求头与签名匹配:检查
canonical_headers中的头名称大小写,比如x-amz-security-token在签名中是小写,但实际请求头是X-Amz-Security-Token,大小写不匹配会导致签名验证失败。同时确保signed_headers的顺序与canonical_headers一致。 - Payload Hash头对应:
x-Amz-Content-Sha256头的值应该是代码生成的payload_hash,而不是硬编码的contenthash,否则头中的Hash与实际请求体Hash不匹配,触发403。 - URI标准化检查:确认
canonical_uri的编码是否符合亚马逊规范,亚马逊要求Canonical URI使用未编码的路径(除了必须编码的特殊字符),避免过度编码。 - 时间戳有效性:确保
ISO8601Date和datestamp是当前UTC时间,且格式严格符合YYYYMMDD'T'HHMMSS'Z',请求发送时间与签名中的时间戳差不能超过5分钟。
内容的提问来源于stack exchange,提问作者Gopi nadendla
相关产品推荐
相关产品推荐

