微信小程序之微信支付功能

本文需要的外部js下载地址 

这几天做微信支付功能,可谓是一步一个坑,在这里总结一下,供以后查看(详细代码在最后)

大致的步骤:

  1. 通过微信的登录接口获取到code
  2. 用code去请求微信接口换取openid
  3. 用openid生成统一订单得到prepay_id
  4. 调用微信接口完成支付

详细步骤:

  实现的前提:你要有正确的appid,serect,mch_id(商户号) ,out_trade_no(订单号)

  appid 和 serect 在小程序的公众平台里(开发 -> 开发设置)可以找到。然后创建项目,项目的appid一定要和前面提到的appid    保持一致。

  • 获取code

   调用微信的登陆接口

wx.login({
    success: function(res){
        // 接口调用成功后。返回值中包含有code
    }
})
  •   code换取openid

   调用微信的接口

 这个接口请求参数有三个:appid , secret , js_code , grant_type

wx.request({
     // 这里的url参数需要拼接,appid和secret  填写对应的数据  js_code:就是刚才登陆接口返回的code,  grant_type=authorization_code  这个参数是固定的 不用改变  
     url: "https://api.weixin.qq.com/sns/jscode2session?appid=aaaaaaaaaaaaa&secret=sssssssssssss&js_code=" + code + "&grant_type=authorization_code",
     method: 'GET',
     success: function (res) {
         // 请求成功 这里会返回openid
     },
     fail: function () {
     }
}) 

这里碰到的问题:报错说code失效

后来发现是因为建小程序项目时用的appid和上面接口请求参数中的appid不一样,所以这就是为什么前面说一定要提前把appid这些东西弄好,把项目建立在正确的appid下面

  • 生成统一订单

 这里就是最麻烦的地方了,需要处理很多数据,下面是即将发出去的下一个请求,为了它我们需要提前准备好它需要的口粮(参数)

var formData = "<xml>"
    formData += "<appid>" + obj.appid + "</appid>"                //appid 
    formData += "<body>" + obj.body + "</body>"                   //标题
    formData += "<mch_id>" + obj.mch_id + "</mch_id>"             //商户号 
    formData += "<nonce_str>" + obj.nonce_str + "</nonce_str>"    //随机字符串,不长于32位。 
    formData += "<notify_url>" + obj.notify_url + "</notify_url>" //异步接收微信支付结果通知的回调地址
    formData += "<openid>" + msg.openid + "</openid>"             //openid
    formData += "<out_trade_no>" + obj.out_trade_no + "</out_trade_no>" //商户订单号
    formData += "<spbill_create_ip>" + obj.spbill_create_ip + "</spbill_create_ip>"   //用户Id
    formData += "<total_fee>" + obj.total_fee + "</total_fee>" //金额
    formData += "<trade_type>" + obj.trade_type + "</trade_type>"  //公共号支付
    formData += "<sign>" + obj.sign + "</sign>"//签名
    formData += "</xml>"
wx.request({
    // 这就是即将请求的地址,它需要的参数formData是xml类型的,长的就是上面那个变量的样子
    url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
    method: 'POST',
    data: formData,
    success: function (res) {

    }
})

首先要知道它需要什么样的参数:

参考H5支付功能参数:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1

注意:这里面对参数进行签名的函数如下,传入的对象是:将需要加密的参数以对象形式传进去,就可以得到加密后的结果

          函数中涉及到key  根据自己的情况填写  

/* 签名 */
makeSign: function (obj){
    var arr = [];
    for(var i in obj){
        arr.push(i + "=" + obj[i]);
    }
    // 字典排序
    arr = arr.sort();
    // 将参数拼接成url
    var str = arr.join('&');
    // 签名第二步:在str后面加上key
    str += "&key=1111111111111";
    // 签名第三步:MD5加密
    str = md5.hexMD5(str);
    // 签名第四步:所有字符转大写
    str = str.toUpperCase();
    return str;
},

用户ip获取函数:(提示:这里的请求地址只适合用在练手阶段,实际开发中请换成后台获取ip的对应接口,否则小程序上线会报错)

/* 用户端ip */
    getIP: function(){
        var that = this;
        wx.request({
            url: 'http://ip-api.com/json',
            success: function (e) {
                that.setData({
                    ip: e.data.query
                })
            }
        })
    }

还有一个坑:当你这一步过去以后,请求成功了!发现返回一堆xml形式的东西,这下该处理xml了,这里就是我自己找的可以用在微信小程序中的xml解析js :dom-parser.js  dom.js (单独下载地址)

const parser = require('../../lib/dom-parser');
const Dom = require('../../lib/dom.js');

...

wx.request({
    url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
    method: 'POST',
    data: formData,
    success: function (res) {
         // xml转json
          var XMLParser = new parser.DOMParser()
          var doc = XMLParser.parseFromString(res.data);

          // 找到prepay_id
          var prepay_id_node = doc.getElementsByTagName('prepay_id')[0];
          var prepay_id;
          if (prepay_id_node){
             prepay_id = prepay_id_node.firstChild.nodeValue;
          }else{
             wx.showToast({
                title: '支付失败',
                icon: 'none',
                duration: 1800
             })
             console.log(res)
             return;
          }
     },
     fail: function () {
     }
}) 

 

  • 支付

最后一步了,现在有了prepay_id,就可以去挑起支付接口了,支付前先对参数进行罗列与处理

这里还有个坑:明明官方文档中参数没有说传appid  可是我当时不传appid时,发现支付一直不能成功,后来碰到一篇帖子说他加了appid就好了,我想要不试一下,加上后,果然支付成功了!内心是多么绝望,这坑谁能想到,哎!!

// timeStamp  nonceStr  package  signType  paySign
var payData = {
    appId: 'aaaaaaaaaaaaa',
    timeStamp: Date.now() + '',
    nonceStr: that.rdmNum(),
    package: "prepay_id="+prepay_id,
    signType: 'MD5'
}
var paySign = that.makeSign(payData);        // 加密参数,方法跟上面统一订单中的一样
payData.paySign = paySign;
payData.out_trade_no = msg.out_trade_no;      //订单号,用来关闭订单用
that.pay(payData);                            // 调用支付函数

支付函数

/* 支付 */
    pay: function (param) {
        var that = this;
        wx.requestPayment({
            appId: param.appId,
            timeStamp: param.timeStamp,
            nonceStr: param.nonceStr,
            package: param.package,
            signType: param.signType,
            paySign: param.paySign,
            success: function (res) {
                wx.showToast({
                    title: '支付成功',
                    icon: 'none',
                    duration: 1800
                })
            },
            fail: function (res) {
                wx.showToast({
                    title: '支付失败',
                    icon: 'none',
                    duration: 1800
                })
                // 支付失败,关闭订单
                that.closeOrder({
                    appid: 'aaaaaaaaaaaaa',
                    mch_id: '999999999',
                    out_trade_no: param.out_trade_no,
                    nonce_str: that.rdmNum()
                })
            },
            complete: function () {

            }
        })
    },

关闭订单函数

/* 关闭订单 order:关闭订单的对象 */
    closeOrder: function(order){
        // appid mch_id out_trade_no  nonce_str  sign 
        // 对传进来的order对象进行md5加密
        var sign = this.makeSign(order);

        var formData = "<xml>"
        formData += "<appid>" + order.appid + "</appid>" //appid 
        formData += "<mch_id>" + order.mch_id + "</mch_id>" //商户号 
        formData += "<nonce_str>" + order.nonce_str + "</nonce_str>" //随机字符串,不长于32位。 
        formData += "<out_trade_no>" + order.out_trade_no + "</out_trade_no>" //商户订单号
        formData += "<sign>" + sign + "</sign>"//签名
        formData += "</xml>"

        wx.request({
            url: 'https://api.mch.weixin.qq.com/pay/closeorder ',
            method: 'POST',
            data: formData,
            success: function(res){
                console.log(res)
            }
        })
        
    },
  • 最后粘贴上所有代码:

// 假数据,请根据自己的情况替换
// appid:aaaaaaaaaaaaa
// secret: sssssssssssss
// mch_id: 999999999     商户号
// out_trade_no: this.data.msg.recordId    订单号(后台接口得到)
// 通知地址notify_url:http://xxxxxxx
// key:1111111111111

const md5 = require('../../utils/md5.js');
const parser = require('../../lib/dom-parser');
const Dom = require('../../lib/dom.js');

Page({
    data: {
        msg: {},                            // 这里是订单的一些信息
        ip: ''                             // 用户ip
    },
    onLoad: function (options) {
        this.setData({
            msg: app.globalData.payData
        })
        this.getIP();            // 获取用户的ip,存放在data的ip中农,供后面使用          
    },
    // 点击支付按钮触发
    payBtn: function(){
        var that = this;
        wx.login({
            success: function (res) { 
                // 通过登录接口返回的code去换取openid           
                wx.request({
                    url: "https://api.weixin.qq.com/sns/jscode2session?appid=aaaaaaaaaaaaa&secret=sssssssssssss&js_code=" + res.code + "&grant_type=authorization_code",
                    method: 'GET',
                    success: function (res) {
                        // 生成统一订单
                        that.order({
                            mch_id: '999999999',
                            out_trade_no: that.data.msg.recordId,
                            total_fee: that.data.msg.payCharge,
                            openid: res.data.openid
                        });
                        console.log(res.data.openid)
                    },
                    fail: function () {
                        wx.showToast({
                            title: '支付失败',
                            icon: 'none',
                            duration: 1800
                        })
                    }
                })             
            }
        });
        
    },
    /* 生成统一订单 
    * mch_id:商户号
    * out_trade_no:商户订单号
    * total_fee:总金额
    * openid: openid
    */
    order: function (msg){
        var that = this;
        // 针对商户订单号重复的问题,在实际订单号后面加上_随机数来实现
        // mch_id, out_trade_no, total_fee, openid
        // var out_trade_no = msg.out_trade_no + '_' + that.rdmNum(6); 
        console.log(msg)  
        var obj = {
            appid: 'aaaaaaaaaaaaa',                   //appid
            mch_id: msg.mch_id,                       //商户号
            nonce_str: this.rdmNum(),                 //随机字符串
            body: 'payTest',                          //商品描述
            out_trade_no: msg.out_trade_no,               //商户订单号
            total_fee: msg.total_fee,                 //总金额
            openid: msg.openid,                       //openid
            spbill_create_ip: this.data.ip,           //终端ip
            notify_url: 'http://xxxxxxx',    //通知地址
            trade_type: 'JSAPI'                                           // 交易类型:公众号支付
        }
        // 签名
        var sign = this.makeSign(obj);
        obj.sign = sign;

        var formData = "<xml>"
        formData += "<appid>" + obj.appid + "</appid>"                //appid 
        formData += "<body>" + obj.body + "</body>"                   //标题
        formData += "<mch_id>" + obj.mch_id + "</mch_id>"             //商户号 
        formData += "<nonce_str>" + obj.nonce_str + "</nonce_str>"    //随机字符串,不长于32位。 
        formData += "<notify_url>" + obj.notify_url + "</notify_url>" //异步接收微信支付结果通知的回调地址
        formData += "<openid>" + msg.openid + "</openid>"             //openid
        formData += "<out_trade_no>" + obj.out_trade_no + "</out_trade_no>" //商户订单号
        formData += "<spbill_create_ip>" + obj.spbill_create_ip + "</spbill_create_ip>"   //用户Id
        formData += "<total_fee>" + obj.total_fee + "</total_fee>" //金额
        formData += "<trade_type>" + obj.trade_type + "</trade_type>"  //公共号支付
        formData += "<sign>" + obj.sign + "</sign>"//签名
        formData += "</xml>"

        wx.request({
            url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
            method: 'POST',
            data: formData,
            success: function (res) {
                // xml转json
                var XMLParser = new parser.DOMParser()
                var doc = XMLParser.parseFromString(res.data);

                // 找到prepay_id
                var prepay_id_node = doc.getElementsByTagName('prepay_id')[0];
                var prepay_id;
                if (prepay_id_node){
                    prepay_id = prepay_id_node.firstChild.nodeValue;
                }else{
                    wx.showToast({
                        title: '支付失败',
                        icon: 'none',
                        duration: 1800
                    })
                    return;
                }

                // timeStamp  nonceStr  package  signType  paySign
                var payData = {
                    appId: 'aaaaaaaaaaaaa',
                    timeStamp: Date.now() + '',
                    nonceStr: that.rdmNum(),
                    package: "prepay_id=" + prepay_id,
                    signType: 'MD5'
                }
                var paySign = that.makeSign(payData);
                payData.paySign = paySign;
                payData.out_trade_no = msg.out_trade_no;      //订单号,用来关闭订单用
                that.pay(payData);
            }
        })
    },
    /* 支付 */
    pay: function (param) {
        var that = this;
        wx.requestPayment({
            appId: param.appId,
            timeStamp: param.timeStamp,
            nonceStr: param.nonceStr,
            package: param.package,
            signType: param.signType,
            paySign: param.paySign,
            success: function (res) {
                wx.showToast({
                    title: '支付成功',
                    icon: 'none',
                    duration: 1800
                })
            },
            fail: function (res) {
                wx.showToast({
                    title: '支付失败',
                    icon: 'none',
                    duration: 1800
                })
                // 支付失败,关闭订单
                that.closeOrder({
                    appid: 'aaaaaaaaaaaaa',
                    mch_id: '999999999',
                    out_trade_no: param.out_trade_no,
                    nonce_str: that.rdmNum()
                })
            },
            complete: function () {

            }
        })
    },
    /* 关闭订单 order:关闭订单的对象 */
    closeOrder: function(order){
        // appid mch_id out_trade_no  nonce_str  sign 
        // 对传进来的order对象进行md5加密
        var sign = this.makeSign(order);

        var formData = "<xml>"
        formData += "<appid>" + order.appid + "</appid>" //appid 
        formData += "<mch_id>" + order.mch_id + "</mch_id>" //商户号 
        formData += "<nonce_str>" + order.nonce_str + "</nonce_str>" //随机字符串,不长于32位。 
        formData += "<out_trade_no>" + order.out_trade_no + "</out_trade_no>" //商户订单号
        formData += "<sign>" + sign + "</sign>"//签名
        formData += "</xml>"

        wx.request({
            url: 'https://api.mch.weixin.qq.com/pay/closeorder ',
            method: 'POST',
            data: formData,
            success: function(res){
                console.log(res)
            }
        })
        
    },
    /* 生成随机字符串 */
    rdmNum: function (len) {
        len = len || 32;
        var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
        var maxPos = chars.length;
        var num = '';
        for (var i = 0; i < len; i++) {
            num += chars.charAt(Math.floor(Math.random() * maxPos));
        }
        return num;
    },
    /* 签名 */
    makeSign: function (obj){
        var arr = [];
        for(var i in obj){
            arr.push(i + "=" + obj[i]);
        }
        // 字典排序
        arr = arr.sort();
        // 将参数拼接成url
        var str = arr.join('&');
        // 签名第二步:在str后面加上key
        str += "&key=1111111111111";
        // 签名第三步:MD5加密
        str = md5.hexMD5(str);
        // 签名第四步:所有字符转大写
        str = str.toUpperCase();
        return str;
    },
    /* 用户端ip */
    getIP: function(){
        var that = this;
        wx.request({
            url: 'http://ip-api.com/json',
            success: function (e) {
                that.setData({
                    ip: e.data.query
                })
            }
        })
    }
})

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值