UNPKG

wechat-open-service

Version:

apis set for wechat open platform 3rd-party service 微信开放平台的第三方平台接口

290 lines (272 loc) 8.92 kB
var urllib = require('urllib'); var util = require('./util'); var extend = require('util')._extend; var wrapper = util.wrapper; var postJSON = util.postJSON; var ComponentAccessToken = function (data) { if (!(this instanceof ComponentAccessToken)) { return new ComponentAccessToken(data); } this.component_access_token = data.component_access_token; this.expires_in = data.expires_in; }; /** * 根据component_appid、component_appsecret和component_verify_ticket创建API的构造函数。 * * 如需跨进程跨机器进行操作Wechat API(依赖access token),access token需要进行全局维护 * 使用策略如下: * * 1. 调用用户传入的获取token的异步方法,获得token之后使用 * 2. 使用appid/appsecret获取token。并调用用户传入的保存token方法保存 * * Examples: * ``` * var API = require('wechat-open-service'); * var api = new API('component_appid', 'component_appsecret', 'component_verify_ticket'); * ``` * 以上即可满足单进程使用。 * 当多进程时,token需要全局维护,以下为保存token的接口。 * ``` * var api = new API('component_appid', 'component_appsecret', 'component_verify_ticket', function (callback) { * // 传入一个获取全局token的方法 * fs.readFile('component_access_token.txt', 'utf8', function (err, txt) { * if (err) {return callback(err);} * callback(null, JSON.parse(txt)); * }); * }, function (token, callback) { * // 请将token存储到全局,跨进程、跨机器级别的全局,比如写到数据库、redis等 * // 这样才能在cluster模式及多机情况下使用,以下为写入到文件的示例 * fs.writeFile('component_access_token.txt', JSON.stringify(token), callback); * }); * ``` * 参数请参考 开发平台的 -> 管理中心 / 平台详情 * * @param {String} component_appid 在开放平台上申请得到的 AppID * @param {String} component_appsecret 在PaaS平台上申请得到的 AppSecret * @param {String} component_verify_ticket 微信服务器每10分钟向回调接口推送的component_verify_ticket消息 * @param {Function} getToken 可选的。获取全局token对象的方法,多进程模式部署时需在意 * @param {Function} saveToken 可选的。保存全局token对象的方法,多进程模式部署时需在意 */ var API = function (component_appid, component_appsecret, component_verify_ticket, getToken, saveToken) { this.component_appid = component_appid; this.component_appsecret = component_appsecret; this.component_verify_ticket = component_verify_ticket; this.store = null; this.getToken = getToken || function (callback) { callback(null, this.store); }; this.saveToken = saveToken || function (token, callback) { this.store = token; if (process.env.NODE_ENV === 'production') { console.warn('Don\'t save token in memory, when cluster or multi-computer!'); } callback(null); }; this.prefix = 'https://api.weixin.qq.com/cgi-bin/'; this.defaults = {}; }; /** * 用于设置urllib的默认options * * Examples: * ``` * api.setOpts({timeout: 15000}); * ``` * @param {Object} opts 默认选项 */ API.prototype.setOpts = function (opts) { this.defaults = opts; }; /** * 设置urllib的options * */ API.prototype.request = function (url, opts, callback) { var options = {}; extend(options, this.defaults); if (typeof opts === 'function') { callback = opts; opts = {}; } for (var key in opts) { if (key !== 'headers') { options[key] = opts[key]; } else { if (opts.headers) { options.headers = options.headers || {}; extend(options.headers, opts.headers); } } } urllib.request(url, options, callback); }; /*! * 根据创建API时传入的componentId,componentSecret和componentTicket获取component access token * 进行后续所有API调用时,需要先获取access token * 详细请看:<http://mp.weixin.qq.com/wiki/index.php?title=获取access_token> * * 应用开发者无需直接调用本API。 * * Examples: * ``` * api.getComponentToken(callback); * ``` * Callback: * * - `err`, 获取access token出现异常时的异常对象 * - `result`, 成功时得到的响应结果 * * Result: * ``` * {"component_access_token": "ACCESS_TOKEN", * "expires_in": 7200 } * ``` * @param {Function} callback 回调函数 */ API.prototype.getComponentToken = function (callback) { var url = this.prefix + 'component/api_component_token'; var data = { component_appid: this.component_appid, component_appsecret: this.component_appsecret, component_verify_ticket: this.component_verify_ticket }; var that = this; this.request(url, postJSON(data), wrapper(function (err, data) { if (err) { return callback(err); } var token = ComponentAccessToken(data); // console.log(token); that.saveToken(token, function (err) { if (err) { return callback(err); } callback(err, token); }); })); return this; }; /*! * generateAuthUrl * 拼装出第三方授权用的URL * 文档地址 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318292&token=&lang=zh_CN * Examples: * ``` * api.generateAuthUrl((preAuthCode, redirectUri); * ``` */ API.prototype.generateAuthUrl = function (preAuthCode, redirectUri) { //https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=xxxx&pre_auth_code=xxxxx&redirect_uri=xxxx return 'https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=' + this.component_appid + "&pre_auth_code=" + preAuthCode + "&redirect_uri=" + redirectUri; }; /*! * 需要access token的接口调用如果采用preRequest进行封装后,就可以直接调用。 * 无需依赖getAccessToken为前置调用。 * 应用开发者无需直接调用此API。 * * Examples: * ``` * api.preRequest(method, arguments); * ``` * @param {Function} method 需要封装的方法 * @param {Array} args 方法需要的参数 */ API.prototype.componentPreRequest = function (method, args, retryed) { var that = this; var callback = args[args.length - 1]; // 调用用户传入的获取token的异步方法,获得token之后使用(并缓存它)。 that.getToken(function (err, token) { if (err) { return callback(err); } // console.log(token); // 有token并且token有效直接调用 if (token) { // 暂时保存token that.componentToken = ComponentAccessToken(token); if (!retryed) { var retryHandle = function (err, data, res) { // 42001 重试 if (data && data.errcode && data.errcode === 42001) { return that.preRequest(method, args, true); } callback(err, data, res); }; // 替换callback var newargs = Array.prototype.slice.call(args, 0, -1); newargs.push(retryHandle); method.apply(that, newargs); } else { method.apply(that, args); } } else { // 使用appid/appsecret获取token that.getComponentToken(function (err, token) { // 如遇错误,通过回调函数传出 if (err) { return callback(err); } // 暂时保存token that.componentToken = token; method.apply(that, args); }); } }); }; /** * 获取最新的token。 * * - 如果还没有请求过token,则发起获取Token请求。 * - 如果请求过,则调用getToken从获取之前保存的token * * Examples: * ``` * api.getLatestToken(callback); * ``` * Callback: * * - `err`, 获取access token出现异常时的异常对象 * - `token`, 获取的token * * @param {Function} callback 回调函数 */ API.prototype.getLatestToken = function (callback) { var that = this; // 调用用户传入的获取token的异步方法,获得token之后使用(并缓存它)。 that.getToken(function (err, token) { if (err) { return callback(err); } // 有token if (token) { callback(null, ComponentAccessToken(token)); } else { // 使用appid/corpsecret获取token that.getComponentToken(callback); } }); }; /* API.prototype.setComponentToken = function (newTicket) { this.componentToken = newTicket; };*/ /** * 用于支持对象合并。将对象合并到API.prototype上,使得能够支持扩展 * Examples: * ``` * // 媒体管理(上传、下载) * API.mixin(require('./lib/api_media')); * ``` * @param {Object} obj 要合并的对象 */ API.mixin = function (obj) { for (var key in obj) { if (API.prototype.hasOwnProperty(key)) { throw new Error('Don\'t allow override existed prototype method. method: '+ key); } API.prototype[key] = obj[key]; } }; API.componentToken = ComponentAccessToken; module.exports = API;