微信小程序支付流程梳理

发表于:2018-6-29 16:37

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:未知    来源:51Testing软件测试网采编

分享:
    用 thinkjs 封装了个小程序支付的 Service 在这里记录一下,顺便梳理一下小程序支付的流程和思路。首先,先把官网的流程图放上来,然后按照图一步步的来。

    第一步: 用户请求开发者后台,发起下单请求
    发起请求前在小程序端调用wx.checkSession()查看session_key是否过期
    如果过期了 重新调用 wx.login() 返回 session_key和openid
    如果没过期 继续下一步操作,请求开发者后台
    第二步: 开发者查找一下数据库或者缓存里是否有 openid 和 session_key
    如果有生成订单编号 out_trade_no
    如果没有返回错误消息,缺少openid、session_key
    第三步: 开发者服务器请求统一下单API,带上要求的参数:
    小程序的 appid
    商户号 mch_id
    随机字符串 nonce_str(下面会给出方法)
    签名 sign (具体方法见下面)
    商品描述 body(商品简单描述比如:短信平台-短信套餐充值)
    商品订单号 out_trade_no
    标价金额 total_fee
    终端IP spbill_create_ip
    nonce_str获取随机字符串:
    test.jsjs
    /*
     *  功能: 返回32位随机字符串
     *  create by tiankai on 2018-06-25 15:39
     */
    getNonceStr(){
        let char = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        let len = 32;
        let result = '';
        for(let i = 0; i < len; i++){
            let randomNum = Math.floor(Math.random()*char.length);
            result += char[randomNum];
        }
        return result;
    }

    sign 参数签名, 第二个tab(js)是调用函数的方法,key为商户平台设置的密钥key
    test.jsjsjs
    /*
     *  功能:sign 参数签名
     *  create by tiankai on 2018-06-26 12:00
     */
    async makeSign(params, key){
        // 生成签名 sign
        let strOrderArr = Object.keys(params).sort();
        let stringA = "";
        strOrderArr.map(val =>{
            //如果参数值为空,或者验证返回的 sign 不参与签名
            if(
                think.isNullOrUndefined(params[val]) ||
                val === 'sign' ||
                params[val].length === 0
            ) return;
            stringA += val + "=" + params[val] + "&";
        });
        let stringSignTemp = stringA + "key=" + key;
        let sign = think.md5(stringSignTemp).toUpperCase();
        return sign;
    }

    得到这几个参数就开始发起请求统一下单 API 了,这里需要注意的是,请求参数应该以 xml的形式传送过去,这里借助一个工具xml2js把对象转换为 xml,也可以把 xml 转换为 对象、json。
    安装 xml2js
    npm i xml2js
    使用 xml2js
    test.jsjs
    const xml2js = require('xml2js');
    //xml->json
    //xml2js默认会把子子节点的值变为一个数组, explicitArray设置为false
    var xmlParser = new xml2js.Parser({explicitArray : false, ignoreAttrs : true})
    //json->xml
    var jsonBuilder = new xml2js.Builder();

    请求参数转换为 xml
    test.jsjs
    /*
     *  功能:获取统一下单 API 请求XML参数
     *  create by tiankai on 2018-06-25 15:23
     */
    async getUnifiedOrderParams(){
        let signString = {
            appid: config.appid,
            mch_id: config.mchid,
            nonce_str: await this.getNonceStr(),
            body: '短信平台-短信套餐购买',
            out_trade_no: '20180926125346',//订单号
            total_fee: 88,//订单金额
            //APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
            //需要自行获取,这里只是为了测试直接写上了
            spbill_create_ip: '123.12.12.123',
            notify_url: config.notify_url,
            trade_type: 'JSAPI',
            openid: await think.cache('openId')
        }
        let sign = await this.makeSign(signString,config.key);
        signString.sign = sign;
        /* console.log("--------------------------");
         * console.log(sign);
         * console.log("--------------------------"); */
        let xml = await jsonBuilder.buildObject(signString);
        /* console.log(xml); */
        return xml;
    }

    然后就可以发起统一下单API请求了
    test.jsjs

    /*
     *  功能:调用统一下单API接口
     *  create by tiankai on 2018-06-26 11:01
     */
    async unifiedOrder(){
        let xmlParams = await this.getUnifiedOrderParams();
        let unifiedOrderUrl = config.unifiedOrderUrl;
        let opt = {
            method: "POST",
            mode: 'cors',
            headers: {
                'content-type': 'text/xml'
            },
            body: xmlParams
        }
        let res = await this.fetch(unifiedOrderUrl, opt);
        //这里微信返回的也是 xml
        let result = await res.text();
        let data = null;
        // 微信返回的 XML 转换为 JSON
        xmlParser.parseString(result,function(err, jsonData){
            if(!err){
                /* console.log(jsonData); */
                data = jsonData;
            }
        });
        return data;
    }

    第四步: 当请求成功时 判断一下 return_code 和 result_code
    如果它们都等于 SUCCESS 的时候,微信会把我们需要的预支付订单信息prepay_id返回来,
    否则返回 return_msg 给前端 展示具体错误
    第五步: 拿上prepay_id,进行再次签名,然后返回给前端
    test.jsjs
    /*
     *  功能:统一下单接口返回 prepay_id 再次签名 返回给前端
     *  create by tiankai on 2018-06-26 15:45
     */
    async payParams(){
        let signString = {
            appid: config.appid,
            timeStamp: +new Date(),
            nonce_str: await this.getNonceStr(),
            package: null,
            signType: 'MD5'
        }
        //调用 统一下单 API
        let jsonData = await this.unifiedOrder();
        if(think.isNullOrUndefined(jsonData) &&
           jsonData.xml.return_code === 'SUCCESS' &&
           jsonData.xml.result_code === 'SUCCESS'
        ){
            signString.package = 'perpay_id='+jsonData.xml.perpay_id
        }else{
            return jsonData.xml.return_msg;
        }
        //进行再次签名
        let paySign = await this.makeSign(signString,config.key);
        signString.paySign = paySign;
        let { appid, signType, ...result } = signString;
        // result 中不包括 appid 和 signType 返回给前端
        return result;
    }

    第六步: 用户确认支付后,小程序端调用支付接口,根据返回结果提示用户
    test.jsjs
    wx.requestPayment({
       'timeStamp': '',
       'nonceStr': '',//后端返回的随机字符串
       'package': '',//后端返回的
       'signType': 'MD5',
       'paySign': '',//后端返回的
       'success':function(res){
       },
       'fail':function(res){
       }
    });

    第七步: 支付成功后,微信服务器会把支付结果返回给配置的notify_url,开发者根据支付结果,更新服务器的订单状态。
    大体流程就是这,现在进行到了 第四步 公司小程序 appid 还没申请下来 商户号 mch_id也没有,等过段时间继续更新。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号