微信支付签名遇到的坑

  • Post author:
  • Post category:其他


这两天做了下微信支付,其中也遇到不少问题,记录下,以免下次重复踩坑。

结合我们项目使用场景,我选择了扫码支付 模式二 (具体开发步骤见 官方文档

扫码支付模式二

)

微信下单方法:

 SortedMap<Object,Object> param = new TreeMap<Object,Object>();
			//公众号ID
			param.put("appid", appid);
			//订单主题
			param.put("body", body);
			//商户号
			param.put("mch_id", mch_id);
			//随机字符串
			param.put("nonce_str", randomStr);
			//回调地址
			param.put("notify_url", NOTIFY_URL);
			//商户订单号
			param.put("out_trade_no",out_trade_no);
			//客户端IP
			param.put("spbill_create_ip",ip);
			//金额
			param.put("total_fee",changeY2F(ppd.getPartyFee().toString()));
			//订单类型
			param.put("trade_type", "NATIVE");
			String sign = createSign(param, apiKey);
			//签名
			param.put("sign", sign);
			System.out.println("========== sign: " + sign);
			String xml = getRequestXML(param); 
			System.out.println("========== xml: " + xml);
			String content = HttpClinetUtil.sendPost(UNIFORMORDER, new String(xml.getBytes(),"ISO8859-1"));
			System.out.println("==========content: " + content);
			JSONObject result_xml = JSONObject.parseObject(xml2JSON(content)) ;
			System.out.println("================return_msg: " +result_xml.get("return_msg"));
			String return_code = result_xml.getString("return_code");
			String result_code =  result_xml.getString("result_code");    

由于微信total_fee 参数要求是具体到多少分 (整数),这里我们需要将金额进行转换  changeY2F 具体实现如下 (直接引用了网上的  不需要重复造轮子)

public  String changeY2F(String amount) {
        String currency = amount.replaceAll("\\$|\\¥|\\,", ""); // 处理包含, ¥
                                                                // 或者$的金额
        int index = currency.indexOf(".");
        int length = currency.length();
        Long amLong = 0l;
        if (index == -1) {
            amLong = Long.valueOf(currency + "00");
        } else if (length - index >= 3) {
            amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
        } else if (length - index == 2) {
            amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
        } else {
            amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
        }
        return amLong.toString();
    }

随机字符串的生成方法:

// 随机字符串生成  
    public static String getRandomString(int length) { // length表示生成字符串的长度  
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";  
        Random random = new Random();  
        StringBuffer sb = new StringBuffer();  
        for (int i = 0; i < length; i++) {  
            int number = random.nextInt(base.length());  
            sb.append(base.charAt(number));  
        }  
        return sb.toString();  
    }  

生成签名方法:

//创建md5 数字签证  
    public String createSign(SortedMap<Object, Object> parame,String apiKey) throws Exception{  
        StringBuffer buffer = new StringBuffer();  
        Set set = parame.entrySet();  
        Iterator iterator = set.iterator();  
        while(iterator.hasNext()){  
            Map.Entry entry = (Map.Entry) iterator.next();  
            String key = (String)entry.getKey();  
            Object value = (String)entry.getValue();  
            if(null != value && !"".equals(value) && !"sign".equals(key) && !"key".equals(key)){  
                buffer.append(key+"="+value+"&");  
            }             
        }  
        buffer.append("key="+apiKey);
        System.out.println("============ " + buffer.toString());
        String sign = MD5(buffer.toString()).toUpperCase();   
        System.out.println("签名参数:"+sign);  
        return sign;  
    }  

MD5 我直接引用了官方sdk的方法:

 public  String MD5(String data) throws Exception {
        java.security.MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

生成xml请求参数方法:

public static String getRequestXML(SortedMap<Object, Object> parame){  
        StringBuffer buffer = new StringBuffer();  
        buffer.append("<xml>");  
        Set set = parame.entrySet();  
        Iterator iterator = set.iterator();  
        while(iterator.hasNext()){  
            Map.Entry entry = (Map.Entry) iterator.next();  
            String key = (String)entry.getKey();  
            String value = (String)entry.getValue();  
            //过滤相关字段sign  
            if("sign".equalsIgnoreCase(key)){  
                buffer.append("<"+key+">"+"<![CDATA["+value+"]]>"+"</"+key+">");  
            }else{  
                buffer.append("<"+key+">"+value+"</"+key+">");  
            }             
        }  
        buffer.append("</xml>");  
        return buffer.toString();  
    }


这里需要注意的是   参数排序需要安装ascii码升序排列   即  a b c 这个顺序



这里一个遇到一个大坑,一直签名错误,同样的参数在官网签名验证可以通过,但是调用接口就返回乱码,经过各种尝试 还是不行 (参数包含中文时候) 后来发现这里需要

转码为

ISO8859-1



最后就是对返回xml数据的解析了

public static  String xml2JSON(String xml) {    
        JSONObject obj = new JSONObject();    
        try {    
            InputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));    	
    		// 读取输入流
    		SAXReader reader = new SAXReader();
    		Document document = reader.read(is);
    		// 得到xml根元素
    		Element root = document.getRootElement();
    		// 得到根元素的所有子节点
    		List<Element> elementList = root.elements();
    		for (Element e : elementList)
    			obj.put(e.getName(), e.getText());
            return obj.toString();    
        } catch (Exception e) {    
            e.printStackTrace();    
            return null;    
        }    
    }   

核心代码就这么多了,具体过程官方文档给的很详细,这里就不多做累述了

关键点就是签名算法 以及最后对请求参数的转码和金额的单位(分)



版权声明:本文为jin5203344原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。