├── 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 | [![npm version](https://badge.fury.io/js/wx_jsapi_sign.svg)](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 | 微信JS-SDK Demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | 57 |
58 |
59 | 60 | 判断当前客户端是否支持指定JS接口 61 | 62 | 63 | 64 | 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 65 | 66 | 获取“分享给朋友”按钮点击状态及自定义分享内容接口 67 | 68 | 获取“分享到QQ”按钮点击状态及自定义分享内容接口 69 | 70 | 获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 71 | 72 | 73 | 74 | 拍照或从手机相册中选图接口 75 | 76 | 预览图片接口 77 | 78 | 上传图片接口 79 | 80 | 下载图片接口 81 | 82 | 83 | 84 | 开始录音接口 85 | 86 | 停止录音接口 87 | 88 | 播放语音接口 89 | 90 | 暂停播放接口 91 | 92 | 停止播放接口 93 | 94 | 上传语音接口 95 | 96 | 下载语音接口 97 | 98 | 99 | 100 | 识别音频并返回识别结果接口 101 | 102 | 103 | 104 | 获取网络状态接口 105 | 106 | 107 | 108 | 使用微信内置地图查看位置接口 109 | 110 | 获取地理位置接口 111 | 112 | 113 | 114 | 隐藏右上角菜单接口 115 | 116 | 显示右上角菜单接口 117 | 118 | 关闭当前网页窗口接口 119 | 120 | 批量隐藏功能按钮接口 121 | 122 | 批量显示功能按钮接口 123 | 124 | 隐藏所有非基础按钮接口 125 | 126 | 显示所有功能按钮接口 127 | 128 | 129 | 130 | 调起微信扫一扫接口 131 | 132 | 133 | 134 | 135 | 跳转微信商品页接口 136 | 137 | 138 | 139 | 批量添加卡券接口 140 | 141 | 调起适用于门店的卡券列表并获取用户选择列表 142 | 143 | 查看微信卡包中的卡券接口 144 | 145 | 146 | 147 | 发起一个微信支付请求 148 | 149 |
150 |
151 | 152 | 153 | 154 | 274 | 275 | 276 | 277 | -------------------------------------------------------------------------------- /test/public/demo.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 注意: 3 | * 1. 所有的JS接口只能在公众号绑定的域名下调用,公众号开发者需要先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。 4 | * 2. 如果发现在 Android 不能分享自定义内容,请到官网下载最新的包覆盖安装,Android 自定义分享接口需升级至 6.0.2.58 版本及以上。 5 | * 3. 完整 JS-SDK 文档地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html 6 | * 7 | * 如有问题请通过以下渠道反馈: 8 | * 邮箱地址:weixin-open@qq.com 9 | * 邮件主题:【微信JS-SDK反馈】具体问题 10 | * 邮件内容说明:用简明的语言描述问题所在,并交代清楚遇到该问题的场景,可附上截屏图片,微信团队会尽快处理你的反馈。 11 | */ 12 | wx.ready(function () { 13 | // 1 判断当前版本是否支持指定 JS 接口,支持批量判断 14 | document.querySelector('#checkJsApi').onclick = function () { 15 | wx.checkJsApi({ 16 | jsApiList: [ 17 | 'getNetworkType', 18 | 'previewImage' 19 | ], 20 | success: function (res) { 21 | alert(JSON.stringify(res)); 22 | } 23 | }); 24 | }; 25 | 26 | // 2. 分享接口 27 | // 2.1 监听“分享给朋友”,按钮点击、自定义分享内容及分享结果接口 28 | document.querySelector('#onMenuShareAppMessage').onclick = function () { 29 | wx.onMenuShareAppMessage({ 30 | title: '互联网之子', 31 | desc: '在长大的过程中,我才慢慢发现,我身边的所有事,别人跟我说的所有事,那些所谓本来如此,注定如此的事,它们其实没有非得如此,事情是可以改变的。更重要的是,有些事既然错了,那就该做出改变。', 32 | link: 'http://movie.douban.com/subject/25785114/', 33 | imgUrl: 'http://img3.douban.com/view/movie_poster_cover/spst/public/p2166127561.jpg', 34 | trigger: function (res) { 35 | alert('用户点击发送给朋友'); 36 | }, 37 | success: function (res) { 38 | alert('已分享'); 39 | }, 40 | cancel: function (res) { 41 | alert('已取消'); 42 | }, 43 | fail: function (res) { 44 | alert(JSON.stringify(res)); 45 | } 46 | }); 47 | alert('已注册获取“发送给朋友”状态事件'); 48 | }; 49 | 50 | // 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口 51 | document.querySelector('#onMenuShareTimeline').onclick = function () { 52 | wx.onMenuShareTimeline({ 53 | title: '互联网之子', 54 | link: 'http://movie.douban.com/subject/25785114/', 55 | imgUrl: 'http://img3.douban.com/view/movie_poster_cover/spst/public/p2166127561.jpg', 56 | trigger: function (res) { 57 | alert('用户点击分享到朋友圈'); 58 | }, 59 | success: function (res) { 60 | alert('已分享'); 61 | }, 62 | cancel: function (res) { 63 | alert('已取消'); 64 | }, 65 | fail: function (res) { 66 | alert(JSON.stringify(res)); 67 | } 68 | }); 69 | alert('已注册获取“分享到朋友圈”状态事件'); 70 | }; 71 | 72 | // 2.3 监听“分享到QQ”按钮点击、自定义分享内容及分享结果接口 73 | document.querySelector('#onMenuShareQQ').onclick = function () { 74 | wx.onMenuShareQQ({ 75 | title: '互联网之子', 76 | desc: '在长大的过程中,我才慢慢发现,我身边的所有事,别人跟我说的所有事,那些所谓本来如此,注定如此的事,它们其实没有非得如此,事情是可以改变的。更重要的是,有些事既然错了,那就该做出改变。', 77 | link: 'http://movie.douban.com/subject/25785114/', 78 | imgUrl: 'http://img3.douban.com/view/movie_poster_cover/spst/public/p2166127561.jpg', 79 | trigger: function (res) { 80 | alert('用户点击分享到QQ'); 81 | }, 82 | complete: function (res) { 83 | alert(JSON.stringify(res)); 84 | }, 85 | success: function (res) { 86 | alert('已分享'); 87 | }, 88 | cancel: function (res) { 89 | alert('已取消'); 90 | }, 91 | fail: function (res) { 92 | alert(JSON.stringify(res)); 93 | } 94 | }); 95 | alert('已注册获取“分享到 QQ”状态事件'); 96 | }; 97 | 98 | // 2.4 监听“分享到微博”按钮点击、自定义分享内容及分享结果接口 99 | document.querySelector('#onMenuShareWeibo').onclick = function () { 100 | wx.onMenuShareWeibo({ 101 | title: '互联网之子', 102 | desc: '在长大的过程中,我才慢慢发现,我身边的所有事,别人跟我说的所有事,那些所谓本来如此,注定如此的事,它们其实没有非得如此,事情是可以改变的。更重要的是,有些事既然错了,那就该做出改变。', 103 | link: 'http://movie.douban.com/subject/25785114/', 104 | imgUrl: 'http://img3.douban.com/view/movie_poster_cover/spst/public/p2166127561.jpg', 105 | trigger: function (res) { 106 | alert('用户点击分享到微博'); 107 | }, 108 | complete: function (res) { 109 | alert(JSON.stringify(res)); 110 | }, 111 | success: function (res) { 112 | alert('已分享'); 113 | }, 114 | cancel: function (res) { 115 | alert('已取消'); 116 | }, 117 | fail: function (res) { 118 | alert(JSON.stringify(res)); 119 | } 120 | }); 121 | alert('已注册获取“分享到微博”状态事件'); 122 | }; 123 | 124 | 125 | // 3 智能接口 126 | var voice = { 127 | localId: '', 128 | serverId: '' 129 | }; 130 | // 3.1 识别音频并返回识别结果 131 | document.querySelector('#translateVoice').onclick = function () { 132 | if (voice.localId == '') { 133 | alert('请先使用 startRecord 接口录制一段声音'); 134 | return; 135 | } 136 | wx.translateVoice({ 137 | localId: voice.localId, 138 | complete: function (res) { 139 | if (res.hasOwnProperty('translateResult')) { 140 | alert('识别结果:' + res.translateResult); 141 | } else { 142 | alert('无法识别'); 143 | } 144 | } 145 | }); 146 | }; 147 | 148 | // 4 音频接口 149 | // 4.2 开始录音 150 | document.querySelector('#startRecord').onclick = function () { 151 | wx.startRecord({ 152 | cancel: function () { 153 | alert('用户拒绝授权录音'); 154 | } 155 | }); 156 | }; 157 | 158 | // 4.3 停止录音 159 | document.querySelector('#stopRecord').onclick = function () { 160 | wx.stopRecord({ 161 | success: function (res) { 162 | voice.localId = res.localId; 163 | }, 164 | fail: function (res) { 165 | alert(JSON.stringify(res)); 166 | } 167 | }); 168 | }; 169 | 170 | // 4.4 监听录音自动停止 171 | wx.onVoiceRecordEnd({ 172 | complete: function (res) { 173 | voice.localId = res.localId; 174 | alert('录音时间已超过一分钟'); 175 | } 176 | }); 177 | 178 | // 4.5 播放音频 179 | document.querySelector('#playVoice').onclick = function () { 180 | if (voice.localId == '') { 181 | alert('请先使用 startRecord 接口录制一段声音'); 182 | return; 183 | } 184 | wx.playVoice({ 185 | localId: voice.localId 186 | }); 187 | }; 188 | 189 | // 4.6 暂停播放音频 190 | document.querySelector('#pauseVoice').onclick = function () { 191 | wx.pauseVoice({ 192 | localId: voice.localId 193 | }); 194 | }; 195 | 196 | // 4.7 停止播放音频 197 | document.querySelector('#stopVoice').onclick = function () { 198 | wx.stopVoice({ 199 | localId: voice.localId 200 | }); 201 | }; 202 | 203 | // 4.8 监听录音播放停止 204 | wx.onVoicePlayEnd({ 205 | complete: function (res) { 206 | alert('录音(' + res.localId + ')播放结束'); 207 | } 208 | }); 209 | 210 | // 4.8 上传语音 211 | document.querySelector('#uploadVoice').onclick = function () { 212 | if (voice.localId == '') { 213 | alert('请先使用 startRecord 接口录制一段声音'); 214 | return; 215 | } 216 | wx.uploadVoice({ 217 | localId: voice.localId, 218 | success: function (res) { 219 | alert('上传语音成功,serverId 为' + res.serverId); 220 | voice.serverId = res.serverId; 221 | } 222 | }); 223 | }; 224 | 225 | // 4.9 下载语音 226 | document.querySelector('#downloadVoice').onclick = function () { 227 | if (voice.serverId == '') { 228 | alert('请先使用 uploadVoice 上传声音'); 229 | return; 230 | } 231 | wx.downloadVoice({ 232 | serverId: voice.serverId, 233 | success: function (res) { 234 | alert('下载语音成功,localId 为' + res.localId); 235 | voice.localId = res.localId; 236 | } 237 | }); 238 | }; 239 | 240 | // 5 图片接口 241 | // 5.1 拍照、本地选图 242 | var images = { 243 | localId: [], 244 | serverId: [] 245 | }; 246 | document.querySelector('#chooseImage').onclick = function () { 247 | wx.chooseImage({ 248 | success: function (res) { 249 | images.localId = res.localIds; 250 | alert('已选择 ' + res.localIds.length + ' 张图片'); 251 | } 252 | }); 253 | }; 254 | 255 | // 5.2 图片预览 256 | document.querySelector('#previewImage').onclick = function () { 257 | wx.previewImage({ 258 | current: 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg', 259 | urls: [ 260 | 'http://img3.douban.com/view/photo/photo/public/p2152117150.jpg', 261 | 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg', 262 | 'http://img3.douban.com/view/photo/photo/public/p2152134700.jpg' 263 | ] 264 | }); 265 | }; 266 | 267 | // 5.3 上传图片 268 | document.querySelector('#uploadImage').onclick = function () { 269 | if (images.localId.length == 0) { 270 | alert('请先使用 chooseImage 接口选择图片'); 271 | return; 272 | } 273 | var i = 0, length = images.localId.length; 274 | images.serverId = []; 275 | function upload() { 276 | wx.uploadImage({ 277 | localId: images.localId[i], 278 | success: function (res) { 279 | i++; 280 | alert('已上传:' + i + '/' + length); 281 | images.serverId.push(res.serverId); 282 | if (i < length) { 283 | upload(); 284 | } 285 | }, 286 | fail: function (res) { 287 | alert(JSON.stringify(res)); 288 | } 289 | }); 290 | } 291 | upload(); 292 | }; 293 | 294 | // 5.4 下载图片 295 | document.querySelector('#downloadImage').onclick = function () { 296 | if (images.serverId.length === 0) { 297 | alert('请先使用 uploadImage 上传图片'); 298 | return; 299 | } 300 | var i = 0, length = images.serverId.length; 301 | images.localId = []; 302 | function download() { 303 | wx.downloadImage({ 304 | serverId: images.serverId[i], 305 | success: function (res) { 306 | i++; 307 | alert('已下载:' + i + '/' + length); 308 | images.localId.push(res.localId); 309 | if (i < length) { 310 | download(); 311 | } 312 | } 313 | }); 314 | } 315 | download(); 316 | }; 317 | 318 | // 6 设备信息接口 319 | // 6.1 获取当前网络状态 320 | document.querySelector('#getNetworkType').onclick = function () { 321 | wx.getNetworkType({ 322 | success: function (res) { 323 | alert(res.networkType); 324 | }, 325 | fail: function (res) { 326 | alert(JSON.stringify(res)); 327 | } 328 | }); 329 | }; 330 | 331 | // 7 地理位置接口 332 | // 7.1 查看地理位置 333 | document.querySelector('#openLocation').onclick = function () { 334 | wx.openLocation({ 335 | latitude: 23.099994, 336 | longitude: 113.324520, 337 | name: 'TIT 创意园', 338 | address: '广州市海珠区新港中路 397 号', 339 | scale: 14, 340 | infoUrl: 'http://weixin.qq.com' 341 | }); 342 | }; 343 | 344 | // 7.2 获取当前地理位置 345 | document.querySelector('#getLocation').onclick = function () { 346 | wx.getLocation({ 347 | success: function (res) { 348 | alert(JSON.stringify(res)); 349 | }, 350 | cancel: function (res) { 351 | alert('用户拒绝授权获取地理位置'); 352 | } 353 | }); 354 | }; 355 | 356 | // 8 界面操作接口 357 | // 8.1 隐藏右上角菜单 358 | document.querySelector('#hideOptionMenu').onclick = function () { 359 | wx.hideOptionMenu(); 360 | }; 361 | 362 | // 8.2 显示右上角菜单 363 | document.querySelector('#showOptionMenu').onclick = function () { 364 | wx.showOptionMenu(); 365 | }; 366 | 367 | // 8.3 批量隐藏菜单项 368 | document.querySelector('#hideMenuItems').onclick = function () { 369 | wx.hideMenuItems({ 370 | menuList: [ 371 | 'menuItem:readMode', // 阅读模式 372 | 'menuItem:share:timeline', // 分享到朋友圈 373 | 'menuItem:copyUrl' // 复制链接 374 | ], 375 | success: function (res) { 376 | alert('已隐藏“阅读模式”,“分享到朋友圈”,“复制链接”等按钮'); 377 | }, 378 | fail: function (res) { 379 | alert(JSON.stringify(res)); 380 | } 381 | }); 382 | }; 383 | 384 | // 8.4 批量显示菜单项 385 | document.querySelector('#showMenuItems').onclick = function () { 386 | wx.showMenuItems({ 387 | menuList: [ 388 | 'menuItem:readMode', // 阅读模式 389 | 'menuItem:share:timeline', // 分享到朋友圈 390 | 'menuItem:copyUrl' // 复制链接 391 | ], 392 | success: function (res) { 393 | alert('已显示“阅读模式”,“分享到朋友圈”,“复制链接”等按钮'); 394 | }, 395 | fail: function (res) { 396 | alert(JSON.stringify(res)); 397 | } 398 | }); 399 | }; 400 | 401 | // 8.5 隐藏所有非基本菜单项 402 | document.querySelector('#hideAllNonBaseMenuItem').onclick = function () { 403 | wx.hideAllNonBaseMenuItem({ 404 | success: function () { 405 | alert('已隐藏所有非基本菜单项'); 406 | } 407 | }); 408 | }; 409 | 410 | // 8.6 显示所有被隐藏的非基本菜单项 411 | document.querySelector('#showAllNonBaseMenuItem').onclick = function () { 412 | wx.showAllNonBaseMenuItem({ 413 | success: function () { 414 | alert('已显示所有非基本菜单项'); 415 | } 416 | }); 417 | }; 418 | 419 | // 8.7 关闭当前窗口 420 | document.querySelector('#closeWindow').onclick = function () { 421 | wx.closeWindow(); 422 | }; 423 | 424 | // 9 微信原生接口 425 | // 9.1.1 扫描二维码并返回结果 426 | document.querySelector('#scanQRCode0').onclick = function () { 427 | wx.scanQRCode({ 428 | desc: 'scanQRCode desc' 429 | }); 430 | }; 431 | // 9.1.2 扫描二维码并返回结果 432 | document.querySelector('#scanQRCode1').onclick = function () { 433 | wx.scanQRCode({ 434 | needResult: 1, 435 | desc: 'scanQRCode desc', 436 | success: function (res) { 437 | alert(JSON.stringify(res)); 438 | } 439 | }); 440 | }; 441 | 442 | // 10 微信支付接口 443 | // 10.1 发起一个支付请求 444 | document.querySelector('#chooseWXPay').onclick = function () { 445 | wx.chooseWXPay({ 446 | timestamp: 1414723227, 447 | nonceStr: 'noncestr', 448 | package: 'addition=action_id%3dgaby1234%26limit_pay%3d&bank_type=WX&body=innertest&fee_type=1&input_charset=GBK¬ify_url=http%3A%2F%2F120.204.206.246%2Fcgi-bin%2Fmmsupport-bin%2Fnotifypay&out_trade_no=1414723227818375338&partner=1900000109&spbill_create_ip=127.0.0.1&total_fee=1&sign=432B647FE95C7BF73BCD177CEECBEF8D', 449 | paySign: 'bd5b1933cda6e9548862944836a9b52e8c9a2b69' 450 | }); 451 | }; 452 | 453 | // 11.3 跳转微信商品页 454 | document.querySelector('#openProductSpecificView').onclick = function () { 455 | wx.openProductSpecificView({ 456 | productId: 'pDF3iY_m2M7EQ5EKKKWd95kAxfNw' 457 | }); 458 | }; 459 | 460 | // 12 微信卡券接口 461 | // 12.1 添加卡券 462 | document.querySelector('#addCard').onclick = function () { 463 | wx.addCard({ 464 | cardList: [ 465 | { 466 | cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo', 467 | cardExt: '{"code": "", "openid": "", "timestamp": "1418301401", "signature":"64e6a7cc85c6e84b726f2d1cbef1b36e9b0f9750"}' 468 | }, 469 | { 470 | cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo', 471 | cardExt: '{"code": "", "openid": "", "timestamp": "1418301401", "signature":"64e6a7cc85c6e84b726f2d1cbef1b36e9b0f9750"}' 472 | } 473 | ], 474 | success: function (res) { 475 | alert('已添加卡券:' + JSON.stringify(res.cardList)); 476 | } 477 | }); 478 | }; 479 | 480 | // 12.2 选择卡券 481 | document.querySelector('#chooseCard').onclick = function () { 482 | wx.chooseCard({ 483 | cardSign: '97e9c5e58aab3bdf6fd6150e599d7e5806e5cb91', 484 | timestamp: 1417504553, 485 | nonceStr: 'k0hGdSXKZEj3Min5', 486 | success: function (res) { 487 | alert('已选择卡券:' + JSON.stringify(res.cardList)); 488 | } 489 | }); 490 | }; 491 | 492 | // 12.3 查看卡券 493 | document.querySelector('#openCard').onclick = function () { 494 | alert('您没有该公众号的卡券无法打开卡券。'); 495 | wx.openCard({ 496 | cardList: [ 497 | ] 498 | }); 499 | }; 500 | 501 | var shareData = { 502 | title: '微信JS-SDK Demo', 503 | desc: '微信JS-SDK,帮助第三方为用户提供更优质的移动web服务', 504 | link: 'http://demo.open.weixin.qq.com/jssdk/', 505 | imgUrl: 'http://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRt8Qia4lv7k3M9J1SKqKCImxJCt7j9rHYicKDI45jRPBxdzdyREWnk0ia0N5TMnMfth7SdxtzMvVgXg/0' 506 | }; 507 | wx.onMenuShareAppMessage(shareData); 508 | wx.onMenuShareTimeline(shareData); 509 | }); 510 | 511 | wx.error(function (res) { 512 | alert(res.errMsg); 513 | }); 514 | --------------------------------------------------------------------------------