Application for Wechat Public Account; Official website
2. Developing documents; Website
III. Application for Interface Test Number and Test Connection
url: To be accessed by the external network, the local server can build ngrok. Of course, if you have money, you can buy one; ngrok runs differently every time. Instruction ngrok http port number. Intranet penetration, sometimes timeliness.
token:Parameters for WeChat message encryption
Test whether to connect and what parameters are there
Configuring local servers
index.js
// Introducing express module const express = require('express') // app application object const app = express(); app.use((req,res,next) => { // Which is the Wechat Server Auto Developer Server? //Test number console.log(req.query) }) //port number app.listen(4000,()=>console.log("Server enablement"))
Make sure the local server opens and click on the submit button, where the Wechat Public Number has just been configured
// Signature:'35eab388c24c9d0deafa3bd12513d6f0cac7ac03', / / / Wechat Encrypted Signature // echostr:'4361760528703928317', // Weixin random string // timestamp:'1555216285', / / / the time of the occurrence of wechat // Nonce:'316998338'}//Random Number
4. Calculate whether the parameters of signature encrypted signature are the same as those transmitted by wechat. Similarly, it means the message from the Wechat server.
a. The three parameters (timestamp, nonce, token) involved in the encrypting of Wechat are grouped together, and dictionaries are arranged and grouped together to form an array.
b. Splice all the parameters of the array into a string and encrypt it with sha1.
c. If the same, the message comes from the Wechat server and returns echostr to the Wechat server; if not, an error will be reported.
index.js
// Introducing express module const express = require('express') // app application object const app = express(); const sha1= require('sha1') const config = { token:'sd*********dssda22345', appID:'wxf*******0cf5a8b', appsecret:'9d1fa6c8eee80c5**********cf8b53' } app.use((req,res,next) => { // console.log(req.query) // Signature:'35eab388c24c9d0deafa3bd12513d6f0cac7ac03', //Wechat Encrypted Signature // Echostr:'4361760528703928317', // Weixin random string // Timestamp:'1555216285', / / / the time of the occurrence of wechat // Nonce:'316998338'}//Random Number const {signature,echostr,timestamp,nonce} = req.query; const {token} =config; const arr = [timestamp , nonce , token] const arrSort = arr.sort(); console.log(arrSort) const str = arrSort.join('') const sha1Str = sha1(str) console.log(sha1Str) if(sha1Str === signature){ res.send(echostr) }else{ res.send('error') } }) //port number app.listen(4000,()=>console.log("Server enablement"))
Wechat Public Number Prompt Configuration Successful
V. Get access_token
Access_token is the globally unique interface invocation credential of public number, and it is necessary to use access_token when public number invokes each interface. Developers need to be properly saved. Access_token should be stored in at least 512 character spaces. Access_token is currently valid for 2 hours and needs to be refreshed regularly. Repeated access_token will invalidate the last access_token retrieved, and the number of requests.
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
Design ideas:
1. The first time there is no local, send a request to get access_token, save (local)
2. The second time, read locally to determine whether it is overdue. No direct use; if expired, re-request. Judgment of expiration time. Current time + 7200, before
//Get the unique credentials for access_token Wechat calling interface // Required url parameters const {appID , appsecret} = require('../config') const rp = require('request-promise-native') const {readFile , writeFile} = require('fs') class Wechat { constructor(){ } //Get access_token getAccessToken(){ const url =`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appID}&secret=${appsecret}`; // Send request // The server side cannot use request request-promise-native for ajax, and the promise object is returned. return new Promise((resolve,reject) => { rp({method:'get',url,json:true}) .then(res =>{ console.log(res) res.expires_in = Date() + (res.expires_in -300)*1000; resolve(res) // { access_token: // '20_ZCrQnrFmz20x21BwfYy9b2fvJvqQPvQZ5NQMeiy5LZwRfKXgtgArpNFl-WnGDT0QvvTI94CjJ459Fy8IfFn4g00tskZ_Z76xgEDe8fXYnGXd9PwkZhQJ0_mN8FE_sVPVPz1x7OFy0RRPbyfMVXGaAJAIKT', // expires_in: 7200 } }) .catch(err =>{ console.log(err); reject('getAccessToken errer') }) }) } //Save access_token /** * @param accessToken */ saveAccessToken(accessToken){ //Converting objects to json strings accessToken = JSON.stringify(accessToken); return new Promise((resolve,reject)=>{ writeFile('./accessToken.txt',accessToken,err => { if(!err){ console.log("file write success"); resolve(); }else{ console.log(err); reject(); } }) }) } // Read access_token readAccessToken(){ return new Promise((resolve,reject)=>{ readFile('./accessToken.txt',(err,data) => { if(!err){ console.log("file read success"); data = JSON.parse(data) resolve(data); }else{ console.log(err); reject(); } }) }) } // Is it effective? isValidAccessToken(data){ if(!data&&data.access_token&&!data.expires_in){ return false; } return data.expires_in > data.now() } // Get access_token without expiration /** * @param */ fetchAccessToken(){ // console.log(this.access_token) if(this.access_token && this.expires_in && this.isValidAccessToken(this)) { // Save access_token return Promise.resolve({ access_token:this.access_token, expires_in:this.expires_in, }) } return this.readAccessToken() .then( async res => { // Local Documents if(this.isValidAccessToken(res)){ resolve(res) }else{ // Be overdue const res= await this.getAccessToken(); await this.saveAccessToken(res); return Promise.resolve(res) } }) .catch( async err => { const res= await this.getAccessToken(); await this.saveAccessToken(res); return Promise.resolve(res) }) .then(res => { this.access_token = res.access_token; this.expires_in = res.expires_in; return Promise.resolve(res); }) } } // To test whether the file successfully creates a new class, call fetchAccessToken() method
6. Receiving and reusing user information
Data information flow Data xml format text
Automatic recovery
Attention points
Automatically reply within 1.5 seconds
2. Developer Data Format
until.js
const { parseString } = require('xml2js') module.exports = { // Streaming Information Mosaic getUserDataAsync(req) { return new Promise((resolve, reject) => { let xmlData = ''; req.on('data', data => { console.log(data) xmlData += data.toString(); }) .on('end', () => { resolve(xmlData) }) }) }, //xml to js parseXMLAsync(xmlData){ return new Promise((resolve,reject)=>{ parseString(xmlData,{trim:true},(err,data)=>{ if(!err){ resolve(data) }else{ reject("parseXMLAsync errer:" +err) } }) }) }, // Fields change arrays to corresponding values formatMessage(jsData){ let message = {}; jsData = jsData.xml; //Determine whether the data is an object? if(typeof jsData === "object"){ for(let key in jsData){ let value = jsData[key]; // Filter empty data if(Array.isArray(value)&&value.length>0){ message[key] = value[0]; } } } return message; } }
auth.js
// Like middleware, encapsulation const config = require('../config') const sha1 = require('sha1') const { getUserDataAsync , parseXMLAsync ,formatMessage} = require('../utils/tool.js') module.exports = () => { return async (req, res, next) => { // console.log(req.query) // Signature:'35eab388**************************** 6f0cac7ac03', //Wechat Encrypted Signature // Echostr:'436176052*******8317', //Weixin random string // Timestamp:'155****** 16285', // the time of the occurrence of wechat // Nonce:'316998338'}//Random Number const { signature, echostr, timestamp, nonce } = req.query; const { token } = config; const arr = [timestamp, nonce, token] const arrSort = arr.sort(); const str = arrSort.join('') const sha1Str = sha1(str) // const sha1Str = sha1([timestamp,nonce,token].sort().join('')) // console.log(sha1Str) /** * Wechat Server Sends Two Types of Developers Getpost to Server */ // { signature: 'd25a9573617f**********7e87d4062475a', // timestamp: '1558****176', // nonce: '8018***15', // openid: 'o*************w3nObg' } if (req.method === "GET") { if (sha1Str === signature) { res.send(echostr) } else { res.send('wechat server error') } } else if (req.method === "POST") { if (sha1Str !== signature) { res.send("errer") } else { console.log(req.query) } const xmlData = await getUserDataAsync(req); // buffer data console.log(xmlData) // < xml > // <ToUserName> <![CDATA [gh_d772f38c5ba2]> </ToUserName>//developer id] // <FromUserName> <![CDATA [o1Q3x03Pn0eLpM8wlwFHn7w3nObg]]> </FromUserName>//user openid // <CreateTime>1555231639</CreateTime>//Time of Delivery // <MsgType><![CDATA[text]></MsgType>//information type // <Content><![CDATA[123]></Content>//Information Content // <MsgId>22265565114514992</MsgId>//Message id Wechat Server for 3 days // </xml > // xml object to js object const jsData = await parseXMLAsync (xmlData) console.log(jsData) /** * Format */ var message = formatMessage(jsData) // Is user information text information? let content = 'What are you talking about? I can't hear you clearly.'; if(message.MsgType == 'text'){ if(message.Content === '0'){ content = 'Sorry, I'm a policeman.'; }else if (message.Content === '1'){ content = 'No previous choice'; }else if (message.Content === '2'){ content = 'Now I want to be a good man.'; }else if (message.Content.match('Hate')){ content = 'It's all right. I'm here.'; }else if (message.Content.match('like')){ content = 'I like you, too.'; } } let replyMessage =`<xml> <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName> <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName> <CreateTime>${Date.now()}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[${content}]]></Content> </xml>` //Return to the server console.log(replyMessage) res.send(replyMessage); //If the request is not answered, it will be three times. // res.end('') } else { res.send("errer") } } }