├── index.js ├── config.js ├── config.example.js ├── package.json ├── .gitignore ├── test ├── app.js └── public │ ├── style.css │ ├── jweixin-1.0.0.js │ ├── test.html │ └── demo.js ├── README.md └── lib └── wx_jsapi_sign.js /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/wx_jsapi_sign'); 2 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | // 输入你的配置 3 | return { 4 | appId: '', 5 | appSecret: '', 6 | appToken: 'SHANG', 7 | cache_json_file:'/tmp' 8 | }; 9 | }; -------------------------------------------------------------------------------- /config.example.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | // 输入你的配置 3 | return { 4 | appId: '', 5 | appSecret: '', 6 | appToken: 'SHANG', 7 | cache_json_file:'/tmp' 8 | }; 9 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wx_jsapi_sign", 3 | "version": "1.0.4", 4 | "description": "", 5 | "main": "index.js", 6 | "author": "", 7 | "scripts": { 8 | "start": "npm publish .", 9 | "test": "node_modules/.bin/supervisor test/app.js" 10 | }, 11 | "dependencies": { 12 | "async": "^0.9.0", 13 | "bufferhelper": "^0.2.0", 14 | "iconv-lite": "^0.4.8", 15 | "request": "^2.55.0" 16 | }, 17 | "devDependencies": { 18 | "express": "^3.0.6", 19 | "art-template": "^3.0.3", 20 | "supervisor": "^0.6.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | # node_modules 46 | *node_modules* 47 | 48 | # user 49 | .git 50 | .idea 51 | .zip 52 | upload.bat 53 | *.log 54 | test/config.js 55 | test/cache.json 56 | -------------------------------------------------------------------------------- /test/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | http = require('http'), 3 | path = require('path'), 4 | fs = require('fs'); 5 | var template = require('art-template');//此处基本无用 6 | var config = require('./config')(); 7 | console.log(config); 8 | 9 | 10 | var app = express(); 11 | app.configure(function() { 12 | app.set('port', process.env.PORT || 1342); 13 | 14 | template.config('base', ''); 15 | template.config('extname', '.html'); 16 | app.engine('.html', template.__express); 17 | app.set('view engine', 'html'); 18 | app.use(express.favicon()); 19 | app.use(express.logger('dev')); 20 | app.use(express.bodyParser()); 21 | app.use(express.methodOverride()); 22 | app.use(app.router); 23 | app.use(express.static(path.join(__dirname, 'public'))); 24 | 25 | // user 26 | // 这是用来 在接口配置信息 中验证的; 仅仅使用 JS-SDK 不需要使用; 27 | // app.use(signature.checkSignature(config)); 28 | }); 29 | 30 | app.configure('development', function() { 31 | app.use(express.errorHandler()); 32 | }); 33 | 34 | var signature = require('../index'); 35 | var config = require('./config')(); 36 | 37 | 38 | app.get('/test', function(req, res) { 39 | var u = req.protocol + "://" + req.get('Host') + req.url; 40 | signature.getSignature(config)(u, function(error, result) { 41 | console.log(result); 42 | res.render(__dirname + '/public/test.html', result); 43 | }); 44 | }); 45 | 46 | app.post('/getsignature', function(req, res){ 47 | var url = req.body.url; 48 | console.log(url); 49 | signature.getSignature(config)(url, function(error, result) { 50 | if (error) { 51 | res.json({ 52 | 'error': error 53 | }); 54 | } else { 55 | res.json(result); 56 | } 57 | }); 58 | }); 59 | 60 | 61 | http.createServer(app).listen(app.get('port'), function() { 62 | console.log("Express server listening on port " + app.get('port')); 63 | }); 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wx_jsapi_sign(nodejs) 2 | 3 | wx_jsapi_sign = wechat(微信) js-api signature implement. 4 | 5 | [](http://badge.fury.io/js/wx_jsapi_sign) 6 | 7 | - 支持集群,将获得的前面写到cache.json里 8 | - 使用cache.json保存,比如用redis省事,更省内存 9 | - 足够小巧,便于集成 10 | 11 | ## Install 12 | 13 | npm install --save wx_jsapi_sign 14 | 15 | ## Usage 16 | 17 | copy config file 18 | 19 | ``` 20 | cp node_modules/wx_jsapi_sign/config.example.js config.js 21 | ``` 22 | 23 | **change appId && appSecret** 24 | 25 | - [微信公众平台如何获取appid和appsecret](http://jingyan.baidu.com/article/6525d4b12af618ac7c2e9468.html) 26 | 27 | then mount a route in app.js 28 | 29 | ``` 30 | var signature = require('wx_jsapi_sign'); 31 | var config = require('./config')(); 32 | 33 | .... 34 | 35 | app.post('/getsignature', function(req, res){ 36 | var url = req.body.url; 37 | console.log(url); 38 | signature.getSignature(config)(url, function(error, result) { 39 | if (error) { 40 | res.json({ 41 | 'error': error 42 | }); 43 | } else { 44 | res.json(result); 45 | } 46 | }); 47 | }); 48 | ``` 49 | 50 | more usages see `test/public/test.html` 51 | 52 | ## Test 53 | 54 | 微信访问网址 `http://127.0.0.1:1342/test` 55 | 56 | 57 | ## 原作者博客 58 | 59 | http://blog.xinshangshangxin.com/2015/04/22/%E4%BD%BF%E7%94%A8nodejs-%E8%B8%A9%E5%9D%91%E5%BE%AE%E4%BF%A1JS-SDK%E8%AE%B0%E5%BD%95/ 60 | 61 | 62 | ## 微信接口 63 | 64 | ### getWechatToken 65 | 66 | 获取微信 access token,7200秒刷新一次 ( http://mp.weixin.qq.com/wiki/15/54ce45d8d30b6bf6758f68d2e95bc627.html ) 67 | 68 | 参数:需要正确设置config.js 69 | 70 | 71 | ### getWechatJsapiTicket 72 | 73 | 获取微信 JS API 所要求的 ticket,7200秒刷新一次 ( http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html ) 74 | 75 | 参数:需要正确设置config.js 76 | 77 | 78 | ### getWechatJsapiSign 79 | 80 | 根据用户参数生成微信 JS API 要求的签名 ( http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html ) 81 | 82 | 参数: 83 | * `noncestr` 必须参数,使用者自己生成的一个随机字符串,签名用的noncestr必须与wx.config中的nonceStr相同 84 | * `timestamp` 必须参数,使用者在调用微信 JS API 时的Unix时间戳,签名用的timestamp必须与wx.config中的timestamp相同 85 | * `url` 必须参数,签名用的url必须是调用JS接口页面的完整URL,其中的特殊字符,例如&、空格必须转义为%26、%20,参考:http://www.w3school.com.cn/tags/html_ref_urlencode.html 86 | 87 | -------------------------------------------------------------------------------- /test/public/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | -ms-text-size-adjust: 100%; 3 | -webkit-text-size-adjust: 100%; 4 | -webkit-user-select: none; 5 | user-select: none; 6 | } 7 | body { 8 | line-height: 1.6; 9 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 10 | background-color: #f1f0f6; 11 | } 12 | * { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | button { 17 | font-family: inherit; 18 | font-size: 100%; 19 | margin: 0; 20 | *font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 21 | } 22 | ul, 23 | ol { 24 | padding-left: 0; 25 | list-style-type: none; 26 | } 27 | a { 28 | text-decoration: none; 29 | } 30 | .label_box { 31 | background-color: #ffffff; 32 | } 33 | .label_item { 34 | padding-left: 15px; 35 | } 36 | .label_inner { 37 | padding-top: 10px; 38 | padding-bottom: 10px; 39 | min-height: 24px; 40 | position: relative; 41 | } 42 | .label_inner:before { 43 | content: " "; 44 | position: absolute; 45 | left: 0; 46 | top: 0; 47 | width: 200%; 48 | height: 1px; 49 | border-top: 1px solid #ededed; 50 | -webkit-transform-origin: 0 0; 51 | transform-origin: 0 0; 52 | -webkit-transform: scale(0.5); 53 | transform: scale(0.5); 54 | top: auto; 55 | bottom: -2px; 56 | } 57 | .lbox_close { 58 | position: relative; 59 | } 60 | .lbox_close:before { 61 | content: " "; 62 | position: absolute; 63 | left: 0; 64 | top: 0; 65 | width: 200%; 66 | height: 1px; 67 | border-top: 1px solid #ededed; 68 | -webkit-transform-origin: 0 0; 69 | transform-origin: 0 0; 70 | -webkit-transform: scale(0.5); 71 | transform: scale(0.5); 72 | } 73 | .lbox_close:after { 74 | content: " "; 75 | position: absolute; 76 | left: 0; 77 | top: 0; 78 | width: 200%; 79 | height: 1px; 80 | border-top: 1px solid #ededed; 81 | -webkit-transform-origin: 0 0; 82 | transform-origin: 0 0; 83 | -webkit-transform: scale(0.5); 84 | transform: scale(0.5); 85 | top: auto; 86 | bottom: -2px; 87 | } 88 | .lbox_close .label_item:last-child .label_inner:before { 89 | display: none; 90 | } 91 | .btn { 92 | display: block; 93 | margin-left: auto; 94 | margin-right: auto; 95 | padding-left: 14px; 96 | padding-right: 14px; 97 | font-size: 18px; 98 | text-align: center; 99 | text-decoration: none; 100 | overflow: visible; 101 | /*.btn_h(@btnHeight);*/ 102 | height: 42px; 103 | border-radius: 5px; 104 | -moz-border-radius: 5px; 105 | -webkit-border-radius: 5px; 106 | box-sizing: border-box; 107 | -moz-box-sizing: border-box; 108 | -webkit-box-sizing: border-box; 109 | color: #ffffff; 110 | line-height: 42px; 111 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0); 112 | } 113 | .btn.btn_inline { 114 | display: inline-block; 115 | } 116 | .btn_primary { 117 | background-color: #04be02; 118 | } 119 | .btn_primary:not(.btn_disabled):visited { 120 | color: #ffffff; 121 | } 122 | .btn_primary:not(.btn_disabled):active { 123 | color: rgba(255, 255, 255, 0.9); 124 | background-color: #039702; 125 | } 126 | button.btn { 127 | width: 100%; 128 | border: 0; 129 | outline: 0; 130 | -webkit-appearance: none; 131 | } 132 | button.btn:focus { 133 | outline: 0; 134 | } 135 | .wxapi_container { 136 | font-size: 16px; 137 | } 138 | h1 { 139 | font-size: 14px; 140 | font-weight: 400; 141 | line-height: 2em; 142 | padding-left: 15px; 143 | color: #8d8c92; 144 | } 145 | .desc { 146 | font-size: 14px; 147 | font-weight: 400; 148 | line-height: 2em; 149 | color: #8d8c92; 150 | } 151 | .wxapi_index_item a { 152 | display: block; 153 | color: #3e3e3e; 154 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 155 | } 156 | .wxapi_form { 157 | background-color: #ffffff; 158 | padding: 0 15px; 159 | margin-top: 30px; 160 | padding-bottom: 15px; 161 | } 162 | h3 { 163 | padding-top: 16px; 164 | margin-top: 25px; 165 | font-size: 16px; 166 | font-weight: 400; 167 | color: #3e3e3e; 168 | position: relative; 169 | } 170 | h3:first-child { 171 | padding-top: 15px; 172 | } 173 | h3:before { 174 | content: " "; 175 | position: absolute; 176 | left: 0; 177 | top: 0; 178 | width: 200%; 179 | height: 1px; 180 | border-top: 1px solid #ededed; 181 | -webkit-transform-origin: 0 0; 182 | transform-origin: 0 0; 183 | -webkit-transform: scale(0.5); 184 | transform: scale(0.5); 185 | } 186 | .btn { 187 | margin-bottom: 15px; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /lib/wx_jsapi_sign.js: -------------------------------------------------------------------------------- 1 | var url = require('url'); 2 | var crypto = require('crypto'); 3 | var request = require('request'); 4 | var async = require('async'); 5 | var BufferHelp = require('bufferhelper'); 6 | var iconv = require('iconv-lite'); 7 | var fs = require('fs'); 8 | 9 | 10 | var cache = { 11 | ticket: null, 12 | time: 0 13 | }; 14 | 15 | function getSignature(config, url, cb) { 16 | // console.log('start getSignature'); 17 | // 判断内存中是否有缓存 18 | if (!cache || !cache.ticket) { 19 | console.log('readCache'); 20 | readFile(config.cache_json_file + '/cache.json', function(str) { 21 | if (str) { 22 | console.log(str); 23 | cache = JSON.parse(str); 24 | } 25 | tryGetSignature(config, url, cb); 26 | }); 27 | } 28 | else { 29 | tryGetSignature(config, url, cb); 30 | } 31 | } 32 | 33 | function checkSignature(config) { 34 | return function(req, res, next) { 35 | // console.log('checkSignature'); 36 | req.query = url.parse(req.url, true).query; 37 | 38 | if (req.query.getsignature) { 39 | // console.log('req.query.getsignature'); 40 | return next(); 41 | } 42 | 43 | 44 | if (!req.query.signature) { 45 | return res.end('Access Denied!'); 46 | } 47 | var tmp = [config.appToken, req.query.timestamp, req.query.nonce].sort().join(''); 48 | var signature = crypto.createHash('sha1').update(tmp).digest('hex'); 49 | if (req.query.signature != signature) { 50 | console.log('req.query.signature != signature'); 51 | return res.end('Auth failed!'); // 指纹码不匹配时返回错误信息,禁止后面的消息接受及发送 52 | } 53 | if (req.query.echostr) { 54 | // console.log('req.query.echostr'); 55 | return res.end(req.query.echostr); // 添加公众号接口地址时,返回查询字符串echostr表示验证通过 56 | } 57 | // 消息真实性验证通过,继续后面的处理 58 | return next(); 59 | }; 60 | } 61 | 62 | 63 | function getToken(config, cb) { 64 | var tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appId=' + config.appId + '&secret=' + config.appSecret; 65 | 66 | request.get(tokenUrl, function(error, response, body) { 67 | if (error) { 68 | cb('getToken error', error); 69 | } 70 | else { 71 | 72 | try { 73 | var token = JSON.parse(body).access_token; 74 | cb(null, token); 75 | } 76 | catch (e) { 77 | cb('getToken error', e); 78 | } 79 | } 80 | }); 81 | } 82 | 83 | function getNewTicket(token, cb) { 84 | request.get('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + token + '&type=jsapi', function(error, res, body) { 85 | if (error) { 86 | cb('getNewTicket error', error); 87 | } 88 | else { 89 | try { 90 | // console.log(JSON.parse(body)); 91 | var ticket = JSON.parse(body).ticket; 92 | cb(null, ticket); 93 | } 94 | catch (e) { 95 | cb('getNewTicket error', e); 96 | } 97 | } 98 | }); 99 | } 100 | 101 | 102 | 103 | function tryGetSignature(config, u, cb) { 104 | // 判断cache 是否过期 105 | if (!cache.ticket || (new Date().getTime() - cache.time) > 7000000) { 106 | cache.time = cache.time + 10 * 1000; // 缓解高并发多次获取 107 | async.waterfall([function(cb) { 108 | console.log('start getNew Ticket', cache); 109 | getToken(config, cb); 110 | }, function(token, cb) { 111 | getNewTicket(token, cb); 112 | }], function(error, result) { 113 | if (error) { 114 | cb('getToken getNewTicket error', error); 115 | } 116 | else { 117 | cache.ticket = result; 118 | cache.time = new Date().getTime(); 119 | // 文件保存 120 | writeFile(config.cache_json_file + '/cache.json', JSON.stringify(cache)); 121 | // console.log(result); 122 | 123 | var timestamp = getTimesTamp(); 124 | var noncestr = getNonceStr(); 125 | var str = 'jsapi_ticket=' + result + '&noncestr='+ noncestr+'×tamp=' + timestamp + '&url=' + u; 126 | // console.log(str); 127 | var signature = crypto.createHash('sha1').update(str).digest('hex'); 128 | cb(null, { 129 | appId: config.appId, 130 | timestamp: timestamp, 131 | nonceStr: noncestr, 132 | signature: signature 133 | }); 134 | } 135 | }); 136 | } 137 | else { 138 | // console.log('缓存获取'); 139 | var timestamp = getTimesTamp(); 140 | var noncestr = getNonceStr(); 141 | var str = 'jsapi_ticket=' + cache.ticket + '&noncestr=' + noncestr + '×tamp=' + timestamp + '&url=' + u; 142 | // console.log(str); 143 | var signature = crypto.createHash('sha1').update(str).digest('hex'); 144 | cb(null, { 145 | appId: config.appId, 146 | timestamp: timestamp, 147 | nonceStr: noncestr, 148 | signature: signature 149 | }); 150 | } 151 | } 152 | 153 | function getTimesTamp() { 154 | return parseInt(new Date().getTime() / 1000) + ''; 155 | } 156 | 157 | function getNonceStr() { 158 | return Math.random().toString(36).substr(2, 15); 159 | } 160 | 161 | 162 | function readFile(path, cb) { 163 | var readstream = fs.createReadStream(path); 164 | var bf = new BufferHelp(); 165 | readstream.on('data', function(chunk) { 166 | bf.concat(chunk); 167 | }); 168 | readstream.on('end', function() { 169 | cb && cb(decodeBuffer(bf)); 170 | }); 171 | } 172 | 173 | function writeFile(path, str, cb) { 174 | var writestream = fs.createWriteStream(path); 175 | 176 | writestream.write(str); 177 | writestream.on('close', function() { 178 | cb && cb(); 179 | }); 180 | } 181 | 182 | function decodeBuffer(bf, encoding) { 183 | var val = iconv.decode(bf.toBuffer(), encoding || 'utf8'); 184 | if (val.indexOf('�') != -1) { 185 | val = iconv.decode(bf.toBuffer(), 'gbk'); 186 | } 187 | return val; 188 | } 189 | 190 | exports.checkSignature = checkSignature; 191 | 192 | exports.getSignature = function(config) { 193 | cache_json_file = config.cache_json_file + '/cache.json'; 194 | fs.exists(cache_json_file, function (exists) { 195 | if(exists === false){ 196 | fs.writeFile(cache_json_file, '{}', function (err) { 197 | if (err) throw err; 198 | console.log('cache_json_file is created!'); 199 | }); 200 | }else{ 201 | console.log('cache_json_file is exist!'); 202 | } 203 | }); 204 | 205 | return function(url, cb) { 206 | getSignature(config, url, cb); 207 | } 208 | }; 209 | -------------------------------------------------------------------------------- /test/public/jweixin-1.0.0.js: -------------------------------------------------------------------------------- 1 | !function(a,b){"function"==typeof define&&(define.amd||define.cmd)?define(function(){return b(a)}):b(a,!0)}(this,function(a,b){function c(b,c,d){a.WeixinJSBridge?WeixinJSBridge.invoke(b,e(c),function(a){i(b,a,d)}):l(b,d)}function d(b,c,d){a.WeixinJSBridge?WeixinJSBridge.on(b,function(a){d&&d.trigger&&d.trigger(a),i(b,a,c)}):d?l(b,d):l(b,c)}function e(a){return a=a||{},a.appId=B.appId,a.verifyAppId=B.appId,a.verifySignType="sha1",a.verifyTimestamp=B.timestamp+"",a.verifyNonceStr=B.nonceStr,a.verifySignature=B.signature,a}function f(a,b){return{scope:b,signType:"sha1",timeStamp:a.timestamp+"",nonceStr:a.nonceStr,addrSign:a.addrSign}}function g(a){return{timeStamp:a.timestamp+"",nonceStr:a.nonceStr,"package":a.package,paySign:a.paySign,signType:"SHA1"}}function i(a,b,c){var d,e,f;switch(delete b.err_code,delete b.err_desc,delete b.err_detail,d=b.errMsg,d||(d=b.err_msg,delete b.err_msg,d=j(a,d,c),b.errMsg=d),c=c||{},c._complete&&(c._complete(b),delete c._complete),d=b.errMsg||"",B.debug&&!c.isInnerInvoke&&alert(JSON.stringify(b)),e=d.indexOf(":"),f=d.substring(e+1)){case"ok":c.success&&c.success(b);break;case"cancel":c.cancel&&c.cancel(b);break;default:c.fail&&c.fail(b)}c.complete&&c.complete(b)}function j(a,b){var d,e,f,g;if(b){switch(d=b.indexOf(":"),a){case q.config:e="config";break;case q.openProductSpecificView:e="openProductSpecificView";break;default:e=b.substring(0,d),e=e.replace(/_/g," "),e=e.replace(/\b\w+\b/g,function(a){return a.substring(0,1).toUpperCase()+a.substring(1)}),e=e.substring(0,1).toLowerCase()+e.substring(1),e=e.replace(/ /g,""),-1!=e.indexOf("Wcpay")&&(e=e.replace("Wcpay","WCPay")),f=r[e],f&&(e=f)}g=b.substring(d+1),"confirm"==g&&(g="ok"),-1!=g.indexOf("failed_")&&(g=g.substring(7)),-1!=g.indexOf("fail_")&&(g=g.substring(5)),g=g.replace(/_/g," "),g=g.toLowerCase(),("access denied"==g||"no permission to execute"==g)&&(g="permission denied"),"config"==e&&"function not exist"==g&&(g="ok"),b=e+":"+g}return b}function k(a){var b,c,d,e;if(a){for(b=0,c=a.length;c>b;++b)d=a[b],e=q[d],e&&(a[b]=e);return a}}function l(a,b){if(B.debug&&!b.isInnerInvoke){var c=r[a];c&&(a=c),b&&b._complete&&delete b._complete,console.log('"'+a+'",',b||"")}}function m(){if(!("6.0.2">y)){var b=new Image;A.appId=B.appId,A.initTime=z.initEndTime-z.initStartTime,A.preVerifyTime=z.preVerifyEndTime-z.preVerifyStartTime,E.getNetworkType({isInnerInvoke:!0,success:function(a){A.networkType=a.networkType;var c="https://open.weixin.qq.com/sdk/report?v="+A.version+"&o="+A.isPreVerifyOk+"&s="+A.systemType+"&c="+A.clientVersion+"&a="+A.appId+"&n="+A.networkType+"&i="+A.initTime+"&p="+A.preVerifyTime+"&u="+A.url;b.src=c}})}}function n(){return(new Date).getTime()}function o(b){v&&(a.WeixinJSBridge?b():s.addEventListener&&s.addEventListener("WeixinJSBridgeReady",b,!1))}function p(){E.invoke||(E.invoke=function(b,c,d){a.WeixinJSBridge&&WeixinJSBridge.invoke(b,e(c),d)},E.on=function(b,c){a.WeixinJSBridge&&WeixinJSBridge.on(b,c)})}var q,r,s,t,u,v,w,x,y,z,A,B,C,D,E;if(!a.jWeixin)return q={config:"preVerifyJSAPI",onMenuShareTimeline:"menu:share:timeline",onMenuShareAppMessage:"menu:share:appmessage",onMenuShareQQ:"menu:share:qq",onMenuShareWeibo:"menu:share:weiboApp",previewImage:"imagePreview",getLocation:"geoLocation",openProductSpecificView:"openProductViewWithPid",addCard:"batchAddCard",openCard:"batchViewCard",chooseWXPay:"getBrandWCPayRequest"},r=function(){var b,a={};for(b in q)a[q[b]]=b;return a}(),s=a.document,t=s.title,u=navigator.userAgent.toLowerCase(),v=-1!=u.indexOf("micromessenger"),w=-1!=u.indexOf("android"),x=-1!=u.indexOf("iphone")||-1!=u.indexOf("ipad"),y=function(){var a=u.match(/micromessenger\/(\d+\.\d+\.\d+)/)||u.match(/micromessenger\/(\d+\.\d+)/);return a?a[1]:""}(),z={initStartTime:n(),initEndTime:0,preVerifyStartTime:0,preVerifyEndTime:0},A={version:1,appId:"",initTime:0,preVerifyTime:0,networkType:"",isPreVerifyOk:1,systemType:x?1:w?2:-1,clientVersion:y,url:encodeURIComponent(location.href)},B={},C={_completes:[]},D={state:0,res:{}},o(function(){z.initEndTime=n()}),E={config:function(a){B=a,l("config",a),o(function(){c(q.config,{verifyJsApiList:k(B.jsApiList)},function(){C._complete=function(a){z.preVerifyEndTime=n(),D.state=1,D.res=a},C.success=function(){A.isPreVerifyOk=0},C.fail=function(a){C._fail?C._fail(a):D.state=-1};var a=C._completes;return a.push(function(){B.debug||m()}),C.complete=function(b){for(var c=0,d=a.length;d>c;++c)a[c](b);C._completes=[]},C}()),z.preVerifyStartTime=n()}),B.beta&&p()},ready:function(a){0!=D.state?a():(C._completes.push(a),!v&&B.debug&&a())},error:function(a){"6.0.2">y||(-1==D.state?a(D.res):C._fail=a)},checkJsApi:function(a){var b=function(a){var c,d,b=a.checkResult;for(c in b)d=r[c],d&&(b[d]=b[c],delete b[c]);return a};c("checkJsApi",{jsApiList:k(a.jsApiList)},function(){return a._complete=function(a){if(w){var c=a.checkResult;c&&(a.checkResult=JSON.parse(c))}a=b(a)},a}())},onMenuShareTimeline:function(a){d(q.onMenuShareTimeline,{complete:function(){c("shareTimeline",{title:a.title||t,desc:a.title||t,img_url:a.imgUrl,link:a.link||location.href},a)}},a)},onMenuShareAppMessage:function(a){d(q.onMenuShareAppMessage,{complete:function(){c("sendAppMessage",{title:a.title||t,desc:a.desc||"",link:a.link||location.href,img_url:a.imgUrl,type:a.type||"link",data_url:a.dataUrl||""},a)}},a)},onMenuShareQQ:function(a){d(q.onMenuShareQQ,{complete:function(){c("shareQQ",{title:a.title||t,desc:a.desc||"",img_url:a.imgUrl,link:a.link||location.href},a)}},a)},onMenuShareWeibo:function(a){d(q.onMenuShareWeibo,{complete:function(){c("shareWeiboApp",{title:a.title||t,desc:a.desc||"",img_url:a.imgUrl,link:a.link||location.href},a)}},a)},startRecord:function(a){c("startRecord",{},a)},stopRecord:function(a){c("stopRecord",{},a)},onVoiceRecordEnd:function(a){d("onVoiceRecordEnd",a)},playVoice:function(a){c("playVoice",{localId:a.localId},a)},pauseVoice:function(a){c("pauseVoice",{localId:a.localId},a)},stopVoice:function(a){c("stopVoice",{localId:a.localId},a)},onVoicePlayEnd:function(a){d("onVoicePlayEnd",a)},uploadVoice:function(a){c("uploadVoice",{localId:a.localId,isShowProgressTips:a.isShowProgressTips||1},a)},downloadVoice:function(a){c("downloadVoice",{serverId:a.serverId,isShowProgressTips:a.isShowProgressTips||1},a)},translateVoice:function(a){c("translateVoice",{localId:a.localId,isShowProgressTips:a.isShowProgressTips||1},a)},chooseImage:function(a){c("chooseImage",{scene:"1|2"},function(){return a._complete=function(a){if(w){var b=a.localIds;b&&(a.localIds=JSON.parse(b))}},a}())},previewImage:function(a){c(q.previewImage,{current:a.current,urls:a.urls},a)},uploadImage:function(a){c("uploadImage",{localId:a.localId,isShowProgressTips:a.isShowProgressTips||1},a)},downloadImage:function(a){c("downloadImage",{serverId:a.serverId,isShowProgressTips:a.isShowProgressTips||1},a)},getNetworkType:function(a){var b=function(a){var c,d,e,b=a.errMsg;if(a.errMsg="getNetworkType:ok",c=a.subtype,delete a.subtype,c)a.networkType=c;else switch(d=b.indexOf(":"),e=b.substring(d+1)){case"fail":case"permission denied":case"localparameters":a.errMsg="getNetworkType:fail";break;default:a.networkType=e}return a};c("getNetworkType",{},function(){return a._complete=function(a){a=b(a)},a}())},openLocation:function(a){c("openLocation",{latitude:a.latitude,longitude:a.longitude,name:a.name||"",address:a.address||"",scale:a.scale||28,infoUrl:a.infoUrl||""},a)},getLocation:function(a){c(q.getLocation,f(a,"jsapi_location"),a)},hideOptionMenu:function(a){c("hideOptionMenu",{},a)},showOptionMenu:function(a){c("showOptionMenu",{},a)},closeWindow:function(a){c("closeWindow",{immediate_close:a&&a.immediateClose||0},a)},hideMenuItems:function(a){c("hideMenuItems",{menuList:a.menuList},a)},showMenuItems:function(a){c("showMenuItems",{menuList:a.menuList},a)},hideAllNonBaseMenuItem:function(a){c("hideAllNonBaseMenuItem",{},a)},showAllNonBaseMenuItem:function(a){c("showAllNonBaseMenuItem",{},a)},scanQRCode:function(a){c("scanQRCode",{desc:a.desc,needResult:a.needResult||0,scanType:a.scanType||["qrCode","barCode"]},a)},openProductSpecificView:function(a){c(q.openProductSpecificView,{pid:a.productId,view_type:a.viewType||0},a)},addCard:function(a){var e,f,g,h,b=a.cardList,d=[];for(e=0,f=b.length;f>e;++e)g=b[e],h={card_id:g.cardId,card_ext:g.cardExt},d.push(h);c(q.addCard,{card_list:d},function(){return a._complete=function(a){var c,d,e,b=a.card_list;if(b){for(b=JSON.parse(b),c=0,d=b.length;d>c;++c)e=b[c],e.cardId=e.card_id,e.cardExt=e.card_ext,e.isSuccess=e.is_succ?!0:!1,delete e.card_id,delete e.card_ext,delete e.is_succ;a.cardList=b,delete a.card_list}},a}())},chooseCard:function(a){c("chooseCard",{app_id:B.appId,location_id:a.shopId||"",sign_type:"SHA1",card_id:a.cardId||"",card_type:a.cardType||"",card_sign:a.cardSign,time_stamp:a.timestamp+"",nonce_str:a.nonceStr},function(){return a._complete=function(a){a.cardList=a.choose_card_info,delete a.choose_card_info},a}())},openCard:function(a){var e,f,g,h,b=a.cardList,d=[];for(e=0,f=b.length;f>e;++e)g=b[e],h={card_id:g.cardId,code:g.code},d.push(h);c(q.openCard,{card_list:d},a)},chooseWXPay:function(a){c(q.chooseWXPay,g(a),a)}},b&&(a.wx=a.jWeixin=E),E}); -------------------------------------------------------------------------------- /test/public/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |