流程:前端创建订单,后端保存订单并调用微信统一下单接口,将微信返回的预支付回话标识返回给客户端。
统一下单:
1.统一下单参数设置(map)
2.将参数加签,并将sign加入到map(这里注意,key是需要自己去设置的,设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置。加签官方文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.PHP?chapter=4_3)
3.map转xml然后转string
4.请求api(注意:请求方式POST,设置编码格式UTF-8)
5.解析微信返回的xml格式数据,并根据业务需求,格式化后返回给客户端
遇到的报错:
1.APP没有支付权限或者商户号和APP_ID不符合。这里是因为你的参数不是微信APP支付需要的参数,你用了微信其他的参数,建议关注微信官方公众号:WXPayService,里面有个接入指南,按照他的流程做,一次通过。公众号支付和扫码支付流程及技术指引也有。
2.签名错误。一方面可能是你的签名过程有问题,另一方面是可能你的参数(例如KEY有问题,他也会报签名错误),遇到这个错误建议用官方提供的 加签工具 验证你的签名是否正确,如果你的结果和他的结果一样,那应该就是你的参数有问题,加签工具地址:https://pay.weixin.qq.com/wiki/tools/signverify/。
2.time_expire时间过短。过期时间应该在当前系统时间之后。
附上代码:
加签public static String getSign(Map<String,String> map){ String StringA = util.StringUtil.formatUrlMap(map,true,true); String stringSignTemp = MD5.MD5Encode(StringA+"&key="+AliPayConfig.weixin_key).toUpperCase(); return stringSignTemp; }/** * * 方法用途: 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序),并且生成url参数串 * 实现步骤: * @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) { if(paraMap == null){ return ""; } 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) { String key = item.getKey(); String val = item.getValue(); if (urlEncode) {//如果是中文,则不参与编码 if(key.equals("package")){ }else if(key.equals("body")){ }else if(key.equals("notify_url")){ }else{ 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.equals("") == false) { buff = buff.substring(0,buff.length() - 1); } } catch (Exception e) { return null; } return buff; } /** * MD5编码 * @param origin 原始字符串 * @return 经过MD5加密之后的结果 */ public static String MD5Encode(String origin) { String resultString = null; try { resultString = origin; MessageDigest md = MessageDigest.getInstance("MD5"); resultString = byteArrayToHexString(md.digest(resultString.getBytes())); } catch (Exception e) { e.printStackTrace(); } return resultString; } /** * 转换字节数组为16进制字串 * @param b 字节数组 * @return 16进制字串 */ public static String byteArrayToHexString(byte[] b) { StringBuilder resultSb = new StringBuilder(); for (byte aB : b) { resultSb.append(byteToHexString(aB)); } return resultSb.toString(); } /** * 转换byte到16进制 * @param b 要转换的byte * @return 16进制格式 */ private static String byteToHexString(byte b) { int n = b; if (n < 0) { n = 256 + n; } int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; }===============================================================================map转xml然后转stringprivate static final String PREFIX_XML = "<xml>"; private static final String SUFFIX_XML = "</xml>"; private static final String PREFIX_CDATA = "<![CDATA["; private static final String SUFFIX_CDATA = "]]>"; public static String xmlFormat(Map<String,String> parm,boolean isAddCDATA) { StringBuffer strbuff = new StringBuffer(PREFIX_XML); if (StringUtil.isNotEmpty(parm)) { for (Entry<String,String> entry : parm.entrySet()) { strbuff.append("<").append(entry.getKey()).append(">"); //这里是用CDATA标签包起来,原本以为body和sign需要包起来,但是发现不需要,又懒得删了,就改成了body1,sign1 //isNotNullOrEmptyStr是判断不为空的方法 if("attach".equalsIgnoreCase(entry.getKey()) ||"body1".equalsIgnoreCase(entry.getKey()) ||"sign1".equalsIgnoreCase(entry.getKey()) ){ strbuff.append(PREFIX_CDATA); if (StringUtil.isNotNullOrEmptyStr(entry.getValue())) { strbuff.append(entry.getValue()); } strbuff.append(SUFFIX_CDATA); }else{ if (StringUtil.isNotNullOrEmptyStr(entry.getValue())) { strbuff.append(entry.getValue()); } } strbuff.append("</").append(entry.getKey()).append(">"); } } return strbuff.append(SUFFIX_XML).toString(); }===============================================================================请求api(注意:请求方式POST,设置编码格式UTF-8)
public static String httpsRequest(String requestUrl,String requestMethod,String outputStr) { try { URL url = new URL(requestUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 设置请求方式(GET/POST) conn.setRequestMethod(requestMethod); conn.setRequestProperty("content-type","application/x-www-form-urlencoded"); // 当outputStr不为null时向输出流写数据 if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); // 注意编码格式 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { // System.out.println("bf != null"); buffer.append(str); } // 释放资源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; conn.disconnect(); return buffer.toString(); } catch (ConnectException ce) { System.out.println("连接超时:{}"+ ce); } catch (Exception e) { System.out.println("https请求异常:{}"+ e); } return null; }===============================================================================解析微信返回的xml格式数据,并根据业务需求,格式化后返回给客户端import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; public static Map<String,String> doXMLParse(String strxml) throws Exception { strxml = strxml.replaceFirst("encoding=\".*\"","encoding=\"UTF-8\""); if(null == strxml || "".equals(strxml)) { return null; } Map<String,String> m = new HashMap<String,String>(); InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String k = e.getName(); String v = ""; List children = e.getChildren(); if(children.isEmpty()) { v = e.getTextNormalize(); } else { v = getChildrenText(children); } m.put(k,v); } in.close(); return m; } public static String getChildrenText(List children) { StringBuffer sb = new StringBuffer(); if(!children.isEmpty()) { Iterator it = children.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String name = e.getName(); String value = e.getTextNormalize(); List list = e.getChildren(); sb.append("<" + name + ">"); if(!list.isEmpty()) { sb.append(getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); }