1

前言:花了好几天的时间搞了下微信公众号支付,期间历程无比心酸,总的来说还是理解能力不够和经验不足,现在归纳下支付中遇到的坑。

先看业务流程
图片描述

前期猜想及准备工作

首先,公众号支付采用的是,利用微信的js-sdk调起支付的方式进行的,其中分为两种情况:微信jssdk调起和微信支付开放平台调起。但是,无论是采用什么方法首先都要先配置wx.config。

wx.config({
    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: '', // 必填,公众号的唯一标识
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: '', // 必填,生成签名的随机串
    signature: '',// 必填,签名
    jsApiList: [] // 必填,需要使用的[JS接口列表][4],chooseWXPay 微信支付接口
});

参数分析:appId,timestamp,nonceStr 这里需要注意一下变量的大小写,这里采用的是驼峰命名法,timestamp因为本身就是一个单词,所以不要写错。signature这个需要注意一下,毕竟涉及到的 sign(签名)、token 实在太多了,极其容易混淆。同时,生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取,获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket ,然而access_token的有效期也只有7200秒,所以把jsapi_ticket和access_token同时缓存(当然你可以只存jsapi_ticket) 。这里推荐参数从后台获取,代码如下:

{
  const  moment = require('moment'), //时间处理类  cnpm i moment
         request = require("request"); //cnpm i request  https://github.com/request/request
         wxConfig = {
             appid:'', //appid
             appsecret:'', //appsecret
             mcnsecret:'', //商户key
             mcnid:'', //商户号
             notify_url:'' //支付成功想要回调的后台地址,用于修改订单状态等等操作
         }
  // 获取 公众号的access_token和ticket ,需要存入缓存或者数据库,有效期7200s,请注意和获取用户access_token api进行区分
  app.get('/accpetSubscriptionNow',async function (req,res,next) {
    let task = await XXXXModel.findOne({where:where});//获取存在数据库里面的公众号的accesstoken,XXXXModel是自己的表
    let cc = {};
    let WXInitConfig = {};//创建一个空对象用以存储微信公众号配置信息
    // 判断数据库里面是否存在accesstoken等信息,且存放的时间是否超过7200秒
    if(JSON.stringify(task.updateBy)==='{}' || parseInt(moment(task.updateBy.updateTime).format('X')) + 7200 < parseInt(moment().format('X'))){
      console.log('#1.判断accesstoken的更新时间大于7200s,重新拉取accesstoken')
      request.get(
        {
          // 获取access_token的接口
          url: `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${wxConfig.appid}&secret=${wxConfig.appsecret}`
        },
        async function (error, response, body) {
          console.log('body', body)
          //成功获取到access_token,这里应该有个判断
          const ACCESS_TOKEN = JSON.parse(response.body).access_token;
          let  json = JSON.parse(response.body);
          request.get({
            // 获取jsapi_ticket的接口
            url: `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${ACCESS_TOKEN}&type=jsapi`
          },
          async function (error, response, body) {
            //成功获取到jsapi_ticket,这里应该有个判断
            console.log('body2', body)
            let updateBy = {
              updateTime : new Date()
            }
            json.ticket = JSON.parse(body).ticket;
            // 把获取到的access_token和jsapi_ticket存放到数据库,并标明更新时间
            cc = await XXXXModel.findOneAndUpdate({
              where:where
            }, {data: data})
            
            WXInitConfig = cc.content;
            //console.log('WXInitConfig',WXInitConfig)
            let data = await acceptsignature(WXInitConfig);
            //console.log('data',data)
            res.send(data);//传递微信号配置信息到前端
          })
        }
      )
    }else {
      console.log('#2.判断accesstoken的更新时间小于7200s,从数据库获取access_token和ticket')
      WXInitConfig = task.content;
      let data = await acceptsignature(WXInitConfig);
      res.send(data);
    }
  })
  
  // 获取signature签名的方法
  function acceptsignature(WXInitConfig) {
    console.log('WXInitConfig2',WXInitConfig)
    let appid = wxConfig.appid;
    let nonceStr = Math.random().toString(36).substr(2, 15);
    let access_token = WXInitConfig.access_token;
    let jsapi_ticket = WXInitConfig.ticket;
    let timestamp = moment().format('X');
    let url = 'http://mmobile.hwason.cn/';
    
    // 把所需的参数ASCII序列化
    let ASC = `jsapi_ticket=${jsapi_ticket}&noncestr=${nonceStr}&timestamp=${timestamp}&url=${url}`;
    console.log('ASCII',ASC)
    // 把序列化的数据使用 SHA1 方法加密,记住得到的是小写的加密串
    let signature = SHA1(ASC).toLowerCase();
    console.log('signature',signature)
    let data = {
      appid:appid,
      nonceStr:nonceStr,
      // access_token:access_token,
      // jsapi_ticket:jsapi_ticket,
      timestamp:timestamp,
      // url:url,
      signature:signature,
      result:'00'
    }
    return data;
  }
}

到此,前端调用上述的接口就可以获取到微信的配置参数了。
参数配置好了,就开始正式启动微信公众号支付了。

步骤①:设置支付目录
步骤②:设置授权域名
步骤③:前台进入微信授权页面
微信授权又分为5步:
1)进入微信授权页,用户同意授权,获取code

//进入授权页有两张方式
// 一、前端直接跳转
let return_url = ''; //授权后回调的页面,这里是前端的地址url
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx1a1cf7c547b07420&redirect_uri=" + encodeURIComponent(return_url) + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
// 二、后端重定向

//尤其注意:由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问



liuoomei
175 声望18 粉丝

走出舒适区,外面的风景格外迷人!