nodejs微信扫码支付功能实现


Posted in NodeJs onFebruary 17, 2018

前言

本篇文章主要是记录本人在微信扫码支付过程中所遇到的问题,给大家一个借鉴作用,希望对你们有帮助

开发环境

  • nodejs v8.1.0
  • egg v1.1.0

准备工作

微信公众号-appid

微信商户号-mch_id

key值(签名算法所需,其实就是一个32位的密码,可以用md5生成一个)(key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置)

扫码支付-统一下单

以下才用的是微信模式二,因为比较简单

let MD5 = require('md5'),
    xml2js = require('xml2js'),
    url = "https://api.mch.weixin.qq.com/pay/unifiedorder",// 下单请求地址
    appid = '公众号id',
    mch_id = '微信商户号';
    notify_url = '回调地址',
    out_trade_no = '自己设置的订单号',// 微信会有自己订单号、我们自己的系统需要设置自己的订单号
    total_fee = '订单金额',// 注意,单位为分
    body = '商品简单描述', 
    trade_type = 'NATIVE',// 交易类型,JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
    nonce_str = moment().format('YYYYMMDDHHmmssSSS'),// 随机字符串32位以下
    stringA = `appid=${公众号id}&body=${body}&mch_id=${微信商户号}&nonce_str=${nonce_str}¬ify_url=${
    notify_url}&out_trade_no=${out_trade_no}&spbill_create_ip=${ctx.request.ip}&total_fee=${total_fee}&trade_type=${trade_type}`,
    stringSignTemp = stringA + "&key=xxxxxxxxxxxxxxxxx", //注:key为商户平台设置的密钥key
    sign = MD5(stringSignTemp).toUpperCase(); //注:MD5签名方式

以上就是我们所需要的一些参数

签名生成算法见微信官方:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3

spbill_create_ip 是 终端ip地址

下面把所有的参数拼接成xml

const formData = "<xml>";
    formData += "<appid>" + appid + "</appid>"; //appid
    formData += "<body>" + body + "</body>"; //商品或支付单简要描述
    formData += "<mch_id>" + mch_id + "</mch_id>"; //商户号
    formData += "<nonce_str>" + nonce_str + "</nonce_str>"; //随机字符串,不长于32位
    formData += "<notify_url>" + notify_url + "</notify_url>"; //支付成功后微信服务器通过POST请求通知这个地址
    formData += "<out_trade_no>" + out_trade_no + "</out_trade_no>"; //订单号
    formData += "<total_fee>" + total_fee + "</total_fee>"; //金额
    formData += "<spbill_create_ip>" + ctx.request.ip + "</spbill_create_ip>"; //ip
    formData += "<trade_type>NATIVE</trade_type>"; //NATIVE会返回code_url ,JSAPI不会返回
    formData += "<sign>" + sign + "</sign>";
    formData += "</xml>";
  // 这里使用了egg里面请求的方式
  const resultData = yield ctx.curl(url, {
      method: 'POST',
      content: formData,
      headers: {
        'content-type': 'text/html',
      },
    });

  // xml转json格式
  xml2js.parseString(resultData.data, function (err, json) {
    if (err) {
      new Error("解析xml报错")
    } else {
      var result = formMessage(json.xml); // 转换成正常的json 数据
      console.log(result) //打印出返回的结果
    }
  })
  var formMessage = function (result) {
    var message = {};
    if (typeof result === 'object') {
      var keys = Object.keys(result);
      for (var i = 0; i < keys.length; i++) {
        var item = result[keys[i]];
        var key = keys[i];
        if (!(item instanceof Array) || item.length === 0) {
          continue;
        }
        if (item.length === 1) {
          var val = item[0];
          if (typeof val === 'object') {
            message[key] = formMessage(val);
          } else {
            message[key] = (val || '').trim();
          }
        } else {
          message[key] = [];
          for (var j = 0, k = item.length; j < k; j++) {
            message[key].push(formMessage(itemp[j]));
          }
        }
      }
    }
    return message;
  }

上面使用了egg的请求方式,原生node可以使用request

var request = require('request');
  request({
    url: url,
    method: "POST",
    body: formData
  }, function(error, response, body) {
    if (!error && response.statusCode == 200) {
    }
  });

如果请求成功会最终返回一个xml,然后我们进行解析成json的格式,里面会有一个code_url和out_trade_no,我们需要把这两个返回给前端,然后通过生成二维码展示给用户扫码,完成支付

监听支付是否成功

上面操作完成之后,我们需要知道用户是否完成支付,因为用户会停留在该页面,我们需要在用户付完款之后,通知用户支付成功。

首先,用户发起支付的时候我们会生成二维码,让用户完成扫码支付,我们还要做的是,开一个定时器,每隔一段时间去发送一个请求,这个时候,我们node后台就需要写一个查询订单的接口,之前我们拿到了out_trade_no,也就是我们系统内部的订单号,我们把这个数据发送给后台查询订单的接口,然后后台接收到之后会请求微信的查询接口地址https://api.mch.weixin.qq.com/pay/orderquery,流程跟上面一样,只是接口地址和微信返回的xml不一样而已,返回的字段会有一个状态即SUCCESS和NOTPAY,我们可以通过判断是否支付返回给前端,成功之后提示给用户支付成功,关闭定时器。

回调地址

这个是非常重要的一环,大部分的操作其实在上面就可以完成,但是有特殊的情况,比如用户电脑断网发送不了请求,但是手机付款了,这就会导致我们记录不到用户支付的信息。这个时候回调地址就很重要了

设置回调地址

微信商户中心->产品中心->开发配置->扫码支付

之后我们需要做的是后端用post来接收微信发送的异步回调信息,也是xml的格式,这里注意,如果不支持接收xml,可能会得到空的数据

这里还需要注意的是,我们在保存用户支付信息的同时,得先查改订单是否支付,以免重复操作,可能会插入多条记录的情况

总结

微信扫码支付坑还是有的,如果你是第一次摸索的话,下面罗列一下需要注意的地方

  1. 签名算法要写正确,不然是不会成功的,要拼接正确才行
  2. 微信返回的是xml格式的数据,我们得通过插件转成json,这样才方便获取数据
  3. 返回的code_url要给前端生成二维码用,然后需要开一个定时器查询该订单是否完成支付,最终通知用户结果
  4. 回调地址很重要,我们后端需要post接收微信返回的回调信息,然后保存信息,不过在保存用户支付信息的之前,我们得知道该订单是否已经保存过,以免重复添加。还有就是返回的是xml的数据,后端一定要保证能够接收得到,按照正常的方式是接收不了的,得额外设置。
NodeJs 相关文章推荐
NodeJS实现阿里大鱼短信通知发送
Jan 17 NodeJs
使用NodeJs 开发微信公众号(三)微信事件交互实例
Mar 02 NodeJs
Nodejs中的this详解
Mar 26 NodeJs
图片上传之FileAPI与NodeJs
Jan 24 NodeJs
Express与NodeJs创建服务器的两种方法
Feb 06 NodeJs
NodeJs下的测试框架Mocha的简单介绍
Feb 22 NodeJs
浅谈nodejs中的类定义和继承的套路
Jul 26 NodeJs
Mac下通过brew安装指定版本的nodejs教程
May 17 NodeJs
详解NodeJs开发微信公众号
May 25 NodeJs
详解NodeJS Https HSM双向认证实现
Mar 12 NodeJs
使用nodeJS中的fs模块对文件及目录进行读写,删除,追加,等操作详解
Feb 06 NodeJs
分享五个Node.js开发的优秀实践 
Apr 07 NodeJs
nodejs+express搭建多人聊天室步骤
Feb 12 #NodeJs
nodeJs实现基于连接池连接mysql的方法示例
Feb 10 #NodeJs
NodeJS简单实现WebSocket功能示例
Feb 10 #NodeJs
nodejs使用redis作为缓存介质实现的封装缓存类示例
Feb 07 #NodeJs
nodejs中Express与Koa2对比分析
Feb 06 #NodeJs
nodejs实现的连接MySQL数据库功能示例
Jan 25 #NodeJs
详解nodeJs文件系统(fs)与流(stream)
Jan 24 #NodeJs
You might like
php实现的简单数据库操作Model类
2016/11/16 PHP
JQuery 解析多维的Json数据格式
2009/11/02 Javascript
jQuery实现可拖动的浮动层完整代码
2013/05/27 Javascript
jQuery之排序组件的深入解析
2013/06/19 Javascript
js 鼠标移动显示图片的简单实例
2013/12/25 Javascript
javascript写的异步加载js文件函数(支持数组传参)
2014/06/07 Javascript
JS弹出窗口的运用与技巧大全
2016/11/01 Javascript
springMVC + easyui + $.ajaxFileUpload实现文件上传注意事项
2017/04/23 Javascript
vue.js中引入vuex储存接口数据及调用的详细流程
2017/12/14 Javascript
在ES5与ES6环境下处理函数默认参数的实现方法
2018/05/13 Javascript
jquery.tagsinput.js实现记录checkbox勾选的顺序
2019/09/21 jQuery
javascript头像上传代码实例
2019/09/28 Javascript
详解vue 组件
2020/06/11 Javascript
Nest.js散列与加密实例详解
2021/02/24 Javascript
[01:57]DOTA2上海特锦赛小组赛解说单车采访花絮
2016/02/27 DOTA
[59:35]DOTA2上海特级锦标赛主赛事日 - 3 败者组第三轮#1COL VS Alliance第二局
2016/03/04 DOTA
[58:32]EG vs Liquid 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
python字典多条件排序方法实例
2014/06/30 Python
Python连接PostgreSQL数据库的方法
2016/11/28 Python
利用 python 对目录下的文件进行过滤删除
2017/12/27 Python
Python matplotlib画图实例之绘制拥有彩条的图表
2017/12/28 Python
Python抓取聚划算商品分析页面获取商品信息并以XML格式保存到本地
2018/02/23 Python
Django开发中复选框用法示例
2018/03/20 Python
python基于TCP实现的文件下载器功能案例
2019/12/10 Python
Pycharm 使用 Pipenv 新建的虚拟环境(图文详解)
2020/04/16 Python
三星加拿大官方网上商店:Samsung CA
2020/12/18 全球购物
和平主题的演讲稿
2014/01/12 职场文书
小学生国旗下演讲稿
2014/04/25 职场文书
党的群众路线教育实践活动对照检查材料(四风)
2014/09/27 职场文书
工厂标语大全
2014/10/06 职场文书
个人借款协议书范本
2014/11/17 职场文书
金融专业银行实习证明模板
2014/11/28 职场文书
2014离婚协议书范文(3篇)
2014/11/29 职场文书
2015年重阳节活动总结
2015/03/24 职场文书
用python自动生成日历
2021/04/24 Python
漫画「古见同学有交流障碍症」第25卷封面公开
2022/03/21 日漫