Wechat login, sharing, payment process

[TOC]

preface

For the front end, wechat payment, sharing, login is must master, today’s article, mainly on these three aspects of the process in detail. The main contents are as follows:

Domain name knowledge introduction

  1. Business domain name: Click the text box in the wechat browser, and a prompt will pop upThis site is not secure, please do not enter passwordYou can solve this problem by configuring the business domain name.
  2. JS interface security domain name: Share functionality (JS-SDK) needs to try this domain name.
  3. Web authorized domain name: Used to obtain the user’s unique identifier openID for the public account.

Wechat mini program authorization login process

Op =>operation: openID Determine whether to authorize login. Op2 =>operation: obtain the code according to wx.login. Op3 =>operation: invoke the server to obtain openID according to the code. Obtain the information through user authorization and save it in the database op-> OP2 -> OP3 -> OP4Copy the code

If you have never read the documentation for applets login authorization, you are advised to take a look at the following address:

Server official document Client document

Nodejs + small program to achieve authorized login

The front part

  1. Determine whether to log in based on the local userId. If no, obtain the openID of the user
onLoad() { if(! this.data.userId) { this.getSession() } }, getSession() { wx.login({ success: (res) => { if (res.code) { app.get(Api.getSession, { code: res.code }).then(res => { store.setItem('openid', res.openid) }) } } }) }Copy the code
  1. Click the Authorization button to initiate a login request.
getUserInfo(e) {
    let userInfo = e.detail.userInfo;
    userInfo.openid = store.getItem('openid')
    app.get(Api.login, {
        userInfo
    }).then(res => {
        store.setItem('userId', res.data.userId)
        this.setData({
            userId: res.userId
        })
    })
}Copy the code

Server part

In config, define the common appID and AppSecret

module.exports = {
    wx: {
        appId: 'wx0ef10432747d8f57',
        appsecret: 'cc47a6127687e999a1dffa756ff83c0e'
    },
    mp: {
        appId: 'wx0691f1dcf6e5e231',
        appSecret: 'c7ed875e338120f15f49476a6596eb4f'
    }
}Copy the code

Then by calling the interface of the official document of the small program, obtain the APPID to the client

let express = require('express'); let router = express.Router(); let request = require('request'); let config = require('./config'); let uril = require('./.. /.. /util/index') config = Object.assign({}, config.mp); router.get('/getSession', (req, res) => { let code = req.query.code if (! Json (uril.handlefail ('code cannot be empty ', 10001)) } let sessionUrl = `https://api.weixin.qq.com/sns/jscode2session?appid=${config.appId}&secret=${config.appSecret}&js_code=${code}&grant_typ e=authorization_code`; request(sessionUrl, (err, response, body) => { let result = util.handleResponse(err, response, body) res.json(result) }) })Copy the code

Write the login interface

Router.get ('/login',async function(req,res){let userInfo = json.parse (req.query.userinfo); if (! UserInfo){// If the interface has no information, Json (util.handlefail (' user information cannot be null ',10002))}else{// Query whether the current user is registered let userRes = await dao.query({openID: userInfo.openid},'users_mp'); If (userres.data.length >0){res.json(util.handlesuc ({userId: = 0){if (userres.data.length >0){res.json(util.handlesuc ({userId: = 0); Userres.data [0]._id}))}else{let insertData = await dao.insert(userInfo,'users_mp'); if (insertData.code == 0){ let result = await dao.query({ openid: userInfo.openid }, 'users_mp'); res.json(util.handleSuc({ userId: result.data[0]._id })) }else{ res.json(insertData); } } }else{ res.json(userRes); }}})Copy the code

The handleFail and handleResponse of the above code is a unified processing of the encapsulated data, see github address if you are interested. I won’t show you the code here.

It is important to note that in this implementation, the behavior of obtaining the OpenID is put in the back-end implementation. If the front-end implementation can also be, but will be relatively more troublesome. At this point, the suerId is already stored in the database and saved locally. The next login, if the userId exists, does not need to log in again.

H5 login authorization and sharing process

Login authorization for H5 is slightly different. If the user is not authorized to log in to the authorization page, go to the authorization page. The official document describes the process as follows:

1 Step 1: The user agrees to authorize and obtain code2 Step 2: Exchange for webpage authorization through code Access_token3 Step 3: refresh access_token (if necessary) 4 Step 4: Pull user information (scope is snSAPi_userinfo)5 Attach: Verify that the access_token is valid

The code in the project is as follows :(here the code does not implement refreshing the access_token and pulling the user information)

When the page loads, determine whether it is authorized.

mounted(){ this.checkUserAuth(); }, methods:{openId = this.$cookie.get('openId'); if(! OpenId){// If you do not have login authorization, you will jump to the jump page provided by wechat. window.location.href = API.wechatRedirect; }else{this.getwechatConfig ();}else{this.getwechatConfig (); }},Copy the code

API.wechatRedirect:

wechatRedirect:'/api/wechat/redirect? url=http%3A%2F%2Fm.51purse.com%2F%23%2Findex&scope=snsapi_userinfo',Copy the code

“Note”

  • The URL address is requiredencodeURIComponentCode.
  • m.51purse.comNeed to be configured with you in the background of wechat public numberAuthorization of the domain nameConsistent!

Nodejs implements the login authorization callback interface mainly by obtaining the request parameters of the client and requesting wechat to provide theminterface

Router. get('/redirect',function (req,res) {let redirectUrl = req.query.url, scope = req.query.scope, function (req,res) {let redirectUrl = req. callback = 'http://m.51purse.com/api/wechat/getOpenId'; cache.put('redirectUrl', redirectUrl); // Get the data from the client. Request wechat interface let authorizeUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${config.appId}&redirect_uri=${callback}&response_type=code&s cope=${scope}&state=STATE#wechat_redirect`; res.redirect(authorizeUrl); })Copy the code

When users click on confirm authorization, will perform a jump callbacl:http://m.51purse.com/api/wechat/getOpenId. This interface is also implemented by the Node side. The details are as follows:

// exports.getAccessToken = function(code){let token_url = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${config.appId}&secret=${config.appSecret}&code=${code}&grant_t ype=authorization_code`; return new Promise((resolve, reject) => { request.get(token_url, function (err, response, body) { let result = util.handleResponse(err, response, body); resolve(result); })}); Get ('/getOpenId',async function(req,res){let code = req.query.code; console.log("code:"+code); if(! Json (util.handlefail (' no authorization code is currently available ')); }else{// use code for access_token let result = await common.getAccesstoken (code); If (result.code == 0){// Let data = result.data; let expire_time = 1000 * 60 * 60 * 2; OpenId res.cookie('openId', data. openId, {maxAge: expire_time}); let openId = data.openid; let userRes = await dao.query({ 'openid': openId },'users'); If (userres.code == 0){if (userres.data.length >0){ Let redirectUrl = cache.get('redirectUrl'); res.redirect(redirectUrl); }else{ let userData = await common.getUserInfo(data.access_token, openId); let insertData = await dao.insert(userData.data,'users'); If (insertdata.code == 0){if (insertdata.code == 0){let redirectUrl = cache.get('redirectUrl'); res.redirect(redirectUrl); }else{// return error message res.json(insertData); }}}else{// Return error message res.json(userRes); }}else{// Return error message res.json(result); }}})Copy the code

“Note” : The code above has been removed for simplicity. If you are interested, visit gitHub.

H5 Sharing process

Also, if you haven’t read wechat H5 developmentThe official documentation, I suggest you read it first. Here’s what you should read about sharing:

When the next callback to the page, the openId has been retrieved from the cookie. The client continues to execute the following code. To initialize the sharing function, obtain the configuration information returned by the server.

To do this, you need NPM to install wX-jSSDK

// This information is defined in api.js. GetWechatConfig (){this.$http.get(api.wechatconfig +'? url='+location.href.split('#')[0]).then(function(response){ let res = response.data; if(res.code == 0){ let data = res.data; Wx. config({debug: true, // Enable debug mode, the return value of all API calls will be alert in the client, if you want to view the passed parameters, you can open it in the PC, parameter information will be printed in the log, only on the PC. AppId: data.appId, // Mandatory, unique identifier of public number timestamp: data.timestamp, // Mandatory, generated signature timestamp nonceStr: Data. nonceStr, // Required, random string to generate signature signature: data.signature,// Required, signature jsApiList: Data.jsapilist // Mandatory, list of JS interfaces to use}) wx.ready(()=>{util.initShareInfo(wx); })}})}Copy the code

Util /index.js encapsulates the sharing functionality.

Export default {/ / for your browser's address bar parameter values getUrlParam (name) {let reg = new RegExp (' (^ | &) '+ name +' = / ^ & * ()); let r = window.location.search.substr(1).match(reg); if(r! =null)return decodeURIComponent(r[2]); }, initShareInfo(wx){let shareInfo = {title: 'XXXX ', // Share title desc:' XXXX ', // share description link: ImgUrl: 'http://m.51purse.com/#/index', // Share link. The domain name or path of the link must be the same as the security domain name of the current page. ", // Share icon} wx.onMenushareAppMessage (shareInfo); wx.onMenuShareTimeline(shareInfo); wx.onMenuShareQQ(shareInfo); wx.onMenuShareQZone(shareInfo); // The following two methods are new methods, the above method will be obsolete. wx.updateAppMessageShareData(shareInfo); wx.updateTimelineShareData(shareInfo); }}Copy the code

The implementation of nodeJS to /wechat/ JSSDK interface is as follows:

// common.gettoken () gets the base interface's Token exports.gettoken = function(){let Token = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${config.appId}&secret=${config.appSecret}`;  return new Promise((resolve, reject)=>{ request.get(token, function (err, response, body) { let result = util.handleResponse(err, response, body); resolve(result); }) }) } ---- router.get('/jssdk',async function(req,res){ let url = req.query.url; let result = await common.getToken(); if (result.code == 0){ let token = result.data.access_token; Let the params = {/ / generate random string noncestr: util. CreateNonceStr (), / / generates a timestamp timestamp: util. CreateTimeStamp (), url } let str = util.raw(params); console.log('str:::' + JSON.stringify(params)) let sign = createHash('sha1').update(str).digest('hex'); Json (util.handlesuc ({appId: config.appId, // Required, unique identifier of public number timestamp: params.timestamp, // Required, generated signature's timestamp nonceStr: Params. noncestr, // Required, generates a random string of signatures signature: sign,// Required, jsApiList: [ 'updateAppMessageShareData', 'updateTimelineShareData', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareQZone', 'chooseWXPay']}))}}else{res.json(result); }})Copy the code

The above code mainly obtains the basic token, and then returns the corresponding parameters to the client using the basic token in combination with signature, timestamp, random number and other related parameters.

Note the difference between base tokens and accessToken. Refer to the article.

At this point, wechat H5 access JSSDK to achieve sharing has been completed.

Applets payment

Applets payment front end process

  • To obtain the openId
  • Tune up the digital signature

Back-end payment process

  • Splicing general parameters
  • To generate the signature
  • Concatenating XML data
  • Call single interface
  • Get the prepayment Id: prepay_id
  • Generate payment SDK
  • Define the callback interface to accept wechat payment messages

The primary logic for payment is on the server side

The following is a code representation of the server-side process. First, encapsulate in util some public methods for payment needs

/** * public function definition */ let createHash = require('create-hash'); Module.exports = {generate random number createNonceStr(){return math.random ().tostring (36).substr(2,15); }, createTimeStamp(){return parseInt(new Date().gettime () / 1000) + "}, key){ let string = this.raw(params) + '&key=' + key; let sign = createHash('md5').update(string).digest('hex'); return sign.toUpperCase(); }, // generate the system trade order number getTradeId(type='wx'){let date = new date ().getTime().toString(); let text = ''; let possible = '0123456789'; for(let i=0; i<5; i++){ text += possible.charAt(Math.floor(Math.random() * possible.length)) } return (type == 'wx'? 'ImoocWxJuZi':'ImoocMpJuZi') + date + text; }, // convert Object to json and sort raw(args){let keys = object.keys (args).sort(); let obj = {}; keys.forEach((key)=>{ obj[key] = args[key]; }) / / {a: 1, b: 2} = > & & b = a = 1/2 / converting object & integral parameter let val = ' '; for(let k in obj){ val += '&' + k + '=' +obj[k]; } return val.substr(1); }}Copy the code

Here is a wrapper around the payment method, which calls the function in util. The client calls the order method below.

/** * let config = require('./.. /pay/config') let request = require('request') let util = require('.. /.. /util/util') let createHash = require('create-hash') let xml = require('xml2js') config = config.mch; module.exports = { order: function (appid,attach, body, openid, total_fee, notify_url, ip){ return new Promise((resolve,reject)=>{ let nonce_str = util.createNonceStr(); let out_trade_no = util.getTradeId('mp'); Let sign = this.getPrePaySign(appID, attach, body, openID, Total_fee, notifY_URL, IP, nonce_str, out_trade_no); // Assemble XML data from parameters and signatures, Let sendData = this.wxSendData(AppID, attach, body, OpenID, Total_fee, notifY_URL, IP, nonce_str, out_trade_no, sign); let self = this; let url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; request({ url, method: 'POST', body: sendData }, function (err, response, body) { if (! err && response.statusCode == 200) { xml.parseString(body.toString('utf-8'),(error,res)=>{ if(! error){ let data = res.xml; console.log('data:' + JSON.stringify(data)); If (data. Return_code [0] = = 'SUCCESS' &&. Result_code [0] = = 'SUCCESS') {/ / for prepaid ID let prepay_id = data. Prepay_id | | []; let payResult = self.getPayParams(appid, prepay_id[0]); resolve(payResult); } } }) } else { resolve(util.handleFail(err)); }})})}, // Generate prepaid signature getPrePaySign: function (appid, attach, body, openid, total_fee, notify_url, ip, nonce_str, out_trade_no) { let params = { appid, attach, body, mch_id: config.mch_id, nonce_str, notify_url, openid, out_trade_no, spbill_create_ip: ip, total_fee, trade_type: 'JSAPI' } let string = util.raw(params) + '&key=' + config.key; let sign = createHash('md5').update(string).digest('hex'); return sign.toUpperCase(); }, // After the signature is successful, assemble the data in XML format according to the parameter stitching, call the order interface wxSendData: function (appid, attach, body, openid, total_fee, notify_url, ip, nonce_str, out_trade_no,sign) { let data = '<xml>' + '<appid><! [CDATA[' + appid + ']]></appid>' + '<attach><! [CDATA[' + attach + ']]></attach>' + '<body><! [CDATA[' + body + ']]></body>' + '<mch_id><! [CDATA[' + config.mch_id + ']]></mch_id>' + '<nonce_str><! [CDATA[' + nonce_str + ']]></nonce_str>' + '<notify_url><! [CDATA[' + notify_url + ']]></notify_url>' + '<openid><! [CDATA[' + openid + ']]></openid>' + '<out_trade_no><! [CDATA[' + out_trade_no + ']]></out_trade_no>' + '<spbill_create_ip><! [CDATA[' + ip + ']]></spbill_create_ip>' + '<total_fee><! [CDATA[' + total_fee + ']]></total_fee>' + '<trade_type><! [CDATA[JSAPI]]></trade_type>' + '<sign><! [CDATA['+sign+']]></sign>' + '</xml>' return data; }, getPayParams:function(appId,prepay_id){ let params = { appId, timeStamp:util.createTimeStamp(), nonceStr:util.createNonceStr(), package: 'prepay_id=' + prepay_id, signType:'MD5' } let paySign = util.getSign(params,config.key); params.paySign = paySign; return params; }}Copy the code

Finally, define /pay/payWallet’s payment interface, which calls the common order method.

Router.get ('/pay/payWallet',function(req,res){let openId = req.query.openid; // User openID let appId = config.appid; // App ID let attach = "pay for course experience "; // Let body = "Welcome to the first moOCs payment special course "; // let total_fee = req.query.money; / / pay the total amount of the let notify_url = "http://localhost:3000/api/mp/pay/callback" let IP = "123.57.2.144"; wxpay.order(appId,attach,body,openId,total_fee,notify_url,ip).then((result)=>{ res.json(util.handleSuc(result)); }).catch((result)=>{ res.json(util.handleFail(result.toString())) }); })Copy the code

Please refer to the official description for the process. The official description is very clear, there is no more description here, in fact, the main is to concatenate some parameters, get the signature. It then adds other required parameters based on the signature (see the code above) and relies on the XML data. Then call the unified order interface at https://api.mch.weixin.qq.com/pay/unifiedorder. After prepay_id is generated, some parameters required by the applet side are generated, and these parameters are returned to the applet client for the applet client to call the payment function of wechat applet.

The front end of the small program payment is very simple, just simply call the payWallet interface provided by the server, pass in openId and Money. Then get the corresponding parameters and call requestPayment provided by wechat to pull up the payment.

The main code logic is as follows:

pay() { app.get(Api.payWallet,{ openId: Store.getItem('openId'), money: This.data.index}). Then ((res) => {wx.requestPayment({timeStamp: res.timestamp, nonceStr: res.noncestr, package: res.package, signType: res.signType, paySign: res.paySign, success: Function (errmsg) {if (errmsg == 'requestPayment:ok') {wx.showtoast ({title: 'payment :ok', icon: 'success'}); }}, fail: function (res) {if (res.errmsg == 'requestPayment:fail cancel') {wx.showtoast ({title: 'pay cancel', icon: 'none' }); } else { wx.showToast({ title: res.errmsg, icon: 'none' }); }}})}); }Copy the code

At this point, the payment function of the applet side has been implemented.