微信支付中文乱码,带中文签名不成功

  • 在做微信公众号支付和H5支付时发现支付参数带中文就会签名失败,试过很多种办法如:
new String(xml.toString().getBytes(), "ISO8859-1");

把xml转为ISO8859-1提交到微信统一下单接口签名不正确,网上一般都是说这种做法。

  • 后面我用这样的方式能签名成功,也能支付
paraMap.put("body", URLEncoder.encode("棋牌", "UTF-8")); 

但是这样编码后,微信支付显示的body是编码后的一串乱码。

  • 最后终于查到是需要改md5签名前字符串的编码。

  • 统一下单的代码:WeChatH5PayImpl.java 微信h5支付

@Override
    public JsonObject gateway(PayPlatform payPlatform, PayOrder order, Map<String, String> params) {
        JsonObject result = new JsonObject();
        try {
            //字典序列排序
            //第一次签名
            Map<String, String> paraMap = new HashMap<>();
            // paraMap.put("total_fee", order.getPrice().toString());
            paraMap.put("total_fee", "1");
            paraMap.put("appid", APPID);
            paraMap.put("out_trade_no", order.getMerchantOrderNo());
            paraMap.put("attach", order.getMerchantOrderNo());
            //TODO 中文编码有问题
            paraMap.put("body", "棋牌" );  //如果不转码,参数带中文会签名失败
            paraMap.put("mch_id", MCH_ID);
            paraMap.put("nonce_str", WeChatPublicNumberPayImpl.getNonceStr());
            paraMap.put("notify_url", notifyUrl);
            //paraMap.put("openid", params.getOrDefault("operId", ""));//"oPKW80lcsqmHLWvPLElQoN2p6Eow");
            String spbill_create_ip = params.getOrDefault("spbillCreateIp","127.0.0.1");
            if(-1 != spbill_create_ip.indexOf(",")){
                spbill_create_ip = spbill_create_ip.split(",")[0];
            }
            paraMap.put("spbill_create_ip",  spbill_create_ip);
            paraMap.put("trade_type", "MWEB");
         
            paraMap.put("scene_info","{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"http://www.test.com\",\"wap_name\": \"棋牌\"}} ");
			//字典序列排序
            String url = WeChatPublicNumberPayImpl.formatUrlMap(paraMap, false, true);
            url = url + "&key=" + MCH_ID_KEY;
            String sign = MD5Utils.MD5Encoding(url).toUpperCase();
            StringBuffer xml = new StringBuffer();
            xml.append("<xml>");
            for (Map.Entry<String, String> entry : paraMap.entrySet()) {
                xml.append("<" + entry.getKey() + ">");
                xml.append(entry.getValue());
                xml.append("</" + entry.getKey() + ">" + "\n");
            }
            xml.append("<sign>");
            xml.append(sign);
            xml.append("</sign>");
            xml.append("</xml>");

            log.info("xml \n {} ", xml.toString());
            String responseBosy = HttpUtils.sentPost(PAYURL, xml.toString(), "UTF-8");
            log.info("responseBosy \n {}  " , responseBosy );
            Map<String,String> respBodyMap = WeChatPublicNumberPayImpl.readStringXmlOut(responseBosy);
            String return_code = respBodyMap.getOrDefault("return_code","");
            if(null != return_code && "SUCCESS".equals(return_code)){
                //成功
                result.put("code", 1);
                result.put("type", ReturnType.JUMP_PAGE_TYPE.getCode());
                result.put("redirect", respBodyMap.getOrDefault("mweb_url","")+"&redirect_url=http://h5.ccac7.com/api/login");
            }else{
                //失败
                result.put("code",0);
                result.put("message","sign error");
            }
        } catch (Exception e) {
            result.put("code", 0);
            result.put("message","创建订单失败");
        }
        return result;
    }
  • WeChatPublicNumberPayImpl.java ASCII 码从小到大排序的代码
 /**
     * 方法用途: 对所有传入参数按照字段名的Unicode码从小到大排序(字典序),并且生成url参数串<br>   
	   特别注意如果是微信公众号第二次签名使用这个 (prepay_id=wx2018011916085772ffb69ce20165288425)拼接出来的url package会有问题
     * 实现步骤: <br>
     *
     * @param paraMap    要排序的Map对象
     * @param urlEncode  是否需要URLENCODE
     * @param keyToLower 是否需要将Key转换为全小写
     *                   true:key转化成小写,false:不转化
     * @return
     */
    public static String formatUrlMap(Map<String, String> paraMap, boolean urlEncode, boolean keyToLower) {
        String buff = "";
        Map<String, String> tmpMap = paraMap;
        try {
            List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet());
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
            Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
                @Override
                public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                    return (o1.getKey()).toString().compareTo(o2.getKey());
                }
            });
            // 构造URL 键值对的格式
            StringBuilder buf = new StringBuilder();
            for (Map.Entry<String, String> item : infoIds) {
                if (StringUtils.isNotBlank(item.getKey())) {
                    String key = item.getKey();
                    String val = item.getValue();
                    if (urlEncode) {
                        val = URLEncoder.encode(val, "utf-8");
                    }
                    if (keyToLower) {
                        buf.append(key.toLowerCase() + "=" + val);
                    } else {
                        buf.append(key + "=" + val);
                    }
                    buf.append("&");
                }

            }
            buff = buf.toString();
            if (buff.isEmpty() == false) {
                buff = buff.substring(0, buff.length() - 1);
            }
        } catch (Exception e) {
            return null;
        }
        return buff;
    }
	
  • MD5Utils.java
package com.rw.common.utils;

import java.security.MessageDigest;

public class MD5Utils {

    private static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
   
   //重要的就是这里,要调这个方法签名才可以
    public static String MD5Encoding(String s) {
        byte[] btInput = null;
        try {
            btInput = s.getBytes("UTF-8");
        }catch (Exception e){
        }
        return MD5(btInput, 32);
    }


    public static String MD5(String s) {
        byte[] btInput = s.getBytes();
        return MD5(btInput, 32);
    }

    public static String MD5_16(String str) {
        byte[] btInput = str.getBytes();
        return MD5(btInput, 16);
    }

    private static String MD5(byte[] btInput, int length) {
        try {
            // 获得MD5摘要算法的 MessageDigest 对象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // MessageDigest mdInst = MessageDigest.getInstance("SHA-1");
            // 使用指定的字节更新摘要
            mdInst.update(btInput);
            // 获得密文
            byte[] md = mdInst.digest();
            // 把密文转换成十六进制的字符串形式
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (byte byte0 : md) {
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            String result = new String(str);
            return length == 16 ? result.substring(8, 24) : result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
  • 这是最后执行结果:
    这里写图片描述

  • 还有注意的:微信公众号支付的支付授权地址路径不能只写域名地址

posted on 2018-01-20 21:35  愤怒的苹果ext  阅读(114)  评论(0编辑  收藏  举报

导航