I made a website, put it online, open it with WeChat and click Share, but after sharing, the link card to a friend comes with WeChat by default, as follows:
This title, description and picture comes with it by default. Ugly, sharing with others thought it was a stolen website. When you join WeChat's JSSDK, sharing can be customized as follows:
I admit that although the title and content of this sharing is not decent, it does not prevent me from expressing that we can define the content to be shared through WeChat JSSDK, and that we will step by step implement JSSDK's access from back-end Node.js from zero.
Become Test Public Number Developer
Logon Test Public Number Background
First we need to WeChat Public Platform Request Test Interface, Address: https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
After logging in using WeChat scanning, you can test the account system on the WeChat public platform.
Become Test Public Number Developer
Secondly, in the WeChat public platform test account, scan the test number 2D, become the developer of the test public number
Interface Configuration Information
Modify interface configuration information
- The URL address must be the address on your server, which can be accessed through the browser's address bar (no server?That's okay. We'll build one later)
Suppose the server address I filled in here is " http://www.your_server_name.com/wxJssdk" - Token can be filled out as you like and used to generate signatures. (Don't know signatures?That's okay. I'll use it later)
Suppose the Token I filled in here is " jegfjaeghfuccawegfgjdbh"
Clicking Submit at this time will prompt the configuration to fail, because at the time of submission, WeChat will request your server address, and your current configured address is not accessible, so it will prompt the configuration to fail.But don't worry, let's set up a simple Node server that WeChat can access.
Set up a simple Node server
We need to set up a server on the domain name http://www.your_server_name.com and expose an interface as/wxJssdk
const express = require('express') const app = express() app.get('/wxJssdk', (req, res) => { res.send('The request succeeded') }) app.listen(80, err => { if(!err) console.log('connect succeed') })
Now we'll go to http://www.your_server_name.com/wxJssdk in the address bar. If the page says "The request succeeded", we'll move on to the next step. If it doesn't succeed, check to see if your server starts a Node server, such as node index.js
Save the interface configuration information of the WeChat test public number background at this time, and still prompt that the configuration failed because we did not return as it requested.
Return corresponding content based on WeChat test public number request information
according to WeChat Public Number Development Document Access Guide When WeChat requests our configured interface, it will bring the following information
parameter | describe |
---|---|
signature | The WeChat cryptographic signature, signature combines the token parameter filled out by the developer with the timestamp parameter and the nonce parameter in the request. |
timestamp | time stamp |
nonce | random number |
echostr | Random String |
The WeChat server will request the interface we configured with the information from the above tables through GET requests, and we must verify the information sent by WeChat according to the following requirements to ensure that it is the information sent by WeChat with the following verification process:
1) Dictionary ordering of three parameters: token, timestamp and nonce
2) sha1 encryption by splicing three parameter strings into one string
3) Developers can compare encrypted strings with signature s to identify that the request originated from WeChat
const express = require('express') const app = express() const sha1 = require('sha1') app.get('/wxJssdk', (req, res) => { let wx = req.query let token = 'jegfjaeghfuccawegfgjdbh' let timestamp = wx.timestamp let nonce = wx.nonce // 1) Dictionary ordering of three parameters: token, timestamp and nonce let list = [token, timestamp, nonce].sort() // 2) sha1 encryption by splicing three parameter strings into one string let str = list.join('') let result = sha1(str) // 3) Developers can compare encrypted strings with signature s to identify that the request originated from WeChat if (result === wx.signature) { res.send(wx.echostr) // Returning echostr from WeChat indicates that the check was successful and no other one can be returned here } else { res.send(false) } })
At this point, we restart the Node server and save the interface configuration information again to configure successfully.
Usage steps of WeChat JSSDK
according to WeChat JSSDK Description Document We need to do the following:
Fill in Secure Domain Name
Login to the WeChat public platform, enter the "Public Number Settings" and fill in the "JS Interface Security Domain Name", that is, the domain name of the interface to be invoked, does not contain the protocol
Front End Introducing JS
Introduce this JS file on a page where you need to call the JS interface, (supports https): http://res.wx.qq.com/open/js/jweixin-1.2.0.js
Fill in the interface configuration information
wx.config({ debug: true, // Turn on debugging mode, and the return values of all APIs invoked will be displayed in the client alert. To view the incoming parameters, you can open them on the pc side, and the parameter information will be printed through the log, only on the pc side. appId: '', // Required, unique identification of public number timestamp: , // Required, time stamp for signature generation nonceStr: '', // Required, generate a random string of signatures signature: '',// Required, Signature jsApiList: [] // Required, list of JS interfaces to use, all JS interfaces listed in Appendix 2 });
Call Interface
Do what your front end should do, call the WeChat sharing interface, or other interfaces provided by WeChat, whatever you need. Of course, this is not the focus of our discussion. Let's see where the configuration information of WeChat comes from.
Configuration information needed to generate jssdk in Node Server
As you can see from the previous section, calling WeChat JSSDK requires the following information
- appId
- timestamp
- nonceStr
- signature
- jsApiList
Where:
- The first appId is the appId that tests the Public Number background, as we know
- The second timestamp can also be generated by ourselves
- The third nonceStr is optional and you can interpret it as a key
- The fourth signature requires us to generate it as required
- Item 5 is the interface name of the required interface
Generate signature
You must know about jsapi_ticket, which is a temporary ticket used by the public number to call the WeChat JS interface, before generating a signature.Normally, jsapi_ticket has a validity period of 7200 seconds, which is obtained by access_token.Because of the limited number of api calls to obtain jsapi_ticket, frequent refreshes of jsapi_ticket can result in limited api calls and affect their business, developers must cache jsapi_ticket globally in their own services.
To ensure that our appid, appsecret, nonceStr and other information is not exposed in the front end, the following steps will be taken on the server to prevent others from stealing information from obtaining (Note: WeChat requests have a daily limit, once exceeded, they can not be used, specific requests are limited to WeChat public number background viewable)
Generate access_token
According to the WeChat development document [Get access_token documentation], we need to request token from https://api.weixin.qq.com/cgi-bin/token by GET for appid and appsecret in the Wechat test public number background. Grant_type=client_credential&appid=APPID&secret=APPSECRET. After successful request, we will get the string that returns JSON transformation.
{"access_token":"ACCESS_TOKEN","expires_in":7200}
The specific request code is as follows:
const request = require('request') const grant_type = 'client_credential' const appid = 'your app id' const secret = 'your app secret' request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => { let access_toekn = JSON.parse(body).access_token })
Get jsapi_ticket
const request = require('request') const grant_type = 'client_credential' const appid = 'your app id' const secret = 'your app secret' request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => { let access_toekn = JSON.parse(body).access_token request('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi', (err, response, body) => { let jsapi_ticket = JSON.parse(body).ticket }) })
Generate Signature
The steps for generating signatures are consistent with the initial algorithm for/wxJssdk as follows:
let jsapi_ticket = jsapi_ticket // The jsapi_ticket obtained from the previous step let nonce_str = '123456' // Key, any string, can be randomly generated let timestamp = new Date().getTime() // time stamp let url = req.query.url // url link using interface, does not contain content after # // Request the above strings, sorted by dictionary, then stitched by'&'as follows: where j > n > t > u, sorted manually directly here let str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + nonce_str + '×tamp=' + timestamp + '&url=' + url // Encrypt with sha1 let signature = sha1(str)
The connected code is:
const request = require('request') const grant_type = 'client_credential' const appid = 'your app id' const secret = 'your app secret' request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => { let access_toekn = JSON.parse(body).access_token request('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi', (err, response, body) => { let jsapi_ticket = JSON.parse(body).ticket let nonce_str = '123456' // Key, any string, can be randomly generated let timestamp = new Date().getTime() // time stamp let url = req.query.url // url link using interface, does not contain content after # // Request the above strings, sorted by dictionary, then stitched by'&'as follows: where j > n > t > u, sorted manually directly here let str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + nonce_str + '×tamp=' + timestamp + '&url=' + url // Encrypt with sha1 let signature = sha1(str) }) })
Expose interface, return to front end
app.post('/wxJssdk/getJssdk', (req, res) => { const request = require('request') const grant_type = 'client_credential' const appid = 'your app id' const secret = 'your app secret' request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => { let access_toekn = JSON.parse(body).access_token request('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi', (err, response, body) => { let jsapi_ticket = JSON.parse(body).ticket let nonce_str = '123456' // Key, any string, can be randomly generated let timestamp = new Date().getTime() // time stamp let url = req.query.url // url link using interface, does not contain content after # // Request the above strings, sorted by dictionary, then stitched by'&'as follows: where j > n > t > u, sorted manually directly here let str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + nonce_str + '×tamp=' + timestamp + '&url=' + url // Encrypt with sha1 let signature = sha1(str) res.send({ appId: appid, timestamp: timpstamp, nonceStr: nonce_str, signature: signature, }) }) }) })
Front-end requests back-end interfaces for configuration information
Get Configuration
axios.post('/wxJssdk/getJssdk', {url: location.href}).then((response) => { var data = response.data wx.config({ debug: false, // Turn on debugging mode, and the return values of all APIs invoked will be displayed in the client alert. To view the incoming parameters, you can open them on the pc side, and the parameter information will be printed through the log, only on the pc side. appId: data.appId, // Required, unique identification of public number timestamp: data.timestamp, // Required, time stamp for signature generation nonceStr: data.nonceStr, // Required, generate a random string of signatures signature: data.signature,// Required, signed, see Appendix 1 jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage'] // Required, list of JS interfaces to use, all JS interfaces listed in Appendix 2 }); })
Do what you want, for example, customize sharing
if (wx) { axios.post('/wxJssdk/getJssdk', {url: location.href}).then((response) => { var data = response.data wx.config({ debug: false, // Turn on debugging mode, and the return values of all APIs invoked will be displayed in the client alert. To view the incoming parameters, you can open them on the pc side, and the parameter information will be printed through the log, only on the pc side. appId: data.appId, // Required, unique identification of public number timestamp: data.timestamp, // Required, time stamp for signature generation nonceStr: data.nonceStr, // Required, generate a random string of signatures signature: data.signature,// Required, signed, see Appendix 1 jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage'] // Required, list of JS interfaces to use, all JS interfaces listed in Appendix 2 }); wx.ready(function () { wx.onMenuShareTimeline({ title: wxShare.title, desc: wxShare.desc, link: wxShare.link, imgUrl: wxShare.imgUrl }); wx.onMenuShareAppMessage({ title: wxShare.title, desc: wxShare.desc, link: wxShare.link, imgUrl: wxShare.imgUrl }); }) wx.error(function (res) { // Failure to verify config information executes error functions, such as signature expiration that causes verification to fail. The specific error information can be viewed either by opening config's debug mode or by returning the res parameter, where the SPA can update the signature. }) }) }
So far, the back-end is configured and we can use the WeChat interface properly, but WeChat's daily interface requests are limited to 2000 times per day, so if the website goes online, your interface will fail if you visit more than 2000 times a day, and request the WeChat interface twice each time, which wastes requesting time, so we need to slow down the retrieval of information above.There is a backend to avoid interface failure and multiple requests for WeChat backend.
Cache access_token and jsapi_ticket
Code directly here, caching using the node_cache package
const request = require('request') const express = require('express') const app = express() const sha1 = require('sha1') const waterfall = require('async/waterfall') const NodeCache = require('node-cache') const cache = new NodeCache({stdTTL: 3600, checkperiod: 3600}) //Expires after 3600 seconds app.get('/wxJssdk', (req, res) => { let wx = req.query // 1) Dictionary ordering of three parameters: token, timestamp and nonce let token = 'jegfjaeghfuyawegfgjdbh' let timestamp = wx.timestamp let nonce = wx.nonce // 2) sha1 encryption by splicing three parameter strings into one string let list = [token, timestamp, nonce] let result = sha1(list.sort().join('')) // 3) Developers can compare encrypted strings with signature s to identify that the request originated from WeChat if (result === wx.signature) { res.send(wx.echostr) } else { res.send(false) } }) app.get('/wxJssdk/getJssdk', (req, res) => { let grant_type = 'client_credential' let appid = 'your app id' let secret = 'your app secret' // appscret let steps = [] // First step, get access_token steps.push((cb) => { let steps1 = [] // Step 1.1, read access_token from the cache steps1.push((cb1) => { let access_token = cache.get('access_token', (err, access_token) => { cb1(err, access_token) }) }) // Step 1.2, if there is access_token in the cache, returns directly, if not, reads access_token from the server steps1.push((access_token, cb1) => { if (access_token) { cb1(null, access_token, 'from_cache') } else { request('https://api.weixin.qq.com/cgi-bin/token?grant_type=' + grant_type + '&appid=' + appid + '&secret=' + secret, (err, response, body) => { cb1(err, JSON.parse(body).access_token, 'from_server') }) } }) // Step 1.3, Cache access_token if it is newly fetched from the server, otherwise return directly steps1.push((access_token, from_where, cb1) => { if (from_where === 'from_cache') { console.log(' === Successfully read from cache access_token: ' + access_token + ' ===') cb1(null, access_token) } else if (from_where === 'from_server') { cache.set('access_token', access_token, (err, success) => { if (!err && success) { console.log(' === Cache expired, read from server access_token: ' + access_token + ' ===') cb1(null, access_token) } else { cb1(err || 'cache Set up access_token An unknown error occurred') } }) } else { cb1('1.3 Obtain from_where When, from_where Value is empty') } }) waterfall(steps1, (err, access_token) => { cb(err, access_token) }) }) // Step 2, get a ticket steps.push((access_token, cb) => { let steps1 = [] // Step 2.1, Read ticket s from the cache steps1.push((cb1) => { let ticket = cache.get('ticket', (err, ticket) => { cb1(err, ticket) }) }) // Step 2.2, tickets in the cache are returned directly, if not, tickets are read from the server steps1.push((ticket, cb1) => { if (ticket) { cb1(null, ticket, 'from_cache') } else { request('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi', (err, response, body) => { cb1(err, JSON.parse(body).ticket, 'from_server') }) } }) // Step 2.3, Cache the new ticket fetched from the server, otherwise return directly steps1.push((ticket, from_where, cb1) => { if (from_where === 'from_cache') { console.log(' === Successfully read from cache ticket: ' + ticket + ' ===') cb1(null, ticket) } else if (from_where === 'from_server') { cache.set('ticket', ticket, (err, success) => { if (!err && success) { console.log(' === Cache expired, read from server ticket: ' + ticket + ' ==='); cb1(null, ticket) } else { cb1(err || 'cache Set up ticket An unknown error occurred') } }) } else { cb1('2.3 Obtain from_where When, from_where Value is empty') } }) waterfall(steps1, (err, ticket) => { cb(err, ticket) }) }) // Step 3, Generate Signatures steps.push((ticket, cb) => { let jsapi_ticket = ticket let nonce_str = '123456' let timestamp = new Date().getTime() let url = req.query.url let str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + nonce_str + '×tamp=' + timestamp + '&url=' + url let signature = sha1(str) cb(null, { appId: appid, timestamp: timestamp, nonceStr: nonce_str, signature: signature, ticket: ticket }) }) waterfall(steps, (err, data) => { if (err) { res.send({status: 'error', data: err}) } else { res.send({status: 'success', data: data}) } }) }) app.use('/wxJssdk/public', express.static('public')) app.listen(80, err => { if(!err) console.log('connect succeed') })