Introduction to the
TNW: TypeScript(The) + Node.js(Next) + WeChat public account development scaffolding, support any Node.js server framework (Express, Nest, Egg, etc.)
Interface permissions and documentation
Public number interface permission description
Wechat JS-SDK documentation
How to use JSSDK
Step 1: Bind the domain name
Official number: Log in wechat public platform, enter the function setting of the public number, and fill in the JS interface security domain name
Test Number: Enter the wechat public account test number application system find the JS interface security domain name and click modify
Step 2: Import the JS file
Need to call in JS interface pages to introduce the following JS file: res2.wx.qq.com/open/js/jwe… (HTTPS is supported)
Step 3: Verify the configuration through the injection permission of the Config interface
wx.config({
debug: true.appId: '{{ appId }}'.timestamp: '{{ timestamp }}'.nonceStr: '{{ nonceStr }}'.signature: '{{ signature }}'.jsApiList: [
'checkJsApi'.'... Omit other ']});Copy the code
TNW signature implementation
For the signature algorithm, see Appendix 1 of the official document
Special note: URL must include parameters for the complete URL of the page
@Get('jssdk')
@Render('jssdk.hbs')
async jssdk(@Req() request: Request, @Res() response: Response) {
let appId = ApiConfigKit.getApiConfig.getAppId;
let timestamp = new Date().getTime() / 1000;
let nonceStr = uuid.v1();
let url = "http://xxxx/jssdk?....";// Fill in the URL of the complete page including parameters
// Generate a signature
let signature = await WeChat.jssdkSignature(nonceStr, timestamp, url);
return {
appId: appId,
timestamp: timestamp,
nonceStr: nonceStr,
signature: signature
};
}
Copy the code
Step 4: Verify the processing success through the ready interface
wx.ready(function(){
// All interface calls must be made after the result of the config interface. Config is an asynchronous operation on the client side, so if the interface needs to be called when the page loads, the ready method will be executed. The relevant interface must be called in the ready function to ensure correct execution. Interfaces that are invoked only when triggered by the user can be invoked directly without being placed in the ready function.
});
Copy the code
Step 5: Verify the processing failure through the ERROR interface
wx.error(function(res){
If the config information fails to be verified, the error function will be executed. For example, the verification fails due to the expiration of the signature. You can view the specific error information in the debug mode of config or in the returned res parameter.
});
Copy the code
JSSDK case
See Appendix 2 of the official document for a list of all JS interfaces
Special attention:
- The existing wx.onMenuShareTimeline, wx.onMenuShareAppMessage, wx.onMenuShareQQ, and wx.onMenuShareQZone interfaces are about to be abandoned. Migration as soon as possible, please use the client 6.7.2 and JSSDK version 1.4.0 above support wx. UpdateAppMessageShareData, updateTimelineShareData interface.
- Share interface
link
The link must be under the JS interface security domain name - The official account wechat pay will be introduced in detail later
<html>
<head>
<meta charset="utf-8">
<title>TNW wechat public account development scaffolding</title>
<meta charset="utf-8">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="TNW, Nodejs, wechat official account development">
<meta http-equiv="description" content="TNW wechat public account development scaffolding, can be integrated into any Node.js MVC backend framework">
<meta name="viewport"
content="Width =device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="https://www.weixinsxy.com/jssdk/css/style.css">
</head>
<body>
<div class="wxapi_container">
<div class="wxapi_index_container">
<ul class="label_box lbox_close wxapi_index_list">
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-basic">Based interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-share">Sharing interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-image">Image interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-voice">Audio interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-smart">Intelligent interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-device">Device information interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-location">Geographic interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-webview">Interface operation interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-scan">Wechat scan interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-shopping">Wechat store interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-card">Wechat card coupon interface</a></li>
<li class="label_item wxapi_index_item"><a class="label_inner" href="#menu-pay">Wechat pay interface</a></li>
</ul>
</div>
<div class="lbox_close wxapi_form">
<h3 id="menu-basic">Based interface</h3>
<span class="desc">Check whether the current client supports the specified JS interface</span>
<button class="btn btn_primary" id="checkJsApi">checkJsApi</button>
<h3 id="menu-share">Sharing interface</h3>
<span class="desc">"Share with Friends" and "Share with QQ"</span>
<button class="btn btn_primary" id="updateAppMessageShareData">updateAppMessageShareData</button>
<span class="desc">"Share to Moments" and "Share to QQ Zone"</span>
<button class="btn btn_primary" id="updateTimelineShareData">updateTimelineShareData</button>
<span class="desc">Share it on Tencent Weibo</span>
<button class="btn btn_primary" id="onMenuShareWeibo">onMenuShareWeibo</button>
<h3 id="menu-image">Image interface</h3>
<span class="desc">Take photos or select photos from your phone's photo gallery</span>
<button class="btn btn_primary" id="chooseImage">chooseImage</button>
<span class="desc">Preview picture interface</span>
<button class="btn btn_primary" id="previewImage">previewImage</button>
<span class="desc">Image uploading interface</span>
<button class="btn btn_primary" id="uploadImage">uploadImage</button>
<span class="desc">Download picture interface</span>
<button class="btn btn_primary" id="downloadImage">downloadImage</button>
<h3 id="menu-voice">Audio interface</h3>
<span class="desc">Start Recording interface</span>
<button class="btn btn_primary" id="startRecord">startRecord</button>
<span class="desc">Stop recording interface</span>
<button class="btn btn_primary" id="stopRecord">stopRecord</button>
<span class="desc">Voice playback interface</span>
<button class="btn btn_primary" id="playVoice">playVoice</button>
<span class="desc">Pause play interface</span>
<button class="btn btn_primary" id="pauseVoice">pauseVoice</button>
<span class="desc">Stop play interface</span>
<button class="btn btn_primary" id="stopVoice">stopVoice</button>
<span class="desc">Voice Uploading Interface</span>
<button class="btn btn_primary" id="uploadVoice">uploadVoice</button>
<span class="desc">Download voice Interface</span>
<button class="btn btn_primary" id="downloadVoice">downloadVoice</button>
<h3 id="menu-smart">Intelligent interface</h3>
<span class="desc">Identify audio and return recognition result interface</span>
<button class="btn btn_primary" id="translateVoice">translateVoice</button>
<h3 id="menu-device">Device information interface</h3>
<span class="desc">Gets the network status interface</span>
<button class="btn btn_primary" id="getNetworkType">getNetworkType</button>
<h3 id="menu-location">Geographic interface</h3>
<span class="desc">Use wechat's built-in map to view location interface</span>
<button class="btn btn_primary" id="openLocation">openLocation</button>
<span class="desc">Gets the geolocation interface</span>
<button class="btn btn_primary" id="getLocation">getLocation</button>
<h3 id="menu-webview">Interface operation interface</h3>
<span class="desc">Hide the menu interface in the upper right corner</span>
<button class="btn btn_primary" id="hideOptionMenu">hideOptionMenu</button>
<span class="desc">The menu interface is displayed in the upper right corner</span>
<button class="btn btn_primary" id="showOptionMenu">showOptionMenu</button>
<span class="desc">Close the current page window interface</span>
<button class="btn btn_primary" id="closeWindow">closeWindow</button>
<span class="desc">Interface for batch hiding function buttons</span>
<button class="btn btn_primary" id="hideMenuItems">hideMenuItems</button>
<span class="desc">Batch display function button interface</span>
<button class="btn btn_primary" id="showMenuItems">showMenuItems</button>
<span class="desc">Hide all non-base button interfaces</span>
<button class="btn btn_primary" id="hideAllNonBaseMenuItem">hideAllNonBaseMenuItem</button>
<span class="desc">Display all function button interfaces</span>
<button class="btn btn_primary" id="showAllNonBaseMenuItem">showAllNonBaseMenuItem</button>
<h3 id="menu-scan">Wechat scan</h3>
<span class="desc">Turn on the wechat scan interface</span>
<button class="btn btn_primary" id="scanQRCode0">ScanQRCode (wechat processing result)</button>
<button class="btn btn_primary" id="scanQRCode1">ScanQRCode (returns results directly)</button>
</div>
</div>
</body>
<script type="text/javascript" src="http://res2.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
<script type="text/javascript">Wx. config({debug: true,// Enable debug mode official environment set to false appId: '{{ appId }}', timestamp: '{{ timestamp }}', nonceStr: '{{ nonceStr }}', signature: '{{ signature }}',
jsApiList: [
'checkJsApi',
'updateAppMessageShareData',
'updateTimelineShareData',
'onMenuShareWeibo',
'hideMenuItems',
'showMenuItems',
'hideAllNonBaseMenuItem',
'showAllNonBaseMenuItem',
'translateVoice',
'startRecord',
'stopRecord',
'onRecordEnd',
'playVoice',
'pauseVoice',
'stopVoice',
'uploadVoice',
'downloadVoice',
'chooseImage',
'previewImage',
'uploadImage',
'downloadImage',
'getNetworkType',
'openLocation',
'getLocation',
'hideOptionMenu',
'showOptionMenu',
'closeWindow',
'scanQRCode',
'chooseWXPay',
'openProductSpecificView',
'addCard',
'chooseCard',
'openCard'
]
});
wx.ready(function () {
// 1 判断当前版本是否支持指定 JS 接口,支持批量判断
document.querySelector('#checkJsApi').onclick = function () {
wx.checkJsApi({
jsApiList: [
'getNetworkType',
'previewImage',
'onMenuShareTimeline',
'onMenuShareAppMessage',
'onMenuShareQQ',
'onMenuShareWeibo',
],
success: function (res) {
alert(JSON.stringify(res));
}
});
};
// 2. 分享接口
// 2.1 监听“分享给朋友”,按钮点击、自定义分享内容及分享结果接口
// 自定义“分享给朋友”及“分享到QQ”按钮的分享内容(1.4.0)
document.querySelector('#updateAppMessageShareData').onclick = function () {
wx.updateAppMessageShareData({
title: 'TNW 微信公众号开发脚手架',
desc: '支持任何 Node.js 的 MVC 服务端框架',
link: 'https://gitee.com/javen205/IJPay(此链接必须JS接口安全域名下的链接)',
imgUrl: 'https://gitee.com/javen205/TNW/raw/master/docs/img/logo.png',
trigger: function (res) {
alert('用户点击发送给朋友');
},
success: function (res) {
alert('已分享');
},
cancel: function (res) {
alert('已取消');
},
fail: function (res) {
alert(JSON.stringify(res));
}
});
alert('已注册获取“发送给朋友”状态事件');
};
// 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口
// 自定义“分享到朋友圈”及“分享到QQ空间”按钮的分享内容(1.4.0)
document.querySelector('#updateTimelineShareData').onclick = function () {
wx.updateTimelineShareData({
title: 'TNW 微信公众号开发脚手架',
desc: '支持任何 Node.js 的 MVC 服务端框架',
link: 'https://gitee.com/javen205/TNW(此链接必须JS接口安全域名下的链接)',
imgUrl: 'https://gitee.com/javen205/TNW/raw/master/docs/img/logo.png',
trigger: function (res) {
alert('用户点击分享到朋友圈');
},
success: function (res) {
alert('已分享');
},
cancel: function (res) {
alert('已取消');
},
fail: function (res) {
alert(JSON.stringify(res));
}
});
alert('已注册获取“分享到朋友圈”状态事件');
};
// 2.3 监听“分享到微博”按钮点击、自定义分享内容及分享结果接口
document.querySelector('#onMenuShareWeibo').onclick = function () {
wx.onMenuShareWeibo({
title: 'TNW 微信公众号开发脚手架',
desc: '支持任何 Node.js 的 MVC 服务端框架',
link: '此链接必须JS接口安全域名下的链接,
imgUrl: 'https://gitee.com/javen205/TNW/raw/master/docs/img/logo.png',
trigger: function (res) {
alert('用户点击分享到微博');
},
complete: function (res) {
alert(JSON.stringify(res));
},
success: function (res) {
alert('已分享');
},
cancel: function (res) {
alert('已取消');
},
fail: function (res) {
alert(JSON.stringify(res));
}
});
alert('已注册获取“分享到微博”状态事件');
};
// 3 智能接口
var voice = {
localId: '',
serverId: ''
};
// 3.1 识别音频并返回识别结果
document.querySelector('#translateVoice').onclick = function () {
if (voice.localId == '') {
alert('请先使用 startRecord 接口录制一段声音');
return;
}
wx.translateVoice({
localId: voice.localId,
complete: function (res) {
if (res.hasOwnProperty('translateResult')) {
alert('识别结果:' + res.translateResult);
} else {
alert('无法识别');
}
}
});
};
// 4 音频接口
// 4.2 开始录音
document.querySelector('#startRecord').onclick = function () {
wx.startRecord({
cancel: function () {
alert('用户拒绝授权录音');
}
});
};
// 4.3 停止录音
document.querySelector('#stopRecord').onclick = function () {
wx.stopRecord({
success: function (res) {
voice.localId = res.localId;
},
fail: function (res) {
alert(JSON.stringify(res));
}
});
};
// 4.4 监听录音自动停止
wx.onVoiceRecordEnd({
complete: function (res) {
voice.localId = res.localId;
alert('录音时间已超过一分钟');
}
});
// 4.5 播放音频
document.querySelector('#playVoice').onclick = function () {
if (voice.localId == '') {
alert('请先使用 startRecord 接口录制一段声音');
return;
}
wx.playVoice({
localId: voice.localId
});
};
// 4.6 暂停播放音频
document.querySelector('#pauseVoice').onclick = function () {
wx.pauseVoice({
localId: voice.localId
});
};
// 4.7 停止播放音频
document.querySelector('#stopVoice').onclick = function () {
wx.stopVoice({
localId: voice.localId
});
};
// 4.8 监听录音播放停止
wx.onVoicePlayEnd({
complete: function (res) {
alert('录音(' + res.localId + ')播放结束');
}
});
// 4.8 上传语音
document.querySelector('#uploadVoice').onclick = function () {
if (voice.localId == '') {
alert('请先使用 startRecord 接口录制一段声音');
return;
}
wx.uploadVoice({
localId: voice.localId,
success: function (res) {
alert('上传语音成功,serverId 为' + res.serverId);
voice.serverId = res.serverId;
}
});
};
// 4.9 下载语音
document.querySelector('#downloadVoice').onclick = function () {
if (voice.serverId == '') {
alert('请先使用 uploadVoice 上传声音');
return;
}
wx.downloadVoice({
serverId: voice.serverId,
success: function (res) {
alert('下载语音成功,localId 为' + res.localId);
voice.localId = res.localId;
}
});
};
// 5 图片接口
// 5.1 拍照、本地选图
var images = {
localId: [],
serverId: []
};
document.querySelector('#chooseImage').onclick = function () {
wx.chooseImage({
success: function (res) {
images.localId = res.localIds;
alert('已选择 ' + res.localIds.length + ' 张图片');
}
});
};
// 5.2 图片预览
document.querySelector('#previewImage').onclick = function () {
wx.previewImage({
current: 'https://gitee.com/javen205/TNW/raw/master/docs/img/logo.png',
urls: [
'https://gitee.com/javen205/IJPay/raw/master/assets/img/IJPay-t.png',
'https://gitee.com/javen205/TNW/raw/master/docs/img/logo.png'
]
});
};
// 5.3 上传图片
document.querySelector('#uploadImage').onclick = function () {
if (images.localId.length == 0) {
alert('请先使用 chooseImage 接口选择图片');
return;
}
var i = 0, length = images.localId.length;
images.serverId = [];
function upload() {
wx.uploadImage({
localId: images.localId[i],
success: function (res) {
alert(JSON.stringify(res));
i++;
alert('已上传:' + i + '/' + length);
images.serverId.push(res.serverId);
if (i < length) {
upload();
}
},
fail: function (res) {
alert(JSON.stringify(res)); }}); }upload(a); }; //5.4Download the picturesdocument.querySelector(' #downloadImage').onclick = function () {
if (images.serverId.length= = =0) {
alert'Please use it firstuploadImageUpload pictures ');return;
}
var i = 0, length = images.serverId.length;
images.localId = [];
function download() {
wx.downloadImage({
serverId: images.serverId[i].success: function (res) {
alert(JSON.stringify(res));
i+ +;alert(' Downloaded: '+i+ + '/'length);
images.localId.push(res.localId);
if (i < length) {
download(a); }}}); }download(a); }; //6Device info interface //6.1Gets the current network statusdocument.querySelector(' #getNetworkType').onclick = function () {
wx.getNetworkType({
success: function (res) {
alert(res.networkType);
},
fail: function (res) {
alert(JSON.stringify(res)); }}); }; //7Geolocation interface //7.1View geographical locationdocument.querySelector(' #openLocation').onclick = function () {
wx.openLocation({
latitude: 23.099994.longitude: 113.324520.name: 'TITCreative garden ',address:Xingang Middle Road, Haizhu District, Guangzhou397No. ',scale: 14.infoUrl: 'http://weixin.qq.com'}); }; //7.2Gets the current geographic locationdocument.querySelector(' #getLocation').onclick = function () {
wx.getLocation({
success: function (res) {
alert(JSON.stringify(res));
},
cancel: function (res) {
alert(' User denies permission to obtain location '); }}); }; //8Interface Operation interface //8.1Hide menu in upper right cornerdocument.querySelector(' #hideOptionMenu').onclick = function () {
wx.hideOptionMenu(a); }; //8.2Display menu in upper right cornerdocument.querySelector(' #showOptionMenu').onclick = function () {
wx.showOptionMenu(a); }; //8.3Hide menu items in batchesdocument.querySelector(' #hideMenuItems').onclick = function () {
wx.hideMenuItems({
menuList:['menuItem:readMode', // Reading mode 'menuItem:share:timeline', // Share to moments'menuItem:copyUrl'// copy link],success: function (res) {
alert(' Hide 'buttons for' reading mode ', 'Share to moments',' copy link 'etc. '); },fail: function (res) {
alert(JSON.stringify(res)); }}); }; //8.4Batch display menu itemsdocument.querySelector(' #showMenuItems').onclick = function () {
wx.showMenuItems({
menuList:['menuItem:readMode', // Reading mode 'menuItem:share:timeline', // Share to moments'menuItem:copyUrl'// copy link],success: function (res) {
alert(' Read mode ', 'Share to moments',' copy link 'and other buttons have been displayed '); },fail: function (res) {
alert(JSON.stringify(res)); }}); }; //8.5Hide all non-basic menu itemsdocument.querySelector(' #hideAllNonBaseMenuItem').onclick = function () {
wx.hideAllNonBaseMenuItem({
success: function () {
alert(' All non-basic menu items have been hidden '); }}); }; //8.6Displays all hidden non-basic menu itemsdocument.querySelector(' #showAllNonBaseMenuItem').onclick = function () {
wx.showAllNonBaseMenuItem({
success: function () {
alert(' All non-basic menu items displayed '); }}); }; //8.7Close current windowdocument.querySelector(' #closeWindow').onclick = function () {
wx.closeWindow(a); }; //9Wechat native interface //9.1.1Scan the QR code and return the resultdocument.querySelector(' #scanQRCode0').onclick = function () {
wx.scanQRCode({
desc: 'scanQRCode desc'}); }; //9.1.2Scan the QR code and return the resultdocument.querySelector(' #scanQRCode1').onclick = function () {
wx.scanQRCode({
needResult: 1.desc: 'scanQRCode desc',
success: function (res) {
alert(JSON.stringify(res)); }}); };var shareData = typeof (shareData) = = ='undefined' ? {
title: 'TNWWechat public account development scaffolding ',desc:'Support anyNode.js 的 MVCServer framework ',link: 'https://gitee.com/javen205/TNW',
imgUrl: 'https://gitee.com/javen205/TNW/raw/master/docs/img/logo.png'}: shareData;
wx.onMenuShareAppMessage(shareData);
wx.onMenuShareTimeline(shareData);
});
wx.error(function (res) {
alert(res.errMsg);
});
</script>
</html>
Copy the code
Java version
How to use JSSDK for wechat public account development
Open source is recommended
TNW
Scaffolding for the development of wechat official account:Gitee.com/javen205/TN…IJPay
Make payment at your fingertips:Gitee.com/javen205/IJ…- Efficient development of SpringBoot microservices
mica
Tools:Gitee.com/596392912/m… Avue
A fantastic framework based on VUE configurable:Gitee.com/smallweigit…pig
The Strongest Microservices in the universe (required for Architects) :gitee.com/log4j/pigSpringBlade
Complete online solution (necessary for enterprise development) :Gitee.com/smallc/Spri…