diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000..b503745
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,27 @@
+{
+ plugins: [
+ "transform-decorators-legacy",
+ ["transform-es2015-template-literals", { "loose": true }],
+ "transform-es2015-literals",
+ "transform-es2015-function-name",
+ "transform-es2015-arrow-functions",
+ "transform-es2015-block-scoped-functions",
+ ["transform-es2015-classes", { "loose": true }],
+ "transform-es2015-object-super",
+ "transform-es2015-shorthand-properties",
+ ["transform-es2015-computed-properties", { "loose": true }],
+ ["transform-es2015-for-of", { "loose": true }],
+ "transform-es2015-sticky-regex",
+ "transform-es2015-unicode-regex",
+ "check-es2015-constants",
+ ["transform-es2015-spread", { "loose": true }],
+ "transform-es2015-parameters",
+ ["transform-es2015-destructuring", { "loose": true }],
+ "transform-es2015-block-scoping",
+ ["transform-es2015-modules-commonjs", { "loose": true }],
+ "transform-object-rest-spread",
+ "transform-react-jsx",
+ "transform-async-to-generator",
+ "syntax-jsx"
+ ]
+}
diff --git a/lib/wechat.js b/lib/wechat.js
index 538dd2c..baf6fb3 100644
--- a/lib/wechat.js
+++ b/lib/wechat.js
@@ -1,10 +1,20 @@
+'use strict';
+
+var _xml2js = require('xml2js');
+
+var _xml2js2 = _interopRequireDefault(_xml2js);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
+
var getRawBody = require('raw-body');
-var xml2js = require('xml2js');
+
var crypto = require('crypto');
var ejs = require('ejs');
var WXBizMsgCrypt = require('wechat-crypto');
-var wechat = function (config) {
+var wechat = function wechat(config) {
if (!(this instanceof wechat)) {
return new wechat(config);
}
@@ -23,7 +33,7 @@ wechat.prototype.setToken = function (config) {
}
};
-var getSignature = function (timestamp, nonce, token) {
+var getSignature = function getSignature(timestamp, nonce, token) {
var shasum = crypto.createHash('sha1');
var arr = [token, timestamp, nonce].sort();
shasum.update(arr.join(''));
@@ -31,16 +41,26 @@ var getSignature = function (timestamp, nonce, token) {
return shasum.digest('hex');
};
-var parseXML = function (xml) {
+var parseXML = function parseXML(xml) {
return function (done) {
- xml2js.parseString(xml, {trim: true}, done);
+ _xml2js2.default.parseString(xml, { trim: true }, done);
};
};
+var parseXML2 = function parseXML2(xml) {
+ return new Promise(function (resolve, reject) {
+ _xml2js2.default.parseString(xml, { trim: true }, function (err, res) {
+ if (err) return reject(err);
+ if (arguments.length > 2) res = slice.call(arguments, 1);
+ resolve(res);
+ });
+ });
+};
+
/*!
* 将xml2js解析出来的对象转换成直接可访问的对象
*/
-var formatMessage = function (result) {
+var formatMessage = function formatMessage(result) {
var message = {};
if (typeof result === 'object') {
for (var key in result) {
@@ -68,73 +88,21 @@ var formatMessage = function (result) {
/*!
* 响应模版
*/
-var tpl = ['',
- ']]>',
- ']]>',
- '<%=createTime%>',
- ']]>',
- '<% if (msgType === "news") { %>',
- '<%=content.length%>',
- '',
- '<% content.forEach(function(item){ %>',
- '- ',
- ']]>',
- ']]>',
- ']]>',
- ']]>',
- '
',
- '<% }); %>',
- '',
- '<% } else if (msgType === "music") { %>',
- '',
- ']]>',
- ']]>',
- ']]>',
- ']]>',
- '',
- '<% } else if (msgType === "voice") { %>',
- '',
- ']]>',
- '',
- '<% } else if (msgType === "image") { %>',
- '',
- ']]>',
- '',
- '<% } else if (msgType === "video") { %>',
- '',
- '<% } else if (msgType === "transfer_customer_service") { %>',
- '<% if (content && content.kfAccount) { %>',
- '',
- ']]>',
- '',
- '<% } %>',
- '<% } else { %>',
- ']]>',
- '<% } %>',
- ''].join('');
+var tpl = ['', ']]>', ']]>', '<%=createTime%>', ']]>', '<% if (msgType === "news") { %>', '<%=content.length%>', '', '<% content.forEach(function(item){ %>', '- ', ']]>', ']]>', ']]>', ']]>', '
', '<% }); %>', '', '<% } else if (msgType === "music") { %>', '', ']]>', ']]>', ']]>', ']]>', '', '<% } else if (msgType === "voice") { %>', '', ']]>', '', '<% } else if (msgType === "image") { %>', '', ']]>', '', '<% } else if (msgType === "video") { %>', '', '<% } else if (msgType === "transfer_customer_service") { %>', '<% if (content && content.kfAccount) { %>', '', ']]>', '', '<% } %>', '<% } else { %>', ']]>', '<% } %>', ''].join('');
/*!
* 编译过后的模版
*/
var compiled = ejs.compile(tpl);
-var wrapTpl = '' +
- ']]>' +
- ']]>' +
- '<%-timestamp%>' +
- ']]>' +
-'';
+var wrapTpl = '' + ']]>' + ']]>' + '<%-timestamp%>' + ']]>' + '';
var encryptWrap = ejs.compile(wrapTpl);
/*!
* 将内容回复给微信的封装方法
*/
-var reply = function (content, fromUsername, toUsername) {
+var reply = function reply(content, fromUsername, toUsername) {
var info = {};
var type = 'text';
info.content = content || '';
@@ -158,7 +126,7 @@ var reply = function (content, fromUsername, toUsername) {
return compiled(info);
};
-var reply2CustomerService = function (fromUsername, toUsername, kfAccount) {
+var reply2CustomerService = function reply2CustomerService(fromUsername, toUsername, kfAccount) {
var info = {};
info.msgType = 'transfer_customer_service';
info.createTime = new Date().getTime();
@@ -286,14 +254,13 @@ wechat.prototype.middleware = function (handle) {
} else {
var wrap = {};
wrap.encrypt = that.cryptor.encrypt(replyMessageXml);
- wrap.nonce = parseInt((Math.random() * 100000000000), 10);
+ wrap.nonce = parseInt(Math.random() * 100000000000, 10);
wrap.timestamp = new Date().getTime();
wrap.signature = that.cryptor.getSignature(wrap.timestamp, wrap.nonce, wrap.encrypt);
this.body = encryptWrap(wrap);
}
this.type = 'application/xml';
-
} else {
this.status = 501;
this.body = 'Not Implemented';
@@ -301,4 +268,141 @@ wechat.prototype.middleware = function (handle) {
};
};
-module.exports = wechat;
+wechat.prototype.middleware2 = function (handle) {
+ var that = this;
+ if (this.encodingAESKey) {
+ that.cryptor = new WXBizMsgCrypt(this.token, this.encodingAESKey, this.appid);
+ }
+ return function () {
+ var _ref = _asyncToGenerator(function* (ctx, next) {
+ var query = ctx.query;
+ // 加密模式
+ var encrypted = !!(query.encrypt_type && query.encrypt_type === 'aes' && query.msg_signature);
+ var timestamp = query.timestamp;
+ var nonce = query.nonce;
+ var echostr = query.echostr;
+ var method = ctx.method;
+
+ if (method === 'GET') {
+ var valid = false;
+ if (encrypted) {
+ var signature = query.msg_signature;
+ valid = signature === that.cryptor.getSignature(timestamp, nonce, echostr);
+ } else {
+ // 校验
+ valid = query.signature === getSignature(timestamp, nonce, that.token);
+ }
+ if (!valid) {
+ ctx.status = 401;
+ ctx.body = 'Invalid signature';
+ } else {
+ if (encrypted) {
+ var decrypted = that.cryptor.decrypt(echostr);
+ // TODO 检查appId的正确性
+ ctx.body = decrypted.message;
+ } else {
+ ctx.body = echostr;
+ }
+ }
+ } else if (method === 'POST') {
+ if (!encrypted) {
+ // 校验
+ if (query.signature !== getSignature(timestamp, nonce, that.token)) {
+ ctx.status = 401;
+ ctx.body = 'Invalid signature';
+ return;
+ }
+ }
+ // 取原始数据
+ var xml = yield getRawBody(ctx.req, {
+ length: ctx.length,
+ limit: '1mb',
+ encoding: ctx.charset
+ });
+
+ ctx.weixin_xml = xml;
+ // 解析xml
+ var result = yield parseXML2(xml);
+ var formated = formatMessage(result.xml);
+ if (encrypted) {
+ var encryptMessage = formated.Encrypt;
+ if (query.msg_signature !== that.cryptor.getSignature(timestamp, nonce, encryptMessage)) {
+ ctx.status = 401;
+ ctx.body = 'Invalid signature';
+ return;
+ }
+ var decryptedXML = that.cryptor.decrypt(encryptMessage);
+ var messageWrapXml = decryptedXML.message;
+ if (messageWrapXml === '') {
+ ctx.status = 401;
+ ctx.body = 'Invalid signature';
+ return;
+ }
+ //var decodedXML = yield parseXML(messageWrapXml);
+ var decodedXML = yield parseXML2(messageWrapXml);
+ formated = formatMessage(decodedXML.xml);
+ }
+
+ // 挂载处理后的微信消息
+ ctx.weixin = formated;
+ yield handle(ctx, next);
+ /*
+ // 取session数据
+ if (ctx.sessionStore) {
+ ctx.wxSessionId = formated.FromUserName;
+ ctx.wxsession = yield ctx.sessionStore.get(ctx.wxSessionId);
+ if (!ctx.wxsession) {
+ ctx.wxsession = {};
+ ctx.wxsession.cookie = ctx.session.cookie;
+ }
+ }
+
+ // 业务逻辑处理
+ yield* handle.call(ctx);
+
+ // 更新session
+ if (ctx.sessionStore) {
+ if (!ctx.wxsession) {
+ if (ctx.wxSessionId) {
+ yield ctx.sessionStore.destroy(ctx.wxSessionId);
+ }
+ } else {
+ yield ctx.sessionStore.set(ctx.wxSessionId, ctx.wxsession);
+ }
+ }
+ */
+ /*
+ * 假如服务器无法保证在五秒内处理并回复,可以直接回复空串。
+ * 微信服务器不会对此作任何处理,并且不会发起重试。
+ */
+ if (ctx.body === '') {
+ return;
+ }
+
+ var replyMessageXml = reply(ctx.body, formated.ToUserName, formated.FromUserName);
+
+ if (!query.encrypt_type || query.encrypt_type === 'raw') {
+ ctx.body = replyMessageXml;
+ } else {
+ var wrap = {};
+ wrap.encrypt = that.cryptor.encrypt(replyMessageXml);
+ wrap.nonce = parseInt(Math.random() * 100000000000, 10);
+ wrap.timestamp = new Date().getTime();
+ wrap.signature = that.cryptor.getSignature(wrap.timestamp, wrap.nonce, wrap.encrypt);
+ ctx.body = encryptWrap(wrap);
+ }
+
+ ctx.type = 'application/xml';
+ } else {
+ ctx.status = 501;
+ ctx.body = 'Not Implemented';
+ }
+ });
+
+ return function (_x, _x2) {
+ return _ref.apply(this, arguments);
+ };
+ }();
+};
+
+module.exports = wechat;
\ No newline at end of file
diff --git a/package.json b/package.json
index 2e73bcc..6e409a5 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,8 @@
"description": "wechat api for co",
"main": "index.js",
"scripts": {
+ "build": "npm run build:lib",
+ "build:lib": "babel src --out-dir lib",
"test": "make test-all"
},
"repository": {
@@ -23,17 +25,46 @@
"raw-body": "*"
},
"devDependencies": {
- "supertest": "*",
- "mocha": "*",
- "should": "~3.0.0",
+ "babel-cli": "^6.3.17",
+ "babel-core": "^6.3.26",
+ "babel-eslint": "^5.0.0-beta9",
+ "babel-loader": "^6.2.0",
+ "babel-plugin-check-es2015-constants": "^6.3.13",
+ "babel-plugin-syntax-jsx": "^6.3.13",
+ "babel-plugin-transform-async-to-generator": "^6.16.0",
+ "babel-plugin-transform-decorators-legacy": "^1.2.0",
+ "babel-plugin-transform-es2015-arrow-functions": "^6.3.13",
+ "babel-plugin-transform-es2015-block-scoped-functions": "^6.3.13",
+ "babel-plugin-transform-es2015-block-scoping": "^6.3.13",
+ "babel-plugin-transform-es2015-classes": "^6.3.13",
+ "babel-plugin-transform-es2015-computed-properties": "^6.3.13",
+ "babel-plugin-transform-es2015-destructuring": "^6.3.13",
+ "babel-plugin-transform-es2015-for-of": "^6.3.13",
+ "babel-plugin-transform-es2015-function-name": "^6.3.13",
+ "babel-plugin-transform-es2015-literals": "^6.3.13",
+ "babel-plugin-transform-es2015-modules-commonjs": "^6.3.13",
+ "babel-plugin-transform-es2015-object-super": "^6.3.13",
+ "babel-plugin-transform-es2015-parameters": "^6.3.13",
+ "babel-plugin-transform-es2015-shorthand-properties": "^6.3.13",
+ "babel-plugin-transform-es2015-spread": "^6.3.13",
+ "babel-plugin-transform-es2015-sticky-regex": "^6.3.13",
+ "babel-plugin-transform-es2015-template-literals": "^6.3.13",
+ "babel-plugin-transform-es2015-unicode-regex": "^6.3.13",
+ "babel-plugin-transform-object-rest-spread": "^6.3.13",
+ "babel-plugin-transform-react-display-name": "^6.4.0",
+ "babel-plugin-transform-react-jsx": "^6.4.0",
+ "babel-register": "^6.3.13",
+ "coveralls": "*",
"expect.js": "*",
+ "istanbul-harmony": "*",
"koa": "0.13.*",
"koa-generic-session": "*",
- "travis-cov": "*",
- "coveralls": "*",
+ "mocha": "*",
"mocha-lcov-reporter": "*",
"muk": "*",
"rewire": "*",
- "istanbul-harmony": "*"
+ "should": "~3.0.0",
+ "supertest": "*",
+ "travis-cov": "*"
}
}
diff --git a/src/wechat.js b/src/wechat.js
new file mode 100644
index 0000000..481eaf6
--- /dev/null
+++ b/src/wechat.js
@@ -0,0 +1,446 @@
+var getRawBody = require('raw-body');
+import xml2js from 'xml2js';
+var crypto = require('crypto');
+var ejs = require('ejs');
+var WXBizMsgCrypt = require('wechat-crypto');
+
+var wechat = function (config) {
+ if (!(this instanceof wechat)) {
+ return new wechat(config);
+ }
+ this.setToken(config);
+};
+
+wechat.prototype.setToken = function (config) {
+ if (typeof config === 'string') {
+ this.token = config;
+ } else if (typeof config === 'object' && config.token) {
+ this.token = config.token;
+ this.appid = config.appid || '';
+ this.encodingAESKey = config.encodingAESKey || '';
+ } else {
+ throw new Error('please check your config');
+ }
+};
+
+var getSignature = function (timestamp, nonce, token) {
+ var shasum = crypto.createHash('sha1');
+ var arr = [token, timestamp, nonce].sort();
+ shasum.update(arr.join(''));
+
+ return shasum.digest('hex');
+};
+
+var parseXML = function (xml) {
+ return function (done) {
+ xml2js.parseString(xml, {trim: true}, done);
+ };
+};
+
+var parseXML2 = function (xml) {
+ return new Promise(function (resolve, reject) {
+ xml2js.parseString(xml, {trim: true}, function (err, res) {
+ if (err) return reject(err);
+ if (arguments.length > 2) res = slice.call(arguments, 1);
+ resolve(res);
+ });
+ });
+}
+
+/*!
+ * 将xml2js解析出来的对象转换成直接可访问的对象
+ */
+var formatMessage = function (result) {
+ var message = {};
+ if (typeof result === 'object') {
+ for (var key in result) {
+ if (!(result[key] instanceof Array) || result[key].length === 0) {
+ continue;
+ }
+ if (result[key].length === 1) {
+ var val = result[key][0];
+ if (typeof val === 'object') {
+ message[key] = formatMessage(val);
+ } else {
+ message[key] = (val || '').trim();
+ }
+ } else {
+ message[key] = [];
+ result[key].forEach(function (item) {
+ message[key].push(formatMessage(item));
+ });
+ }
+ }
+ }
+ return message;
+};
+
+/*!
+ * 响应模版
+ */
+var tpl = ['',
+ ']]>',
+ ']]>',
+ '<%=createTime%>',
+ ']]>',
+ '<% if (msgType === "news") { %>',
+ '<%=content.length%>',
+ '',
+ '<% content.forEach(function(item){ %>',
+ '- ',
+ ']]>',
+ ']]>',
+ ']]>',
+ ']]>',
+ '
',
+ '<% }); %>',
+ '',
+ '<% } else if (msgType === "music") { %>',
+ '',
+ ']]>',
+ ']]>',
+ ']]>',
+ ']]>',
+ '',
+ '<% } else if (msgType === "voice") { %>',
+ '',
+ ']]>',
+ '',
+ '<% } else if (msgType === "image") { %>',
+ '',
+ ']]>',
+ '',
+ '<% } else if (msgType === "video") { %>',
+ '',
+ '<% } else if (msgType === "transfer_customer_service") { %>',
+ '<% if (content && content.kfAccount) { %>',
+ '',
+ ']]>',
+ '',
+ '<% } %>',
+ '<% } else { %>',
+ ']]>',
+ '<% } %>',
+ ''].join('');
+
+/*!
+ * 编译过后的模版
+ */
+var compiled = ejs.compile(tpl);
+
+var wrapTpl = '' +
+ ']]>' +
+ ']]>' +
+ '<%-timestamp%>' +
+ ']]>' +
+'';
+
+var encryptWrap = ejs.compile(wrapTpl);
+
+/*!
+ * 将内容回复给微信的封装方法
+ */
+var reply = function (content, fromUsername, toUsername) {
+ var info = {};
+ var type = 'text';
+ info.content = content || '';
+ if (Array.isArray(content)) {
+ type = 'news';
+ } else if (typeof content === 'object') {
+ if (content.hasOwnProperty('type')) {
+ if (content.type === 'customerService') {
+ return reply2CustomerService(fromUsername, toUsername, content.kfAccount);
+ }
+ type = content.type;
+ info.content = content.content;
+ } else {
+ type = 'music';
+ }
+ }
+ info.msgType = type;
+ info.createTime = new Date().getTime();
+ info.toUsername = toUsername;
+ info.fromUsername = fromUsername;
+ return compiled(info);
+};
+
+var reply2CustomerService = function (fromUsername, toUsername, kfAccount) {
+ var info = {};
+ info.msgType = 'transfer_customer_service';
+ info.createTime = new Date().getTime();
+ info.toUsername = toUsername;
+ info.fromUsername = fromUsername;
+ info.content = {};
+ if (typeof kfAccount === 'string') {
+ info.content.kfAccount = kfAccount;
+ }
+ return compiled(info);
+};
+
+wechat.prototype.middleware = function (handle) {
+ var that = this;
+ if (this.encodingAESKey) {
+ that.cryptor = new WXBizMsgCrypt(this.token, this.encodingAESKey, this.appid);
+ }
+ return function* (next) {
+ var query = this.query;
+ // 加密模式
+ var encrypted = !!(query.encrypt_type && query.encrypt_type === 'aes' && query.msg_signature);
+ var timestamp = query.timestamp;
+ var nonce = query.nonce;
+ var echostr = query.echostr;
+ var method = this.method;
+
+ if (method === 'GET') {
+ var valid = false;
+ if (encrypted) {
+ var signature = query.msg_signature;
+ valid = signature === that.cryptor.getSignature(timestamp, nonce, echostr);
+ } else {
+ // 校验
+ valid = query.signature === getSignature(timestamp, nonce, that.token);
+ }
+ if (!valid) {
+ this.status = 401;
+ this.body = 'Invalid signature';
+ } else {
+ if (encrypted) {
+ var decrypted = that.cryptor.decrypt(echostr);
+ // TODO 检查appId的正确性
+ this.body = decrypted.message;
+ } else {
+ this.body = echostr;
+ }
+ }
+ } else if (method === 'POST') {
+ if (!encrypted) {
+ // 校验
+ if (query.signature !== getSignature(timestamp, nonce, that.token)) {
+ this.status = 401;
+ this.body = 'Invalid signature';
+ return;
+ }
+ }
+ // 取原始数据
+ var xml = yield getRawBody(this.req, {
+ length: this.length,
+ limit: '1mb',
+ encoding: this.charset
+ });
+
+ this.weixin_xml = xml;
+ // 解析xml
+ var result = yield parseXML(xml);
+ var formated = formatMessage(result.xml);
+ if (encrypted) {
+ var encryptMessage = formated.Encrypt;
+ if (query.msg_signature !== that.cryptor.getSignature(timestamp, nonce, encryptMessage)) {
+ this.status = 401;
+ this.body = 'Invalid signature';
+ return;
+ }
+ var decryptedXML = that.cryptor.decrypt(encryptMessage);
+ var messageWrapXml = decryptedXML.message;
+ if (messageWrapXml === '') {
+ this.status = 401;
+ this.body = 'Invalid signature';
+ return;
+ }
+ var decodedXML = yield parseXML(messageWrapXml);
+ formated = formatMessage(decodedXML.xml);
+ }
+
+ // 挂载处理后的微信消息
+ this.weixin = formated;
+
+ // 取session数据
+ if (this.sessionStore) {
+ this.wxSessionId = formated.FromUserName;
+ this.wxsession = yield this.sessionStore.get(this.wxSessionId);
+ if (!this.wxsession) {
+ this.wxsession = {};
+ this.wxsession.cookie = this.session.cookie;
+ }
+ }
+
+ // 业务逻辑处理
+ yield* handle.call(this);
+
+ // 更新session
+ if (this.sessionStore) {
+ if (!this.wxsession) {
+ if (this.wxSessionId) {
+ yield this.sessionStore.destroy(this.wxSessionId);
+ }
+ } else {
+ yield this.sessionStore.set(this.wxSessionId, this.wxsession);
+ }
+ }
+
+ /*
+ * 假如服务器无法保证在五秒内处理并回复,可以直接回复空串。
+ * 微信服务器不会对此作任何处理,并且不会发起重试。
+ */
+ if (this.body === '') {
+ return;
+ }
+
+ var replyMessageXml = reply(this.body, formated.ToUserName, formated.FromUserName);
+
+ if (!query.encrypt_type || query.encrypt_type === 'raw') {
+ this.body = replyMessageXml;
+ } else {
+ var wrap = {};
+ wrap.encrypt = that.cryptor.encrypt(replyMessageXml);
+ wrap.nonce = parseInt((Math.random() * 100000000000), 10);
+ wrap.timestamp = new Date().getTime();
+ wrap.signature = that.cryptor.getSignature(wrap.timestamp, wrap.nonce, wrap.encrypt);
+ this.body = encryptWrap(wrap);
+ }
+
+ this.type = 'application/xml';
+
+ } else {
+ this.status = 501;
+ this.body = 'Not Implemented';
+ }
+ };
+};
+
+wechat.prototype.middleware2 = function (handle) {
+ var that = this;
+ if (this.encodingAESKey) {
+ that.cryptor = new WXBizMsgCrypt(this.token, this.encodingAESKey, this.appid);
+ }
+ return async function (ctx, next) {
+ var query = ctx.query;
+ // 加密模式
+ var encrypted = !!(query.encrypt_type && query.encrypt_type === 'aes' && query.msg_signature);
+ var timestamp = query.timestamp;
+ var nonce = query.nonce;
+ var echostr = query.echostr;
+ var method = ctx.method;
+
+ if (method === 'GET') {
+ var valid = false;
+ if (encrypted) {
+ var signature = query.msg_signature;
+ valid = signature === that.cryptor.getSignature(timestamp, nonce, echostr);
+ } else {
+ // 校验
+ valid = query.signature === getSignature(timestamp, nonce, that.token);
+ }
+ if (!valid) {
+ ctx.status = 401;
+ ctx.body = 'Invalid signature';
+ } else {
+ if (encrypted) {
+ var decrypted = that.cryptor.decrypt(echostr);
+ // TODO 检查appId的正确性
+ ctx.body = decrypted.message;
+ } else {
+ ctx.body = echostr;
+ }
+ }
+ } else if (method === 'POST') {
+ if (!encrypted) {
+ // 校验
+ if (query.signature !== getSignature(timestamp, nonce, that.token)) {
+ ctx.status = 401;
+ ctx.body = 'Invalid signature';
+ return;
+ }
+ }
+ // 取原始数据
+ var xml = await getRawBody(ctx.req, {
+ length: ctx.length,
+ limit: '1mb',
+ encoding: ctx.charset
+ });
+
+ ctx.weixin_xml = xml;
+ // 解析xml
+ var result = await parseXML2(xml);
+ var formated = formatMessage(result.xml);
+ if (encrypted) {
+ var encryptMessage = formated.Encrypt;
+ if (query.msg_signature !== that.cryptor.getSignature(timestamp, nonce, encryptMessage)) {
+ ctx.status = 401;
+ ctx.body = 'Invalid signature';
+ return;
+ }
+ var decryptedXML = that.cryptor.decrypt(encryptMessage);
+ var messageWrapXml = decryptedXML.message;
+ if (messageWrapXml === '') {
+ ctx.status = 401;
+ ctx.body = 'Invalid signature';
+ return;
+ }
+ //var decodedXML = yield parseXML(messageWrapXml);
+ var decodedXML = await parseXML2(messageWrapXml);
+ formated = formatMessage(decodedXML.xml);
+ }
+
+ // 挂载处理后的微信消息
+ ctx.weixin = formated;
+ await handle(ctx, next);
+/*
+ // 取session数据
+ if (ctx.sessionStore) {
+ ctx.wxSessionId = formated.FromUserName;
+ ctx.wxsession = yield ctx.sessionStore.get(ctx.wxSessionId);
+ if (!ctx.wxsession) {
+ ctx.wxsession = {};
+ ctx.wxsession.cookie = ctx.session.cookie;
+ }
+ }
+
+ // 业务逻辑处理
+ yield* handle.call(ctx);
+
+ // 更新session
+ if (ctx.sessionStore) {
+ if (!ctx.wxsession) {
+ if (ctx.wxSessionId) {
+ yield ctx.sessionStore.destroy(ctx.wxSessionId);
+ }
+ } else {
+ yield ctx.sessionStore.set(ctx.wxSessionId, ctx.wxsession);
+ }
+ }
+*/
+ /*
+ * 假如服务器无法保证在五秒内处理并回复,可以直接回复空串。
+ * 微信服务器不会对此作任何处理,并且不会发起重试。
+ */
+ if (ctx.body === '') {
+ return;
+ }
+
+ var replyMessageXml = reply(ctx.body, formated.ToUserName, formated.FromUserName);
+
+ if (!query.encrypt_type || query.encrypt_type === 'raw') {
+ ctx.body = replyMessageXml;
+ } else {
+ var wrap = {};
+ wrap.encrypt = that.cryptor.encrypt(replyMessageXml);
+ wrap.nonce = parseInt((Math.random() * 100000000000), 10);
+ wrap.timestamp = new Date().getTime();
+ wrap.signature = that.cryptor.getSignature(wrap.timestamp, wrap.nonce, wrap.encrypt);
+ ctx.body = encryptWrap(wrap);
+ }
+
+ ctx.type = 'application/xml';
+
+ } else {
+ ctx.status = 501;
+ ctx.body = 'Not Implemented';
+ }
+ };
+};
+
+module.exports = wechat;