• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

微信支付小程序版

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

近日,因工作需要开始研究在小程序上面实现微信支付功能,经过自己研究加上参考网上前辈的部分代码,终于实现了。期间遇到过很多坑,故在此笔记供自己和有需要的朋友参考。

开发环境:  微信小程序+nodejs(eggjs框架)

参数准备:  微信商户平台申请支付功能,获得商户ID(商户号)、商户key(API KEY)、获得openId、Appsecret()AppSecret是APPID对应的接口密码)

请求流程:  

      1. 小程序调用wx.request请求到后台(含一些支付信息,如金额、交易名称等)

      2.后台在收到前台小程序请求后,按微信支付开发文档的要求进行拼接,然后调用统一下单API发送请求到微信获取签名(这个签名个人理解主要是为了验证报文信息是否被篡改)

      3.连同生成的签名及一些重要信息在后台加密后生成一个签名(后文称第二次签名),连同第二次签名已经信息返回给 1 中的小程序request请求。

      4.小程序前台收到返回的成功信息后,调用API wx.requestPayment发送请求,成功后会条用微信的支付,让输入密码。(开发环境会生成一个二维码,然开发者微信扫描后才会调用微信支付输入密码界面),最后更新请求结果反馈给页面。

跳坑指南:

    两次签名时候都需要输入小程序ID(appId),但是两次签名参数中小程序ID变量名不一样,第一次是全部小写,第二次是第二个单词的首字母大写appId。如此处写错,会提示“支付验证签名失败”{因为签名的目的就是为了验证信息是否被篡改,此处变量名称变了,自然就对不上了}

以下为代码,供参考:(为了测试,交易金额信息直接在后台写死了)

  //小程序前台事件函数  xx.js
  wxPay:function(){
    //发送请求到后台,获取prepay_id
    console.log("&&&&&", app.gData.userInfo.openId);
    wx.request({
      url: app.gData.iServerUrl + \'/getPrepayId\',
      method: \'GET\',
      data: { openId: app.gData.userInfo.openId},
      header: { \'content-type\': \'application/json\' },
      success: function (res) {
        console.log("payment:",res);
        if (res.data.status == 100) {
          var payModel = res.data;
          wx.requestPayment({
            \'timeStamp\': payModel.timestamp,
            \'nonceStr\': payModel.nonceStr,
            \'package\': payModel.package,
            \'signType\': \'MD5\',
            \'paySign\': payModel.paySign,
            success: function (res) {
              console.log("success+++",res);
              wx.showToast({
                title: \'支付成功\',
                icon: \'success\',
                duration: 2000
              })
            },
            fail: function (res) {
              console.log("fail+++", res);
            }
          })
        }
      }
    })

  }

  

 

//后台eggjs  xxservice.js 中的方法   

 async getPrepayId(parm) {

            var {ctx,res }= this;

            var openid = parm.openId;
            var str = app.config.getWxPayOrdrID();
            var spbill_create_ip = \'192.168.3.11\'//ctx.header.host.replace(/::ffff:/, \'\'); // 获取客户端ip
            var body = \'测试支付\'; // 商品描述
            var notify_url = \'https://localhost:7001/api/v1/getPrepayId\' // 支付成功的回调地址  可访问 不带参数
            var nonce_str = str; // 随机字符串
            var out_trade_no = str; // 商户订单号
            var total_fee = \'1\'; // 订单价格 单位是 分
            var timestamp = Math.round(new Date().getTime()/1000); // 当前时间
            console.log("nonce_str",str);
            var ret ={
                appid:app.config.wx.appid,
                mch_id:app.config.wx.Mch_id,
                nonce_str:nonce_str,
                body:body,
                out_trade_no:out_trade_no,
                total_fee:total_fee,
                spbill_create_ip:spbill_create_ip,
                notify_url:notify_url,
                openid:openid,
                trade_type: \'JSAPI\',
                key:app.config.wx.Mch_key
            }

            // 签名
            var sign = paysign.paysignjsapi(ret);
            var bodyData = \'<xml>\';
            bodyData += \'<appid>\' + app.config.wx.appid + \'</appid>\';  // 小程序ID
            bodyData += \'<body>\' + body + \'</body>\'; // 商品描述
            bodyData += \'<mch_id>\' + app.config.wx.Mch_id + \'</mch_id>\'; // 商户号
            bodyData += \'<nonce_str>\' + nonce_str + \'</nonce_str>\'; // 随机字符串
            bodyData += \'<notify_url>\' + notify_url + \'</notify_url>\'; // 支付成功的回调地址
            bodyData += \'<openid>\' + openid + \'</openid>\'; // 用户标识
            bodyData += \'<out_trade_no>\' + out_trade_no + \'</out_trade_no>\'; // 商户订单号
            bodyData += \'<spbill_create_ip>\' + spbill_create_ip + \'</spbill_create_ip>\'; // 终端IP
            bodyData += \'<total_fee>\' + total_fee + \'</total_fee>\'; // 总金额 单位为分
            bodyData += \'<trade_type>JSAPI</trade_type>\'; // 交易类型 小程序取值如下:JSAPI
            bodyData += \'<sign>\' + sign + \'</sign>\';
            bodyData += \'</xml>\';

            // 微信小程序统一下单接口
            var returnValue = {};
            var urlStr = \'https://api.mch.weixin.qq.com/pay/unifiedorder\';

            const rst = await ctx.curl(urlStr, {
                // 必须指定 method
                method: \'POST\',
                data: bodyData

            });

            let signBody = rst.data.toString();
            if (!rst.error && rst.status == 200) {
                        console.log("test1 &&&&");
                        parseString(signBody, function (err, result) {
                            if (result.xml.return_code[0] == \'SUCCESS\') {
                                returnValue.msg = \'操作成功\';
                                returnValue.status = \'100\';
                                returnValue.out_trade_no = out_trade_no;  // 商户订单号
                                // 小程序 客户端支付需要 nonceStr,timestamp,package,paySign  这四个参数
                                returnValue.nonceStr = result.xml.nonce_str[0]; // 随机字符串
                                returnValue.timestamp = timestamp.toString(); // 时间戳
                                returnValue.package = \'prepay_id=\' + result.xml.prepay_id[0]; // 统一下单接口返回的 prepay_id 参数值
                                var wxSign = {
                                    appId: app.config.wx.appid,    //小程序ID
                                    nonceStr: returnValue.nonceStr,  //随机串
                                    package: returnValue.package,   //数据包
                                    signType: \'MD5\',    //签名方式
                                    timeStamp: returnValue.timestamp, //时间戳
                                    key:app.config.wx.Mch_key
                                }
                                returnValue.paySign = paysign.paysignjsapi(wxSign); // 签名
                            } else{
                                returnValue.msg = result.xml.return_msg[0];
                                returnValue.status = \'102\';
                            }
                        });
                    }

            return returnValue;
        }

  

//自己写的拼报文及md5加密,两次生成签名时都会调用,所有写成了一个独立的方法   paysign.js
\'use strict\'; const crypto = require(\'crypto\'); //const request = require(\'axios\'); function paysignjsapi(params) { let param = params; var key=\'\'; var ret ={}; // console.log("param.key",param.key); if(param.key){ key = param.key; delete param.key; ret = param; }else{ ret = param; } var str = raw(ret); //将参数拼接成字符串 str = str + \'&key=\'+key; console.log("MD5Str:",str); var md5Str = crypto.createHash(\'md5\').update(str).digest(\'hex\'); md5Str = md5Str.toUpperCase(); ; return md5Str; }; //字符排序连接 function raw(args) { var keys = Object.keys(args); keys = keys.sort(); var newArgs = {}; keys.forEach(function(key) { // newArgs[key.toLowerCase()] = args[key]; newArgs[key] = args[key]; }); var str = \'\'; for(var k in newArgs) { str += \'&\' + k + \'=\' + newArgs[k]; } str = str.substr(1); return str; }; module.exports={ paysignjsapi }

  


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
微信小程序应用安全分析及设计发布时间:2022-07-18
下一篇:
微信小程序web-view组件横空出世 - 今天的代码你撸了嘛发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap