微信支付签名遇到的坑

来源:互联网 发布:数据库系统的二级映射 编辑:程序博客网 时间:2024/05/16 14:14


        

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


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


        微信下单方法:

             

 SortedMap<Object,Object> param = new TreeMap<Object,Object>();//公众号IDparam.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);//客户端IPparam.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;            }        }   


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


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