美文网首页
node.js实现微信支付退款

node.js实现微信支付退款

作者: 黄秀杰 | 来源:发表于2017-12-18 23:49 被阅读0次

    缘起

    有付款就会有退款

    注意,退款支持部分退款

    左口袋的钱退到右口袋罗

    退款0.01元

    这次发起的退款请求0.01元是实时到账的,因此,用户在小程序端发起的退款只是一个请求到后台,后台审核人员审核无误后才后微信发起退款操作。

    引入第三方module

    在package.json 中加入"weixin-pay": "^1.1.7"这一条

    代码目录结构

    目录结构如下

    入参

    { transaction_id: '4200000005201712165508745023', // 交易
      out_trade_no: '5b97cba0ae164bd58dfe9e77891d3aaf', // 自己这头的交易号
      out_refund_no: '6f3240c353934105be34eb9f2d364cec', // 退款订单,自己生成
      total_fee: 1, // 退款总额
      nonce_str: '1xSZW0op0KcdKoMYxnyxhEuF1fAQefhU', // 随机串
      appid: 'wxff154ce14ad59a55', // 小程序 appid
      mch_id: '1447716902', // 微信支付商户id
      sign: '416FCB62F9B8F03C82E83052CC77524B' // 签名,weixin-pay这个module帮助生成 }
    

    然后由wxpay为我们生成其余字段,比如nonce_str,sign,当然还少不了p12证书,
    这个早选在wxpay初始代码里已经配置了,pfx: fs.readFileSync(__dirname + '/../../../cert/apiclient_cert.p12'), //微信商户平台证书

    lib/wechat/utils/wxpay.js的源码

    const WXPay = require('weixin-pay'); // 引入weixin-pay这个第三方模块
    const {weapp} = require('../../../utils/config'); // 我自己的全局配置文件,包括了appid key 等
    const fs = require('fs');
    
    const wxpay = WXPay({
        appid: weapp.APPID,
        mch_id: weapp.MCHID,
        partner_key: weapp.KEY, //微信商户平台 API secret,非小程序 secret
        pfx: fs.readFileSync(__dirname + '/../../../cert/apiclient_cert.p12'), 
    });
    
    module.exports = wxpay;
    

    另外还有一个util.js工具类

    用于验证与错误回调

    const wxpay = require('./wxpay');
    
    const validateSign = results => {
      const sign = wxpay.sign(results);
      if (sign !== results.sign) {
        const error = new Error('微信返回参数签名结果不正确');
        error.code = 'INVALID_RESULT_SIGN';
        throw error;
      };
      return results;
    };
    
    const handleError = results => {
      if (results.return_code === 'FAIL') {
        throw new Error(results.return_msg);
      }
      if (results.result_code !== 'SUCCESS') {
        const error = new Error(results.err_code_des);
        error.code = results.err_code;
        throw error;
      }
      return results;
    };
    
    module.exports = {
      validateSign,
      handleError,
    };
    

    发起退款请求

    退款逻辑是这样的,先从自己这边的Order数据表中查出transaction_id/out_trade_no/total_fee,再拼上自己生成的out_refund_no退款单号,本次退款部分金额refund_fee,最后由weixin-pay这个模块下的wxpay.refund调起就可以了,成功就把订单状态改成"退款成功"

    // 退款
    router.post('/refund', function(req, res) {
        Order.findById(req.body._id, (err, order) => {
            if (err) {
                console.log(err);
            }
            console.log(order);
            // 生成微信设定的订单格式
            var data = {
                transaction_id: order.transactionId,
                out_trade_no: order.tradeId,
                out_refund_no: uuid().replace(/-/g, ''),
                total_fee: order.amount,
                refund_fee: order.amount
            };
            console.log(data);
            // 先查询订单,再退订单
            wxpay.refund(data, (err, result) => {
                if (err) {
                    console.log(err);
                    res.send(
                        utils.json({
                            code: 500,
                            msg: '退款失败'
                        })
                    );
                }
                // 返回退款请求成功后,要将订单状态改成REFUNDED
                if (result.result_code === 'SUCCESS') {
                    console.log(result);
                    order.status = 'REFUNDED';
                    order.save((err, response) => {
                        res.send(
                            utils.json({
                                msg: '退款成功'
                            })
                        );
                    });
                } else {
                    res.send(
                        utils.json({
                            code: 500,
                            msg: result.err_code_des
                        })
                    );
                }
    
            });
        });
    });
    

    入参的坑

    1.这次遇到的坑是refund_fee忘记传值,也就是说微信退款是支持部分退款的,如果是全额退款,那么将它赋值为total_fee相同

    2.网上说的op_user_id: weapp.MCHID这个参数是非必选的

    3.transaction_id 与 out_trade_no 二选一即可,这样在没有记录transaction_id的情况(比如没有写支付成功的callback)下,也能发起退款;其中优先级前者大于后者,在我在分别前其一故意给错的过程中得到了验证。

    4.报了一个appid与商户号不匹配的报错,return_code: 'FAIL', return_msg: '商户号mch_id与appid不匹配’原来是小程序还没绑定公众号微信支付,这真是一个乌龙。

    成功退款微信返回的数据

    1. appid:"wxff154ce14ad59a55"
    2. cash_fee:"1"
    3. cash_refund_fee:"1"
    4. coupon_refund_count:"0"
    5. coupon_refund_fee:"0"
    6. mch_id:"1447716902"
    7. nonce_str:"c44wOvB6a4bQJfRk"
    8. out_refund_no:"9ace1466432a4d548065dc8df95d904a"
    9. out_trade_no:"5b97cba0ae164bd58dfe9e77891d3aaf"
    10. refund_channel:""
    11. refund_fee:"1"
    12. refund_id:"50000705182017121702756172970"
    13. result_code:"SUCCESS"
    14. return_code:"SUCCESS"
    15. return_msg:"OK"
    16. sign:"5C2E67B3250054E8A665BF1AE2E9BDA3"
    17. total_fee:"1"
    18. transaction_id:”4200000005201712165508745023”
    

    重复退款将返回如下

    1. appid:"wxff154ce14ad59a55"
    2. err_code:"ERROR"
    3. err_code_des:"订单已全额退款"
    4. mch_id:"1447716902"
    5. nonce_str:"KP1YWlU7a5viZEgK"
    6. result_code:"FAIL"
    7. return_code:"SUCCESS"
    8. return_msg:"OK"
    9. sign:”C2A7DED787BEA644C325E37D96E9F41C”
    
    人工退款

    最后

    如果没有写退款功能或者不想写退款功能怎么办,其实可以从微信支付的后台pay.weixin.qq.com,也是能退款出去的,只是不想忘记了要人工将订单状态置为退款状态。

    鸣谢

    感谢weixin-pay作者提供这个module,上述源码移植于LeanCloud小程序支付的demo,git地址是https://github.com/leancloud/weapp-pay-getting-started.git,一并感谢。

    praise mp

    相关文章

      网友评论

          本文标题:node.js实现微信支付退款

          本文链接:https://www.haomeiwen.com/subject/lnpvwxtx.html