zoukankan      html  css  js  c++  java
  • 微信支付小程序版

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

    开发环境:  微信小程序+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 }

      

  • 相关阅读:
    51Nod 1009 数字1的数量(思维)
    「CTSC 2008」祭祀
    「CSA Round #41」BFS-DFS
    「CEOI2008」order
    「HEOI 2016/TJOI 2016」求和
    「HAOI 2018」染色
    「CF 961G」Partitions
    「WC 2007」剪刀石头布
    「POI 2010」Bridges
    「CQOI 2014」危桥
  • 原文地址:https://www.cnblogs.com/chaichai/p/8353601.html
Copyright © 2011-2022 走看看