@liuliang520500/pdd-sdk
Version:
拼多多开放平台SDK,支持多多进宝API
255 lines (225 loc) • 7.57 kB
JavaScript
var util = require('../pddUtil.js');
var RestClient = require('./network.js');
/**
* 拼多多开放平台客户端
* @param {Object} options 配置选项
* @param {String} options.clientId 应用ID(client_id)
* @param {String} options.clientSecret 应用密钥(client_secret)
* @param {String} [options.accessToken] 访问令牌
* @param {String} [options.apiVersion] API版本号
* @constructor
*/
function PddClient(options) {
if (!(this instanceof PddClient)) {
return new PddClient(options);
}
options = options || {};
if (!options.clientId || !options.clientSecret) {
throw new Error('clientId和clientSecret是必须的');
}
// 拼多多开放平台网关地址
this.gatewayUrl = 'https://gw-api.pinduoduo.com/api/router';
this.clientId = options.clientId;
this.clientSecret = options.clientSecret;
this.accessToken = options.accessToken;
this.apiVersion = options.apiVersion || 'V1';
this.dataType = 'JSON';
}
/**
* 执行API调用
* @param {String} type 请求类型,'get'或'post'
* @param {String} method 接口方法名,例如:'pdd.goods.list.get'
* @param {Object} params 业务参数
* @param {Object} [options] 可选配置参数
* @param {Function} callback 回调函数,接收(err, response)
*/
PddClient.prototype.invoke = function(type, method, params, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
params.type = method;
this.request(type, params, options, function(err, result) {
if (err) {
return callback(err);
}
// 获取API响应名称
var responseName = util.getApiResponseName(method);
// 解析响应数据
var response = result[responseName];
if (!response) {
// 检查是否有错误响应
if (result.error_response) {
return callback(result.error_response, result);
}
// 如果既没有正常响应也没有错误响应,返回原始结果
return callback(null, result);
}
callback(null, response);
});
};
/**
* 构建请求参数并执行请求
* @param {String} type 请求类型,'get'或'post'
* @param {Object} params 业务参数
* @param {Object} options 请求选项
* @param {Function} callback 回调函数
*/
PddClient.prototype.request = function(type, params, options, callback) {
var err = util.checkRequired(params, 'type');
if (err) {
return callback(err);
}
// 构建系统参数
var sysParams = {
client_id: this.clientId,
timestamp: Math.floor(util.getTimeMillis() / 1000).toString(), // 秒级时间戳
data_type: this.dataType,
version: this.apiVersion
};
// 添加access_token(如果有)
if (this.accessToken) {
sysParams.access_token = this.accessToken;
}
// 合并业务参数和系统参数
var allParams = {};
for (var key in params) {
// 如果参数是对象或数组,转为JSON字符串
if (typeof params[key] === 'object') {
allParams[key] = JSON.stringify(params[key]);
} else {
allParams[key] = params[key];
}
}
// 添加系统参数
for (var key in sysParams) {
allParams[key] = sysParams[key];
}
// 生成签名
allParams.sign = this.sign(allParams);
// 创建HTTP请求
var request = null;
if (type.toLowerCase() === 'get') {
request = RestClient.get(this.gatewayUrl);
} else {
request = RestClient.post(this.gatewayUrl);
}
// 设置请求头
request.header('Content-Type', 'application/x-www-form-urlencoded');
// 设置请求参数
for (var key in allParams) {
request.field(key, allParams[key]);
}
// 发送请求
request.end(function(response) {
if (response.statusCode === 200) {
var data = response.body;
callback(null, data);
} else {
var error = new Error('网络错误');
error.code = response.statusCode;
error.data = response.body;
callback(error);
}
});
};
/**
* 生成API签名
* @param {Object} params 请求参数
* @return {String} 签名字符串
*/
PddClient.prototype.sign = function(params) {
// 按键名升序排序
var keys = Object.keys(params).sort();
// 拼接参数字符串
var stringToSign = this.clientSecret;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
// 签名不包含sign字段本身
if (key !== 'sign' && params[key] !== undefined && params[key] !== null) {
stringToSign += key + params[key];
}
}
stringToSign += this.clientSecret;
// 计算MD5并转为大写
return util.md5(stringToSign).toUpperCase();
};
/**
* 执行GET请求
* @param {String} method 接口方法名
* @param {Object} params 业务参数
* @param {Function} callback 回调函数
*/
PddClient.prototype.get = function(method, params, callback) {
this.invoke('get', method, params, callback);
};
/**
* 执行POST请求
* @param {String} method 接口方法名
* @param {Object} params 业务参数
* @param {Function} callback 回调函数
*/
PddClient.prototype.post = function(method, params, callback) {
this.invoke('post', method, params, callback);
};
/**
* 使用Promise方式执行API调用
* @param {String} method 接口方法名
* @param {Object} params 业务参数
* @param {Object} [options] 可选配置参数
* @return {Promise} Promise对象
*/
PddClient.prototype.execute = function(method, params, options) {
var self = this;
return new Promise(function(resolve, reject) {
self.invoke('post', method, params, options, function(err, response) {
if (err) {
reject(err);
} else {
resolve(response);
}
});
});
};
/**
* 商品相关API
*/
PddClient.prototype.goods = {
/**
* 获取商品列表
* @param {Object} params 请求参数
* @param {Function} callback 回调函数
*/
list: function(params, callback) {
this.parent.invoke('post', 'pdd.goods.list.get', params, callback);
},
/**
* 获取商品详情
* @param {Object} params 请求参数
* @param {Function} callback 回调函数
*/
detail: function(params, callback) {
this.parent.invoke('post', 'pdd.goods.detail.get', params, callback);
}
};
/**
* 推广相关API
*/
PddClient.prototype.promotion = {
/**
* 生成商品推广链接
* @param {Object} params 请求参数
* @param {Function} callback 回调函数
*/
generateUrl: function(params, callback) {
this.parent.invoke('post', 'pdd.ddk.goods.promotion.url.generate', params, callback);
}
};
// 为子对象设置父引用
Object.defineProperty(PddClient.prototype.goods, 'parent', {
value: PddClient.prototype
});
Object.defineProperty(PddClient.prototype.promotion, 'parent', {
value: PddClient.prototype
});
exports.PddClient = PddClient;