十 Node.js实现微信小程序支付功能

一 开发前提

1.一个注册好,并且通过微信认证的,主体信息不能是个人的小程序
主体信息为个人的小程序是没有微信支付功能的,并且也无法获取用户手机号码
2.一个注册好并且通过微信认证的商户号
3.商户号与小程序绑定
4.这篇文章是以旧版微信支付为例开发微信支付。文档地址:旧版微信支付文档

二 支付流程介绍

1.由小程序端发起后台请求,后台根据传回的参数请求微信同一下单接口,将拿到的参数返回给小程序端,再由小程序拉起微信支付,支付完成以后,在回调函数中执行对应的更新操作。

三 上代码

1.小程序端发起后台请求,完成统一下单

1.1 小程序端发起后台请求

// request是我自己封装的wx.request方法,方便调用。
// wechatapp/wechatpay是我的接口地址,以post方式访问
// data 是参数对象
request('wechatapp/wechatpay', data).then(res => {
      let result = res.data
      //result 是后台返回的数据,我的封装格式如下:
      // {
      //     flag:Boolean,       //用于判断请求结果是否正常
      //     state:Int,          //状态码,用于确定请求状态是否正常
      //     data:Array|Object,  //返回的数据,根据情况返回数组或者对象,增删改操作不返回data
      //     message:String,     //增删改的操作完成提示
      //     error:String        //增删改的错误提示
      // }
      if (result.flag) {
         let obj = result.data
         //这里得到微信统一下单的数据 
         //拉起小程序微信支付,此处粘贴1.3节点的代码
         //**************************************
      }
 })

1.2 Node.js后台接收请求,完成统一下单(重点)

//此处是Node.js后台的router,与1.1步骤中的访问API一致
wechatapp.post('/wechatpay', (req, res) => {
    let b = req.body
    //PayOrder方法中封装了完成微信统一下单的全部重要内容
    // req.ip 是当前访问该接口的ip地址,返回一个IPv6格式的地址
    // b.openid 是当前请求访问该接口的唯一码,该接口需要在小程序中完成登录才能获得,然后传回后台
    common2.PayOrder(b, b.openid, req.ip, result => {
        res.send(result)
    })
})

下面是PayOrder方法的完整内容
需要引入的第三方module:
1.request 用于访问第三方接口 npm i request
2. md5 用于完成MD5加密 npm i md5
3. xml-js 用于接口回调后格式化xml数据 npm i xml-js

PayOrder(b, openid, currip, cb) {
        let nonceStr = common.CreateCode('', 20) //随机字符串
        let out_trade_no = common.CreateCode('WPAY', 20) //支付订单号
        //第一次签名
        let body = this.WePaySign(b, openid, nonceStr, out_trade_no, currip)
        //此处的this.BaseURL=‘https://api.mch.weixin.qq.com/pay/unifiedorder’
        //这是微信旧版统一下单接口
        request({ url: this.BaseURL, method: "POST", body }, (err, response, result) => {
            if (!err && response.statusCode == 200) {
                //将返回的数据格式化为JSON,再由JSON格式化为JS对象
                //此处的convert是引入xml-js的别名
                var result1 = convert.xml2json(result, { compact: true, spaces: 4 });
                let obj = JSON.parse(result1)
                let xmlobj = obj.xml
                //根据文档,通过下面两个值得结果判断是否完成微信统一下单
                if (xmlobj.return_code._cdata == "SUCCESS" && xmlobj.result_code._cdata == "SUCCESS") {
                    //prepay_id  该参数为统一下单接口返回的关键参数,我们就是要用它完成微信支付
                    let prepay_id = xmlobj.prepay_id._cdata
                    
                    
                    /*
                       以下代码用于在微信小程序中拉起微信支付时使用
                       根据接口要求,需要完成二次签名
                    */
                    
                    let appid = xmlobj.appid._cdata
                    //获取时间戳
                    let timeStamp = parseInt((new Date().getTime() / 1000)) + ''
                    let stringA = `appId=${appid}`
                    stringA += `&nonceStr=${nonceStr.toUpperCase()}`
                    stringA += `&package=${'prepay_id=' + prepay_id}`
                    stringA += `&signType=MD5`
                    stringA += `&timeStamp=${timeStamp}`
                    stringA += '&key=' + common.mch_secret
                    //第二次签名
                    let key = md5(stringA).toUpperCase()

                    let data = {
                        key,
                        prepay_id,
                        nonceStr,
                        appid,
                        timeStamp,
                        out_trade_no,
                        package: 'prepay_id=' + prepay_id
                    }
                    cb({ state: 200, flag: true, data })
                }

            } else {
                cb({ state: 200, flag: false, message: err })
            }
        });
 },
 /**
  * 微信支付下单签名
  */
 WePaySign(b, openid, nonce_str, out_trade_no, currip) {
        //需要将参数封装成XML格式
        let total_fee = b.total * 100   //支付金额,以分为单位
        //拼接签名字符串
        // 此处的body是商品说明,在微信支付完成界面显示
        // notify_url 返回通知URL
        // mch_id 商户号
        let stringA = `appid=${common.appid}&body=微信统一下单测试  &mch_id=${common.mch_id}&nonce_str=${nonce_str}&notify_url=${common.notify_url}&openid=${openid}&out_trade_no=${out_trade_no}&spbill_create_ip=${currip}&total_fee=${total_fee}&trade_type=JSAPI`
        //mch_secret 商户秘钥
        stringA += `&key=${common.mch_secret}`

        //生成秘钥
        let key = MD5(stringA).toUpperCase()

        let formData = "<xml>"
        formData += "<appid>" + common.appid + "</appid>"
        // formData += "<body>" + JSON.stringify(b) + "</body>"
        formData += "<body>微信统一下单测试</body>"
        formData += "<mch_id>" + common.mch_id + "</mch_id>"
        formData += "<nonce_str>" + nonce_str + "</nonce_str>"
        formData += "<notify_url>" + common.notify_url + "</notify_url>"
        formData += "<openid>" + openid + "</openid>"
        formData += "<out_trade_no>" + out_trade_no + "</out_trade_no>"
        formData += "<sign>" + key + "</sign>"
        formData += "<spbill_create_ip>" + currip + "</spbill_create_ip>"
        formData += "<total_fee>" + total_fee + "</total_fee>"
        formData += "<trade_type>JSAPI</trade_type>"
        formData += "</xml>"
        return formData
}

1.3 获取统一下单的返回数据,拉起微信支付

下面是小程序拉起微信支付的代码,具体的格式请大家参考文档
将下面的的代码粘贴到1.1节点的标识处,也可以单独封装一个方法

wx.requestPayment({
            appId: obj.appId,
            timeStamp: obj.timeStamp,
            nonceStr: obj.nonceStr,
            package: obj.package,
            signType: 'MD5',
            paySign: obj.key,
            success(e) {
              //用户支付成功时更新数据库订单信息
              data = { total, openid: app.globalData.userInfo.openid, orderStr, out_trade_no: obj.out_trade_no }
              self.UpDateOrder(data)
            },
            fail(err) {
              console.log('取消支付');
              self.onLoad()
           }, complete(res) {
}

四 感悟

第一次做微信支付,遇到的坑比较多,不过最多的还是签名那一块,两次签名折腾了好久,下面附上微信官方提供的签名校验工具地址
签名校验工具
1.第一大坑:一定要注意去空格去回车,否则与官方签名不一致,会导致签名失败。
2.第二大坑:统一下单接口和拉起微信支付时所用的随机码必须是同一个,同一个。否则统一下单接口之后拉起微信支付会提示签名失败,这一点文档中没说明,本人也是折腾了好一会儿才发现这个坑。
3.另外线上的小程序IP地址需要https域名,下一节为大家讲述怎么白嫖SSL证书及怎么为Node.js安装SSL证书

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值