偶发性微信支付签名失败问题

2022-08-30  本文已影响0人  昵称被占用最扎心

线上环境小程序商城突然出现一部分用户拉不起支付的情况,查询日志发现有大量"签名失败"。

微信统一支付请求返回: 
<xml><return_code><![CDATA[FAIL]]></return_code>
<return_msg><![CDATA[签名错误,请检查后再试]]></return_msg>
</xml>

微信签名是根据支付字符串md5生成的,所以这种偶发性的错误,应该不是md5签名方法的问题。

private String encodeMd5(String inStr) {
        StringBuffer hexValue = new StringBuffer();
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");

            byte[] byteArray = inStr.getBytes("UTF-8");
            byte[] md5Bytes = md5.digest(byteArray);

            for (int i = 0; i < md5Bytes.length; i++) {
                int val = ((int) md5Bytes[i]) & 0xff;
                if (val < 16) {
                    hexValue.append("0");
                }
                hexValue.append(Integer.toHexString(val));
            }
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }
        return hexValue.toString();
    }

md5 = MessageDigest.getInstance("MD5") 虽然是创建了一个新的实例,但是仍然有线程安全问题,官方给的是通过md5.digest避免或者换apache有线程安全的md5工具类。本项目中的md5加密是没有线程安全问题的,单独测试过,没有问题。
接着查代码,在生成sign后,最后需要将sign和其他要素组成为一个xml,在这块,用了一个对象转xml方法,核心代码如下:

     public String toXML(Object request) {
        XStream xstreamReq = new XStream(new XppDriver());
        xstreamReq.alias("xml", request.getClass());
        String requestXML = xstreamReq.toXML(request).replace("\n", "").replace("__", "_");
        return requestXML;
    }

在这个方法中,生成的xml的字符串做了两次替换,一次是将换行符去掉,第二次是将两个下划线替换成一个下换线。第一个替换可以理解,第二个替换不知道什么作用,所以去掉后测试下。发现,这样写的目的是简单粗暴的解决对象属性中存在"_"时,转换后的xml文件的标签变成了"__"两个下划线。例如有一个对象的其中一个属性为xx_xxx,xstreamReq.toXML(request) 生成的xml中这个属性的标签成了<xx__xxx></xx__xxx>,而通过.replace("__", "_")可以简单粗暴的将这个问题解决掉。
但是这样写只解决了标签的问题,同时引入了新的bug。微信openID是存在包含“__”两个下划线的情况的,这种情况下,微信的openID被替换成了错误的,而签名是正确的,最终导致签名和xml数据不一致,签名失败。其效果相当于将微信用户openId包含两个下划线的用户拉入了支付黑名单,永远拉不起支付。在网上搜索了下,很多文章都在用上面方法生成最终请求xml做拉起支付请求。

问题找到就好办了,解决xml生成时,因对象属性存在"_",防止生成的xml标签将这类对象属性标签转成“__”两个下划线,解决方案如下:

    public String toXMLNotreplace(Object request) {
        XStream xstreamReq = new XStream(new Xpp3Driver(new NoNameCoder()));
        xstreamReq.alias("xml", request.getClass());
        String requestXML = xstreamReq.toXML(request).replace("\n", "");
        return requestXML;
    }
上一篇 下一篇

猜你喜欢

热点阅读