UNPKG

sync-alisdk

Version:

Taobao Open API & Message Client.

306 lines (280 loc) 8.76 kB
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 */ function TopClient(options) { if (!(this instanceof TopClient)) { return new TopClient(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 */ TopClient.prototype.invoke = function (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 */ TopClient.prototype.request = function (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); } 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.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 = '店铺授权已过期, 请到速卖通服务市场订购后,回到控制台重新添加授权' } 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 TopClient.prototype.lazadaRequest = function (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} */ TopClient.prototype.timestamp = function () { return util.YYYYMMDDHHmmss(); }; /** * Sign API request. * see http://open.taobao.com/doc/detail.htm?id=111#s6 * * @param {Object} params * @return {String} sign string */ TopClient.prototype.sign = function (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(); }; TopClient.prototype.hmac_sign = function (params, api_name) { var sorted = Object.keys(params).sort(); let basestring = 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(); }; /** * execute top api */ TopClient.prototype.execute = function (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;