sync-alisdk
Version:
Taobao Open API & Message Client.
365 lines (339 loc) • 10.1 kB
JavaScript
var util = require("../topUtil.js");
var RestClient = require("./network.js");
var Stream = require("stream");
const v8 = require("v8");
/**
* TOP API Client.
*
* @param {Object} options, must set `appkey` and `appsecret`.
* @constructor
*/
class TopClient {
constructor(options) {
options = options || {};
if (!options.appkey || !options.appsecret) {
throw new Error("appkey or appsecret need!");
}
this.url = options.url || "http://gw.api.taobao.com/router/rest";
this.platform = options.platform || "aliexpress";
this.appkey = options.appkey;
this.appsecret = options.appsecret;
this.newApi = options.newApi;
}
/**
* Invoke an api by method name.
*
* @param {String} method, method name
* @param {Object} params
* @param {Array} reponseNames, e.g. ['tmall_selected_items_search_response', 'tem_list', 'selected_item']
* @param {Object} defaultResponse
* @param {Function(err, response)} callback
*/
invoke(type, method, params, bigIntParamName, reponseNames, callback) {
params.method = method;
const cb = (err, result) => {
if (err) {
return callback(err);
}
var response = result;
if (reponseNames && reponseNames.length > 0) {
var retResult = undefined;
for (var i = 0; i < reponseNames.length; i++) {
var name = reponseNames[i];
retResult = response[name];
if (retResult != undefined) {
response = retResult;
break;
}
}
}
callback(null, response);
};
if (this.platform === "aliexpress") {
return this.request(type, params, bigIntParamName, cb);
}
if (this.platform === "lazada") {
return this.lazadaRequest(type, params, cb);
}
}
/**
* Request API.
*
* @param {Object} params
* @param {String} [type='GET']
* @param {Function(err, result)} callback
* @param {number} retry
* @public
*/
request(type, params, bigIntParamName, callback, retry = 10) {
const backupParams = v8.deserialize(v8.serialize(params));
const skipAuth = params.skipAuth;
if (skipAuth) {
delete params.skipAuth;
} else {
let err = util.checkRequired(params, ["sessionInfo", "method"]);
// const now = Date.now();
// const thirdExpiredTime = params.sessionInfo.sessionExpireTime + 86400000;
// if (now > thirdExpiredTime) {
// err = new Error('店铺授权已过期, 请到速卖通服务市场免费订购后,回到控制台重新添加授权');
// err.code = 27;
// }
if (err) {
return callback(err);
}
if (this.newApi) {
params.access_token = params.sessionInfo.session;
}else {
params.session = params.sessionInfo.session;
}
delete params.sessionInfo;
}
const args = {
timestamp: Date.now(),
format: "json",
app_key: this.appkey,
v: "2.0",
sign_method: this.newApi ? "sha256" : "md5",
};
let request = null;
if (type === "get") {
request = RestClient.get(this.url, {
"Accept-Encoding": "gzip, deflate",
});
} else {
request = RestClient.post(this.url, {
"Accept-Encoding": "gzip, deflate",
});
}
for (var key in params) {
if (typeof params[key] === "object" && Buffer.isBuffer(params[key])) {
request.attach(key, params[key], {
knownLength: params[key].length,
filename: key,
});
} else if (typeof params[key] === "object") {
args[key] = JSON.stringify(params[key]);
} else {
args[key] = params[key];
}
}
args.sign = this.newApi
? this.hmac_sign(args, params.method, this.url === 'https://api-sg.aliexpress.com/rest')
: this.sign(args);
for (var key in args) {
request.field(key, args[key]);
}
const that = this;
request.end(function (response) {
if (response.statusCode === 200) {
const data = response.body;
const errRes = data && data.error_response;
if (errRes && !errRes.message) {
//不加这句async.js不会处理error
errRes.message = errRes.msg || "xxx";
}
if (errRes) {
if (
(errRes.code === 15 ||
errRes.code === "15" ||
errRes.code === "UnknownRuntimeException" ||
errRes.code === "IncompleteSignature" ||
errRes.code === "ServiceTimeout") &&
retry > 0
) {
// console.log(`retry retry ${retry}`, backupParams);
that.request(
type,
backupParams,
bigIntParamName,
callback,
retry - 1
);
return;
}
// if (errRes.code === 'ApiCallLimit' && retry > 0) {
// setTimeout(() => {
// that.request(type, backupParams, bigIntParamName, callback, retry - 1);
// }, 1000);
// return;
// }
if (errRes.code === "IllegalAccessToken") {
errRes.message =
"店铺授权已过期, 请到速卖通服务市场订购后,回到控制台重新添加授权";
}
console.log("errRes", errRes, params, args);
callback(errRes, data);
} else {
callback(null, data);
}
} else {
if (retry > 0) {
that.request(
type,
backupParams,
bigIntParamName,
callback,
retry - 1
);
return;
}
console.log("response", response.statusCode, response.body);
const err = new Error("NetWork-Error");
err.name = "NetWork-Error";
err.code = 15;
err.sub_code = response.statusCode;
callback(err, null);
}
}, bigIntParamName);
}
// when request access_token set params.isAuthRequest = true
lazadaRequest(type, params, callback) {
const isAuthRequest = params.isAuthRequest;
let err = util.checkRequired(params, ["method"]);
if (!isAuthRequest) {
err = util.checkRequired(params, ["sessionInfo"]);
}
if (err) {
return callback(err);
}
const now = Date.now();
if (!isAuthRequest) {
const thirdExpiredTime = params.sessionInfo.sessionExpireTime - 5000;
if (now > thirdExpiredTime) {
err = new Error(
"店铺授权已过期, 请到速卖通服务市场免费订购后,回到控制台重新添加授权"
);
err.code = 27;
return callback(err);
}
params.access_token = params.sessionInfo.session;
delete params.sessionInfo;
} else {
delete params.isAuthRequest;
}
const method = params.method;
delete params.method;
let args = {
timestamp: now,
app_key: this.appkey,
sign_method: "sha256",
};
var request = null;
const url = this.url + method;
if (type == "get") {
request = RestClient.get(url);
} else {
request = RestClient.post(url);
}
for (var key in params) {
if (typeof params[key] === "object" && Buffer.isBuffer(params[key])) {
request.attach(key, params[key], {
knownLength: params[key].length,
filename: key,
});
} else if (typeof params[key] === "object") {
args[key] = JSON.stringify(params[key]);
} else {
args[key] = params[key];
}
}
args.sign = this.hmac_sign(args, method);
for (var key in args) {
request.field(key, args[key]);
}
request.end(function (response) {
if (response.statusCode == 200) {
var data = response.body;
var errRes = data && data.error_response;
if (errRes && !errRes.message) {
//不加这句async.js不会处理error
errRes.message = errRes.msg || "xxx";
}
if (errRes) {
callback(errRes, data);
} else {
callback(err, data);
}
} else {
err = new Error("NetWork-Error");
err.name = "NetWork-Error";
err.code = 15;
err.sub_code = response.statusCode;
callback(err, null);
}
});
}
/**
* Get now timestamp with 'yyyy-MM-dd HH:mm:ss' format.
* @return {String}
*/
timestamp() {
return util.YYYYMMDDHHmmss();
}
/**
* Sign API request.
* see http://open.taobao.com/doc/detail.htm?id=111#s6
*
* @param {Object} params
* @return {String} sign string
*/
sign(params) {
var sorted = Object.keys(params).sort();
let basestring = this.appsecret;
for (let i = 0, l = sorted.length; i < l; i++) {
var k = sorted[i];
basestring += k + params[k];
}
basestring += this.appsecret;
return util.md5(basestring).toUpperCase();
}
hmac_sign(params, api_name, need_api_head=false) {
var sorted = Object.keys(params).sort();
let basestring = need_api_head ? api_name : "";
for (var i = 0, l = sorted.length; i < l; i++) {
var k = sorted[i];
basestring += k + params[k];
}
return util.hmac_sha256(basestring, this.appsecret).toUpperCase();
}
doExecute(apiname, params, bigIntParamName) {
let that = this;
return new Promise(function (resolve, reject) {
that.invoke(
"post",
apiname,
params,
bigIntParamName,
[util.getApiResponseName(apiname)],
(err, response) => {
if (err) {
return reject(err);
}
resolve(response);
}
);
});
}
/**
* execute top api
*/
execute(apiname, params, bigIntParamName) {
let that = this;
return new Promise(function (resolve, reject) {
that.invoke(
"post",
apiname,
params,
bigIntParamName,
[util.getApiResponseName(apiname)],
(err, response) => {
if (err) {
return reject(err);
}
resolve(response);
}
);
});
}
}
exports.TopClient = TopClient;