UNPKG

z_zbapi

Version:

Nodejs API wrapper for zb.com

400 lines (337 loc) 10.7 kB
const util = require('util'); const _ = require('lodash'); const request = require('request'); // const crypto = require('crypto'); const VError = require('verror'); // const md5 = require('md5'); const CryptoJS = require('crypto-js'); var ZB = function(access_key, secret_key, server, timeout) { this.access_key = access_key; this.secret_key = secret_key; this.server = server || 'http://zbapi.honghe56.com'; this.timeout = timeout || 10000; }; var headers = { "User-Agent": "ZB China JavaScript API Wrapper" }; ZB.prototype.privateRequest = function(method, params, callback) { var functionName = 'ZB.privateRequest()', self = this; if(!this.access_key || !this.secret_key) { var error = new VError('%s must provide access_key and secret_key to make this API request.', functionName); return callback(error); } if(!_.isObject(params)) { var error = new VError('%s second parameter %s must be an object. If no params then pass an empty object {}', functionName, params); return callback(error); } if(!callback || typeof(callback) != 'function') { var error = new VError('%s third parameter needs to be a callback function', functionName); return callback(error); } params.accesskey = this.access_key; params.sign = this.signMessage(params); var now = new Date(); params.reqTime = now.getTime(); var options = { url: this.server+'/api/' + method + '?' + formatParameters(params), method: 'GET', headers: headers, timeout: this.timeout, //qs: formatParameters(params), json: {} // request will parse the json response into an object }; var requestDesc = util.format('%s request to url %s with method %s and params %s', options.method, options.url, method, JSON.stringify(params)); executeRequest(options, requestDesc, callback); }; /** * This method returns a signature for a request as a md5-encoded uppercase string * @param {Object} params The object to encode * @return {String} The request signature */ ZB.prototype.signMessage = function getMessageSignature(params) { var presign_params = formatParameters(params); var secret_key = CryptoJS.SHA1(this.secret_key); return _.toString(CryptoJS.HmacMD5(presign_params, _.toString(secret_key))); }; /** * This method returns the parameters as key=value pairs separated by & sorted by the key * @param {Object} params The object to encode * @return {String} formatted parameters */ function formatParameters(params) { var sortedKeys = [], formattedParams = ''; // sort the properties of the parameters sortedKeys = _.keys(params).sort(); // create a string of key value pairs separated by '&' with '=' assignement for(i = 0; i < sortedKeys.length; i++) { if(i != 0) { formattedParams += '&'; } formattedParams += sortedKeys[i] + '=' + params[sortedKeys[i]]; } return formattedParams; } ZB.prototype.publicRequest = function(method, params, callback) { var functionName = 'ZB.publicRequest()'; if(!_.isObject(params)) { var error = new VError('%s second parameter %s must be an object. If no params then pass an empty object {}', functionName, params); return callback(error); } if(!callback || typeof(callback) != 'function') { var error = new VError('%s third parameter needs to be a callback function with err and data parameters', functionName); return callback(error); } var url = this.server + '/data/v1/' + method + '?' + formatParameters(params); var options = { url: url, method: 'GET', headers: headers, timeout: this.timeout, //qs: formatParameters(params), json: {} // request will parse the json response into an object }; var requestDesc = util.format('%s request to url %s with parameters %s', options.method, options.url, JSON.stringify(params)); executeRequest(options, requestDesc, callback) }; function executeRequest(options, requestDesc, callback, retry) { if (typeof retry === 'undefined') { retry = 3; } var functionName = 'ZB.executeRequest()'; request(options, function(err, response, data) { var error = null, // default to no errors returnObject = data; if(err) { if((err.code =='ETIMEDOUT' || err.code =='ESOCKETTIMEDOUT') && retry > 0){//超时重试 console.log('===>超时重试',retry) return executeRequest(options, requestDesc, callback, retry-1); } error = new VError(err, '%s failed %s', functionName, requestDesc); error.name = err.code; } else if(response.statusCode < 200 || response.statusCode >= 300) { error = new VError('%s HTTP status code %s returned from %s', functionName, response.statusCode, requestDesc); error.name = response.statusCode; } else if(options.form) { try { returnObject = JSON.parse(data); } catch(e) { error = new VError(e, 'Could not parse response from server: ' + data); } } // if json request was not able to parse json response into an object else if(options.json && !_.isObject(data)) { error = new VError('%s could not parse response from %s\nResponse: %s', functionName, requestDesc, data); } if(_.has(returnObject, 'error_code')) { var errorMessage = mapErrorMessage(returnObject.error_code); error = new VError('%s %s returned error code %s, message: "%s"', functionName, requestDesc, returnObject.error_code, errorMessage); error.name = returnObject.error_code; } callback(error, returnObject); }); } // // Public Functions // ZB.prototype.getTicker = function getTicker(currency) { return new Promise((resolve, reject) => { this.publicRequest('ticker', { market: currency }, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; ZB.prototype.getDepth = function getDepth(market, size, merge) { var params = { market: market, size: 50, //merge: 1 }; if(!_.isUndefined(size)) params.size = size; if(!_.isUndefined(merge)) params.merge = merge; return new Promise((resolve, reject) => { this.publicRequest('depth', params, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; ZB.prototype.getTrades = function getTrades(market, since) { var params = { market: market }; if(since) params.since = since; return new Promise((resolve, reject) => { this.publicRequest('trades', params, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; ZB.prototype.getKline = function getKline(market, type, size, since) { var params = { market: market }; if(type) params.type = type; if(size) params.size = size; if(since) params.since = since; return new Promise((resolve, reject) => { this.publicRequest('kline', params, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; // // ZB.prototype.getLendDepth = function getLendDepth(callback, market) { // this.publicRequest('kline', {market: market}, callback); // }; // // Private Functions // ZB.prototype.getUserInfo = function getUserInfo() { var params = { method: 'getAccountInfo' }; return new Promise((resolve, reject) => { this.privateRequest('getAccountInfo', params, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; ZB.prototype.createOrder = function createOrder(currency, type, amount, price) { var params = { currency: currency, tradeType: type == 'buy' ? '1' : '0', amount: amount, price: price, }; return new Promise((resolve, reject) => { this.privateRequest('order', params, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; ZB.prototype.cancelOrder = function cancelOrder(currency, order_id) { return new Promise((resolve, reject) => { this.privateRequest('cancelOrder', { currency: currency, id: order_id }, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; ZB.prototype.getOrderInfo = function getOrderInfo(currency, order_id) { return new Promise((resolve, reject) => { this.privateRequest('getOrder', { currency: currency, id: order_id }, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; ZB.prototype.getOrdersNew = function getOrdersNew(currency, type, current_page, page_length) { return new Promise((resolve, reject) => { this.privateRequest('getOrdersNew', { currency: currency, tradeType: type, pageIndex: current_page, pageSize: page_length }, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; ZB.prototype.getOrderHistory = function getOrderHistory(currency, current_page, page_length) { return new Promise((resolve, reject) => { this.privateRequest('getOrdersIgnoreTradeType', { currency: currency, pageIndex: current_page, pageSize: page_length }, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; ZB.prototype.getWithdrawRecord = function getWithdrawRecord(currency, current_page, page_length) { return new Promise((resolve, reject) => { this.privateRequest('getAccountRecords', { currency: currency, pageIndex: current_page, pageSize: page_length, }, (err, data) => { if(err) reject(err) else resolve(data) }); }) } ZB.prototype.addWithdraw = function addWithdraw(currency, chargefee, trade_pwd, withdraw_address, withdraw_amount) { return new Promise((resolve, reject) => { this.privateRequest('withdraw', { currency: currency, fees: chargefee, safePwd: trade_pwd, receiveAddr: withdraw_address, amount: withdraw_amount, itransfer: 0 }, (err, data) => { if(err) reject(err) else resolve(data) }); }) }; /** * Maps the ZB error codes to error message * @param {Integer} error_code ZB error code * @return {String} error message */ function mapErrorMessage(error_code) { var errorCodes = { 1000: '调用成功', 1001: '一般错误提示', 1002: '内部错误', 1003: '验证不通过', 1004: '资金安全密码锁定', 1005: '资金安全密码错误,请确认后重新输入。', 1006: '实名认证等待审核或审核不通过', 1009: '此接口维护中', 2001: '人民币账户余额不足', 2002: '比特币账户余额不足', 2003: '莱特币账户余额不足', 2005: '以太币账户余额不足', 2006: 'ETC币账户余额不足', 2007: 'BTS币账户余额不足', 2009: '账户余额不足', 3001: '挂单没有找到', 3002: '无效的金额', 3003: '无效的数量', 3004: '用户不存在', 3005: '无效的参数', 3006: '无效的IP或与绑定的IP不一致', 3007: '请求时间已失效', 3008: '交易记录没有找到', 4001: 'API接口被锁定或未启用', 4002: '请求过于频繁', }; if(!errorCodes[error_code]) { return 'Unknown ZB error code: ' + error_code; } return(errorCodes[error_code]); } module.exports = ZB;