diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..a146beb --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +*.debug.js +*.min.js +node_modules/* diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..baa3ad0 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,56 @@ +{ + "rules": { + "indent": [ + 2, + 2 + ], + "quotes": [ + 2, + "single" + ], + "linebreak-style": [ + 2, + "unix" + ], + "semi": [2, "always"], + "strict": [2, "global"], + "curly": 2, + "eqeqeq": 2, + "no-eval": 2, + "guard-for-in": 2, + "no-caller": 2, + "no-else-return": 2, + "no-eq-null": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-floating-decimal": 2, + "no-implied-eval": 2, + "no-labels": 2, + "no-with": 2, + "no-loop-func": 1, + "no-native-reassign": 2, + "no-redeclare": [2, {"builtinGlobals": true}], + "no-delete-var": 2, + "no-shadow-restricted-names": 2, + "no-undef-init": 2, + "no-use-before-define": 2, + "no-unused-vars": [2, {"args": "none"}], + "no-undef": 2, + "callback-return": [2, ["callback", "cb", "next"]], + "global-require": 0, + "no-console": 0, + "require-yield": 0 + }, + "env": { + "es6": true, + "node": true, + "browser": true + }, + "globals": { + "describe": true, + "it": true, + "before": true, + "after": true + }, + "extends": "eslint:recommended" +} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 20a987b..0000000 --- a/.jshintrc +++ /dev/null @@ -1,41 +0,0 @@ -{ - "predef": [ - "document", - "module", - "require", - "__dirname", - "process", - "console", - "it", - "xit", - "describe", - "xdescribe", - "before", - "beforeEach", - "after", - "afterEach" - ], - - "node": true, - "bitwise": true, - "curly": true, - "eqeqeq": true, - "forin": false, - "immed": true, - "latedef": true, - "noarg": true, - "noempty": true, - "nonew": true, - "plusplus": false, - "undef": true, - "strict": false, - "trailing": false, - "globalstrict": true, - "nonstandard": true, - "white": false, - "indent": 2, - "expr": true, - "multistr": true, - "onevar": false, - "unused": "vars" -} diff --git a/.travis.yml b/.travis.yml index 5a2018a..19f35fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: node_js node_js: - - "0.11" - - "0.10" + - "8" + - "6" + - "4" + - "0.12" script: make test-coveralls diff --git a/Makefile b/Makefile index e7104de..ad5ea0a 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,11 @@ ISTANBUL = ./node_modules/.bin/istanbul MOCHA = ./node_modules/mocha/bin/_mocha COVERALLS = ./node_modules/coveralls/bin/coveralls.js +all:test + +lint: + @eslint --fix lib test index.js + test: @NODE_ENV=test $(MOCHA) -R $(REPORTER) -t $(TIMEOUT) \ $(MOCHA_OPTS) \ diff --git a/README.md b/README.md index f515ebf..6899b2d 100644 --- a/README.md +++ b/README.md @@ -49,10 +49,41 @@ api.updateRemark('open_id', 'remarked', function (err, data, res) { }); ``` +### patch()扩展 + +当微信官方文档已更新,但本库未来得及更新,而又想用新的微信 api 时,可调用 patch 方法来扩展新功能。 +```js +var WechatAPI = require('wechat-api'); +var api = new WechatAPI(appid, appsecret); + +// 扩展新api : updateInfo +// 第一个参数为扩展的新方法名,第二个参数为此 api 调用的微信的 apiurl 地址,会自动加上 token +WechatAPI.patch("updateInfo", "https://api.weixin.qq.com/card/membercard/updateuser"); + + +// 调用刚扩展的方法,与其它 api 接口方法一样。 +api.updateInfo(jsonInfo, function (err, data, res) { + // TODO +}); +``` +#### 覆盖已有的接口 +当要扩展的新接口名称已在 API 内定义,会抛出异常: +`wechat-api already has a prototype named [uploadLogo]` +如果知道这个异常的含义且依然想定义这个接口名称,应该给第三个参数传入`true`: +```js +WechatAPI.patch("uploadLogo", "https://api.weixin.qq.com/card/membercard/updateuser", true); +``` +执行后控制台会输出警告,并覆盖原来`uploadLogo`这个接口。 + +> **提示** 当有新的微信接口开放或发现有用但 wechat-api 尚未加入开发的接口时,请在本库中相应的 js 文件中增加对新接口定义并提交 PullRequest 以便大家都能使用这个新接口。如果没有开发能力,请提交 issue. + +> **警告** 这个覆盖不只会覆盖 api 中的微信接口,所有定义给 api 的方法/成员变量 等都有可能被覆盖,请谨慎请用 + ### 多进程 当多进程时,token需要全局维护,以下为保存token的接口。 ``` -var api = new API('appid', 'secret', function (callback) { +var WechatAPI = require('wechat-api'); +var api = new WechatAPI('appid', 'secret', function (callback) { // 传入一个获取全局token的方法 fs.readFile('access_token.txt', 'utf8', function (err, txt) { if (err) {return callback(err);} @@ -92,32 +123,45 @@ QQ群:157964097,使用疑问,开发,贡献代码请加群。 ``` project : wechat-api - repo age : 12 months - active : 61 days - commits : 141 - files : 69 + repo age : 1 year, 9 months + active : 97 days + commits : 211 + files : 72 authors : - 99 Jackson Tian 70.2% - 9 tedyyu 6.4% - 5 Guan Bo 3.5% - 4 shuhankuang 2.8% - 4 Lou Lin 2.8% - 3 minxianlong 2.1% - 2 xuming314 1.4% - 2 Colt Xie 1.4% - 1 liuxiaodong 0.7% - 1 looping 0.7% - 1 wuxq 0.7% - 1 wxhuang 0.7% - 1 xumian.wei 0.7% - 1 yelo 0.7% - 1 Ezios 0.7% - 1 Lin@Cloud+ 0.7% - 1 Qun Lin 0.7% - 1 Will 0.7% - 1 brucewar 0.7% - 1 dan 0.7% - 1 ifeiteng 0.7% + 136 Jackson Tian 64.5% + 10 tedyyu 4.7% + 9 greenkeeperio-bot 4.3% + 7 Limjoe 3.3% + 6 Guan Bo 2.8% + 5 SunLn 2.4% + 4 shuhankuang 1.9% + 4 Lou Lin 1.9% + 3 minxianlong 1.4% + 2 Colt Xie 0.9% + 2 xuming314 0.9% + 1 brucewar 0.5% + 1 dan 0.5% + 1 ifeiteng 0.5% + 1 kai_hao 0.5% + 1 liuxiaodong 0.5% + 1 looping 0.5% + 1 simonyan 0.5% + 1 wuxq 0.5% + 1 wxhuang 0.5% + 1 xumian.wei 0.5% + 1 yelo 0.5% + 1 BeeSui 0.5% + 1 yuyuan 0.5% + 1 Eric 0.5% + 1 Ezios 0.5% + 1 JaydanHuang 0.5% + 1 Lin@Cloud+ 0.5% + 1 Monson Shao 0.5% + 1 Qun Lin 0.5% + 1 Silver Lao 0.5% + 1 Table 0.5% + 1 Wang Yitong 0.5% + 1 Will 0.5% ``` diff --git a/index.js b/index.js index 7629066..5a2b6ba 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,16 @@ +'use strict'; + var API = require('./lib/api_common'); +// 接口调用频次限制 +API.mixin(require('./lib/api_quota')); // 自定义菜单接口 API.mixin(require('./lib/api_menu')); // 个性化菜单接口 API.mixin(require('./lib/api_menu_custom')); // 分组管理 API.mixin(require('./lib/api_group')); +// 标签管理 +API.mixin(require('./lib/api_tag')); // 用户信息 API.mixin(require('./lib/api_user')); // 二维码 diff --git a/lib/api_card.js b/lib/api_card.js index ead2061..8d86d98 100644 --- a/lib/api_card.js +++ b/lib/api_card.js @@ -1,3 +1,5 @@ +'use strict'; + var path = require('path'); var fs = require('fs'); @@ -60,7 +62,7 @@ make(exports, 'addLocations', function (locations, callback) { var data = { location_list: locations }; - var url = 'https://api.weixin.qq.com/card/location/batchadd?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/location/batchadd?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }); @@ -69,17 +71,17 @@ make(exports, 'getLocations', function (offset, count, callback) { offset: offset, count: count }; - var url = 'https://api.weixin.qq.com/card/location/batchget?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/location/batchget?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }); make(exports, 'getColors', function (callback) { - var url = 'https://api.weixin.qq.com/card/getcolors?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/getcolors?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }); make(exports, 'createCard', function (card, callback) { - var url = 'https://api.weixin.qq.com/card/create?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/create?access_token=' + this.token.accessToken; var data = {card: card}; this.request(url, postJSON(data), wrapper(callback)); }); @@ -89,7 +91,7 @@ exports.getRedirectUrl = function (url, encryptCode, cardId) { }; make(exports, 'createQRCode', function (card, callback) { - var url = 'https://api.weixin.qq.com/card/qrcode/create?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/qrcode/create?access_token=' + this.token.accessToken; var data = { action_name: 'QR_CARD', action_info: { @@ -156,7 +158,7 @@ make(exports, 'createCardQRCode', function (info, expire_seconds, callback) { callback = expire_seconds; expire_seconds = null; } - var url = 'https://api.weixin.qq.com/card/qrcode/create?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/qrcode/create?access_token=' + this.token.accessToken; var data = { action_name: 'QR_'+Object.keys(info)[0].toUpperCase(), expire_seconds: expire_seconds, @@ -166,7 +168,7 @@ make(exports, 'createCardQRCode', function (info, expire_seconds, callback) { }); make(exports, 'consumeCode', function (code, cardId, callback) { - var url = 'https://api.weixin.qq.com/card/code/consume?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/code/consume?access_token=' + this.token.accessToken; var data = { code: code, card_id: cardId @@ -175,7 +177,7 @@ make(exports, 'consumeCode', function (code, cardId, callback) { }); make(exports, 'decryptCode', function (encryptCode, callback) { - var url = 'https://api.weixin.qq.com/card/code/decrypt?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/code/decrypt?access_token=' + this.token.accessToken; var data = { encrypt_code: encryptCode }; @@ -183,7 +185,7 @@ make(exports, 'decryptCode', function (encryptCode, callback) { }); make(exports, 'deleteCard', function (cardId, callback) { - var url = 'https://api.weixin.qq.com/card/delete?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/delete?access_token=' + this.token.accessToken; var data = { card_id: cardId }; @@ -191,7 +193,7 @@ make(exports, 'deleteCard', function (cardId, callback) { }); make(exports, 'getCode', function (code, cardId, callback) { - var url = 'https://api.weixin.qq.com/card/code/get?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/code/get?access_token=' + this.token.accessToken; var data = { code: code }; @@ -204,7 +206,7 @@ make(exports, 'getCode', function (code, cardId, callback) { }); make(exports, 'getCards', function (offset, count, status_list, callback) { - var url = 'https://api.weixin.qq.com/card/batchget?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/batchget?access_token=' + this.token.accessToken; var data = { offset: offset, count: count @@ -213,12 +215,12 @@ make(exports, 'getCards', function (offset, count, status_list, callback) { data.status_list = status_list; } else { callback = status_list; - } + } this.request(url, postJSON(data), wrapper(callback)); }); make(exports, 'getCard', function (cardId, callback) { - var url = 'https://api.weixin.qq.com/card/get?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/get?access_token=' + this.token.accessToken; var data = { card_id: cardId }; @@ -226,7 +228,7 @@ make(exports, 'getCard', function (cardId, callback) { }); make(exports, 'updateCode', function (code, cardId, newcode, callback) { - var url = 'https://api.weixin.qq.com/card/code/update?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/code/update?access_token=' + this.token.accessToken; var data = { code: code, card_id: cardId, @@ -236,7 +238,7 @@ make(exports, 'updateCode', function (code, cardId, newcode, callback) { }); make(exports, 'unavailableCode', function (code, cardId, callback) { - var url = 'https://api.weixin.qq.com/card/code/unavailable?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/code/unavailable?access_token=' + this.token.accessToken; var data = { code: code }; @@ -249,7 +251,7 @@ make(exports, 'unavailableCode', function (code, cardId, callback) { }); make(exports, 'updateCard', function (cardId, cardType, cardInfo, callback) { - var url = 'https://api.weixin.qq.com/card/update?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/update?access_token=' + this.token.accessToken; var data = { card_id: cardId }; @@ -258,7 +260,7 @@ make(exports, 'updateCard', function (cardId, cardType, cardInfo, callback) { }); make(exports, 'updateCardStock', function (cardId, num, callback) { - var url = 'https://api.weixin.qq.com/card/modifystock?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/modifystock?access_token=' + this.token.accessToken; var data = { card_id: cardId }; @@ -271,7 +273,7 @@ make(exports, 'updateCardStock', function (cardId, num, callback) { }); make(exports, 'activateMembercard', function (info, callback) { - var url = 'https://api.weixin.qq.com/card/membercard/activate?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/membercard/activate?access_token=' + this.token.accessToken; this.request(url, postJSON(info), wrapper(callback)); }); @@ -301,42 +303,47 @@ make(exports, 'activateMembercard', function (info, callback) { * @param {Function} callback 回调函数 */ make(exports, 'activateMembercardUserForm', function (info, callback) { - var url = 'https://api.weixin.qq.com/card/membercard/activateuserform/set?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/membercard/activateuserform/set?access_token=' + this.token.accessToken; + this.request(url, postJSON(info), wrapper(callback)); +}); + +make(exports, 'getMembercard', function (info, callback) { + var url = this.endpoint + '/card/membercard/userinfo/get?access_token=' + this.token.accessToken; this.request(url, postJSON(info), wrapper(callback)); }); make(exports, 'updateMembercard', function (info, callback) { - var url = 'https://api.weixin.qq.com/card/membercard/updateuser?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/membercard/updateuser?access_token=' + this.token.accessToken; this.request(url, postJSON(info), wrapper(callback)); }); make(exports, 'updateMovieTicket', function (info, callback) { - var url = 'https://api.weixin.qq.com/card/movieticket/updateuser?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/movieticket/updateuser?access_token=' + this.token.accessToken; this.request(url, postJSON(info), wrapper(callback)); }); make(exports, 'checkInBoardingPass', function (info, callback) { - var url = 'https://api.weixin.qq.com/card/boardingpass/checkin?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/boardingpass/checkin?access_token=' + this.token.accessToken; this.request(url, postJSON(info), wrapper(callback)); }); make(exports, 'updateLuckyMonkeyBalance', function (code, cardId, balance, callback) { - var url = 'https://api.weixin.qq.com/card/luckymonkey/updateuserbalance?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/luckymonkey/updateuserbalance?access_token=' + this.token.accessToken; var data = { - "code": code, - "card_id": cardId, - "balance": balance + 'code': code, + 'card_id': cardId, + 'balance': balance }; this.request(url, postJSON(data), wrapper(callback)); }); make(exports, 'updateMeetingTicket', function (info, callback) { - var url = 'https://api.weixin.qq.com/card/meetingticket/updateuser?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/meetingticket/updateuser?access_token=' + this.token.accessToken; this.request(url, postJSON(info), wrapper(callback)); }); make(exports, 'setTestWhitelist', function (info, callback) { - var url = 'https://api.weixin.qq.com/card/testwhitelist/set?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/testwhitelist/set?access_token=' + this.token.accessToken; this.request(url, postJSON(info), wrapper(callback)); }); @@ -376,10 +383,10 @@ make(exports, 'setTestWhitelist', function (info, callback) { * @param {Function} callback 回调函数 */ make(exports, 'importCustomizedCodes', function (cardId, code, callback) { - var url = 'https://api.weixin.qq.com/card/code/deposit?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/code/deposit?access_token=' + this.token.accessToken; var data = { - "card_id": cardId, - "code": code + 'card_id': cardId, + 'code': code }; this.request(url, postJSON(data), wrapper(callback)); }); @@ -421,10 +428,10 @@ make(exports, 'importCustomizedCodes', function (cardId, code, callback) { * @param {Function} callback 回调函数 */ make(exports, 'checkCustomizedCodes', function (cardId, code, callback) { - var url = 'http://api.weixin.qq.com/card/code/checkcode?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/code/checkcode?access_token=' + this.token.accessToken; var data = { - "card_id": cardId, - "code": code + 'card_id': cardId, + 'code': code }; this.request(url, postJSON(data), wrapper(callback)); }); @@ -456,9 +463,9 @@ make(exports, 'checkCustomizedCodes', function (cardId, code, callback) { * @param {Function} callback 回调函数 */ make(exports, 'getDepositCodesCount', function (cardId, callback) { - var url = 'http://api.weixin.qq.com/card/code/getdepositcount?access_token=' + this.token.accessToken; + var url = this.endpoint + '/card/code/getdepositcount?access_token=' + this.token.accessToken; var data = { - "card_id": cardId + 'card_id': cardId }; this.request(url, postJSON(data), wrapper(callback)); }); @@ -470,11 +477,11 @@ make(exports, 'getDepositCodesCount', function (cardId, callback) { * @param {int} source 卡券来源,0为公众平台创建的卡券数据、1是API创建的卡券数据 */ make(exports, 'getTotalCardDataInfo', function (beginDate, endDate, source, callback) { - var url = 'https://api.weixin.qq.com/datacube/getcardbizuininfo?access_token=' + this.token.accessToken; + var url = this.endpoint + '/datacube/getcardbizuininfo?access_token=' + this.token.accessToken; var data = { - "begin_date": beginDate, - "end_date": endDate, - "cond_source": source + 'begin_date': beginDate, + 'end_date': endDate, + 'cond_source': source }; this.request(url, postJSON(data), wrapper(callback)); }); @@ -488,14 +495,274 @@ make(exports, 'getTotalCardDataInfo', function (beginDate, endDate, source, call * @param {int} source 卡券来源,0为公众平台创建的卡券数据、1是API创建的卡券数据 */ make(exports, 'getCardDataInfo', function (cardId, beginDate, endDate, source, callback) { - var url = 'https://api.weixin.qq.com/datacube/getcardcardinfo?access_token=' + this.token.accessToken; + var url = this.endpoint + '/datacube/getcardcardinfo?access_token=' + this.token.accessToken; + var data = { + 'begin_date': beginDate, + 'end_date': endDate, + 'cond_source': source, + 'card_id': cardId + }; + this.request(url, postJSON(data), wrapper(callback)); +}); + +/** + * 设置商户的核销员 + * 接口说明 + * 开发者需调用该接口设置商户的核销员,并指定核销员的门店。 + * Examples: + * ``` + * api.addConsumer('username', 'locationId', callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * { + * "errcode":0, + * "errmsg":"ok" + * } + * ``` + * + * @name addConsumer + * @param {String} username 店员的微信号,开发者须确认该微信号在设置之前已 经关注”卡券商户助手公众号“ + * @param {String} locationId 当前核销员关联的门店值 + * @param {Function} callback 回调函数 + */ +make(exports, 'addConsumer', function (username, locationId, callback) { + var url = this.endpoint + '/card/consumer/add?access_token=' + this.token.accessToken; + var data = { + 'username': username, + 'is_super_consumer': true + }; + if (locationId) { + data.location_id = locationId; + data.is_super_consumer = false; + } + this.request(url, postJSON(data), wrapper(callback)); +}); + +/** + * 卡券开放类目查询接口 + * 接口说明 + * 通过调用该接口查询卡券开放的类目ID,类目会随业务发展变更,请每次用接口去查询获取实时卡券类目。 + * 注意: + * 1. 本接口查询的返回值还有卡券资质ID,此处的卡券资质为:已微信认证的公众号通过微信公众平台申请卡券功能时,所需的资质。 + * 2.对于第三方开发者代制(无公众号)模式,子商户无论选择什么类目,均暂不需按照此返回提供资质,返回值仅参考类目ID 即可。 + * Examples: + * ``` + * api.getApplyProtocol(callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * { + * "category": [ + * { + * "primary_category_id": 1, + * "category_name": "美食", + * "secondary_category": [ + * { + * "secondary_category_id": 101, + * "category_name": "粤菜", + * "need_qualification_stuffs": [ + * "food_service_license_id", + * "food_service_license_bizmedia_id" + * ], + * "can_choose_prepaid_card": 1, + * "can_choose_payment_card": 1 + * }, + * } + * ], + * "errcode":0, + * "errmsg":"ok" + * } + * ``` + * + * @name getApplyProtocol + * @param {Object} options 子商户相关资料 + * @param {Function} callback 回调函数 + */ +make(exports, 'getApplyProtocol', function (callback) { + var url = this.endpoint + '/card/getapplyprotocol?access_token=' + this.token.accessToken; + this.request(url, {dataType: 'json'}, wrapper(callback)); +}); + +/** + * 创建子商户接口 + * 接口说明 + * 支持母商户调用该接口传入子商户的相关资料,并获取子商户ID,用于子商户的卡券功能管理。 + * 子商户的资质包括:商户名称、商户logo(图片)、卡券类目、授权函(扫描件或彩照)、授权函有效期截止时间。 + * Examples: + * ``` + * api.submitSubmerchant(options, callback); + * ``` + * options: + * { + * "brand_name": "aaaaaa", + * "app_id":"xxxxxxxxxxx", + * "logo_url": "http://mmbiz.xxxx", + * "protocol": "xxxxxxxxxx", + * "agreement_media_id":"xxxxxxxxxx", + * "operator_media_id":"xxxxxxxx", + * "end_time": 1438990559, + * "primary_category_id": 1, + * "secondary_category_id": 101 + * } + * + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @name submitSubmerchant + * @param {Object} options 子商户相关资料 + * @param {Function} callback 回调函数 + */ +make(exports, 'submitSubmerchant', function (options, callback) { + var url = this.endpoint + '/card/submerchant/submit?access_token=' + this.token.accessToken; + var data = { + 'info': options + }; + this.request(url, postJSON(data), wrapper(callback)); +}); + +/** + * 更新子商户接口 + * 接口说明 + * 支持调用该接口更新子商户信息。 + * Examples: + * ``` + * api.updateSubmerchant(options, callback); + * ``` + * options: + * { + * "merchant_id": 12, + * "brand_name": "aaaaaa", + * "app_id":"xxxxxxxxxxx", + * "logo_url": "http://mmbiz.xxxx", + * "protocol": "xxxxxxxxxx", + * "agreement_media_id":"xxxxxxxxxx", + * "operator_media_id":"xxxxxxxx", + * "end_time": 1438990559, + * "primary_category_id": 1, + * "secondary_category_id": 101 + * } + * + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @name updateSubmerchant + * @param {Object} options 子商户相关资料 + * @param {Function} callback 回调函数 + */ +make(exports, 'updateSubmerchant', function (options, callback) { + var url = this.endpoint + '/card/submerchant/update?access_token=' + this.token.accessToken; var data = { - "begin_date": beginDate, - "end_date": endDate, - "cond_source": source, - "card_id": cardId + 'info': options }; this.request(url, postJSON(data), wrapper(callback)); }); +/** + * 拉取单个子商户信息接口 + * 接口说明 + * 通过指定的子商户merchant_id,拉取该子商户的基础信息。 + * 注意,用母商户去调用接口,但接口内传入的是子商户的merchant_id。 + * Examples: + * ``` + * api.getSubmerchant('merchantId', callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @name getSubmerchant + * @param {String} merchantId 子商户相关资料 + * @param {Function} callback 回调函数 + */ +make(exports, 'getSubmerchant', function (merchantId, callback) { + var url = this.endpoint + '/card/submerchant/get?access_token=' + this.token.accessToken; + var data = { + 'merchant_id': merchantId + }; + this.request(url, postJSON(data), wrapper(callback)); +}); + + +/** + * 批量拉取子商户信息接口 + * 接口说明 + * 母商户可以通过该接口批量拉取子商户的相关信息,一次调用最多拉取100个子商户的信息,可以通过多次拉去满足不同的查询需求 + * Examples: + * ``` + * api.batchgetSubmerchant(data, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @name batchgetSubmerchant + * @param {Object} data 查询条件 {"begin_id": 0,"limit": 50,"status": "CHECKING"} + * @param {Function} callback 回调函数 + */ +make(exports, 'batchgetSubmerchant', function (data, callback) { + var url = this.endpoint + '/card/submerchant/batchget?access_token=' + this.token.accessToken; + this.request(url, postJSON(data), wrapper(callback)); +}); + +/** + * 拉取拉取会员信息接口 + * 接口说明 + * 支持开发者根据CardID和Code查询会员信息。 + * Examples: + * ``` + * api.getMemberCardUserInfo({"card_id": abd5e78412e5d12ff,"code": 5566778811002233}, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @name getMemberCardUserInfo + * @param {Object} info 查询条件 {"card_id": abd5e78412e5d12ff,"code": 5566778811002233} + * @param {Function} callback 回调函数 + */ +make(exports, 'getMemberCardUserInfo', function (info, callback) { + var url = this.endpoint + '/card/membercard/userinfo/get?access_token=' + this.token.accessToken; + var data = info; + this.request(url, postJSON(data), wrapper(callback)); +}); + +/** + * 更新会员信息 + * 接口说明 + * 当会员持卡消费后,支持开发者调用该接口更新会员信息。会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。 + * Examples: + * ``` + * api.updateMemberCardUserInfo(data, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @name getMemberCardUserInfo + * @param {Object} data 查询条件 {"card_id": abd5e78412e5d12ff,"code": 5566778811002233, "record_bonus" : 3000} + * @param {Function} callback 回调函数 + */ +make(exports, 'updateMemberCardUserInfo', function (data, callback) { + var url = this.endpoint + '/card/membercard/updateuser?access_token=' + this.token.accessToken; + this.request(url, postJSON(data), wrapper(callback)); +}); diff --git a/lib/api_common.js b/lib/api_common.js index 1c81812..a53f705 100644 --- a/lib/api_common.js +++ b/lib/api_common.js @@ -1,3 +1,5 @@ +'use strict'; + // 本文件用于wechat API,基础文件,主要用于Token的处理和mixin机制 var urllib = require('urllib'); var util = require('./util'); @@ -74,17 +76,32 @@ var API = function (appid, appsecret, getToken, saveToken) { } callback(null); }; - this.prefix = 'https://api.weixin.qq.com/cgi-bin/'; + this.endpoint = 'https://api.weixin.qq.com'; this.mpPrefix = 'https://mp.weixin.qq.com/cgi-bin/'; this.fileServerPrefix = 'http://file.api.weixin.qq.com/cgi-bin/'; - this.payPrefix = 'https://api.weixin.qq.com/pay/'; - this.merchantPrefix = 'https://api.weixin.qq.com/merchant/'; - this.customservicePrefix = 'https://api.weixin.qq.com/customservice/'; this.defaults = {}; // set default js ticket handle this.registerTicketHandle(); }; +/** + * 用于设置接入点 + * + * - 通用域名(api.weixin.qq.com),使用该域名将访问官方指定就近的接入点; + * - 上海域名(sh.api.weixin.qq.com),使用该域名将访问上海的接入点; + * - 深圳域名(sz.api.weixin.qq.com),使用该域名将访问深圳的接入点; + * - 香港域名(hk.api.weixin.qq.com),使用该域名将访问香港的接入点。 + * + * Examples: + * ``` + * api.setEndpoint('api.weixin.qq.com'); + * ``` + * @param {String} domain 域名,默认为api.weixin.qq.com + */ +API.prototype.setEndpoint = function (domain) { + this.endpoint = 'https://' + domain; +}; + /** * 用于设置urllib的默认options * @@ -153,7 +170,7 @@ API.prototype.request = function (url, opts, callback) { */ API.prototype.getAccessToken = function (callback) { var that = this; - var url = this.prefix + 'token?grant_type=client_credential&appid=' + this.appid + '&secret=' + this.appsecret; + var url = this.endpoint + '/cgi-bin/token?grant_type=client_credential&appid=' + this.appid + '&secret=' + this.appsecret; this.request(url, {dataType: 'json'}, wrapper(function (err, data) { if (err) { return callback(err); @@ -251,11 +268,10 @@ API.prototype.getLatestToken = function (callback) { var accessToken; // 有token并且token有效直接调用 if (token && (accessToken = AccessToken(token.accessToken, token.expireTime)).isValid()) { - callback(null, accessToken); - } else { - // 使用appid/appsecret获取token - that.getAccessToken(callback); + return callback(null, accessToken); } + // 使用appid/appsecret获取token + that.getAccessToken(callback); }); }; @@ -277,6 +293,48 @@ API.mixin = function (obj) { } }; +/** + * 用于扩展API. + * 作用是:当微信的官方 API 添加新功能,而wechat-api没有来得及升级时,用这个方法快速添加此功能。 + * 当 api 升级后应该用 API 内提供的方法。 + * Examples: + * ``` + * // 为 API 添加一个 createQr 方法。 + * API.ext("createQr", "https://api.weixin.qq.com/card/qrcode/create"); + * ``` + * Usage: + * ``` + * // 调用这个 createQr 方法。 + * api.createQr({'card_id': ’dkjeuDfsfeu3242w3dnjlq23i'}, callback); + * ``` + * @param {String} functionName 用户调用的方法名 + * @param {String} apiUrl 此 API 的 url 地址 + * @param {Bool} override 如果填写 true 则覆盖原来 api 已有的方法, false 或不传,则抛错。 + */ +API.patch = function (functionName, apiUrl, override) { + if (typeof apiUrl !== 'string') { + throw new Error('The second argument expect a type of string as the request url of wechat'); + } + + if (typeof functionName !== 'string') { + throw new Error('The first argument expect a type of string as the name of new function'); + } + + if (API.prototype[functionName] || API.prototype['_' + functionName] ) { + if (override !== true) { + throw new Error('wechat-api already has a prototype named ['+ functionName + '], use "true" as third param to override it or change your new function name.'); + } else { + console.warn('wechat-api already has a prototype named ['+ functionName + '], will override the orignal one.'); + } + } + + util.make(API.prototype, functionName, function (info, callback) { + var hasMark = apiUrl.indexOf('?') >= 0; + var url = apiUrl + (hasMark ? '&access_token=': '?access_token=') + this.token.accessToken; + this.request(url, util.postJSON(info), wrapper(callback)); + }); +}; + API.AccessToken = AccessToken; module.exports = API; diff --git a/lib/api_custom_service.js b/lib/api_custom_service.js index 6eb9b7b..9f3de6e 100644 --- a/lib/api_custom_service.js +++ b/lib/api_custom_service.js @@ -1,22 +1,25 @@ -var util = require('./util'); -var wrapper = util.wrapper; -var postJSON = util.postJSON; +'use strict'; + var path = require('path'); var fs = require('fs'); var formstream = require('formstream'); +var util = require('./util'); +var wrapper = util.wrapper; +var postJSON = util.postJSON; +var make = util.make; + /** * 获取客服聊天记录 - * 详细请看:http://mp.weixin.qq.com/wiki/19/7c129ec71ddfa60923ea9334557e8b23.html + * 详细请看:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1464937269_mUtmK&token=&lang=zh_CN * * Opts: * ``` * { - * "starttime" : 123456789, - * "endtime" : 987654321, - * "openid": "OPENID", // 非必须 - * "pagesize" : 10, - * "pageindex" : 1, + * "starttime" : 123456789, 起始时间,unix时间戳 + * "endtime" : 987654321, 结束时间,unix时间戳,每次查询时段不能超过24小时 + * "msgid" : 1, 消息id顺序从小到大,从1开始 + * "number" : 10000 每次获取条数,最多10000条 * } * ``` * Examples: @@ -52,18 +55,12 @@ var formstream = require('formstream'); * @param {Object} opts 查询条件 * @param {Function} callback 回调函数 */ -exports.getRecords = function (opts, callback) { - this.preRequest(this._getRecords, arguments); -}; - -/*! - * 获取客服聊天记录的未封装版本 - */ -exports._getRecords = function (opts, callback) { - // https://api.weixin.qq.com/customservice/msgrecord/getrecord?access_token=ACCESS_TOKEN - var url = this.customservicePrefix + 'msgrecord/getrecord?access_token=' + this.token.accessToken; +make(exports, 'getRecords', function (opts, callback) { + // https://api.weixin.qq.com/customservice/msgrecord/getmsglist?access_token=ACCESS_TOKEN + opts.msgid = opts.msgid || 1; + var url = this.endpoint + '/customservice/msgrecord/getmsglist?access_token=' + this.token.accessToken; this.request(url, postJSON(opts), wrapper(callback)); -}; +}); /** * 获取客服基本信息 @@ -102,18 +99,11 @@ exports._getRecords = function (opts, callback) { * ``` * @param {Function} callback 回调函数 */ -exports.getCustomServiceList = function (callback) { - this.preRequest(this._getCustomServiceList, arguments); -}; - -/*! - * 获取客服基本信息的未封装版本 - */ -exports._getCustomServiceList = function (callback) { +make(exports, 'getCustomServiceList', function (callback) { // https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token= ACCESS_TOKEN - var url = this.prefix + 'customservice/getkflist?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/customservice/getkflist?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); -}; +}); /** * 获取在线客服接待信息 @@ -152,17 +142,16 @@ exports._getCustomServiceList = function (callback) { * ``` * @param {Function} callback 回调函数 */ -exports.getOnlineCustomServiceList = function (callback) { - this.preRequest(this._getOnlineCustomServiceList, arguments); -}; - -/*! - * 获取在线客服接待信息的未封装版本 - */ -exports._getOnlineCustomServiceList = function (callback) { +make(exports, 'getOnlineCustomServiceList', function (callback) { // https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist?access_token= ACCESS_TOKEN - var url = this.prefix + 'customservice/getonlinekflist?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/customservice/getonlinekflist?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); +}); + +var md5 = function (input) { + var crypto = require('crypto'); + var hash = crypto.createHash('md5'); + return hash.update(input).digest('hex'); }; /** @@ -191,31 +180,17 @@ exports._getOnlineCustomServiceList = function (callback) { * @param {String} password 密码,可以直接传递明文,wechat模块自动进行md5加密 * @param {Function} callback 回调函数 */ -exports.addKfAccount = function (account, nick, password, callback) { - this.preRequest(this._addKfAccount, arguments); -}; - -var md5 = function (input) { - var crypto = require('crypto'); - var hash = crypto.createHash('md5'); - return hash.update(input).digest('hex'); -}; - -/*! - * 添加客服账号的未封装版本 - */ -exports._addKfAccount = function (account, nick, password, callback) { +make(exports, 'addKfAccount', function (account, nick, password, callback) { // https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN - var prefix = 'https://api.weixin.qq.com/'; - var url = prefix + 'customservice/kfaccount/add?access_token=' + this.token.accessToken; + var url = this.endpoint + '/customservice/kfaccount/add?access_token=' + this.token.accessToken; var data = { - "kf_account": account, - "nickname": nick, - "password": md5(password), + 'kf_account': account, + 'nickname': nick, + 'password': md5(password) }; this.request(url, postJSON(data), wrapper(callback)); -}; +}); /** * 设置客服账号 @@ -243,25 +218,17 @@ exports._addKfAccount = function (account, nick, password, callback) { * @param {String} password 密码,可以直接传递明文,wechat模块自动进行md5加密 * @param {Function} callback 回调函数 */ -exports.updateKfAccount = function (account, nick, password, callback) { - this.preRequest(this._updateKfAccount, arguments); -}; - -/*! - * 设置客服账号的未封装版本 - */ -exports._updateKfAccount = function (account, nick, password, callback) { +make(exports, 'updateKfAccount', function (account, nick, password, callback) { // https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN - var prefix = 'https://api.weixin.qq.com/'; - var url = prefix + 'customservice/kfaccount/update?access_token=' + this.token.accessToken; + var url = this.endpoint + '/customservice/kfaccount/update?access_token=' + this.token.accessToken; var data = { - "kf_account": account, - "nickname": nick, - "password": md5(password), + 'kf_account': account, + 'nickname': nick, + 'password': md5(password) }; this.request(url, postJSON(data), wrapper(callback)); -}; +}); /** * 删除客服账号 @@ -287,20 +254,11 @@ exports._updateKfAccount = function (account, nick, password, callback) { * @param {String} account 账号名字,格式为:前缀@公共号名字 * @param {Function} callback 回调函数 */ -exports.deleteKfAccount = function (account, callback) { - this.preRequest(this._deleteKfAccount, arguments); -}; - -/*! - * 删除客服账号的未封装版本 - */ -exports._deleteKfAccount = function (account, callback) { +make(exports, 'deleteKfAccount', function (account, callback) { // https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN - var prefix = 'https://api.weixin.qq.com/'; - var url = prefix + 'customservice/kfaccount/del?access_token=' + this.token.accessToken + '&kf_account=' + account; - + var url = this.endpoint + '/customservice/kfaccount/del?access_token=' + this.token.accessToken + '&kf_account=' + account; this.request(url, {dataType: 'json'}, wrapper(callback)); -}; +}); /** * 设置客服头像 @@ -327,14 +285,7 @@ exports._deleteKfAccount = function (account, callback) { * @param {String} filepath 头像路径 * @param {Function} callback 回调函数 */ -exports.setKfAccountAvatar = function (account, filepath, callback) { - this.preRequest(this._setKfAccountAvatar, arguments); -}; - -/*! - * 上传多媒体文件的未封装版本 - */ -exports._setKfAccountAvatar = function (account, filepath, callback) { +make(exports, 'setKfAccountAvatar', function (account, filepath, callback) { // http://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT var that = this; fs.stat(filepath, function (err, stat) { @@ -343,8 +294,7 @@ exports._setKfAccountAvatar = function (account, filepath, callback) { } var form = formstream(); form.file('media', filepath, path.basename(filepath), stat.size); - var prefix = 'http://api.weixin.qq.com/'; - var url = prefix + 'customservice/kfaccount/uploadheadimg?access_token=' + that.token.accessToken + '&kf_account=' + account; + var url = that.endpoint + '/customservice/kfaccount/uploadheadimg?access_token=' + that.token.accessToken + '&kf_account=' + account; var opts = { dataType: 'json', type: 'POST', @@ -354,4 +304,180 @@ exports._setKfAccountAvatar = function (account, filepath, callback) { }; that.request(url, opts, wrapper(callback)); }); -}; +}); + +/*********会话控制**********/ + +/** + * 创建会话 + * 详细请看:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN + * + * Examples: + * ``` + * api.createKfSession('test@test', 'openidxxx', callback); + * ``` + * + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * { + * "errcode" : 0, + * "errmsg" : "ok", + * } + * ``` + * @param {String} account 完整客服帐号,格式为:帐号前缀@公众号微信号 + * @param {String} openid 粉丝的openid + * @param {Function} callback 回调函数 + */ +make(exports, 'createKfSession', function (account, openid, callback) { + //https://api.weixin.qq.com/customservice/kfsession/create?access_token=ACCESS_TOKEN + var url = this.endpoint + '/customservice/kfsession/create?access_token=' + this.token.accessToken; + this.request(url, postJSON({ + kf_account: account, + openid: openid + }), wrapper(callback)); +}); + +/** + * 关闭会话 + * 详细请看:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN + * + * Examples: + * ``` + * api.closeKfSession('test@test', 'openidxxx', callback); + * ``` + * + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * { + * "errcode" : 0, + * "errmsg" : "ok", + * } + * ``` + * @param {String} account 完整客服帐号,格式为:帐号前缀@公众号微信号 + * @param {String} openid 粉丝的openid + * @param {Function} callback 回调函数 + */ +make(exports, 'closeKfSession', function (account, openid, callback) { + //https://api.weixin.qq.com/customservice/kfsession/close?access_token=ACCESS_TOKEN + var url = this.endpoint + '/customservice/kfsession/close?access_token=' + this.token.accessToken; + this.request(url, postJSON({ + kf_account: account, + openid: openid + }), wrapper(callback)); +}); + +/** + * 获取客户会话状态 + * 详细请看:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN + * + * Examples: + * ``` + * api.getKfSession('openidxxx', callback); + * ``` + * + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * { + * "createtime" : 123456789, + * "kf_account" : "test1@test" + * } + * ``` + * @param {String} openid 粉丝的openid + * @param {Function} callback 回调函数 + */ +make(exports, 'getKfSession', function (openid, callback) { + //https://api.weixin.qq.com/customservice/kfsession/getsession?access_token=ACCESS_TOKEN&openid=OPENID + var url = this.endpoint + '/customservice/kfsession/getsession?access_token=' + this.token.accessToken + '&openid=' + openid; + this.request(url, {dataType: 'json'}, wrapper(callback)); +}); + +/** + * 获取客服会话列表 + * 详细请看:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN + * + * Examples: + * ``` + * api.getKfSessionList('openidxxx', callback); + * ``` + * + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * { + * "sessionlist" : [ + * { + * "createtime" : 123456789, + * "openid" : "OPENID" + * }, + * { + * "createtime" : 123456789, + * "openid" : "OPENID" + * } + * ] + * } + * ``` + * @param {String} account 完整客服帐号,格式为:帐号前缀@公众号微信号 + * @param {Function} callback 回调函数 + */ +make(exports, 'getKfSessionList', function (account, callback) { + //https://api.weixin.qq.com/customservice/kfsession/getsessionlist?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT + var url = this.endpoint + '/customservice/kfsession/getsessionlist?access_token=' + this.token.accessToken + '&kf_account=' + account; + this.request(url, {dataType: 'json'}, wrapper(callback)); +}); + +/** + * 获取未接入会话列表 + * 详细请看:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN + * + * Examples: + * ``` + * api.getKfSessionWaitCase(callback); + * ``` + * + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * { + * "count" : 150, //未接入会话数量 + * "waitcaselist" : [ //未接入会话列表,最多返回100条数据,按照来访顺序 + * { + * "latest_time" : 123456789, + * "openid" : "OPENID" + * }, + * { + * "latest_time" : 123456789, + * "openid" : "OPENID" + * } + * ] + * } + * ``` + * @param {Function} callback 回调函数 + */ +make(exports, 'getKfSessionWaitCase', function (callback) { + //https://api.weixin.qq.com/customservice/kfsession/getwaitcase?access_token=ACCESS_TOKEN + var url = this.endpoint + '/customservice/kfsession/getwaitcase?access_token=' + this.token.accessToken; + this.request(url, {dataType: 'json'}, wrapper(callback)); +}); diff --git a/lib/api_datacube.js b/lib/api_datacube.js index b9d37ca..87c9a4d 100644 --- a/lib/api_datacube.js +++ b/lib/api_datacube.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -83,7 +85,7 @@ methods.forEach(function (method) { begin_date: begin, end_date: end }; - var url = 'https://api.weixin.qq.com/datacube/' + method.toLowerCase() + '?access_token=' + this.token.accessToken; + var url = this.endpoint + '/datacube/' + method.toLowerCase() + '?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; }); diff --git a/lib/api_device.js b/lib/api_device.js index b0e1452..5e59314 100644 --- a/lib/api_device.js +++ b/lib/api_device.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -5,46 +7,46 @@ var make = util.make; make(exports, 'transferMessage', function (deviceType, deviceId, openid, content, callback) { // https://api.weixin.qq.com/device/transmsg?access_token=ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/device/transmsg?access_token=' + this.token.accessToken; + var url = this.endpoint + '/device/transmsg?access_token=' + this.token.accessToken; var info = { - "device_type": deviceType, - "device_id": deviceId, - "open_id": openid, - "content": new Buffer(content).toString('base64') + 'device_type': deviceType, + 'device_id': deviceId, + 'open_id': openid, + 'content': new Buffer(content).toString('base64') }; this.request(url, postJSON(info), wrapper(callback)); }); make(exports, 'transferStatus', function (deviceType, deviceId, openid, status, callback) { // https://api.weixin.qq.com/device/transmsg?access_token=ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/device/transmsg?access_token=' + this.token.accessToken; + var url = this.endpoint + '/device/transmsg?access_token=' + this.token.accessToken; var info = { - "device_type": deviceType, - "device_id": deviceId, - "open_id": openid, - "msg_type": "2", - "device_status": status + 'device_type': deviceType, + 'device_id': deviceId, + 'open_id': openid, + 'msg_type': '2', + 'device_status': status }; this.request(url, postJSON(info), wrapper(callback)); }); make(exports, 'createDeviceQRCode', function (deviceIds, callback) { // https://api.weixin.qq.com/device/create_qrcode?access_token=ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/device/create_qrcode?access_token=' + this.token.accessToken; + var url = this.endpoint + '/device/create_qrcode?access_token=' + this.token.accessToken; var info = { - "device_num": deviceIds.length, - "device_id_list": deviceIds + 'device_num': deviceIds.length, + 'device_id_list': deviceIds }; this.request(url, postJSON(info), wrapper(callback)); }); make(exports, 'authorizeDevices', function (devices, optype, productid, callback) { // https://api.weixin.qq.com/device/authorize_device?access_token=ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/device/authorize_device?access_token=' + this.token.accessToken; + var url = this.endpoint + '/device/authorize_device?access_token=' + this.token.accessToken; var data = { - "device_num": devices.length, - "device_list": devices, - "op_type": optype + 'device_num': devices.length, + 'device_list': devices, + 'op_type': optype }; if (typeof productid !== 'function') { data.product_id = productid; @@ -54,15 +56,16 @@ make(exports, 'authorizeDevices', function (devices, optype, productid, callback this.request(url, postJSON(data), wrapper(callback)); }); -make(exports, 'getDeviceQRCode', function (devices, optype, callback) { - // https://api.weixin.qq.com/device/getqrcode?access_token=ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/device/getqrcode?access_token=' + this.token.accessToken; +//第三方公众账号通过设备id从公众平台批量获取设备二维码。 +make(exports, 'getDeviceQRCode', function (product_id, callback) { + // https://api.weixin.qq.com/device/create_qrcode?access_token=ACCESS_TOKEN + var url = this.endpoint + '/device/getqrcode?access_token=' + this.token.accessToken + '&product_id=' + product_id; this.request(url, {dataType: 'json'}, wrapper(callback)); }); make(exports, 'bindDevice', function (deviceId, openid, ticket, callback) { // https://api.weixin.qq.com/device/bind?access_token=ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/device/bind?access_token=' + this.token.accessToken; + var url = this.endpoint + '/device/bind?access_token=' + this.token.accessToken; var data = { ticket: ticket, device_id: deviceId, @@ -73,7 +76,7 @@ make(exports, 'bindDevice', function (deviceId, openid, ticket, callback) { make(exports, 'unbindDevice', function (deviceId, openid, ticket, callback) { // https://api.weixin.qq.com/device/unbind?access_token=ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/device/unbind?access_token=' + this.token.accessToken; + var url = this.endpoint + '/device/unbind?access_token=' + this.token.accessToken; var data = { ticket: ticket, device_id: deviceId, @@ -85,7 +88,7 @@ make(exports, 'unbindDevice', function (deviceId, openid, ticket, callback) { make(exports, 'compelBindDevice', function (deviceId, openid, callback) { // https://api.weixin.qq.com/device/compel_bind?access_token=ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/device/compel_bind?access_token=' + this.token.accessToken; + var url = this.endpoint + '/device/compel_bind?access_token=' + this.token.accessToken; var data = { device_id: deviceId, openid: openid @@ -95,7 +98,7 @@ make(exports, 'compelBindDevice', function (deviceId, openid, callback) { make(exports, 'compelUnbindDevice', function (deviceId, openid, callback) { // https://api.weixin.qq.com/device/compel_unbind?access_token=ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/device/compel_unbind?access_token=' + this.token.accessToken; + var url = this.endpoint + '/device/compel_unbind?access_token=' + this.token.accessToken; var data = { device_id: deviceId, openid: openid @@ -105,27 +108,27 @@ make(exports, 'compelUnbindDevice', function (deviceId, openid, callback) { make(exports, 'getDeviceStatus', function (deviceId, callback) { // https://api.weixin.qq.com/device/get_stat?access_token=ACCESS_TOKEN&device_id=DEVICE_ID - var url = 'https://api.weixin.qq.com/device/get_stat?access_token=' + this.token.accessToken + "&device_id=" + deviceId; + var url = this.endpoint + '/device/get_stat?access_token=' + this.token.accessToken + '&device_id=' + deviceId; this.request(url, {dataType: 'json'}, wrapper(callback)); }); make(exports, 'verifyDeviceQRCode', function (ticket, callback) { // https://api.weixin.qq.com/device/verify_qrcode?access_token=ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/device/verify_qrcode?access_token=' + this.token.accessToken; + var url = this.endpoint + '/device/verify_qrcode?access_token=' + this.token.accessToken; var data = { - ticket: ticket, + ticket: ticket }; this.request(url, postJSON(data), wrapper(callback)); }); make(exports, 'getOpenID', function (deviceId, deviceType, callback) { // https://api.weixin.qq.com/device/get_openid?access_token=ACCESS_TOKEN&device_type=DEVICE_TYPE&device_id=DEVICE_ID - var url = 'https://api.weixin.qq.com/device/get_openid?access_token=' + this.token.accessToken + "&device_id=" + deviceId + "&device_type=" + deviceType; + var url = this.endpoint + '/device/get_openid?access_token=' + this.token.accessToken + '&device_id=' + deviceId + '&device_type=' + deviceType; this.request(url, {dataType: 'json'}, wrapper(callback)); }); make(exports, 'getBindDevice', function (openid, callback) { // https://api.weixin.qq.com/device/get_bind_device?access_token=ACCESS_TOKEN&openid=OPENID - var url = 'https://api.weixin.qq.com/device/get_bind_device?access_token=' + this.token.accessToken + "&openid=" + openid; + var url = this.endpoint + '/device/get_bind_device?access_token=' + this.token.accessToken + '&openid=' + openid; this.request(url, {dataType: 'json'}, wrapper(callback)); }); diff --git a/lib/api_feedback.js b/lib/api_feedback.js index 4fbf9f7..1bf4514 100644 --- a/lib/api_feedback.js +++ b/lib/api_feedback.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; @@ -28,7 +30,7 @@ exports.updateFeedback = function (openid, feedbackId, callback) { }; exports._updateFeedback = function (openid, feedbackId, callback) { - var feedbackUrl = 'https://api.weixin.qq.com/payfeedback/update'; + var feedbackUrl = this.endpoint + '/payfeedback/update'; // https://api.weixin.qq.com/payfeedback/update?access_token=xxxxx&openid=XXXX&feedbackid=xxxx var data = { 'access_token': this.token.accessToken, diff --git a/lib/api_group.js b/lib/api_group.js index 834c4da..753fa42 100644 --- a/lib/api_group.js +++ b/lib/api_group.js @@ -1,6 +1,9 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; +var make = util.make; /** * 获取分组列表 @@ -25,18 +28,11 @@ var postJSON = util.postJSON; * ``` * @param {Function} callback 回调函数 */ -exports.getGroups = function (callback) { - this.preRequest(this._getGroups, arguments); -}; - -/*! - * 获取分组列表的未封装版本 - */ -exports._getGroups = function (callback) { +make(exports, 'getGroups', function (callback) { // https://api.weixin.qq.com/cgi-bin/groups/get?access_token=ACCESS_TOKEN - var url = this.prefix + 'groups/get?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/groups/get?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); -}; +}); /** * 查询用户在哪个分组 @@ -59,21 +55,14 @@ exports._getGroups = function (callback) { * @param {String} openid Open ID * @param {Function} callback 回调函数 */ -exports.getWhichGroup = function (openid, callback) { - this.preRequest(this._getWhichGroup, arguments); -}; - -/*! - * 查询用户在哪个分组未分组版本 - */ -exports._getWhichGroup = function (openid, callback) { +make(exports, 'getWhichGroup', function (openid, callback) { // https://api.weixin.qq.com/cgi-bin/groups/getid?access_token=ACCESS_TOKEN - var url = this.prefix + 'groups/getid?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/groups/getid?access_token=' + this.token.accessToken; var data = { - "openid": openid + 'openid': openid }; this.request(url, postJSON(data), wrapper(callback)); -}; +}); /** * 创建分组 @@ -94,23 +83,16 @@ exports._getWhichGroup = function (openid, callback) { * @param {String} name 分组名字 * @param {Function} callback 回调函数 */ -exports.createGroup = function (name, callback) { - this.preRequest(this._createGroup, arguments); -}; - -/*! - * 创建分组的未封装版本 - */ -exports._createGroup = function (name, callback) { +make(exports, 'createGroup', function (name, callback) { // https://api.weixin.qq.com/cgi-bin/groups/create?access_token=ACCESS_TOKEN // POST数据格式:json // POST数据例子:{"group":{"name":"test"}} - var url = this.prefix + 'groups/create?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/groups/create?access_token=' + this.token.accessToken; var data = { - "group": {"name": name} + 'group': {'name': name} }; this.request(url, postJSON(data), wrapper(callback)); -}; +}); /** * 更新分组名字 @@ -132,24 +114,17 @@ exports._createGroup = function (name, callback) { * @param {String} name 新的分组名字 * @param {Function} callback 回调函数 */ -exports.updateGroup = function (id, name, callback) { - this.preRequest(this._updateGroup, arguments); -}; - -/*! - * 更新分组名字的未封装版本 - */ -exports._updateGroup = function (id, name, callback) { +make(exports, 'updateGroup', function (id, name, callback) { // http请求方式: POST(请使用https协议) // https://api.weixin.qq.com/cgi-bin/groups/update?access_token=ACCESS_TOKEN // POST数据格式:json // POST数据例子:{"group":{"id":108,"name":"test2_modify2"}} - var url = this.prefix + 'groups/update?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/groups/update?access_token=' + this.token.accessToken; var data = { - "group": {"id": id, "name": name} + 'group': {'id': id, 'name': name} }; this.request(url, postJSON(data), wrapper(callback)); -}; +}); /** * 移动用户进分组 @@ -171,25 +146,18 @@ exports._updateGroup = function (id, name, callback) { * @param {Number} groupId 分组ID * @param {Function} callback 回调函数 */ -exports.moveUserToGroup = function (openid, groupId, callback) { - this.preRequest(this._moveUserToGroup, arguments); -}; - -/*! - * 移动用户进分组的未封装版本 - */ -exports._moveUserToGroup = function (openid, groupId, callback) { +make(exports, 'moveUserToGroup', function (openid, groupId, callback) { // http请求方式: POST(请使用https协议) // https://api.weixin.qq.com/cgi-bin/groups/members/update?access_token=ACCESS_TOKEN // POST数据格式:json // POST数据例子:{"openid":"oDF3iYx0ro3_7jD4HFRDfrjdCM58","to_groupid":108} - var url = this.prefix + 'groups/members/update?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/groups/members/update?access_token=' + this.token.accessToken; var data = { - "openid": openid, - "to_groupid": groupId + 'openid': openid, + 'to_groupid': groupId }; this.request(url, postJSON(data), wrapper(callback)); -}; +}); /** * 删除分组 @@ -210,18 +178,10 @@ exports._moveUserToGroup = function (openid, groupId, callback) { * @param {Number} groupId 分组ID * @param {Function} callback 回调函数 */ -exports.removeGroup = function (groupId, callback) { - this.preRequest(this._removeGroup, arguments); -}; - -/*! - * 移动用户进分组的未封装版本 - */ -exports._removeGroup = function (groupId, callback) { - var url = this.prefix + 'groups/delete?access_token=' + this.token.accessToken; +make(exports, 'removeGroup', function (groupId, callback) { + var url = this.endpoint + '/cgi-bin/groups/delete?access_token=' + this.token.accessToken; var data = { - "group": { id: groupId} + 'group': { id: groupId} }; this.request(url, postJSON(data), wrapper(callback)); -}; - +}); diff --git a/lib/api_ip.js b/lib/api_ip.js index 26dffe4..a462a5d 100644 --- a/lib/api_ip.js +++ b/lib/api_ip.js @@ -1,5 +1,8 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; +var make = util.make; /** * 获取微信服务器IP地址 @@ -21,15 +24,8 @@ var wrapper = util.wrapper; * ``` * @param {Function} callback 回调函数 */ -exports.getIp = function (callback) { - this.preRequest(this._getIp, arguments); -}; - -/*! -* 获取微信服务器IP地址的未封装版本 -*/ -exports._getIp = function (callback) { +make(exports, 'getIp', function (callback) { // https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=ACCESS_TOKEN - var url = this.prefix + 'getcallbackip?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/getcallbackip?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); -}; +}); diff --git a/lib/api_js.js b/lib/api_js.js index c6b8851..efd7419 100644 --- a/lib/api_js.js +++ b/lib/api_js.js @@ -1,5 +1,8 @@ -var util = require('./util'); +'use strict'; + var crypto = require('crypto'); + +var util = require('./util'); var wrapper = util.wrapper; // 错误码 - Ticket无效 @@ -65,7 +68,7 @@ exports.registerTicketHandle = function (getTicketToken, saveTicketToken) { this.ticketStore[type] = ticketToken; if (process.env.NODE_ENV === 'production') { - console.warn("Dont save ticket in memory, when cluster or multi-computer!"); + console.warn('Dont save ticket in memory, when cluster or multi-computer!'); } callback(null); }; @@ -96,7 +99,7 @@ exports._getTicket = function (type, callback) { type = 'jsapi'; } var that = this; - var url = this.prefix + 'ticket/getticket?access_token=' + this.token.accessToken + '&type=' + type; + var url = this.endpoint + '/cgi-bin/ticket/getticket?access_token=' + this.token.accessToken + '&type=' + type; this.request(url, {dataType: 'json'}, wrapper(function(err, data) { if (err) { return callback(err); @@ -139,7 +142,9 @@ var raw = function (args) { }); var string = ''; - for (var k in newArgs) { + var newKeys = Object.keys(newArgs); + for (var i = 0; i < newKeys.length; i++) { + var k = newKeys[i]; string += '&' + k + '=' + newArgs[k]; } return string.substr(1); @@ -322,6 +327,13 @@ exports._getJsConfig = function (param, callback) { signature: signature, jsApiList: param.jsApiList }; + + // 判断beta参数是否存在,微信硬件开发用 + // beta: true + // 开启内测接口调用,注入wx.invoke方法 + if (param.beta) { + result.beta = param.beta; + } callback(null, result); }; diff --git a/lib/api_mass_send.js b/lib/api_mass_send.js index 116f587..527ec37 100644 --- a/lib/api_mass_send.js +++ b/lib/api_mass_send.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -60,7 +62,7 @@ exports.uploadNews = function (news, callback) { */ exports._uploadNews = function (news, callback) { // https://api.weixin.qq.com/cgi-bin/media/uploadnews?access_token=ACCESS_TOKEN - var url = this.prefix + 'media/uploadnews?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/media/uploadnews?access_token=' + this.token.accessToken; this.request(url, postJSON(news), wrapper(callback)); }; @@ -154,18 +156,18 @@ exports._massSend = function (opts, receivers, callback) { var url; if (Array.isArray(receivers)) { opts.touser = receivers; - url = this.prefix + 'message/mass/send?access_token=' + this.token.accessToken; + url = this.endpoint + '/cgi-bin/message/mass/send?access_token=' + this.token.accessToken; } else { if (typeof receivers === 'boolean') { opts.filter = { - "is_to_all": receivers + 'is_to_all': receivers }; } else { opts.filter = { - "group_id": receivers + 'group_id': receivers }; } - url = this.prefix + 'message/mass/sendall?access_token=' + this.token.accessToken; + url = this.endpoint + '/cgi-bin/message/mass/sendall?access_token=' + this.token.accessToken; } // https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=ACCESS_TOKEN this.request(url, postJSON(opts), wrapper(callback)); @@ -198,10 +200,10 @@ exports._massSend = function (opts, receivers, callback) { */ exports.massSendNews = function (mediaId, receivers, callback) { var opts = { - "mpnews": { - "media_id": mediaId + 'mpnews': { + 'media_id': mediaId }, - "msgtype": "mpnews" + 'msgtype': 'mpnews' }; this.massSend(opts, receivers, callback); }; @@ -233,10 +235,10 @@ exports.massSendNews = function (mediaId, receivers, callback) { */ exports.massSendText = function (content, receivers, callback) { var opts = { - "text": { - "content": content + 'text': { + 'content': content }, - "msgtype": "text" + 'msgtype': 'text' }; this.massSend(opts, receivers, callback); }; @@ -268,10 +270,10 @@ exports.massSendText = function (content, receivers, callback) { */ exports.massSendVoice = function (mediaId, receivers, callback) { var opts = { - "voice": { - "media_id": mediaId + 'voice': { + 'media_id': mediaId }, - "msgtype": "voice" + 'msgtype': 'voice' }; this.massSend(opts, receivers, callback); }; @@ -303,10 +305,10 @@ exports.massSendVoice = function (mediaId, receivers, callback) { */ exports.massSendImage = function (mediaId, receivers, callback) { var opts = { - "image": { - "media_id": mediaId + 'image': { + 'media_id': mediaId }, - "msgtype": "image" + 'msgtype': 'image' }; this.massSend(opts, receivers, callback); }; @@ -338,10 +340,10 @@ exports.massSendImage = function (mediaId, receivers, callback) { */ exports.massSendVideo = function (mediaId, receivers, callback) { var opts = { - "mpvideo": { - "media_id": mediaId + 'mpvideo': { + 'media_id': mediaId }, - "msgtype": "mpvideo" + 'msgtype': 'mpvideo' }; this.massSend(opts, receivers, callback); }; @@ -421,7 +423,7 @@ exports._deleteMass = function (messageId, callback) { var opts = { msg_id: messageId }; - var url = this.prefix + 'message/mass/delete?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/mass/delete?access_token=' + this.token.accessToken; this.request(url, postJSON(opts), wrapper(callback)); }; @@ -456,13 +458,13 @@ exports.previewNews = function (openid, mediaId, callback) { exports._previewNews = function (openid, mediaId, callback) { var opts = { - "touser": openid, - "mpnews": { - "media_id": mediaId + 'touser': openid, + 'mpnews': { + 'media_id': mediaId }, - "msgtype": "mpnews" + 'msgtype': 'mpnews' }; - var url = this.prefix + 'message/mass/preview?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/mass/preview?access_token=' + this.token.accessToken; this.request(url, postJSON(opts), wrapper(callback)); }; @@ -497,13 +499,13 @@ exports.previewText = function (openid, content, callback) { exports._previewText = function (openid, content, callback) { var opts = { - "touser": openid, - "text": { - "content": content + 'touser': openid, + 'text': { + 'content': content }, - "msgtype":"text" + 'msgtype':'text' }; - var url = this.prefix + 'message/mass/preview?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/mass/preview?access_token=' + this.token.accessToken; this.request(url, postJSON(opts), wrapper(callback)); }; @@ -538,13 +540,13 @@ exports.previewVoice = function (openid, mediaId, callback) { exports._previewVoice = function (openid, mediaId, callback) { var opts = { - "touser": openid, - "voice": { - "media_id": mediaId + 'touser': openid, + 'voice': { + 'media_id': mediaId }, - "msgtype": "voice" + 'msgtype': 'voice' }; - var url = this.prefix + 'message/mass/preview?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/mass/preview?access_token=' + this.token.accessToken; this.request(url, postJSON(opts), wrapper(callback)); }; @@ -579,13 +581,13 @@ exports.previewImage = function (openid, mediaId, callback) { exports._previewImage = function (openid, mediaId, callback) { var opts = { - "touser": openid, - "image": { - "media_id": mediaId + 'touser': openid, + 'image': { + 'media_id': mediaId }, - "msgtype": "image" + 'msgtype': 'image' }; - var url = this.prefix + 'message/mass/preview?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/mass/preview?access_token=' + this.token.accessToken; this.request(url, postJSON(opts), wrapper(callback)); }; @@ -620,13 +622,13 @@ exports.previewVideo = function (openid, mediaId, callback) { exports._previewVideo = function (openid, mediaId, callback) { var opts = { - "touser": openid, - "mpvideo": { - "media_id": mediaId + 'touser': openid, + 'mpvideo': { + 'media_id': mediaId }, - "msgtype": "mpvideo" + 'msgtype': 'mpvideo' }; - var url = this.prefix + 'message/mass/preview?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/mass/preview?access_token=' + this.token.accessToken; this.request(url, postJSON(opts), wrapper(callback)); }; @@ -659,8 +661,8 @@ exports.getMassMessageStatus = function (messageId, callback) { exports._getMassMessageStatus = function (messageId, callback) { var opts = { - "msg_id": messageId + 'msg_id': messageId }; - var url = this.prefix + 'message/mass/get?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/mass/get?access_token=' + this.token.accessToken; this.request(url, postJSON(opts), wrapper(callback)); }; diff --git a/lib/api_material.js b/lib/api_material.js index 49fef53..31700d2 100644 --- a/lib/api_material.js +++ b/lib/api_material.js @@ -1,11 +1,14 @@ +'use strict'; + var path = require('path'); var fs = require('fs'); var formstream = require('formstream'); + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; - +var make = util.make; /** * 上传永久素材,分别有图片(image)、语音(voice)、和缩略图(thumb) @@ -33,14 +36,7 @@ var postJSON = util.postJSON; * @param {String} type 媒体类型,可用值有image、voice、video、thumb * @param {Function} callback 回调函数 */ -exports.uploadMaterial = function (filepath, type, callback) { - this.preRequest(this._uploadMaterial, arguments); -}; - -/*! - * 上传永久文件的未封装版本 - */ -exports._uploadMaterial = function (filepath, type, callback) { +make(exports, 'uploadMaterial', function (filepath, type, callback) { var that = this; fs.stat(filepath, function (err, stat) { if (err) { @@ -48,7 +44,7 @@ exports._uploadMaterial = function (filepath, type, callback) { } var form = formstream(); form.file('media', filepath, path.basename(filepath), stat.size); - var url = that.prefix + 'material/add_material?access_token=' + that.token.accessToken + '&type=' + type; + var url = that.endpoint + '/cgi-bin/material/add_material?access_token=' + that.token.accessToken + '&type=' + type; var opts = { dataType: 'json', type: 'POST', @@ -58,7 +54,7 @@ exports._uploadMaterial = function (filepath, type, callback) { }; that.request(url, opts, wrapper(callback)); }); -}; +}); ['image', 'voice', 'thumb'].forEach(function (type) { var method = 'upload' + type[0].toUpperCase() + type.substring(1) + 'Material'; @@ -92,14 +88,7 @@ exports._uploadMaterial = function (filepath, type, callback) { * @param {Object} description 描述 * @param {Function} callback 回调函数 */ -exports.uploadVideoMaterial = function (filepath, description, callback) { - this.preRequest(this._uploadVideoMaterial, arguments); -}; - -/*! - * 上传永久文件(视频)的未封装版本 - */ -exports._uploadVideoMaterial = function (filepath, description, callback) { +make(exports, 'uploadVideoMaterial', function (filepath, description, callback) { var that = this; fs.stat(filepath, function (err, stat) { if (err) { @@ -108,7 +97,7 @@ exports._uploadVideoMaterial = function (filepath, description, callback) { var form = formstream(); form.file('media', filepath, path.basename(filepath), stat.size); form.field('description', JSON.stringify(description)); - var url = that.prefix + 'material/add_material?access_token=' + that.token.accessToken + '&type=video'; + var url = that.endpoint + '/cgi-bin/material/add_material?access_token=' + that.token.accessToken + '&type=video'; var opts = { dataType: 'json', type: 'POST', @@ -118,7 +107,7 @@ exports._uploadVideoMaterial = function (filepath, description, callback) { }; that.request(url, opts, wrapper(callback)); }); -}; +}); /** * 新增永久图文素材 @@ -164,7 +153,7 @@ exports.uploadNewsMaterial = function (news, callback) { * 新增永久图文素材的未封装版本 */ exports._uploadNewsMaterial = function (news, callback) { - var url = this.prefix + 'material/add_news?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/material/add_news?access_token=' + this.token.accessToken; this.request(url, postJSON(news), wrapper(callback)); }; @@ -212,7 +201,7 @@ exports.updateNewsMaterial = function (news, callback) { * 更新永久图文素材的未封装版本 */ exports._updateNewsMaterial = function (news, callback) { - var url = this.prefix + 'material/update_news?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/material/update_news?access_token=' + this.token.accessToken; this.request(url, postJSON(news), wrapper(callback)); }; @@ -241,7 +230,7 @@ exports.getMaterial = function (mediaId, callback) { * 下载永久素材的未封装版本 */ exports._getMaterial = function (mediaId, callback) { - var url = this.prefix + 'material/get_material?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/material/get_material?access_token=' + this.token.accessToken; var opts = { type: 'POST', data: {'media_id': mediaId}, @@ -265,14 +254,12 @@ exports._getMaterial = function (mediaId, callback) { err.name = 'WeChatAPIError'; } } catch (ex) { - callback(ex, data, res); - return; + return callback(ex, data, res); } - callback(err, ret, res); - } else { - // 输出Buffer对象 - callback(null, data, res); + return callback(err, ret, res); } + // 输出Buffer对象 + callback(null, data, res); })); }; @@ -300,7 +287,7 @@ exports.removeMaterial = function (mediaId, callback) { * 删除永久素材的未封装版本 */ exports._removeMaterial = function (mediaId, callback) { - var url = this.prefix + 'material/del_material?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/material/del_material?access_token=' + this.token.accessToken; this.request(url, postJSON({'media_id': mediaId}), wrapper(callback)); }; @@ -339,7 +326,7 @@ exports.getMaterialCount = function (callback) { * 删除永久素材的未封装版本 */ exports._getMaterialCount = function (callback) { - var url = this.prefix + 'material/get_materialcount?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/material/get_materialcount?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }; @@ -385,7 +372,7 @@ exports.getMaterials = function (type, offset, count, callback) { * 获取永久素材列表的未封装版本 */ exports._getMaterials = function (type, offset, count, callback) { - var url = this.prefix + 'material/batchget_material?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/material/batchget_material?access_token=' + this.token.accessToken; var data = { type: type, offset: offset, diff --git a/lib/api_media.js b/lib/api_media.js index fc9729b..90122e9 100644 --- a/lib/api_media.js +++ b/lib/api_media.js @@ -1,7 +1,10 @@ +'use strict'; + var path = require('path'); var fs = require('fs'); var formstream = require('formstream'); + var util = require('./util'); var wrapper = util.wrapper; @@ -47,7 +50,7 @@ exports._uploadMedia = function (filepath, type, callback) { } var form = formstream(); form.file('media', filepath, path.basename(filepath), stat.size); - var url = that.prefix + 'media/upload?access_token=' + that.token.accessToken + '&type=' + type; + var url = that.endpoint + '/cgi-bin/media/upload?access_token=' + that.token.accessToken + '&type=' + type; var opts = { dataType: 'json', type: 'POST', @@ -59,11 +62,62 @@ exports._uploadMedia = function (filepath, type, callback) { }); }; +/** + * 流式新增临时素材,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb) + * 拓展自uploadMedia,实现上游上传的流数据重定向到微信服务器,省去自己服务器的文件缓存。 + * Examples: + * ``` + * api.uploadMediaStream(req, type, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * {"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789} + * ``` + * Shortcut: + * + * - `exports.uploadImageStream(req, callback);` + * - `exports.uploadVoiceStream(req, callback);` + * - `exports.uploadVideoStream(req, callback);` + * - `exports.uploadThumbStream(req, callback);` + * + * @param {String} req 上游Stream对象,必须包含headers属性;例如expressjs中request对象。 + * @param {String} type 媒体类型,可用值有image、voice、video、thumb + * @param {Function} callback 回调函数 + */ +exports.uploadMediaStream = function (req, type, callback) { + this.preRequest(this._uploadMediaStream, arguments); +}; + +/*! + * 流式上传多媒体文件的未封装版本 + */ +exports._uploadMediaStream = function (req, type, callback) { + var that = this; + var url = that.endpoint + '/cgi-bin/media/upload?access_token=' + that.token.accessToken + '&type=' + type; + var opts = { + dataType: 'json', + type: 'POST', + timeout: 60000, // 60秒超时 + headers: req.headers, + stream: req + }; + delete opts.headers.host; + that.request(url, opts, callback); +}; + ['image', 'voice', 'video', 'thumb'].forEach(function (type) { var method = 'upload' + type[0].toUpperCase() + type.substring(1); exports[method] = function (filepath, callback) { this.uploadMedia(filepath, type, callback); }; + exports[method+'Stream'] = function (req, callback) { + this.uploadMediaStream(req, type, callback); + }; }); /** @@ -90,7 +144,7 @@ exports.getMedia = function (mediaId, callback) { * 获取临时素材的未封装版本 */ exports._getMedia = function (mediaId, callback) { - var url = this.prefix + 'media/get?access_token=' + this.token.accessToken + '&media_id=' + mediaId; + var url = this.endpoint + '/cgi-bin/media/get?access_token=' + this.token.accessToken + '&media_id=' + mediaId; var opts = { timeout: 60000 // 60秒超时 }; @@ -100,7 +154,7 @@ exports._getMedia = function (mediaId, callback) { return callback(err); } var contentType = res.headers['content-type']; - if (contentType === 'application/json') { + if (contentType === 'application/json' || contentType === 'text/plain') { var ret; try { ret = JSON.parse(data); @@ -112,11 +166,10 @@ exports._getMedia = function (mediaId, callback) { callback(ex, data, res); return; } - callback(err, ret, res); - } else { - // 输出Buffer对象 - callback(null, data, res); + return callback(err, ret, res); } + // 输出Buffer对象 + callback(null, data, res); })); }; @@ -156,7 +209,7 @@ exports._uploadImage = function (filepath, callback) { } var form = formstream(); form.file('media', filepath, path.basename(filepath), stat.size); - var url = that.prefix + 'media/uploadimg?access_token=' + that.token.accessToken; + var url = that.endpoint + '/cgi-bin/media/uploadimg?access_token=' + that.token.accessToken; var opts = { dataType: 'json', type: 'POST', @@ -166,4 +219,45 @@ exports._uploadImage = function (filepath, callback) { }; that.request(url, opts, wrapper(callback)); }); -}; \ No newline at end of file +}; + +/** + * 上传来自上游管道的图文消息内的图片,并获取URL。 + * 拓展于uploadImage,用于客户端直接上传文件管道重定向到微信服务器,不经过自身缓存服务器文件。 + * Examples: + * ``` + * api.uploadImageStream(req, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * {"url": "http://mmbiz.qpic.cn/mmbiz/gLO17UPS6FS2xsypf378iaNhWacZ1G1UplZYWEYfwvuU6Ont96b1roYsCNFwaRrSaKTPCUdBK9DgEHicsKwWCBRQ/0"} + * ``` + * + * @param {Object} req 上游Stream对象,必须包含headers属性;例如expressjs中request对象。 + * @param {Function} callback 回调函数 + */ +exports.uploadImageStream = function (req, callback) { + this.preRequest(this._uploadImageStream, arguments); +}; + +/*! + * 上传来自上游管道的图文消息内的图片未封装版本 + */ +exports._uploadImageStream = function (req, callback) { + var that = this; + var url = that.endpoint + '/cgi-bin/media/uploadimg?access_token=' + that.token.accessToken; + var opts = { + dataType: 'json', + type: 'POST', + timeout: 60000, // 60秒超时 + headers: req.headers, + stream: req + }; + delete opts.headers.host; + that.request(url, opts, callback); +}; diff --git a/lib/api_menu.js b/lib/api_menu.js index 2af9564..a36a981 100644 --- a/lib/api_menu.js +++ b/lib/api_menu.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -57,7 +59,7 @@ exports.createMenu = function (menu, callback) { * 创建自定义菜单的未封装版本 */ exports._createMenu = function (menu, callback) { - var url = this.prefix + 'menu/create?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/menu/create?access_token=' + this.token.accessToken; this.request(url, postJSON(menu), wrapper(callback)); }; @@ -101,7 +103,7 @@ exports.getMenu = function (callback) { * 获取自定义菜单的未封装版本 */ exports._getMenu = function (callback) { - var url = this.prefix + 'menu/get?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/menu/get?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }; @@ -131,7 +133,7 @@ exports.removeMenu = function (callback) { * 删除自定义菜单的未封装版本 */ exports._removeMenu = function (callback) { - var url = this.prefix + 'menu/delete?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/menu/delete?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }; @@ -162,6 +164,6 @@ exports.getMenuConfig = function (callback) { * 获取自定义菜单配置的未封装版本 */ exports._getMenuConfig = function (callback) { - var url = this.prefix + 'get_current_selfmenu_info?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/get_current_selfmenu_info?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }; diff --git a/lib/api_menu_custom.js b/lib/api_menu_custom.js index b9cc855..ba2d7ca 100644 --- a/lib/api_menu_custom.js +++ b/lib/api_menu_custom.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -66,7 +68,7 @@ exports.createCustomMenu = function (menu, callback) { * 创建个性化菜单的未封装版本 */ exports._createCustomMenu = function (menu, callback) { - var url = this.prefix + 'menu/addconditional?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/menu/addconditional?access_token=' + this.token.accessToken; this.request(url, postJSON(menu), wrapper(callback)); }; @@ -100,9 +102,9 @@ exports.removeCustomMenu = function (menu_id,callback) { * 删除个性化菜单的未封装版本 */ exports._removeCustomMenu = function (menu_id,callback) { - var url = this.prefix + 'menu/delconditional?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/menu/delconditional?access_token=' + this.token.accessToken; this.request(url, postJSON({ - "menuid" : menu_id + 'menuid' : menu_id }), wrapper(callback)); }; @@ -159,8 +161,8 @@ exports.testCustomMenu = function (user_id, callback) { * 测试个性化菜单的未封装版本 */ exports._testCustomMenu = function (user_id,callback) { - var url = this.prefix + 'menu/trymatch?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/menu/trymatch?access_token=' + this.token.accessToken; this.request(url, postJSON({ - "user_id" : user_id + 'user_id' : user_id }), wrapper(callback)); }; diff --git a/lib/api_message.js b/lib/api_message.js index a4aa3da..bf0ec31 100644 --- a/lib/api_message.js +++ b/lib/api_message.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -22,6 +24,44 @@ exports.sendText = function (openid, text, callback) { this.preRequest(this._sendText, arguments); }; +/** + * 客服消息,发送文字消息,带客服账号 + * + * @param {String} openid 用户的openid + * @param {String} cs_account 客服账号 + * @param {String} text 发送的消息内容 + * @param {Function} callback 回调函数 + */ +exports.sendTextFromCs = function (openid, cs_account, text, callback) { + this.preRequest(this._sendTextFromCs, arguments); +}; + + +/*! + * 客服消息,带客服账号,发送文字消息的未封装版本 + */ +exports._sendTextFromCs = function (openid, cs_account, text, callback) { + // { + // "touser":"OPENID", + // "msgtype":"text", + // "text": { + // "content":"Hello World" + // } + // } + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; + var data = { + 'touser': openid, + 'msgtype': 'text', + 'text': { + 'content': text + }, + 'customservice':{ + 'kf_account': cs_account + } + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + /*! * 客服消息,发送文字消息的未封装版本 */ @@ -33,12 +73,12 @@ exports._sendText = function (openid, text, callback) { // "content":"Hello World" // } // } - var url = this.prefix + 'message/custom/send?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; var data = { - "touser": openid, - "msgtype": "text", - "text": { - "content": text + 'touser': openid, + 'msgtype': 'text', + 'text': { + 'content': text } }; this.request(url, postJSON(data), wrapper(callback)); @@ -75,12 +115,58 @@ exports._sendImage = function (openid, mediaId, callback) { // "media_id":"MEDIA_ID" // } // } - var url = this.prefix + 'message/custom/send?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; + var data = { + 'touser': openid, + 'msgtype':'image', + 'image': { + 'media_id': mediaId + } + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 客服消息,发送图片消息,带客服账号 + * 详细细节 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html#.E5.AE.A2.E6.9C.8D.E6.8E.A5.E5.8F.A3-.E5.8F.91.E6.B6.88.E6.81.AF + * Examples: + * ``` + * api.sendImage('openid', 'media_id', callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @param {String} openid 用户的openid + * @param {String} cs_account 客服账号 + * @param {String} mediaId 媒体文件的ID,参见uploadMedia方法 + * @param {Function} callback 回调函数 + */ +exports.sendImageFromCs = function (openid,cs_account, mediaId, callback) { + this.preRequest(this._sendImageFromCs, arguments); +}; + +/*! + * 客服消息,发送图片消息的未封装版本 + */ +exports._sendImageFromCs = function (openid,cs_account, mediaId, callback) { + // { + // "touser":"OPENID", + // "msgtype":"image", + // "image": { + // "media_id":"MEDIA_ID" + // } + // } + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; var data = { - "touser": openid, - "msgtype":"image", - "image": { - "media_id": mediaId + 'touser': openid, + 'msgtype':'image', + 'image': { + 'media_id': mediaId + }, + 'customservice':{ + 'kf_account': cs_account } }; this.request(url, postJSON(data), wrapper(callback)); @@ -117,12 +203,59 @@ exports._sendVoice = function (openid, mediaId, callback) { // "media_id":"MEDIA_ID" // } // } - var url = this.prefix + 'message/custom/send?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; + var data = { + 'touser': openid, + 'msgtype': 'voice', + 'voice': { + 'media_id': mediaId + } + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + + +/** + * 客服消息,发送语音消息,带客服账号 + * 详细细节 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html#.E5.AE.A2.E6.9C.8D.E6.8E.A5.E5.8F.A3-.E5.8F.91.E6.B6.88.E6.81.AF + * Examples: + * ``` + * api.sendVoice('openid', 'media_id', callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @param {String} openid 用户的openid + * @param {String} cs_account 客服账号 + * @param {String} mediaId 媒体文件的ID + * @param {Function} callback 回调函数 + */ +exports.sendVoiceFromCs = function (openid,cs_account, mediaId, callback) { + this.preRequest(this._sendVoiceFromCs, arguments); +}; + +/*! + * 客服消息,发送语音消息的未封装版本 + */ +exports._sendVoiceFromCs = function (openid,cs_account, mediaId, callback) { + // { + // "touser":"OPENID", + // "msgtype":"voice", + // "voice": { + // "media_id":"MEDIA_ID" + // } + // } + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; var data = { - "touser": openid, - "msgtype": "voice", - "voice": { - "media_id": mediaId + 'touser': openid, + 'msgtype': 'voice', + 'voice': { + 'media_id': mediaId + }, + 'customservice':{ + 'kf_account': cs_account } }; this.request(url, postJSON(data), wrapper(callback)); @@ -161,13 +294,63 @@ exports._sendVideo = function (openid, mediaId, thumbMediaId, callback) { // "thumb_media_id":"THUMB_MEDIA_ID" // } // } - var url = this.prefix + 'message/custom/send?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; + var data = { + 'touser': openid, + 'msgtype':'video', + 'video': { + 'media_id': mediaId, + 'thumb_media_id': thumbMediaId + } + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + + +/** + * 客服消息,发送视频消息,带客服账号 + * 详细细节 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html#.E5.AE.A2.E6.9C.8D.E6.8E.A5.E5.8F.A3-.E5.8F.91.E6.B6.88.E6.81.AF + * Examples: + * ``` + * api.sendVideo('openid', 'media_id', 'thumb_media_id', callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @param {String} openid 用户的openid + * @param {String} cs_account 客服账号 + * @param {String} mediaId 媒体文件的ID + * @param {String} thumbMediaId 缩略图文件的ID + * @param {Function} callback 回调函数 + */ +exports.sendVideoFromCs = function (openid,cs_account, mediaId, thumbMediaId, callback) { + this.preRequest(this._sendVideoFromCs, arguments); +}; + +/*! + * 客服消息,发送视频消息的未封装版本 + */ +exports._sendVideoFromCs = function (openid,cs_account, mediaId, thumbMediaId, callback) { + // { + // "touser":"OPENID", + // "msgtype":"video", + // "video": { + // "media_id":"MEDIA_ID" + // "thumb_media_id":"THUMB_MEDIA_ID" + // } + // } + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; var data = { - "touser": openid, - "msgtype":"video", - "video": { - "media_id": mediaId, - "thumb_media_id": thumbMediaId + 'touser': openid, + 'msgtype':'video', + 'video': { + 'media_id': mediaId, + 'thumb_media_id': thumbMediaId + }, + 'customservice':{ + 'kf_account': cs_account } }; this.request(url, postJSON(data), wrapper(callback)); @@ -215,11 +398,67 @@ exports._sendMusic = function (openid, music, callback) { // "thumb_media_id":"THUMB_MEDIA_ID" // } // } - var url = this.prefix + 'message/custom/send?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; + var data = { + 'touser': openid, + 'msgtype':'music', + 'music': music + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + + +/** + * 客服消息,发送音乐消息,带客服账号 + * 详细细节 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html#.E5.AE.A2.E6.9C.8D.E6.8E.A5.E5.8F.A3-.E5.8F.91.E6.B6.88.E6.81.AF + * Examples: + * ``` + * var music = { + * title: '音乐标题', // 可选 + * description: '描述内容', // 可选 + * musicurl: 'http://url.cn/xxx', 音乐文件地址 + * hqmusicurl: "HQ_MUSIC_URL", + * thumb_media_id: "THUMB_MEDIA_ID" + * }; + * api.sendMusic('openid', music, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @param {String} openid 用户的openid + * @param {String} cs_account 客服账号 + * @param {Object} music 音乐文件 + * @param {Function} callback 回调函数 + */ +exports.sendMusicFromCs = function (openid,cs_account, music, callback) { + this.preRequest(this._sendMusicFromCs, arguments); +}; + +/*! + * 客服消息,发送音乐消息的未封装版本 + */ +exports._sendMusicFromCs = function (openid,cs_account, music, callback) { + // { + // "touser":"OPENID", + // "msgtype":"music", + // "music": { + // "title":"MUSIC_TITLE", // 可选 + // "description":"MUSIC_DESCRIPTION", // 可选 + // "musicurl":"MUSIC_URL", + // "hqmusicurl":"HQ_MUSIC_URL", + // "thumb_media_id":"THUMB_MEDIA_ID" + // } + // } + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; var data = { - "touser": openid, - "msgtype":"music", - "music": music + 'touser': openid, + 'msgtype':'music', + 'music': music, + 'customservice':{ + 'kf_account': cs_account + } }; this.request(url, postJSON(data), wrapper(callback)); }; @@ -280,18 +519,88 @@ exports._sendNews = function (openid, articles, callback) { // }] // } // } - var url = this.prefix + 'message/custom/send?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; var data = { - "touser": openid, - "msgtype":"news", - "news": { - "articles": articles + 'touser': openid, + 'msgtype':'news', + 'news': { + 'articles': articles } }; this.request(url, postJSON(data), wrapper(callback)); }; +/** + * 客服消息,发送图文消息(点击跳转到外链),带客服账号 + * 详细细节 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html#.E5.AE.A2.E6.9C.8D.E6.8E.A5.E5.8F.A3-.E5.8F.91.E6.B6.88.E6.81.AF + * Examples: + * ``` + * var articles = [ + * { + * "title":"Happy Day", + * "description":"Is Really A Happy Day", + * "url":"URL", + * "picurl":"PIC_URL" + * }, + * { + * "title":"Happy Day", + * "description":"Is Really A Happy Day", + * "url":"URL", + * "picurl":"PIC_URL" + * }]; + * api.sendNews('openid', articles, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @param {String} openid 用户的openid + * @param {String} cs_account 客服账号 + * @param {Array} articles 图文列表 + * @param {Function} callback 回调函数 + */ +exports.sendNewsFromCs = function (openid,cs_account, articles, callback) { + this.preRequest(this._sendNewsFromCs, arguments); +}; + +/*! + * 客服消息,发送图文消息(点击跳转到外链)的未封装版本 + */ +exports._sendNewsFromCs = function (openid,cs_account, articles, callback) { + // { + // "touser":"OPENID", + // "msgtype":"news", + // "news":{ + // "articles": [ + // { + // "title":"Happy Day", + // "description":"Is Really A Happy Day", + // "url":"URL", + // "picurl":"PIC_URL" + // }, + // { + // "title":"Happy Day", + // "description":"Is Really A Happy Day", + // "url":"URL", + // "picurl":"PIC_URL" + // }] + // } + // } + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; + var data = { + 'touser': openid, + 'msgtype':'news', + 'news': { + 'articles': articles + }, + 'customservice':{ + 'kf_account': cs_account + } + }; + this.request(url, postJSON(data), wrapper(callback)); +}; /** @@ -326,17 +635,66 @@ exports._sendMpNews = function (openid, mediaId, callback) { // "media_id":"MEDIA_ID" // } //} - var url = this.prefix + 'message/custom/send?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; + var data = { + 'touser': openid, + 'msgtype':'mpnews', + 'mpnews': { + 'media_id': mediaId + } + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + + +/** + * 客服消息,发送图文消息(点击跳转到图文消息页面),带客服账号 + * 详细细节 http://mp.weixin.qq.com/wiki/14/d9be34fe03412c92517da10a5980e7ee.html#.E5.AE.A2.E6.9C.8D.E6.8E.A5.E5.8F.A3-.E5.8F.91.E6.B6.88.E6.81.AF + * Examples: + * ``` + * api.sendMpNews('openid', 'mediaId' , callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @param {String} openid 用户的openid + * @param {String} cs_account 客服账号 + * @param {String} mediaId 图文消息的id + * @param {Function} callback 回调函数 + */ +exports.sendMpNewsFromCs = function (openid,cs_account, mediaId, callback) { + this.preRequest(this._sendMpNewsFromCs, arguments); +}; + +/*! + * 客服消息,发送图文消息(点击跳转到图文消息页面) 的未封装版本 + */ +exports._sendMpNewsFromCs = function (openid,cs_account, mediaId, callback) { + //{ + // "touser":"OPENID", + // "msgtype":"mpnews", + // "mpnews": + // { + // "media_id":"MEDIA_ID" + // } + //} + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; var data = { - "touser": openid, - "msgtype":"mpnews", - "mpnews": { - "media_id": mediaId + 'touser': openid, + 'msgtype':'mpnews', + 'mpnews': { + 'media_id': mediaId + }, + 'customservice':{ + 'kf_account': cs_account } }; this.request(url, postJSON(data), wrapper(callback)); }; + /** * 客服消息,发送卡卷消息 * 详细细节 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html#.E5.AE.A2.E6.9C.8D.E6.8E.A5.E5.8F.A3-.E5.8F.91.E6.B6.88.E6.81.AF @@ -361,15 +719,59 @@ exports.sendCard = function (openid, card, callback) { * 客服消息,发送卡卷消息的未封装版本 */ exports._sendCard = function (openid, card, callback) { - var url = this.prefix + 'message/custom/send?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; + var that = this; + this.getCardExt(card, function (err, result) { + var data = { + 'touser': openid, + 'msgtype':'wxcard', + 'wxcard': { + 'card_id': card.card_id, + 'card_ext': result + } + }; + that.request(url, postJSON(data), wrapper(callback)); + }); +}; + + +/** + * 客服消息,发送卡卷消息,带客服账号 + * 详细细节 http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html#.E5.AE.A2.E6.9C.8D.E6.8E.A5.E5.8F.A3-.E5.8F.91.E6.B6.88.E6.81.AF + * Examples: + * ``` + * api.sendCard('openid', 'card', callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @param {String} openid 用户的openid + * @param {String} cs_account 客服账号 + * @param {Object} card 卡卷相关信息 + * @param {Function} callback 回调函数 + */ +exports.sendCardFromCs = function (openid,cs_account, card, callback) { + this.preRequest(this._sendCardFromCs, arguments); +}; + +/*! + * 客服消息,发送卡卷消息的未封装版本 + */ +exports._sendCardFromCs = function (openid,cs_account, card, callback) { + var url = this.endpoint + '/cgi-bin/message/custom/send?access_token=' + this.token.accessToken; var that = this; this.getCardExt(card, function (err, result) { var data = { - "touser": openid, - "msgtype":"wxcard", - "wxcard": { - "card_id": card.card_id, - "card_ext": result + 'touser': openid, + 'msgtype':'wxcard', + 'wxcard': { + 'card_id': card.card_id, + 'card_ext': result + }, + 'customservice':{ + 'kf_account': cs_account } }; that.request(url, postJSON(data), wrapper(callback)); diff --git a/lib/api_payment.js b/lib/api_payment.js index e99892a..0a920df 100644 --- a/lib/api_payment.js +++ b/lib/api_payment.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -45,7 +47,7 @@ exports.deliverNotify = function (data, callback) { * 发货通知的未封装版本 */ exports._deliverNotify = function (data, callback) { - var url = this.payPrefix + 'delivernotify?access_token=' + this.token.accessToken; + var url = this.endpoint + '/pay/delivernotify?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -113,6 +115,6 @@ exports.orderQuery = function (query, callback) { * 发货通知的未封装版本 */ exports._orderQuery = function (query, callback) { - var url = this.payPrefix + 'orderquery?access_token=' + this.token.accessToken; + var url = this.endpoint + '/pay/orderquery?access_token=' + this.token.accessToken; this.request(url, postJSON(query), wrapper(callback)); }; diff --git a/lib/api_poi.js b/lib/api_poi.js index b7f5cc7..5609e98 100644 --- a/lib/api_poi.js +++ b/lib/api_poi.js @@ -1,3 +1,5 @@ +'use strict'; + // 微信门店接口文档请参考:http://mp.weixin.qq.com/wiki/16/8f182af4d8dcea02c56506306bdb2f4c.html var util = require('./util'); var wrapper = util.wrapper; @@ -63,7 +65,7 @@ make(exports, 'addPoi', function (poi, callback) { base_info: poi } }; - var url = this.prefix + 'poi/addpoi?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/poi/addpoi?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }); @@ -113,7 +115,7 @@ make(exports, 'addPoi', function (poi, callback) { * @param {Function} callback 回调函数 */ make(exports, 'getPoi', function (poiId, callback) { - var url = this.prefix + 'poi/getpoi?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/poi/getpoi?access_token=' + this.token.accessToken; var data = { poi_id: poiId }; @@ -163,7 +165,7 @@ make(exports, 'getPoi', function (poiId, callback) { * @param {Function} callback 回调函数 */ make(exports, 'getPois', function (begin, limit, callback) { - var url = this.prefix + 'poi/getpoilist?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/poi/getpoilist?access_token=' + this.token.accessToken; var data = { begin: begin, limit: limit @@ -192,7 +194,7 @@ make(exports, 'getPois', function (begin, limit, callback) { * @param {Function} callback 回调函数 */ make(exports, 'delPoi', function (poiId, callback) { - var url = this.prefix + 'poi/delpoi?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/poi/delpoi?access_token=' + this.token.accessToken; var data = { poi_id: poiId }; @@ -249,6 +251,31 @@ make(exports, 'updatePoi', function (poi, callback) { base_info: poi } }; - var url = this.prefix + 'poi/updatepoi?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/poi/updatepoi?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }); + +/** + * 门店类目表 + * + * Tips: + * + * - 类目名称接口是为商户提供自己门店类型信息的接口。门店类目定位的越规范,能够精准的吸引更多用户,提高曝光率。 + * + * Examples: + * ``` + * api.getWXCategory(callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @name getWXCategory + * @param {Function} callback 回调函数 + */ +make(exports, 'getWXCategory', function (callback) { + var url = this.endpoint + '/cgi-bin/poi/getwxcategory?access_token=' + this.token.accessToken; + this.request(url, {dataType: 'json'}, wrapper(callback)); +}); + diff --git a/lib/api_qrcode.js b/lib/api_qrcode.js index 82b15cf..90e067a 100644 --- a/lib/api_qrcode.js +++ b/lib/api_qrcode.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -21,8 +23,8 @@ var postJSON = util.postJSON; * "expire_seconds":1800 * } * ``` - * @param {Number} sceneId 场景ID - * @param {Number} expire 过期时间,单位秒。最大不超过604800(即7天) + * @param {Number|String} sceneId 场景ID。字符串ID长度限制为1到64 + * @param {Number} expire 过期时间,单位秒。最大不超过2592000(即30天) * @param {Function} callback 回调函数 */ exports.createTmpQRCode = function (sceneId, expire, callback) { @@ -33,12 +35,17 @@ exports.createTmpQRCode = function (sceneId, expire, callback) { * 创建临时二维码的未封装版本 */ exports._createTmpQRCode = function (sceneId, expire, callback) { - var url = this.prefix + 'qrcode/create?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/qrcode/create?access_token=' + this.token.accessToken; var data = { - "expire_seconds": expire, - "action_name": "QR_SCENE", - "action_info": {"scene": {"scene_id": sceneId}} + 'expire_seconds': expire, + 'action_name': 'QR_SCENE', + 'action_info': {'scene': {'scene_id': sceneId}} }; + // 字符串支持 + if (typeof sceneId === 'string') { + data.action_name = 'QR_STR_SCENE'; + data.action_info = {'scene': {'scene_str': sceneId}}; + } this.request(url, postJSON(data), wrapper(callback)); }; @@ -71,15 +78,15 @@ exports.createLimitQRCode = function (sceneId, callback) { * 创建永久二维码的未封装版本 */ exports._createLimitQRCode = function (sceneId, callback) { - var url = this.prefix + 'qrcode/create?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/qrcode/create?access_token=' + this.token.accessToken; var data = { - "action_name": "QR_LIMIT_SCENE", - "action_info": {"scene": {"scene_id": sceneId}} + 'action_name': 'QR_LIMIT_SCENE', + 'action_info': {'scene': {'scene_id': sceneId}} }; // 字符串 - if (typeof sceneId === "string") { - data.action_name = "QR_LIMIT_STR_SCENE"; - data.action_info.scene = {"scene_str": sceneId}; + if (typeof sceneId === 'string') { + data.action_name = 'QR_LIMIT_STR_SCENE'; + data.action_info.scene = {'scene_str': sceneId}; } this.request(url, postJSON(data), wrapper(callback)); }; @@ -88,7 +95,7 @@ exports._createLimitQRCode = function (sceneId, callback) { * 生成显示二维码的链接。微信扫描后,可立即进入场景 * Examples: * ``` - * api.showQRCodeURL(titck); + * api.showQRCodeURL(ticket); * // => https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET * ``` * @param {String} ticket 二维码Ticket diff --git a/lib/api_quota.js b/lib/api_quota.js new file mode 100644 index 0000000..bdecbed --- /dev/null +++ b/lib/api_quota.js @@ -0,0 +1,21 @@ +'use strict'; + +var util = require('./util'); +var wrapper = util.wrapper; +var postJSON = util.postJSON; +var make = util.make; + +/** + * 用于清零调用频次限制 + * @name clearQuota + * @param {String} appid 应用id + * @param {Function} callback 回调函数 + */ +make(exports, 'clearQuota', function (appid, callback) { + var data = { + appid: appid + }; + // https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=ACCESS_TOKEN + var url = this.endpoint + '/cgi-bin/clear_quota?access_token=' + this.token.accessToken; + this.request(url, postJSON(data), wrapper(callback)); +}); diff --git a/lib/api_semantic.js b/lib/api_semantic.js index e75ced4..1a472f0 100644 --- a/lib/api_semantic.js +++ b/lib/api_semantic.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -66,7 +68,7 @@ exports.semantic = function (uid, opts, callback) { */ exports._semantic = function (uid, opts, callback) { // https://api.weixin.qq.com/semantic/semproxy/search?access_token=YOUR_ACCESS_TOKEN - var url = 'https://api.weixin.qq.com/semantic/semproxy/search?access_token=' + this.token.accessToken; + var url = this.endpoint + '/semantic/semproxy/search?access_token=' + this.token.accessToken; opts.appid = this.appid; opts.uid = uid; this.request(url, postJSON(opts), wrapper(callback)); diff --git a/lib/api_shakearound.js b/lib/api_shakearound.js index 7d20c34..7427d60 100644 --- a/lib/api_shakearound.js +++ b/lib/api_shakearound.js @@ -1,3 +1,5 @@ +'use strict'; + var fs = require('fs'); var path = require('path'); var formstream = require('formstream'); @@ -53,7 +55,7 @@ var make = util.make; * @param {Function} callback 回调函数 */ make(exports, 'registerShakeAccount', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/account/register?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/account/register?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -88,8 +90,8 @@ make(exports, 'registerShakeAccount', function (options, callback) { * @name checkShakeAccountStatus * @param {Function} callback 回调函数 */ - make(exports, 'checkShakeAccountStatus', function (callback) { - var url = 'https://api.weixin.qq.com/shakearound/account/auditstatus?access_token=' + this.token.accessToken; +make(exports, 'checkShakeAccountStatus', function (callback) { + var url = this.endpoint + '/shakearound/account/auditstatus?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }); @@ -134,7 +136,7 @@ make(exports, 'registerShakeAccount', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'applyBeacons', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/applyid?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/applyid?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -179,7 +181,7 @@ make(exports, 'applyBeaconsStatus', function (apply_id, callback) { var data = { apply_id: apply_id }; - var url = 'https://api.weixin.qq.com/shakearound/device/applystatus?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/applystatus?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }); @@ -225,7 +227,7 @@ make(exports, 'applyBeaconsStatus', function (apply_id, callback) { * @param {Function} callback 回调函数 */ make(exports, 'updateBeacon', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/update?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/update?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -270,7 +272,7 @@ make(exports, 'updateBeacon', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'bindBeaconLocation', function(options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/bindlocation?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/bindlocation?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -356,7 +358,7 @@ make(exports, 'bindBeaconLocation', function(options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'getBeacons', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/search?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/search?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -403,7 +405,7 @@ make(exports, 'getBeacons', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'createPage', function (page, callback) { - var url = 'https://api.weixin.qq.com/shakearound/page/add?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/page/add?access_token=' + this.token.accessToken; this.request(url, postJSON(page), wrapper(callback)); }); @@ -449,7 +451,7 @@ make(exports, 'createPage', function (page, callback) { * @param {Function} callback 回调函数 */ make(exports, 'updatePage', function (page, callback) { - var url = 'https://api.weixin.qq.com/shakearound/page/update?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/page/update?access_token=' + this.token.accessToken; this.request(url, postJSON(page), wrapper(callback)); }); @@ -490,7 +492,7 @@ make(exports, 'updatePage', function (page, callback) { */ make(exports, 'deletePage', function (page_id, callback) { var data = {page_id: page_id}; - var url = 'https://api.weixin.qq.com/shakearound/page/delete?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/page/delete?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }); @@ -557,7 +559,7 @@ make(exports, 'deletePage', function (page_id, callback) { * @param {Function} callback 回调函数 */ make(exports, 'getPages', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/page/search?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/page/search?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -600,7 +602,7 @@ make(exports, 'uploadPageIcon', function (filepath, callback) { } var form = formstream(); form.file('media', filepath, path.basename(filepath), stat.size); - var url = 'https://api.weixin.qq.com/shakearound/material/add?access_token=' + that.token.accessToken; + var url = this.endpoint + '/shakearound/material/add?access_token=' + that.token.accessToken; var opts = { dataType: 'json', type: 'POST', @@ -655,7 +657,7 @@ make(exports, 'uploadPageIcon', function (filepath, callback) { * @param {Function} callback 回调函数 */ make(exports, 'bindBeaconWithPages', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/bindpage?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/bindpage?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -727,7 +729,7 @@ make(exports, 'bindBeaconWithPages', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'searchBeaconPageRelation', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/relation/search?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/relation/search?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -770,7 +772,7 @@ make(exports, 'getShakeInfo', function (ticket, callback) { ticket: ticket }; - var url = 'https://api.weixin.qq.com/shakearound/user/getshakeinfo?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/user/getshakeinfo?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }); @@ -830,7 +832,7 @@ make(exports, 'getShakeInfo', function (ticket, callback) { * @param {Function} callback 回调函数 */ make(exports, 'getDeviceStatistics', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/statistics/device?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/statistics/device?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -899,7 +901,7 @@ make(exports, 'getDeviceStatistics', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'getDeviceStatisticsList', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/statistics/devicelist?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/statistics/devicelist?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -954,7 +956,7 @@ make(exports, 'getDeviceStatisticsList', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'getPageStatistics', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/statistics/page?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/statistics/page?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -1017,7 +1019,7 @@ make(exports, 'getPageStatistics', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'getPageStatisticsList', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/statistics/pagelist?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/statistics/pagelist?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -1065,7 +1067,7 @@ make(exports, 'getPageStatisticsList', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'listBeaconGroup', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/group/getlist?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/group/getlist?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -1124,7 +1126,7 @@ make(exports, 'listBeaconGroup', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'queryGroupBeacons', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/group/getdetail?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/group/getdetail?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -1162,7 +1164,7 @@ make(exports, 'queryGroupBeacons', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'addBeaconGroup', function (group, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/group/add?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/group/add?access_token=' + this.token.accessToken; this.request(url, postJSON(group), wrapper(callback)); }); @@ -1201,7 +1203,7 @@ make(exports, 'addBeaconGroup', function (group, callback) { * @param {Function} callback 回调函数 */ make(exports, 'updateBeaconGroup', function (group, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/group/update?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/group/update?access_token=' + this.token.accessToken; this.request(url, postJSON(group), wrapper(callback)); }); @@ -1241,7 +1243,7 @@ make(exports, 'deleteBeaconGroup', function (group_id, callback) { group_id: group_id }; - var url = 'https://api.weixin.qq.com/shakearound/device/group/delete?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/group/delete?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }); @@ -1288,7 +1290,7 @@ make(exports, 'deleteBeaconGroup', function (group_id, callback) { * @param {Function} callback 回调函数 */ make(exports, 'addGroupBeacons', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/group/adddevice?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/group/adddevice?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -1335,7 +1337,7 @@ make(exports, 'addGroupBeacons', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'deleteGroupBeacons', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/device/group/deletedevice?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/device/group/deletedevice?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -1390,7 +1392,7 @@ make(exports, 'deleteGroupBeacons', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'addLotteryInfo', function (options, body, callback) { - var url = ' https://api.weixin.qq.com/shakearound/lottery/addlotteryinfo?&use_template=' + options.use_template + '&logo_url=' + options.logo_url + '&access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/lottery/addlotteryinfo?&use_template=' + options.use_template + '&logo_url=' + options.logo_url + '&access_token=' + this.token.accessToken; this.request(url, postJSON(body), wrapper(callback)); }); @@ -1436,7 +1438,7 @@ make(exports, 'addLotteryInfo', function (options, body, callback) { * @param {Function} callback 回调函数 */ make(exports, 'setPrizeBucket', function (options, callback) { - var url = 'https://api.weixin.qq.com/shakearound/lottery/setprizebucket?access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/lottery/setprizebucket?access_token=' + this.token.accessToken; this.request(url, postJSON(options), wrapper(callback)); }); @@ -1468,7 +1470,7 @@ make(exports, 'setPrizeBucket', function (options, callback) { * @param {Function} callback 回调函数 */ make(exports, 'setLotterySwitch', function (lotteryId, onoff, callback) { - var url = 'https://api.weixin.qq.com/shakearound/lottery/setlotteryswitch?lottery_id=' + lotteryId + '&onoff=' + onoff + '&access_token=' + this.token.accessToken; + var url = this.endpoint + '/shakearound/lottery/setlotteryswitch?lottery_id=' + lotteryId + '&onoff=' + onoff + '&access_token=' + this.token.accessToken; this.request(url, wrapper(callback)); }); diff --git a/lib/api_shop_common.js b/lib/api_shop_common.js index b25e564..007d293 100644 --- a/lib/api_shop_common.js +++ b/lib/api_shop_common.js @@ -1,3 +1,5 @@ +'use strict'; + var path = require('path'); var fs = require('fs'); var util = require('./util'); @@ -35,7 +37,7 @@ exports.uploadPicture = function (filepath, callback) { */ exports._uploadPicture = function (filepath, callback) { var basename = path.basename(filepath); - var url = this.merchantPrefix + 'common/upload_img?access_token=' + + var url = this.endpoint + '/merchant/common/upload_img?access_token=' + this.token.accessToken + '&filename=' + basename; var reader = fs.createReadStream(filepath); var opts = { diff --git a/lib/api_shop_express.js b/lib/api_shop_express.js index ea9849b..2320998 100644 --- a/lib/api_shop_express.js +++ b/lib/api_shop_express.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -105,7 +107,7 @@ exports.addExpressTemplate = function (express, callback) { * 增加邮费模板的未封装版本 */ exports._addExpressTemplate = function (express, callback) { - var url = this.merchantPrefix + 'express/add?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/express/add?access_token=' + this.token.accessToken; this.request(url, postJSON(express), wrapper(callback)); }; @@ -142,7 +144,7 @@ exports._deleteExpressTemplate = function (templateId, callback) { var data = { template_id: templateId }; - var url = this.merchantPrefix + 'express/del?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/express/del?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -183,7 +185,7 @@ exports.updateExpressTemplate = function (template, callback) { * 修改邮费模板的未封装版本 */ exports._updateExpressTemplate = function (template, callback) { - var url = this.merchantPrefix + 'express/del?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/express/del?access_token=' + this.token.accessToken; this.request(url, postJSON(template), wrapper(callback)); }; @@ -249,7 +251,7 @@ exports._getExpressTemplateById = function (templateId, callback) { var data = { template_id: templateId }; - var url = this.merchantPrefix + 'express/getbyid?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/express/getbyid?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -305,6 +307,6 @@ exports.getAllExpressTemplates = function (callback) { * 获取所有邮费模板的未封装版本 */ exports._getAllExpressTemplates = function (callback) { - var url = this.merchantPrefix + 'express/getall?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/express/getall?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }; diff --git a/lib/api_shop_goods.js b/lib/api_shop_goods.js index 1add51b..ed5f97b 100644 --- a/lib/api_shop_goods.js +++ b/lib/api_shop_goods.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -104,7 +106,7 @@ exports.createGoods = function (goods, callback) { * 增加商品的未封装版本 */ exports._createGoods = function (goods, callback) { - var url = this.merchantPrefix + 'create?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/create?access_token=' + this.token.accessToken; this.request(url, postJSON(goods), wrapper(callback)); }; @@ -139,9 +141,9 @@ exports.deleteGoods = function (productId, callback) { */ exports._deleteGoods = function (productId, callback) { var data = { - "product_id": productId + 'product_id': productId }; - var url = this.merchantPrefix + 'del?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/del?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -247,7 +249,7 @@ exports.updateGoods = function (goods, callback) { * 修改商品的未封装版本 */ exports._updateGoods = function (goods, callback) { - var url = this.merchantPrefix + 'update?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/update?access_token=' + this.token.accessToken; this.request(url, postJSON(goods), wrapper(callback)); }; @@ -342,7 +344,7 @@ exports.getGoods = function (productId, callback) { * 根据状态获取商品列表 */ exports._getGoods = function (productId, callback) { - var url = this.merchantPrefix + 'get?product_id=' + productId + '&access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/get?product_id=' + productId + '&access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }; @@ -387,7 +389,7 @@ exports.getGoodsByStatus = function (status, callback) { */ exports._getGoodsByStatus = function (status, callback) { var data = {status: status}; - var url = this.merchantPrefix + 'getbystatus?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/getbystatus?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -426,7 +428,7 @@ exports._updateGoodsStatus = function (productId, status, callback) { product_id: productId, status: status }; - var url = this.merchantPrefix + 'modproductstatus?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/modproductstatus?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -468,7 +470,7 @@ exports._getSubCats = function (catId, callback) { var data = { cate_id: catId }; - var url = this.merchantPrefix + 'category/getsub?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/category/getsub?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -515,7 +517,7 @@ exports._getSKUs = function (catId, callback) { var data = { cate_id: catId }; - var url = this.merchantPrefix + 'category/getsku?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/category/getsku?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -569,7 +571,7 @@ exports._getProperties = function (catId, callback) { var data = { cate_id: catId }; - var url = this.merchantPrefix + 'category/getproperty?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/category/getproperty?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; diff --git a/lib/api_shop_group.js b/lib/api_shop_group.js index 4925dbb..a386f00 100644 --- a/lib/api_shop_group.js +++ b/lib/api_shop_group.js @@ -1,3 +1,5 @@ +'use strict'; + // 商品分组管理接口 var util = require('./util'); var wrapper = util.wrapper; @@ -33,12 +35,12 @@ exports.createGoodsGroup = function (groupName, productList, callback) { exports._createGoodsGroup = function (groupName, productList, callback) { var data = { - "group_detail": { - "group_name": groupName, - "product_list": productList && productList.length ? productList: [] + 'group_detail': { + 'group_name': groupName, + 'product_list': productList && productList.length ? productList: [] } }; - var url = this.merchantPrefix + 'group/add?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/group/add?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -71,9 +73,9 @@ exports.deleteGoodsGroup = function (groupId, callback) { exports._deleteGoodsGroup = function (groupId, callback) { var data = { - "group_id": groupId + 'group_id': groupId }; - var url = this.merchantPrefix + 'group/del?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/group/del?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -106,10 +108,10 @@ exports.updateGoodsGroup = function (groupId, groupName, callback) { exports._updateGoodsGroup = function (groupId, groupName, callback) { var data = { - "group_id": groupId, - "group_name": groupName + 'group_id': groupId, + 'group_name': groupName }; - var url = this.merchantPrefix + 'group/propertymod?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/group/propertymod?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -143,15 +145,15 @@ exports.updateGoodsForGroup = function (groupId, addProductList, delProductList, exports._updateGoodsForGroup = function (groupId, addProductList, delProductList, callback) { var data = { - "group_id": groupId, - "product": [] + 'group_id': groupId, + 'product': [] }; if (addProductList && addProductList.length) { addProductList.forEach(function (val) { data.product.push({ - "product_id": val, - "mod_action": 1 + 'product_id': val, + 'mod_action': 1 }); }); } @@ -159,13 +161,13 @@ exports._updateGoodsForGroup = function (groupId, addProductList, delProductList if (delProductList && delProductList.length) { delProductList.forEach(function (val) { data.product.push({ - "product_id": val, - "mod_action": 0 + 'product_id': val, + 'mod_action': 0 }); }); } - var url = this.merchantPrefix + 'group/productmod?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/group/productmod?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -204,7 +206,7 @@ exports.getAllGroups = function (callback) { }; exports._getAllGroups = function (callback) { - var url = this.merchantPrefix + 'group/getall?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/group/getall?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }; @@ -245,8 +247,8 @@ exports.getGroupById = function (groupId, callback) { exports._getGroupById = function (groupId, callback) { var data = { - "group_id": groupId + 'group_id': groupId }; - var url = this.merchantPrefix + 'group/getbyid?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/group/getbyid?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; diff --git a/lib/api_shop_order.js b/lib/api_shop_order.js index 635995e..8223278 100644 --- a/lib/api_shop_order.js +++ b/lib/api_shop_order.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -57,9 +59,9 @@ exports.getOrderById = function (orderId, callback) { */ exports._getOrderById = function (orderId, callback) { var data = { - "order_id": orderId + 'order_id': orderId }; - var url = this.merchantPrefix + 'order/getbyid?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/order/getbyid?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -180,7 +182,7 @@ exports._getOrdersByStatus = function (status, beginTime, endTime, callback) { data.begintime = Math.round(beginTime.getTime() / 1000); data.endtime = Math.round(endTime.getTime() / 1000); } - var url = this.merchantPrefix + 'order/getbyfilter?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/order/getbyfilter?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -217,11 +219,11 @@ exports.setExpressForOrder = function (orderId, deliveryCompany, deliveryTrackNo */ exports._setExpressForOrder = function (orderId, deliveryCompany, deliveryTrackNo, callback) { var data = { - "order_id": orderId, - "delivery_company": deliveryCompany, - "delivery_track_no": deliveryTrackNo + 'order_id': orderId, + 'delivery_company': deliveryCompany, + 'delivery_track_no': deliveryTrackNo }; - var url = this.merchantPrefix + 'order/setdelivery?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/order/setdelivery?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -256,8 +258,8 @@ exports.closeOrder = function (orderId, callback) { */ exports._closeOrder = function (orderId, callback) { var data = { - "order_id": orderId + 'order_id': orderId }; - var url = this.merchantPrefix + 'order/close?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/order/close?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; diff --git a/lib/api_shop_shelf.js b/lib/api_shop_shelf.js index 0b69ce5..2f8a7a0 100644 --- a/lib/api_shop_shelf.js +++ b/lib/api_shop_shelf.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -115,7 +117,7 @@ exports.createShelf = function (shelf, callback) { * 增加货架的未封装版本 */ exports._createShelf = function (shelf, callback) { - var url = this.merchantPrefix + 'shelf/add?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/shelf/add?access_token=' + this.token.accessToken; this.request(url, postJSON(shelf), wrapper(callback)); }; @@ -150,9 +152,9 @@ exports.deleteShelf = function (shelfId, callback) { */ exports._deleteShelf = function (shelfId, callback) { var data = { - "shelf_id": shelfId + 'shelf_id': shelfId }; - var url = this.merchantPrefix + 'shelf/del?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/shelf/del?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; @@ -195,7 +197,7 @@ exports.updateShelf = function (shelf, callback) { * 修改货架的未封装版本 */ exports._updateShelf = function (shelf, callback) { - var url = this.merchantPrefix + 'shelf/mod?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/shelf/mod?access_token=' + this.token.accessToken; this.request(url, postJSON(shelf), wrapper(callback)); }; @@ -277,7 +279,7 @@ exports.getAllShelves = function (callback) { * 获取所有货架的未封装版本 */ exports._getAllShelves = function (callback) { - var url = this.merchantPrefix + 'shelf/getall?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/shelf/getall?access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }; @@ -309,17 +311,17 @@ exports._getAllShelves = function (callback) { * @param {String} shelfId 货架Id * @param {Function} callback 回调函数 */ - exports.getShelfById = function (shelfId, callback) { - this.preRequest(this._getShelfById, arguments); - }; +exports.getShelfById = function (shelfId, callback) { + this.preRequest(this._getShelfById, arguments); +}; /*! * 根据货架ID获取货架信息的未封装版本 */ exports._getShelfById = function (shelfId, callback) { var data = { - "shelf_id": shelfId + 'shelf_id': shelfId }; - var url = this.merchantPrefix + 'shelf/getbyid?access_token=' + this.token.accessToken; + var url = this.endpoint + '/merchant/shelf/getbyid?access_token=' + this.token.accessToken; this.request(url, postJSON(data), wrapper(callback)); }; diff --git a/lib/api_shop_stock.js b/lib/api_shop_stock.js index 483c023..c6491c2 100644 --- a/lib/api_shop_stock.js +++ b/lib/api_shop_stock.js @@ -1,3 +1,5 @@ +'use strict'; + // 库存管理接口 var util = require('./util'); var wrapper = util.wrapper; @@ -38,14 +40,14 @@ exports.updateStock = function (number, productId, sku, callback) { exports._updateStock = function (number, productId, sku, callback) { var url; if (number > 0) { - url = this.merchantPrefix + 'stock/add?access_token=' + this.token.accessToken; + url = this.endpoint + '/merchant/stock/add?access_token=' + this.token.accessToken; } else { - url = this.merchantPrefix + 'stock/reduce?access_token=' + this.token.accessToken; + url = this.endpoint + '/merchant/stock/reduce?access_token=' + this.token.accessToken; } var data = { - "product_id": productId, - "sku_info": sku, - "quantity": Math.abs(number) + 'product_id': productId, + 'sku_info': sku, + 'quantity': Math.abs(number) }; this.request(url, postJSON(data), wrapper(callback)); }; diff --git a/lib/api_tag.js b/lib/api_tag.js new file mode 100644 index 0000000..11c6fc7 --- /dev/null +++ b/lib/api_tag.js @@ -0,0 +1,325 @@ +'use strict'; + +var util = require('./util'); +var wrapper = util.wrapper; +var postJSON = util.postJSON; +/** + * 创建标签 + * 详情请见: + * Examples: + * ``` + * api.createTag(callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * { + * "tag": [ + * "id": 134, // 标签id + * "name": "广东" + * ] + * } + * ``` + * @param {String} name tag name + * @param {Function} callback 回调函数 + */ +exports.createTag = function (name, callback) { + this.preRequest(this._createTag, arguments); +}; + +/*! + * 创建标签的未封装版本 + */ +exports._createTag = function (name, callback) { + // https://api.weixin.qq.com/cgi-bin/tags/create?access_token=ACCESS_TOKEN + var url = this.endpoint + '/cgi-bin/tags/create?access_token=' + this.token.accessToken; + var data = { + 'tag': { + 'name': name + } + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 获取公众号已创建的标签 + * 详情请见: + * Examples: + * ``` + * api.getTags(callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * { + * "tags":[{ + * 'id':1, + * 'name': '黑名单', + * 'count': 0 // 此标签下粉丝数 + * },{ + * 'id':2, + * 'name': '星标组', + * 'count':0 + * }] + * } + * ``` + * @param {String} openid Open ID + * @param {Function} callback 回调函数 + */ +exports.getTags = function (callback) { + this.preRequest(this._getTags, arguments); +}; + +/*! + * 获取公众号已创建的标签的未封装版本 + */ +exports._getTags = function (callback) { + // https://api.weixin.qq.com/cgi-bin/tags/get?access_token=ACCESS_TOKEN + var url = this.endpoint + '/cgi-bin/tags/get?access_token=' + this.token.accessToken; + this.request(url, {dataType: 'json'}, wrapper(callback)); +}; + +/** + * 编辑标签 + * 详情请见: + * Examples: + * ``` + * api.editTag(id, name, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * {"errcode": 0, "errmsg": "ok"}} + * ``` + * @param {Number} id 标签ID + * @param {String} name 标签新名字 + * @param {Function} callback 回调函数 + */ +exports.editTag = function (id, name, callback) { + this.preRequest(this._editTag, arguments); +}; + +/*! + * 编辑标签的未封装版本 + */ +exports._editTag = function (id, name, callback) { + // https://api.weixin.qq.com/cgi-bin/tags/update?access_token=ACCESS_TOKEN + // POST数据格式:json + // POST数据例子:{"tag":{"id":134, "name":"test"}} + var url = this.endpoint + '/cgi-bin/tags/update?access_token=' + this.token.accessToken; + var data = { + 'tag': { + 'id': id, + 'name': name + } + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 删除标签 + * 详情请见: + * Examples: + * ``` + * api.deleteTag(id, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * {"errcode": 0, "errmsg": "ok"} + * ``` + * @param {Number} id 标签ID + * @param {Function} callback 回调函数 + */ +exports.deleteTag = function (id, callback) { + this.preRequest(this._deleteTag, arguments); +}; + +/*! + * 删除标签的未封装版本 + */ +exports._deleteTag = function (id, callback) { + // http请求方式: POST(请使用https协议) + // https://api.weixin.qq.com/cgi-bin/tags/delete?access_token=ACCESS_TOKEN + // POST数据格式:json + // POST数据例子:{"tag":{"id":108}} + var url = this.endpoint + '/cgi-bin/tags/delete?access_token=' + this.token.accessToken; + var data = { + 'tag': {'id': id} + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 获取标签下粉丝列表 + * 详情请见: + * Examples: + * ``` + * api.getTagUsers(tagid, openid, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * { + * "count": 2, + * "data":{ + * "openid":[ + * ... + * ] + * }, + * "next_openid": "..." + * } + * ``` + * @param {Number} tagId 标签ID + * @param {String} openid 分页起始openid + * @param {Function} callback 回调函数 + */ +exports.getTagUsers = function (tagId, openid, callback) { + this.preRequest(this._getTagUsers, arguments); +}; + +/*! + * 获取标签下粉丝列表的未封装版本 + */ +exports._getTagUsers = function (tagId, openid, callback) { + // http请求方式: POST(请使用https协议) + // https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=ACCESS_TOKEN + // POST数据格式:json + // POST数据例子:{"tagid":108, "next_openid":"oDF3iYx0ro3_7jD4HFRDfrjdCM58"} + var url = this.endpoint + '/cgi-bin/user/tag/get?access_token=' + this.token.accessToken; + var data = { + 'tagid': tagId, + 'next_openid': openid + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 批量为用户打标签 + * 详情请见: + * Examples: + * ``` + * api.memberBatchtagging(tagId, openList, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * {"errcode": 0, "errmsg": "ok"} + * ``` + * @param {Number} tagId 标签ID + * @param {Array} openList 用户openids + * @param {Function} callback 回调函数 + */ +exports.membersBatchtagging = function (tagId, openList, callback) { + this.preRequest(this._membersBatchtagging, arguments); +}; + +/*! + * 批量为用户打标签的未封装版本 + */ +exports._membersBatchtagging = function (tagId, openList, callback) { + // https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token=ACCESS_TOKEN + var url = this.endpoint + '/cgi-bin/tags/members/batchtagging?access_token=' + this.token.accessToken; + var data = { + 'openid_list':openList, + 'tagid': tagId + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 批量为用户取消标签 + * 详情请见: + * Examples: + * ``` + * api.memberBatchuntagging(tagId, openList, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * {"errcode": 0, "errmsg": "ok"} + * ``` + * @param {Number} tagId 标签ID + * @param {Array} openList 用户openids + * @param {Function} callback 回调函数 + */ +exports.membersBatchuntagging = function (tagId, openList, callback) { + this.preRequest(this._membersBatchuntagging, arguments); +}; + +/*! + * 批量为用户取消标签的未封装版本 + */ +exports._membersBatchuntagging = function (tagId, openList, callback) { + // https://api.weixin.qq.com/cgi-bin/tags/members/batchuntagging?access_token=ACCESS_TOKEN + var url = this.endpoint + '/cgi-bin/tags/members/batchuntagging?access_token=' + this.token.accessToken; + var data = { + 'openid_list':openList, + 'tagid': tagId + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 获取用户身上的标签列表 + * 详情请见: + * Examples: + * ``` + * api.getUserTags(openid, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * {"tagid_list": [134, 2]} + * ``` + * @param {openid} 用户openid + * @param {Function} callback 回调函数 + */ +exports.getUserTags = function (openid, callback) { + this.preRequest(this._getUserTags, arguments); +}; + +/*! + * 获取用户身上的标签列表的未封装版本 + */ +exports._getUserTags = function (openid, callback) { + // https://api.weixin.qq.com/cgi-bin/tags/getidlist?access_token=ACCESS_TOKEN + var url = this.endpoint + '/cgi-bin/tags/getidlist?access_token=' + this.token.accessToken; + var data = { + 'openid':openid + }; + this.request(url, postJSON(data), wrapper(callback)); +}; + diff --git a/lib/api_template.js b/lib/api_template.js index 7c86690..3dcd9e4 100644 --- a/lib/api_template.js +++ b/lib/api_template.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -24,10 +26,40 @@ exports.setIndustry = function(industryIds, callback){ }; exports._setIndustry = function (industryIds, callback) { - var apiUrl = this.prefix + 'template/api_set_industry?access_token=' + this.token.accessToken; + var apiUrl = this.endpoint + '/cgi-bin/template/api_set_industry?access_token=' + this.token.accessToken; this.request(apiUrl, postJSON(industryIds), wrapper(callback)); }; +/** + * 获取设置的行业信息 + * Examples: + * ``` + * api.getIndustry(callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * // 结果示例 + * { + * "primary_industry":{"first_class":"运输与仓储","second_class":"快递"}, + * "secondary_industry":{"first_class":"IT科技","second_class":"互联网|电子商务"} + * } + * ``` + * @param {Function} callback 回调函数 + */ +exports.getIndustry = function(callback){ + this.preRequest(this._getIndustry, arguments); +}; + +exports._getIndustry = function (callback) { + var apiUrl = this.endpoint + '/cgi-bin/template/get_industry?access_token=' + this.token.accessToken; + this.request(apiUrl, {dataType: 'json'}, wrapper(callback)); +}; + /** * 获得模板ID * Examples: @@ -47,13 +79,75 @@ exports.addTemplate = function(templateIdShort, callback){ }; exports._addTemplate = function (templateIdShort, callback) { - var apiUrl = this.prefix + 'template/api_add_template?access_token=' + this.token.accessToken; + var apiUrl = this.endpoint + '/cgi-bin/template/api_add_template?access_token=' + this.token.accessToken; var templateId = { template_id_short: templateIdShort }; this.request(apiUrl, postJSON(templateId), wrapper(callback)); }; +/** + * 获取模板列表 + * Examples: + * ``` + * api.getAllPrivateTemplate(callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * Result: + * ``` + * // 结果示例 + * { + * "template_list": [{ + * "template_id": "iPk5sOIt5X_flOVKn5GrTFpncEYTojx6ddbt8WYoV5s", + * "title": "领取奖金提醒", + * "primary_industry": "IT科技", + * "deputy_industry": "互联网|电子商务", + * "content": "{ {result.DATA} }\n\n领奖金额:{ {withdrawMoney.DATA} }\n领奖 时间:{ {withdrawTime.DATA} }\n银行信息:{ {cardInfo.DATA} }\n到账时间: { {arrivedTime.DATA} }\n{ {remark.DATA} }", + * "example": "您已提交领奖申请\n\n领奖金额:xxxx元\n领奖时间:2013-10-10 12:22:22\n银行信息:xx银行(尾号xxxx)\n到账时间:预计xxxxxxx\n\n预计将于xxxx到达您的银行卡" + * }] + * } + * ``` + * @param {Function} callback 回调函数 + */ +exports.getAllPrivateTemplate = function(callback){ + this.preRequest(this._getAllPrivateTemplate, arguments); +}; + +exports._getAllPrivateTemplate = function (callback) { + var apiUrl = this.endpoint + '/cgi-bin/template/get_all_private_template?access_token=' + this.token.accessToken; + this.request(apiUrl, {dataType: 'json'}, wrapper(callback)); +}; + +/** + * 删除模板 + * Examples: + * ``` + * var templateId = ”Dyvp3-Ff0cnail_CDSzk1fIc6-9lOkxsQE7exTJbwUE” + * api.delPrivateTemplate(templateId, callback); + * ``` + * Callback: + * + * - `err`, 调用失败时得到的异常 + * - `result`, 调用正常时得到的对象 + * + * @param {String} templateId 公众帐号下模板消息ID + */ +exports.delPrivateTemplate = function(templateId, callback){ + this.preRequest(this._delPrivateTemplate, arguments); +}; + +exports._delPrivateTemplate = function (templateId, callback) { + var apiUrl = this.endpoint + '/cgi-bin/template/del_private_template?access_token=' + this.token.accessToken; + var data = { + template_id: templateId + }; + this.request(apiUrl, postJSON(data), wrapper(callback)); +}; + /** * 发送模板消息 * 详细细节: http://mp.weixin.qq.com/wiki/17/304c1885ea66dbedf7dc170d84999a9d.html @@ -84,7 +178,7 @@ exports._addTemplate = function (templateIdShort, callback) { * "color":"#173177" * } * }; - * api.sendTemplate('openid', templateId, url, data, callback); + * api.sendTemplate('openid', templateId, dest, data, callback); * ``` * Callback: * @@ -93,26 +187,41 @@ exports._addTemplate = function (templateIdShort, callback) { * * @param {String} openid 用户的openid * @param {String} templateId 模板ID - * @param {String} url URL置空,则在发送后,点击模板消息会进入一个空白页面(ios),或无法点击(android) + * @param {Object} dest 跳转目的地置空,则在发送后,点击模板消息会进入一个空白页面(ios),或无法点击(android) * @param {Object} data 渲染模板的数据 * @param {Function} callback 回调函数 */ -exports.sendTemplate = function (openid, templateId, url, topColor, data, callback) { +exports.sendTemplate = function (openid, templateId, dest, data, callback, callback2) { this.preRequest(this._sendTemplate, arguments); }; -exports._sendTemplate = function (openid, templateId, url, topColor, data, callback) { - var apiUrl = this.prefix + 'message/template/send?access_token=' + this.token.accessToken; +exports._sendTemplate = function (openid, templateId, dest, data, callback, callback2) { + /* + * duplicated interface `function (openid, templateId, url, topColor, data, callback)` + */ + var apiUrl = this.endpoint + '/cgi-bin/message/template/send?access_token=' + this.token.accessToken; + + if (typeof data === 'string') { + data = callback; + callback = callback2; + } - if (typeof data === 'function') { - callback = data; - data = topColor; + var url, miniprogram; + if (typeof dest === 'string') { + url = dest; + } else { + if (!dest) { + dest = {}; + } + url = dest.url; + miniprogram = dest.miniprogram; } var template = { touser: openid, template_id: templateId, url: url, + miniprogram: miniprogram, data: data }; this.request(apiUrl, postJSON(template), wrapper(callback)); diff --git a/lib/api_url.js b/lib/api_url.js index c5fce86..3115846 100644 --- a/lib/api_url.js +++ b/lib/api_url.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -26,10 +28,10 @@ exports.shorturl = function (longUrl, callback) { */ exports._shorturl = function (longUrl, callback) { // https://api.weixin.qq.com/cgi-bin/shorturl?access_token=ACCESS_TOKEN - var url = this.prefix + 'shorturl?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/shorturl?access_token=' + this.token.accessToken; var data = { - "action": "long2short", - "long_url": longUrl + 'action': 'long2short', + 'long_url': longUrl }; this.request(url, postJSON(data), wrapper(callback)); }; diff --git a/lib/api_user.js b/lib/api_user.js index 3a3a097..f398e06 100644 --- a/lib/api_user.js +++ b/lib/api_user.js @@ -1,3 +1,5 @@ +'use strict'; + var util = require('./util'); var wrapper = util.wrapper; var postJSON = util.postJSON; @@ -48,7 +50,7 @@ exports._getUser = function (options, callback) { }; } // https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID - var url = this.prefix + 'user/info?openid=' + options.openid + '&lang=' + options.lang + '&access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/user/info?openid=' + options.openid + '&lang=' + options.lang + '&access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }; @@ -56,7 +58,7 @@ exports._getUser = function (options, callback) { * 批量获取用户基本信息 * Example: * ``` - * api.batchGetUser(['openid1', 'openid2'], callback) + * api.batchGetUsers(['openid1', 'openid2'], callback) * ``` * Callback: * @@ -98,11 +100,11 @@ exports.batchGetUsers = function (openids, callback) { * 批量获取用户基本信息的未封装版本 */ exports._batchGetUsers = function (openids, callback) { - var url = this.prefix + 'user/info/batchget?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/user/info/batchget?access_token=' + this.token.accessToken; var data = {}; data.user_list = openids.map(function (openid) { - return {openid: openid, language: "zh-CN"}; - }) + return {openid: openid, language: 'zh-CN'}; + }); this.request(url, postJSON(data), wrapper(callback)); }; @@ -147,7 +149,7 @@ exports._getFollowers = function (nextOpenid, callback) { callback = nextOpenid; nextOpenid = ''; } - var url = this.prefix + 'user/get?next_openid=' + nextOpenid + '&access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/user/get?next_openid=' + nextOpenid + '&access_token=' + this.token.accessToken; this.request(url, {dataType: 'json'}, wrapper(callback)); }; @@ -183,7 +185,7 @@ exports.updateRemark = function (openid, remark, callback) { */ exports._updateRemark = function (openid, remark, callback) { // https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token=ACCESS_TOKEN - var url = this.prefix + 'user/info/updateremark?access_token=' + this.token.accessToken; + var url = this.endpoint + '/cgi-bin/user/info/updateremark?access_token=' + this.token.accessToken; var data = { openid: openid, remark: remark diff --git a/lib/util.js b/lib/util.js index 9cf12e0..cd16d1d 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,3 +1,5 @@ +'use strict'; + /*! * 对返回结果的一层封装,如果遇见微信返回的错误,将返回一个错误 * 参见:http://mp.weixin.qq.com/wiki/17/fa4e1434e57290788bde25603fa2fcbd.html @@ -15,6 +17,12 @@ exports.wrapper = function (callback) { err.code = data.errcode; return callback(err, data, res); } + if (data == null) { + err = new Error('No data received.'); + err.name = 'WeChatAPIError'; + err.code = -1; + return callback(err, data, res); + } callback(null, data, res); }; }; diff --git a/package.json b/package.json index ea62138..4bd6328 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wechat-api", - "version": "1.23.0", + "version": "1.35.1", "description": "微信公共平台Node库 API", "main": "index.js", "scripts": { @@ -20,8 +20,8 @@ "wechat" ], "dependencies": { - "formstream": ">=0.0.8", - "urllib": "2.6.0" + "formstream": ">=1.0.0", + "urllib": "2.21.0" }, "devDependencies": { "mocha": "*", @@ -38,5 +38,9 @@ "readmeFilename": "README.md", "directories": { "test": "test" - } + }, + "files": [ + "lib", + "index.js" + ] } diff --git a/test/api_card.test.js b/test/api_card.test.js new file mode 100644 index 0000000..8fbd468 --- /dev/null +++ b/test/api_card.test.js @@ -0,0 +1,86 @@ +'use strict'; + +var config = require('./config'); +var API = require('../'); +var expect = require('expect.js'); + +describe('api_card', function () { + var api = new API(config.appid, config.appsecret); + + describe('getApplyProtocol', function () { + it('should ok', function (done) { + api.getApplyProtocol(function (err, data, res) { + expect(err).to.not.be.ok(); + expect(data).to.have.property('category'); + expect(data).to.have.property('errcode', 0); + expect(data).to.have.property('errmsg', 'ok'); + done(); + }); + }); + }); + + var options = { + 'brand_name': 'aaaaaa', + // "app_id":"xxxxxxxxxxx", + 'logo_url': 'http://mmbiz.qpic.cn/mmbiz/O1DymY4NpO88CjYk0XWw9VAW99RMibqchv2OVDOibPpmMu65H47usx4fjyRwvRaZwCccibCiccMgwPk9unibewSQfjw/0?wx_fmt=jpeg', + 'protocol': 'xxxxxxxxxx', + 'end_time': Math.round(Date.now()/1000+3600*24*365), + 'primary_category_id': 1, + 'secondary_category_id': 101 + }; + + describe('submerchant', function(){ + before(function(done){ + api.uploadMedia('./test/fixture/image.jpg', 'image', function (err, data, res) { + options.protocol = data.media_id; + done(err); + }); + }); + + describe('submit', function(){ + it('should ok', function(done){ + api.submitSubmerchant(options, function (err, data, res) { + expect(err).to.not.be.ok(); + expect(data).to.have.property('info'); + expect(data.info).to.have.property('merchant_id'); + done(); + }); + }); + }); + + describe('update', function(){ + it('should ok', function(done){ + options.merchant_id = options.merchant_id||408592871; + api.updateSubmerchant(options, function (err, data, res) { + expect(err).to.not.be.ok(); + expect(data).to.have.property('info'); + expect(data.info).to.have.property('merchant_id', options.merchant_id); + done(); + }); + }); + }); + + describe('get', function(){ + it('should ok', function(done){ + options.merchant_id = options.merchant_id||408592871; + api.getSubmerchant(options.merchant_id, function (err, data, res) { + expect(err).to.not.be.ok(); + expect(data).to.have.property('info'); + expect(data.info).to.have.property('merchant_id', options.merchant_id); + done(); + }); + }); + }); + + describe('batchget', function(){ + it('should ok', function(done){ + api.batchgetSubmerchant({'begin_id': 0,'limit': 50}, function (err, data) { + expect(err).to.not.be.ok(); + expect(data).to.have.property('info_list'); + expect(data.info_list[0]).to.have.property('merchant_id', options.merchant_id); + done(); + }); + }); + }); + }); +}); diff --git a/test/api_common.test.js b/test/api_common.test.js index d10086e..e62f2c2 100644 --- a/test/api_common.test.js +++ b/test/api_common.test.js @@ -1,3 +1,5 @@ +'use strict'; + var API = require('../'); var urllib = require('urllib'); var muk = require('muk'); @@ -77,7 +79,7 @@ describe('api_common', function () { before(function () { muk(urllib, 'request', function (url, args, callback) { process.nextTick(function () { - callback(null, {"access_token": "ACCESS_TOKEN","expires_in": 7200}); + callback(null, {'access_token': 'ACCESS_TOKEN','expires_in': 7200}); }); }); }); @@ -123,7 +125,7 @@ describe('api_common', function () { it('should ok', function (done) { var api = new API(config.appid, config.appsecret); api.preRequest(function (callback) { - callback(); + return callback(); }, [function (err) { expect(err).not.to.be.ok(); done(); @@ -145,7 +147,7 @@ describe('api_common', function () { it('should not ok', function (done) { api.preRequest(function (callback) { - callback(); + return callback(); }, [function (err) { expect(err).to.be.ok(); expect(err).have.property('message', 'mock getToken error'); @@ -220,10 +222,10 @@ describe('api_common', function () { api.preRequest(function (callback) { i++; if (i === 1) { - callback(null, {errcode: 40001}); - } else { - callback(null, {errcode: 0}); - } + return callback(null, {errcode: 40001}); + } + return callback(null, {errcode: 0}); + }, [function (err) { expect(err).not.to.be.ok(); done(); diff --git a/test/api_custom_service.test.js b/test/api_custom_service.test.js index 7bcce8c..009f9ee 100644 --- a/test/api_custom_service.test.js +++ b/test/api_custom_service.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var muk = require('muk'); @@ -11,7 +13,7 @@ describe('api_custom_service', function () { describe('getRecords mock', function () { before(function () { muk(urllib, 'request', function (url, args, callback) { - var data = {"recordlist": []}; + var data = {'recordlist': []}; var res = { headers: { 'content-type': 'application/json' @@ -29,11 +31,10 @@ describe('api_custom_service', function () { it('getRecords should ok', function (done) { var condition = { - "starttime" : 123456789, - "endtime" : 987654321, - // "openid" : "OPENID", - "pagesize" : 10, - "pageindex" : 1, + 'starttime' : 123456789, + 'endtime' : 987654321, + 'number' : 10000, + 'msgid' : 1 }; api.getRecords(condition, function (err, data, res) { diff --git a/test/api_customer.test.js b/test/api_customer.test.js index 81b2993..44d5e2c 100644 --- a/test/api_customer.test.js +++ b/test/api_customer.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); @@ -7,7 +9,6 @@ var puling = 'ofL4cs7hr04cJIcu600_W-ZwwxHg'; var imageId = 'XDZxzuRWBPqI4R9n_nNR5uRVZVQCSneMoELyWKflwM2qF9K38vnVFzgaD97uCTUu'; var voiceId = '9R5BhAum7AEaGhwku0WhgvtO4C_7Xs78NoiRvm6v7IyoTljE4HH5o8E_UfnPrL0p'; var thumbId = 'BHxGDVy7WY6BCOcv3AwbywUE630Vw0tAV_V8bzBaCZid4Km5fwXrVOso3X0zas4n'; -var movieId = 'b4F8SfaZZQwalDxwPjd923ACV5IUeYvZ9-dYKf5ytXrS-IImXEkl2U8Fl5EH-jCF'; var cardId = 'pAtUNs-HV0evhGTWbU3ohp99tW7k'; describe('api_customer', function () { @@ -15,7 +16,7 @@ describe('api_customer', function () { var mockError = function () { before(function () { muk(urllib, 'request', function (url, args, callback) { - var data = {"errcode":1, "errmsg":"mock error"}; + var data = {'errcode':1, 'errmsg':'mock error'}; var res = { headers: { 'content-type': 'application/json' @@ -157,8 +158,8 @@ describe('api_customer', function () { title: '音乐标题', // 可选 description: '描述内容', // 可选 musicurl: 'http://url.cn/xxx', // 音乐文件地址 - hqmusicurl: "HQ_MUSIC_URL", - thumb_media_id: "THUMB_MEDIA_ID" + hqmusicurl: 'HQ_MUSIC_URL', + thumb_media_id: 'THUMB_MEDIA_ID' }; it('sendMusic should ok', function (done) { @@ -193,16 +194,16 @@ describe('api_customer', function () { describe('sendNews', function () { var articles = [ { - "title":"Happy Day", - "description":"Is Really A Happy Day", - "url":"URL", - "picurl":"PIC_URL" + 'title':'Happy Day', + 'description':'Is Really A Happy Day', + 'url':'URL', + 'picurl':'PIC_URL' }, { - "title":"Happy Day", - "description":"Is Really A Happy Day", - "url":"URL", - "picurl":"PIC_URL" + 'title':'Happy Day', + 'description':'Is Really A Happy Day', + 'url':'URL', + 'picurl':'PIC_URL' } ]; @@ -234,13 +235,13 @@ describe('api_customer', function () { }); }); }); - + describe.skip('sendCard', function() { var card = { // code:'12345', //option // openid: puling, //option card_id: cardId - } + }; it('sendCard should ok', function(done) { api.sendCard(puling, card, function (err, data, res) { if (!err) { diff --git a/test/api_feedback.test.js b/test/api_feedback.test.js index 8eec6b1..339bd08 100644 --- a/test/api_feedback.test.js +++ b/test/api_feedback.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); diff --git a/test/api_group.test.js b/test/api_group.test.js index a365e49..90cd697 100644 --- a/test/api_group.test.js +++ b/test/api_group.test.js @@ -1,3 +1,5 @@ +'use strict'; + var expect = require('expect.js'); var config = require('./config'); var API = require('../'); diff --git a/test/api_ip.test.js b/test/api_ip.test.js index 88e40f5..5792362 100644 --- a/test/api_ip.test.js +++ b/test/api_ip.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); diff --git a/test/api_mass_send.test.js b/test/api_mass_send.test.js index 051ef74..ce97c69 100644 --- a/test/api_mass_send.test.js +++ b/test/api_mass_send.test.js @@ -1,3 +1,5 @@ +'use strict'; + var expect = require('expect.js'); var urllib = require('urllib'); var muk = require('muk'); @@ -19,15 +21,15 @@ describe('api_mass_send.js', function () { describe('_uploadNews', function () { var news = { - "articles": [ + 'articles': [ { - "thumb_media_id": imageId, - "author":"xxx", - "title":"Happy Day", - "content_source_url":"www.qq.com", - "content":"content", - "digest":"digest", - "show_cover_pic":"1" + 'thumb_media_id': imageId, + 'author':'xxx', + 'title':'Happy Day', + 'content_source_url':'www.qq.com', + 'content':'content', + 'digest':'digest', + 'show_cover_pic':'1' } ] }; @@ -81,9 +83,9 @@ describe('api_mass_send.js', function () { before(function () { muk(urllib, 'request', function (url, opts, callback) { var data = { - "type":"news", - "media_id":"CsEf3ldqkAYJAU6EJeIkStVDSvffUJ54vqbThMgplD-VJXXof6ctX5fI6-aYyUiQ", - "created_at":1391857799 + 'type':'news', + 'media_id':'CsEf3ldqkAYJAU6EJeIkStVDSvffUJ54vqbThMgplD-VJXXof6ctX5fI6-aYyUiQ', + 'created_at':1391857799 }; process.nextTick(function () { callback(null, data); @@ -109,9 +111,9 @@ describe('api_mass_send.js', function () { before(function () { muk(api, 'massSend', function (opts, receivers, callback) { var data = { - "errcode": 0, - "errmsg": "send job submission success", - "msg_id": 34182 + 'errcode': 0, + 'errmsg': 'send job submission success', + 'msg_id': 34182 }; var res = { headers: { @@ -223,9 +225,9 @@ describe('api_mass_send.js', function () { it('should ok', function (done) { var opts = { - "media_id": movieId, - "title": "TITLE", - "description": "Description" + 'media_id': movieId, + 'title': 'TITLE', + 'description': 'Description' }; api.massSendMPVideo(opts, [puling], function (err, data) { expect(err).to.be.ok(); @@ -240,9 +242,9 @@ describe('api_mass_send.js', function () { muk(api, 'uploadMPVideo', function (opts, callback) { process.nextTick(function () { callback(null, { - "media_id": "rF4UdIMfYK3efUfyoddYRMU50zMiRmmt_l0kszupYh_SzrcW5Gaheq05p_lHuOTQ", - "title": "TITLE", - "description": "Description" + 'media_id': 'rF4UdIMfYK3efUfyoddYRMU50zMiRmmt_l0kszupYh_SzrcW5Gaheq05p_lHuOTQ', + 'title': 'TITLE', + 'description': 'Description' }); }); }); @@ -253,9 +255,9 @@ describe('api_mass_send.js', function () { }); it('send to openids should ok', function (done) { var opts = { - "media_id": "rF4UdIMfYK3efUfyoddYRMU50zMiRmmt_l0kszupYh_SzrcW5Gaheq05p_lHuOTQ", - "title": "TITLE", - "description": "Description" + 'media_id': 'rF4UdIMfYK3efUfyoddYRMU50zMiRmmt_l0kszupYh_SzrcW5Gaheq05p_lHuOTQ', + 'title': 'TITLE', + 'description': 'Description' }; api.massSendMPVideo(opts, [puling], function (err, data) { expect(err).to.be.ok(); @@ -301,9 +303,9 @@ describe('api_mass_send.js', function () { it('should ok', function (done) { var opts = { - "media_id": "rF4UdIMfYK3efUfyoddYRMU50zMiRmmt_l0kszupYh_SzrcW5Gaheq05p_lHuOTQ", - "title": "TITLE", - "description": "Description" + 'media_id': 'rF4UdIMfYK3efUfyoddYRMU50zMiRmmt_l0kszupYh_SzrcW5Gaheq05p_lHuOTQ', + 'title': 'TITLE', + 'description': 'Description' }; api.uploadMPVideo(opts, function (err, data) { expect(err).to.be.ok(); diff --git a/test/api_media.test.js b/test/api_media.test.js index 1716083..4822e2b 100644 --- a/test/api_media.test.js +++ b/test/api_media.test.js @@ -1,8 +1,11 @@ +'use strict'; + var expect = require('expect.js'); var urllib = require('urllib'); var muk = require('muk'); var path = require('path'); var API = require('../'); +var fs = require('fs'); describe('api_media.js', function () { var api = new API('invalidappid', 'secret'); @@ -17,14 +20,16 @@ describe('api_media.js', function () { api.isAccessTokenValid = isAccessTokenValid; }); - describe('upload media', function () { + describe.only('upload media', function () { + var req = fs.createReadStream(path.join(__dirname, './fixture/pic.jpg')); + req.headers = {}; ['Image', 'Voice', 'Video', 'Thumb'].forEach(function (method) { before(function () { muk(urllib, 'request', function (url, args, callback) { var resp = { - "type":"image", - "media_id":"usr5xL_gcxapoRjwH3bQZw_zKvcXL-DU4tRJtLtrtN71-3bXL52p3xX63ebp7tqA", - "created_at":1383233542 + 'type':'image', + 'media_id':'usr5xL_gcxapoRjwH3bQZw_zKvcXL-DU4tRJtLtrtN71-3bXL52p3xX63ebp7tqA', + 'created_at':1383233542 }; process.nextTick(function () { callback(null, resp); @@ -46,6 +51,17 @@ describe('api_media.js', function () { }); }); + it('upload' + method + 'Stream should ok', function (done) { + req.headers.type = method; + api['upload' + method + 'Stream'](req, function (err, data, res) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('type', 'image'); + expect(data).to.have.property('media_id'); + expect(data).to.have.property('created_at'); + done(); + }); + }); + it('upload' + method + ' should not ok', function (done) { api['upload' + method](path.join(__dirname, './fixture/inexist.jpg'), function (err, data, res) { expect(err).to.be.ok(); @@ -88,7 +104,7 @@ describe('api_media.js', function () { describe('get media with json', function () { before(function () { muk(urllib, 'request', function (url, args, callback) { - var data = JSON.stringify({"errcode":40007, "errmsg":"invalid media_id"}); + var data = JSON.stringify({'errcode':40007, 'errmsg':'invalid media_id'}); var res = { headers: { 'content-type': 'application/json' @@ -139,4 +155,31 @@ describe('api_media.js', function () { }); }); }); + + describe('upload image', function(){ + before(function () { + muk(urllib, 'request', function (url, args, callback) { + var resp = { + 'url': 'http://mmbiz.qpic.cn/mmbiz/gLO17UPS6FS2xsypf378iaNhWacZ1G1UplZYWEYfwvuU6Ont96b1roYsCNFwaRrSaKTPCUdBK9DgEHicsKwWCBRQ/0' + }; + process.nextTick(function () { + callback(null, resp); + }); + }); + }); + + after(function () { + muk.restore(); + }); + + it('should ok from upstream', function(done){ + var req = fs.createReadStream(path.join(__dirname, './fixture/image.jpg')); + req.headers = {}; + api.uploadImageStream(req, function (err, data, res) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('url'); + done(); + }); + }); + }); }); diff --git a/test/api_menu.test.js b/test/api_menu.test.js index 89c2932..13ccef8 100644 --- a/test/api_menu.test.js +++ b/test/api_menu.test.js @@ -1,3 +1,5 @@ +'use strict'; + var expect = require('expect.js'); var config = require('./config'); var API = require('../'); diff --git a/test/api_payment.test.js b/test/api_payment.test.js index da02160..e496fac 100644 --- a/test/api_payment.test.js +++ b/test/api_payment.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); diff --git a/test/api_poi.test.js b/test/api_poi.test.js new file mode 100644 index 0000000..3081ba9 --- /dev/null +++ b/test/api_poi.test.js @@ -0,0 +1,21 @@ +'use strict'; + +var config = require('./config'); +var API = require('../'); +var expect = require('expect.js'); + +describe('api_poi', function(){ + + var api = new API(config.appid, config.appsecret); + + describe('get category', function(){ + it('should ok', function(done){ + api.getWXCategory(function (err, data, res) { + expect(err).to.not.be.ok(); + expect(data).to.have.property('category_list'); + expect(data.category_list.length).to.have.above(0); + done(); + }); + }); + }); +}); diff --git a/test/api_qrcode.test.js b/test/api_qrcode.test.js index f6f6a16..24f7ad1 100644 --- a/test/api_qrcode.test.js +++ b/test/api_qrcode.test.js @@ -1,3 +1,5 @@ +'use strict'; + var expect = require('expect.js'); var config = require('./config'); var API = require('../'); diff --git a/test/api_semantic.test.js b/test/api_semantic.test.js index ed453ae..144c138 100644 --- a/test/api_semantic.test.js +++ b/test/api_semantic.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); @@ -6,12 +8,12 @@ describe('api_semantic', function () { var api = new API(config.appid, config.appsecret); it('should ok', function (done) { var condition = { - "query":"查一下明天从北京到上海的南航机票", - "city":"北京", - "category": "flight,hotel" + 'query':'查一下明天从北京到上海的南航机票', + 'city':'北京', + 'category': 'flight,hotel' }; - var uid = "123456"; + var uid = '123456'; api.semantic(uid, condition, function (err, data, res) { expect(err).to.be.ok(); diff --git a/test/api_shop_common.test.js b/test/api_shop_common.test.js index f4a0855..ab625ed 100644 --- a/test/api_shop_common.test.js +++ b/test/api_shop_common.test.js @@ -1,3 +1,5 @@ +'use strict'; + var path = require('path'); var config = require('./config'); var API = require('../'); diff --git a/test/api_shop_express.test.js b/test/api_shop_express.test.js index 171e374..fa65612 100644 --- a/test/api_shop_express.test.js +++ b/test/api_shop_express.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); @@ -5,68 +7,68 @@ var expect = require('expect.js'); describe('api_shop_express', function () { var api = new API(config.appid, config.appsecret); var express = { - "delivery_template": { - "Name": "testexpress", - "Assumer": 0, - "Valuation": 0, - "TopFee": [ + 'delivery_template': { + 'Name': 'testexpress', + 'Assumer': 0, + 'Valuation': 0, + 'TopFee': [ { - "Type": 10000027, - "Normal": { - "StartStandards": 1, - "StartFees": 2, - "AddStandards": 3, - "AddFees": 1 + 'Type': 10000027, + 'Normal': { + 'StartStandards': 1, + 'StartFees': 2, + 'AddStandards': 3, + 'AddFees': 1 }, - "Custom": [ + 'Custom': [ { - "StartStandards": 1, - "StartFees": 100, - "AddStandards": 1, - "AddFees": 3, - "DestCountry": "中国", - "DestProvince": "广东省", - "DestCity": "广州市" + 'StartStandards': 1, + 'StartFees': 100, + 'AddStandards': 1, + 'AddFees': 3, + 'DestCountry': '中国', + 'DestProvince': '广东省', + 'DestCity': '广州市' } ] }, { - "Type": 10000028, - "Normal": { - "StartStandards": 1, - "StartFees": 3, - "AddStandards": 3, - "AddFees": 2 + 'Type': 10000028, + 'Normal': { + 'StartStandards': 1, + 'StartFees': 3, + 'AddStandards': 3, + 'AddFees': 2 }, - "Custom": [ + 'Custom': [ { - "StartStandards": 1, - "StartFees": 10, - "AddStandards": 1, - "AddFees": 30, - "DestCountry": "中国", - "DestProvince": "广东省", - "DestCity": "广州市" + 'StartStandards': 1, + 'StartFees': 10, + 'AddStandards': 1, + 'AddFees': 30, + 'DestCountry': '中国', + 'DestProvince': '广东省', + 'DestCity': '广州市' } ] }, { - "Type": 10000029, - "Normal": { - "StartStandards": 1, - "StartFees": 4, - "AddStandards": 3, - "AddFees": 3 + 'Type': 10000029, + 'Normal': { + 'StartStandards': 1, + 'StartFees': 4, + 'AddStandards': 3, + 'AddFees': 3 }, - "Custom": [ + 'Custom': [ { - "StartStandards": 1, - "StartFees": 8, - "AddStandards": 2, - "AddFees": 11, - "DestCountry": "中国", - "DestProvince": "广东省", - "DestCity": "广州市" + 'StartStandards': 1, + 'StartFees': 8, + 'AddStandards': 2, + 'AddFees': 11, + 'DestCountry': '中国', + 'DestProvince': '广东省', + 'DestCity': '广州市' } ] } diff --git a/test/api_shop_goods.test.js b/test/api_shop_goods.test.js index d7caace..8eba11a 100644 --- a/test/api_shop_goods.test.js +++ b/test/api_shop_goods.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); @@ -5,70 +7,70 @@ var expect = require('expect.js'); describe('api_shop_goods', function () { var api = new API(config.appid, config.appsecret); var goods = { - "product_base":{ - "category_id":[ - "537074298" + 'product_base':{ + 'category_id':[ + '537074298' ], - "property":[ - {"id":"1075741879","vid":"1079749967"}, - {"id":"1075754127","vid":"1079795198"}, - {"id":"1075777334","vid":"1079837440"} + 'property':[ + {'id':'1075741879','vid':'1079749967'}, + {'id':'1075754127','vid':'1079795198'}, + {'id':'1075777334','vid':'1079837440'} ], - "name":"testaddproduct", - "sku_info":[ + 'name':'testaddproduct', + 'sku_info':[ { - "id":"1075741873", - "vid":["1079742386","1079742363"] + 'id':'1075741873', + 'vid':['1079742386','1079742363'] } ], - "main_img": "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2iccsvYbHvnphkyGtnvjD3ulEKogfsiaua49pvLfUS8Ym0GSYjViaLic0FD3vN0V8PILcibEGb2fPfEOmw/0", - "img":[ - "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2iccsvYbHvnphkyGtnvjD3ulEKogfsiaua49pvLfUS8Ym0GSYjViaLic0FD3vN0V8PILcibEGb2fPfEOmw/0" + 'main_img': 'http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2iccsvYbHvnphkyGtnvjD3ulEKogfsiaua49pvLfUS8Ym0GSYjViaLic0FD3vN0V8PILcibEGb2fPfEOmw/0', + 'img':[ + 'http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2iccsvYbHvnphkyGtnvjD3ulEKogfsiaua49pvLfUS8Ym0GSYjViaLic0FD3vN0V8PILcibEGb2fPfEOmw/0' ], - "detail":[ - {"text":"testfirst"}, - {"img": "4whpV1VZl2iccsvYbHvnphkyGtnvjD3ul1UcLcwxrFdwTKYhH9Q5YZoCfX4Ncx655ZK6ibnlibCCErbKQtReySaVA/0"}, - {"text":"testagain"} + 'detail':[ + {'text':'testfirst'}, + {'img': '4whpV1VZl2iccsvYbHvnphkyGtnvjD3ul1UcLcwxrFdwTKYhH9Q5YZoCfX4Ncx655ZK6ibnlibCCErbKQtReySaVA/0'}, + {'text':'testagain'} ], - "buy_limit":10 + 'buy_limit':10 }, - "sku_list":[ + 'sku_list':[ { - "sku_id":"1075741873:1079742386", - "price":30, - "icon_url": "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl28bJj62XgfHPibY3ORKicN1oJ4CcoIr4BMbfA8LqyyjzOZzqrOGz3f5K Wq1QGP3fo6TOTSYD3TBQjuw/0", - "product_code":"testing", - "ori_price":9000000, - "quantity":800 + 'sku_id':'1075741873:1079742386', + 'price':30, + 'icon_url': 'http://mmbiz.qpic.cn/mmbiz/4whpV1VZl28bJj62XgfHPibY3ORKicN1oJ4CcoIr4BMbfA8LqyyjzOZzqrOGz3f5K Wq1QGP3fo6TOTSYD3TBQjuw/0', + 'product_code':'testing', + 'ori_price':9000000, + 'quantity':800 }, { - "sku_id":"1075741873:1079742363", - "price":30, - "icon_url": "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl28bJj62XgfHPibY3ORKicN1oJ4CcoIr4BMbfA8LqyyjzOZzqrOGz3f5K Wq1QGP3fo6TOTSYD3TBQjuw/0", - "product_code":"testingtesting", - "ori_price":9000000, - "quantity":800 + 'sku_id':'1075741873:1079742363', + 'price':30, + 'icon_url': 'http://mmbiz.qpic.cn/mmbiz/4whpV1VZl28bJj62XgfHPibY3ORKicN1oJ4CcoIr4BMbfA8LqyyjzOZzqrOGz3f5K Wq1QGP3fo6TOTSYD3TBQjuw/0', + 'product_code':'testingtesting', + 'ori_price':9000000, + 'quantity':800 } ], - "attrext":{ - "location":{ - "country":"中国", - "province":"广东省", - "city":"广州市", - "address":"T.I.T创意园" + 'attrext':{ + 'location':{ + 'country':'中国', + 'province':'广东省', + 'city':'广州市', + 'address':'T.I.T创意园' }, - "isPostFree":0, - "isHasReceipt":1, - "isUnderGuaranty":0, - "isSupportReplace":0 + 'isPostFree':0, + 'isHasReceipt':1, + 'isUnderGuaranty':0, + 'isSupportReplace':0 }, - "delivery_info":{ - "delivery_type":0, - "template_id":0, - "express":[ - {"id":10000027,"price":100}, - {"id":10000028,"price":100}, - {"id":10000029,"price":100} + 'delivery_info':{ + 'delivery_type':0, + 'template_id':0, + 'express':[ + {'id':10000027,'price':100}, + {'id':10000028,'price':100}, + {'id':10000029,'price':100} ] } }; diff --git a/test/api_shop_group.test.js b/test/api_shop_group.test.js index a0273b5..ec5b10d 100644 --- a/test/api_shop_group.test.js +++ b/test/api_shop_group.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); diff --git a/test/api_shop_order.test.js b/test/api_shop_order.test.js index 1841291..ee55b79 100644 --- a/test/api_shop_order.test.js +++ b/test/api_shop_order.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); diff --git a/test/api_shop_shelf.test.js b/test/api_shop_shelf.test.js index d77ee91..c69b640 100644 --- a/test/api_shop_shelf.test.js +++ b/test/api_shop_shelf.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); @@ -5,83 +7,83 @@ var expect = require('expect.js'); describe('api_shop_shelf', function () { var api = new API(config.appid, config.appsecret); var shelf = { - "shelf_data": { - "module_infos": [ + 'shelf_data': { + 'module_infos': [ { - "group_info": { - "filter": { - "count": 2 + 'group_info': { + 'filter': { + 'count': 2 }, - "group_id": 50 + 'group_id': 50 }, - "eid": 1 + 'eid': 1 }, { - "group_infos": { - "groups": [ + 'group_infos': { + 'groups': [ { - "group_id": 49 + 'group_id': 49 }, { - "group_id": 50 + 'group_id': 50 }, { - "group_id": 51 + 'group_id': 51 } ] }, - "eid": 2 + 'eid': 2 }, { - "group_info": { - "group_id": 52, - "img": "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl29nqqObBwFwnIX3licVPnFV5Jm64z4I0TTicv0TjN7Vl9bykUUibYKIOjicAwIt6Oy0Y6a1Rjp5Tos8tg/0" + 'group_info': { + 'group_id': 52, + 'img': 'http://mmbiz.qpic.cn/mmbiz/4whpV1VZl29nqqObBwFwnIX3licVPnFV5Jm64z4I0TTicv0TjN7Vl9bykUUibYKIOjicAwIt6Oy0Y6a1Rjp5Tos8tg/0' }, - "eid": 3 + 'eid': 3 }, { - "group_infos": { - "groups": [ + 'group_infos': { + 'groups': [ { - "group_id": 49, - "img": "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl29nqqObBwFwnIX3licVPnFV5uUQx7TLx4tB9qZfbe3JmqR4NkkEmpb5LUWoXF1ek9nga0IkeSSFZ8g/0" + 'group_id': 49, + 'img': 'http://mmbiz.qpic.cn/mmbiz/4whpV1VZl29nqqObBwFwnIX3licVPnFV5uUQx7TLx4tB9qZfbe3JmqR4NkkEmpb5LUWoXF1ek9nga0IkeSSFZ8g/0' }, { - "group_id": 50, - "img": "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl29nqqObBwFwnIX3licVPnFV5G1kdy3ViblHrR54gbCmbiaMnl5HpLGm5JFeENyO9FEZAy6mPypEpLibLA/0" + 'group_id': 50, + 'img': 'http://mmbiz.qpic.cn/mmbiz/4whpV1VZl29nqqObBwFwnIX3licVPnFV5G1kdy3ViblHrR54gbCmbiaMnl5HpLGm5JFeENyO9FEZAy6mPypEpLibLA/0' }, { - "group_id": 52, - "img": "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl29nqqObBwFwnIX3licVPnFV5uUQx7TLx4tB9qZfbe3JmqR4NkkEmpb5LUWoXF1ek9nga0IkeSSFZ8g/0" + 'group_id': 52, + 'img': 'http://mmbiz.qpic.cn/mmbiz/4whpV1VZl29nqqObBwFwnIX3licVPnFV5uUQx7TLx4tB9qZfbe3JmqR4NkkEmpb5LUWoXF1ek9nga0IkeSSFZ8g/0' } ] }, - "eid": 4 + 'eid': 4 }, { - "group_infos": { - "groups": [ + 'group_infos': { + 'groups': [ + { + 'group_id': 43 + }, + { + 'group_id': 44 + }, { - "group_id": 43 - }, - { - "group_id": 44 - }, - { - "group_id": 45 - }, - { - "group_id": 46 - } - ], - "img_background": "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl29nqqObBwFwnIX3licVPnFV5uUQx7TLx4tB9qZfbe3JmqR4NkkEmpb5LUWoXF1ek9nga0IkeSSFZ8g/0" - }, - "eid": 5 - } - ] - }, - "shelf_banner": "http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2ibrWQn8zWFUh1YznsMV0XEiavFfLzDWYyvQOBBszXlMaiabGWzz5B2KhNn2IDemHa3iarmCyribYlZYyw/0", - "shelf_name": "测试货架" + 'group_id': 45 + }, + { + 'group_id': 46 + } + ], + 'img_background': 'http://mmbiz.qpic.cn/mmbiz/4whpV1VZl29nqqObBwFwnIX3licVPnFV5uUQx7TLx4tB9qZfbe3JmqR4NkkEmpb5LUWoXF1ek9nga0IkeSSFZ8g/0' + }, + 'eid': 5 + } + ] + }, + 'shelf_banner': 'http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2ibrWQn8zWFUh1YznsMV0XEiavFfLzDWYyvQOBBszXlMaiabGWzz5B2KhNn2IDemHa3iarmCyribYlZYyw/0', + 'shelf_name': '测试货架' }; describe('createShelf', function () { diff --git a/test/api_shop_stock.test.js b/test/api_shop_stock.test.js index 9ca274c..eb29b3c 100644 --- a/test/api_shop_stock.test.js +++ b/test/api_shop_stock.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); diff --git a/test/api_tag.test.js b/test/api_tag.test.js new file mode 100644 index 0000000..7cfdf76 --- /dev/null +++ b/test/api_tag.test.js @@ -0,0 +1,96 @@ +'use strict'; + +var expect = require('expect.js'); +var config = require('./config'); +var API = require('../'); +var puling = 'oNeQKj7AquX1pADyghgvLmLTqIgQ'; +var tagName = 'test_tag_name_v1'; +var tagId = 103; + +describe('api_tag.js', function () { + var api = new API(config.appid, config.appsecret); + before(function (done) { + api.getAccessToken(done); + }); + + // 1 创建标签 + it('createTag should ok', function (done) { + api.createTag(tagName, function (err, data, res) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('tag'); + done(); + }); + }); + + // 2 获取公众号的所有标签 + it('getTags should ok', function (done) { + api.getTags(function (err, data, res) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('tags'); + expect(data.tags).to.be.an('array'); + done(); + }); + }); + + // 3 编辑标签 + it('editTag should ok', function (done) { + api.editTag(tagId, tagName + '1', function (err, data, res) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('errcode', 0); + expect(data).to.have.property('errmsg', 'ok'); + done(); + }); + }); + + // 4 删除标签 +// it('deleteTag should ok', function (done) { +// api.deleteTag(103, function (err, data, res) { +// expect(err).not.to.be.ok(); +// expect(data).to.have.property('errcode', 0); +// expect(data).to.have.property('errmsg', 'ok'); +// done(); +// }); +// }); + + + // 6 批量为用户打标签 + it('membersBatchtagging should ok', function (done) { + api.membersBatchtagging(tagId, [puling], function (err, data, res) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('errcode', 0); + expect(data).to.have.property('errmsg', 'ok'); + done(); + }); + }); + + // 5 获取标签的所有粉丝 + it('getTagUsers should ok', function (done) { + api.getTagUsers(tagId, '', function (err, data, res) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('data'); + expect(data.data.openid).to.be.an('array'); + done(); + }); + }); + + // 7 批量为用户取消标签 + it('membersBatchuntagging should ok', function (done) { + api.membersBatchuntagging(tagId, [puling], function (err, data, res) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('errcode', 0); + expect(data).to.have.property('errmsg', 'ok'); + done(); + }); + }); + + // 8 获取用户身上的标签列表 + it('getUserTags should ok', function (done) { + api.getUserTags(puling, function (err, data, res) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('tagid_list'); + expect(data.tagid_list).to.be.an('array'); + done(); + }); + }); + +}); diff --git a/test/api_template.test.js b/test/api_template.test.js index 1f0fa56..77ceb2c 100644 --- a/test/api_template.test.js +++ b/test/api_template.test.js @@ -1,17 +1,18 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); var urllib = require('urllib'); var muk = require('muk'); var puling = 'ofL4cs7hr04cJIcu600_W-ZwwxHg'; -var imageId = 'XDZxzuRWBPqI4R9n_nNR5uRVZVQCSneMoELyWKflwM2qF9K38vnVFzgaD97uCTUu'; describe('api_template', function () { var api = new API(config.appid, config.appsecret); var mockError = function () { before(function () { muk(urllib, 'request', function (url, args, callback) { - var data = {"errcode":1, "errmsg":"mock error"}; + var data = {'errcode':1, 'errmsg':'mock error'}; var res = { headers: { 'content-type': 'application/json' @@ -31,15 +32,21 @@ describe('api_template', function () { describe('sendTemplate', function () { it('sendTemplate should ok', function (done) { var templateId = '模板id'; - // URL置空,则在发送后,点击模板消息会进入一个空白页面(ios), 或无法点击(android) - var url = 'http://weixin.qq.com/download'; + // 跳转目的地置空,则在发送后,点击模板消息会进入一个空白页面(ios), 或无法点击(android) + var dest = { + url: 'http://weixin.qq.com/download', + miniprogram:{ + appid: 'xiaochengxuappid12345', + pagepath: 'index?foo=bar' + } + }; var data = { user: { - "value":'黄先生', - "color":"#173177" + 'value':'黄先生', + 'color':'#173177' } }; - api.sendTemplate(puling, templateId, url, data, function (err, data, res) { + api.sendTemplate(puling, templateId, dest, data, function (err, data, res) { if (!err) { expect(err).not.to.be.ok(); expect(data).to.have.property('errcode', 0); @@ -58,15 +65,21 @@ describe('api_template', function () { it('should not ok', function (done) { var templateId = '模板id'; - // URL置空,则在发送后,点击模板消息会进入一个空白页面(ios), 或无法点击(android) - var url = 'http://weixin.qq.com/download'; + // 跳转目的地置空,则在发送后,点击模板消息会进入一个空白页面(ios), 或无法点击(android) + var dest = { + url: 'http://weixin.qq.com/download', + miniprogram:{ + appid: 'xiaochengxuappid12345', + pagepath: 'index?foo=bar' + } + }; var data = { user: { - "value":'黄先生', - "color":"#173177" + 'value':'黄先生', + 'color':'#173177' } }; - api.sendTemplate(puling, templateId, url, data, function (err, data, res) { + api.sendTemplate(puling, templateId, dest, data, function (err, data, res) { expect(err).to.be.ok(); expect(err.name).to.be('WeChatAPIError'); expect(err.message).to.be('mock error'); @@ -75,4 +88,54 @@ describe('api_template', function () { }); }); }); + + describe('getIndustry', function () { + it('getIndustry should ok', function (done) { + api.getIndustry(function (err, data, res) { + if (!err) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('primary_industry'); + expect(data).to.have.property('secondary_industry'); + } else { + expect(err).to.be.ok(); + expect(err).to.have.property('name', 'WeChatAPIError'); + } + done(); + }); + }); + }); + + describe('getAllPrivateTemplate', function () { + it('getAllPrivateTemplate should ok', function (done) { + api.getAllPrivateTemplate(function (err, data, res) { + if (!err) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('template_list'); + expect(data.template_list).to.be.an('array'); + } else { + expect(err).to.be.ok(); + expect(err).to.have.property('name', 'WeChatAPIError'); + } + done(); + }); + }); + }); + + describe('delPrivateTemplate', function () { + it('delPrivateTemplate should ok', function (done) { + var templateId = '模板id'; + api.delPrivateTemplate(templateId, function (err, data, res) { + if (!err) { + expect(err).not.to.be.ok(); + expect(data).to.have.property('errcode', 0); + expect(data).to.have.property('errmsg', 'ok'); + } else { + expect(err).to.be.ok(); + expect(err).to.have.property('name', 'WeChatAPIError'); + expect(err.message).to.contain('invalid template_id'); + } + done(); + }); + }); + }); }); diff --git a/test/api_url.test.js b/test/api_url.test.js index d685637..56b1393 100644 --- a/test/api_url.test.js +++ b/test/api_url.test.js @@ -1,3 +1,5 @@ +'use strict'; + var expect = require('expect.js'); var config = require('./config'); var API = require('../'); diff --git a/test/api_user.test.js b/test/api_user.test.js index f7a394e..f957d4c 100644 --- a/test/api_user.test.js +++ b/test/api_user.test.js @@ -1,3 +1,5 @@ +'use strict'; + var config = require('./config'); var API = require('../'); var expect = require('expect.js'); @@ -48,14 +50,14 @@ describe('api_user', function () { done(); }); }); - + describe('batchGetUser', function() { it('should ok', function(done) { api.batchGetUsers([puling], function (err, data, res) { expect(err).not.to.be.ok(); expect(data).to.only.have.keys('user_info_list'); done(); - }) + }); }); }); }); diff --git a/test/config.js b/test/config.js index cfd50d2..f351516 100644 --- a/test/config.js +++ b/test/config.js @@ -1,4 +1,6 @@ +'use strict'; + module.exports = { - appid: 'wxc9135aade4e81d57', - appsecret: '0461795e98b8ffde5a212b5098f1b9b6' + appid: process.env.WX_APPID || 'wxc9135aade4e81d57', + appsecret: process.env.WX_APPSECRET||'0461795e98b8ffde5a212b5098f1b9b6' };