调用 API 接口
URL 中的参数 Query Params
| 参数 | 说明 | 类型 | 必填 | 默认值 |
|---|---|---|---|---|
| id | Api Id | String | 是 | |
| timestamp | 时间戳 (秒) (服务器可识别的时间格式 例如:1612876331 该时间戳与服务器时间前后相差不能超过 60 秒,合计 2 分钟) | Integer | 是 | |
| signature | Base64 字符串 (查看签名算法) | String | 是 | |
| limit | 返回结果中 rows 的最大数量。 意义等同于 pageSize | Integer | 否 | |
| skip | 忽略查询结果中的前 n 个值 | Integer | 否 | |
| sort | 查询结构排列方式.例: sort[]=regionCode:desc&sort[]=countryCode:asc | 否 | ||
| keywords | 关键字查询以逗号区分 例如在区域查询中 keywords=america,canada | String | 否 |
URL 中的参数用法
POST /some_service?id={id}×tamp={timestamp}&signature={signature}
**POST 请求中 Body 参数 **
请参照具体接口说明以JSON数据类型提供
签名算法
-
将调用接口时 Url 中的 Query 参数(去除 siagnature 参数) 与 Body 参数合并 (GET 方法时, 请忽略 body)
-
将合并后的对象中的参数 第一个字母按升序排列
-
将每个参数值 urlencode
-
将所有参数用“&”号连接,如:orderNumber=XXXXXX&keywords=YYYYYY
-
将连接好的参数加上密钥(App Secret),用 HMAC SHA256 加密
-
将加密的字符串用 base64 编码,得到签名
JS Example
const id = "1234567";
const secret = "secret";
const timestamp = parseInt(new Date().getTime() / 1000, 10);
/* query 参数 */
const queryParams = {
skip: 0,
limit: 100,
sort: ["regionCode:desc", "coutryCode:asc"],
id,
timestamp,
};
/* body 参数 */
const body = {
initiation: {
regionCode: "CA",
},
destination: {
regionCode: "cn",
},
shipmentType: "parcel",
};
/* 合并query & body参数 */
const params = { ...queryPrams, ...body };
/* 格式化参数函数 */
function params2string(params) {
let strArray = [];
const keys = Object.keys(params);
const sortedKeys = keys.sort();
sortedKeys.forEach((key) => {
let str = "";
str += typeof key === "string" ? key.toLowerCase() : key;
str += "=";
str +=
typeof params[key] === "string"
? encodeURIComponent(params[key]).replace("%7E", "~")
: JSON.stringify(params[key]);
strArray.push(str);
});
return strArray.join("&");
}
/* 格式化参数 */
const urlEncodedString = params2string(params);
const { createHmac } = require("crypto");
const signature = createHmac("sha256", secret)
.update(urlEncodedString)
.digest("base64");
请求实例
GET /service?id=7f16f1b0-61bf-11eb-ae75-75abac9d9f41&skip=0&signature=O5XHLnECvcdoMKe6WdvN1AbBUqBa3ZOyneQxSwl1zsw%3D&limit=20×tamp=1612885847
返回结果
请求成功 HTTP Status 200
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| success | 当请求正常结束时返回真值 true | Boolean | |
| elapsed | 请求消耗时间 | ||
| timestamp | 时间戳 (服务器可识别的时间格式 例如:1612876331 该时间戳与服务器时间前后相差不能超过 60 秒,合计 2 分钟) | Integer | |
| query | 提交的请求参数 (id, timestamp, signature, api, livemode) | Object | |
| result | 请求结果 | Object | |
| result/rows | 查询结果 | Array | |
| result/count | 查询结果的 total number | Integer | |
| result/limit | 返回结果中 rows 的最大数量。 意义等同于 pageSize | Integer | |
| result/skip | 忽略查询结果中的前 n 个值 | Integer | |
| result/message | 消息 | String | |
| result/*** | 其他 | Object |
请求失败 HTTP Status 2XX / 4XX / 5XX
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| error | 当请求发生错误时会返回错误类别 例: badRequest, carrierError, usageError, orderError 等 | String | |
| code | 错误代码 E_XXX_YYY_ZZZ 例: E_ACCOUNT_BALANCE_NOT_INSUFFICIENT | String | |
| message | 错误消息 例: options: [OPTION-B, OPTION-A] are not supported with current service | String | |
| Problems | 当遇到多个问题是返回字符串数组 例: [‘pak length show grater than 10cm’, ‘pak width show grater than 30cm’] | Array |
POSTMAN 设置 Pre-request Script
在使用 Postman 进行测试时,可以通过设置 Pre-request Script 来自动生成带时间戳的签名
Example
// 在环境变量中添加id,
// 在环境变量中添加timestamp
// 在环境变量中添加signature
// {X} 大括号中为postman环境变量
Request Url : ///some_service?id={id}&signature={signature}&limit=20×tamp={timestamp}
const apiKey = '1234567';
const id = 'secret';
const timestamp = parseInt(new Date().getTime()/1000,10);
const queryParams = pm.request.url.query.toObject(); // 获取URL中的query参数
queryParams.id = id;
queryParams.timestamp = timestamp;
pm.environment.set('id', id );
pm.environment.set('timestamp',timestamp);
/* 当在url中使用数组Array 参数时, 如sort[] */
for (const i in queryParams) {
if (Object.hasOwnProperty.call(queryParams, i)) {
const item = queryParams[i];
if (Array.isArray(item)) {
newKey = i.replace('[]', '');
queryParams[newKey] = item;
delete queryParams[i];
}
}
}
/* 去除query参数中的signature */
delete queryParams.signature;
/* Post 方法时,获取body参数 */
let body = pm.request.body;
body = body && body.raw? JSON.parse(body.raw): {};
/* 合并query & body参数 */
const params = {...queryParams, body}
/* 格式化参数函数 */
function params2string (params) {
let strArray = [];
const keys = Object.keys(params);
const sortedKeys = keys.sort();
sortedKeys.forEach(key => {
let str = '';
str += typeof key === 'string' ? key.toLowerCase() : key;
str += '=';
str += typeof params[key] === 'string' ? encodeURIComponent(params[key]).replace('%7E', '~') : JSON.stringify(params[key])
strArray.push(str);
});
return strArray.join('&');
}
/* 格式化参数 */
const urlEncodedString = params2string(params);
/* 生成签名 */
const hash = CryptoJS.HmacSHA256(urlEncodedString, secret);
const signature = CryptoJS.enc.Base64.stringify(hash);
/* 将签名urlEncode后在环境变量中更新 */
pm.environment.set("signature", encodeURIComponent(signature));