步驟:
1.預訂單
2.接受微信返回的url
3.將url轉為二維碼顯示到頁面上
4.掃碼支付
5.接收微信的異步通知,在這步修改訂單的狀態
6.收到異步通知的同時給微信返回指定數據,告知對方已成功處理異步通知
難點:
1.根據傳遞的參數生成簽名
2.將數據轉為xml格式發送給微信預訂單接口
常見錯誤:
1.簽名錯誤(推薦用微信的驗簽工具檢驗)
2.xml格式錯誤(很有可能是發送數據的方式出現了問題)
具體實現:
1.預訂單
public String getImageUrl(String body,String out_trade_no,String product_id,String total_fee) throws Exception{ Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("appid", data.getAppid()); //appid:每個公眾號都有一個appid paramMap.put("body", body); //商品描述 paramMap.put("mch_id", data.getMch_id()); //商戶號:開通微信支付后分配 paramMap.put("nonce_str", RandomStringGenerator.getRandomStringByLength(32)); // 隨機字符串 paramMap.put("notify_url", data.getNotify_url()); //支付成功后,回調地址 paramMap.put("out_trade_no", out_trade_no); //商戶訂單號 paramMap.put("product_id", product_id); // 商戶根據自己業務傳遞的參數 當trade_type=NATIVE時必填 paramMap.put("spbill_create_ip", this.localIp()); //本機的Ip paramMap.put("total_fee", total_fee); //金額必須為整數 單位為分 paramMap.put("trade_type", data.getTrade_type()); //交易類型 paramMap.put("sign", WxzfUtil.getSign(paramMap, WXPayAction.KEY));//根據微信簽名規則,生成簽名。隨機參數可以在商戶后台管理系統中進行設置。 String xmlData = XmlUtils.map2xmlBody(paramMap,"xml");//把參數轉換成XML數據格式 String codeUrl = getCodeUrl(xmlData); //獲取二維碼鏈接 return codeUrl; }
public static String getSign(Map map, String key) throws Exception { String signTemp = "appid=" + map.get("appid") + "&body=" + map.get("body") + "&mch_id=" + map.get("mch_id") + "&nonce_str=" + map.get("nonce_str") + "¬ify_url=" + map.get("notify_url") + "&out_trade_no=" + map.get("out_trade_no") + "&product_id=" + map.get("product_id") + "&spbill_create_ip=" + map.get("spbill_create_ip") + "&total_fee=" + map.get("total_fee") + "&trade_type=" + map.get("trade_type") + "&key=" + key; String sign = MD5.MD5Encode(signTemp).toUpperCase();//MD5微信接口中提供,直接調用即可 return sign; }
public static String map2xmlBody(Map<String, Object> vo, String rootElement) { org.dom4j.Document doc = DocumentHelper.createDocument(); Element body = DocumentHelper.createElement(rootElement); doc.add(body); __buildMap2xmlBody(body, vo); return doc.asXML(); } @SuppressWarnings("unchecked") private static void __buildMap2xmlBody(Element body, Map<String, Object> vo) { if (vo != null) { Iterator<String> it = vo.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); if (StringUtil.isNotEmpty(key)) { Object obj = vo.get(key); Element element = DocumentHelper.createElement(key); if (obj != null) { if (obj instanceof java.lang.String) { element.setText((String) obj); } else { if (obj instanceof java.lang.Character || obj instanceof java.lang.Boolean || obj instanceof java.lang.Number || obj instanceof java.math.BigInteger || obj instanceof java.math.BigDecimal) { org.dom4j.Attribute attr = DocumentHelper.createAttribute(element, "type", obj.getClass().getCanonicalName()); element.add(attr); element.setText(String.valueOf(obj)); } else if (obj instanceof java.util.Map) { org.dom4j.Attribute attr = DocumentHelper.createAttribute(element, "type", java.util.Map.class.getCanonicalName()); element.add(attr); __buildMap2xmlBody(element, (Map<String, Object>) obj); } else { } } } body.add(element); } } } }
/** * 獲取一定長度的隨機字符串 * @param length 指定字符串長度 * @return 一定長度的字符串 */ public static String getRandomStringByLength(int 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(); }
//獲取本機ip地址 @SuppressWarnings("rawtypes") private String localIp(){ String ip = null; Enumeration allNetInterfaces; try { allNetInterfaces = NetworkInterface.getNetworkInterfaces(); while (allNetInterfaces.hasMoreElements()) { NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement(); List<InterfaceAddress> InterfaceAddress = netInterface.getInterfaceAddresses(); for (InterfaceAddress add : InterfaceAddress) { InetAddress Ip = add.getAddress(); if (Ip != null && Ip instanceof Inet4Address) { ip = Ip.getHostAddress(); } } } } catch (SocketException e) { log.warn("獲取本機Ip失敗:異常信息:"+e.getMessage()); } return ip; }
2.接受微信返回的url
/** * 獲取二維碼鏈接 * @param xmlData * @return */ private String getCodeUrl(String xmlData) { String resXml = null; try { resXml = WxzfUtil.sendPost(WXPayAction.WX_CREATE_ORDER_URL, xmlData); } catch (UnrecoverableKeyException e1) { log.error(e1.getMessage()); } catch (KeyManagementException e1) { log.error(e1.getMessage()); } catch (KeyStoreException e1) { log.error(e1.getMessage()); } catch (NoSuchAlgorithmException e1) { log.error(e1.getMessage()); } catch (IOException e1) { log.error(e1.getMessage()); } String code_url = ""; Map<String, String> map; try { map = WxzfUtil.parseXml(resXml); Object returnCode = map.get("return_code"); if("SUCCESS".equals(returnCode)) { Object resultCode = map.get("result_code"); if("SUCCESS".equals(resultCode)) { code_url = map.get("code_url").toString(); } } } catch (Exception e) { log.error(e.getMessage()); return ""; } return code_url; }
//將xml數據發送給微信 @SuppressWarnings({ "resource" }) public static String sendPost(String url, String postDataXML) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException { String result = null; HttpPost httpPost = new HttpPost(url); HttpClient client = new DefaultHttpClient(); // 得指明使用UTF-8編碼,否則到API服務器XML的中文不能被成功識別 StringEntity postEntity = new StringEntity(postDataXML, "UTF-8"); httpPost.addHeader("Content-Type", "text/xml"); httpPost.setEntity(postEntity); try { HttpResponse response = client.execute(httpPost); HttpEntity entity = response.getEntity(); result = EntityUtils.toString(entity, "UTF-8"); } catch (ConnectionPoolTimeoutException e) { // LOG.debug("http get throw ConnectionPoolTimeoutException(wait time out)"); } catch (ConnectTimeoutException e) { // LOG.debug("http get throw ConnectTimeoutException"); } catch (SocketTimeoutException e) { // LOG.debug("http get throw SocketTimeoutException"); } catch (Exception e) { e.printStackTrace(); // LOG.debug("http get throw Exception" ); } finally { httpPost.abort(); } System.out.println(result); return result; }
3.將url轉為二維碼顯示到頁面上
public String payWeChat(String ids,String content,HttpServletResponse response,HttpServletRequest request, ModelMap model){ WebErrors errors = WebErrors.create(request); try { OmShopOrder order = orderService.findById(Long.parseLong(ids)); content = this.getImageUrl(order.getSoProductname(), order.getSoId().toString(), order.getSoId().toString(), (int)(order.getSoTotal()*10*10)+""); MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); Hashtable hints = new Hashtable(); hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 400, 400,hints); response.setContentType("image/jpeg"); MatrixToImageWriter.writeToStream(bitMatrix, "jpg", response.getOutputStream()); } catch (Exception e) { e.printStackTrace(); log.warn("訂單已支付,請勿重新支付"); } return null; }
4.掃碼支付
5.接收微信的異步通知,在這步修改訂單的狀態
if ("SUCCESS".equals(returnMap.get("return_code"))&&"SUCCESS".equals(returnMap.get("result_code"))) { //處理你的業務邏輯代碼(修改訂單支付狀態) }
6.收到異步通知的同時給微信返回指定數據,告知對方已成功處理異步通知
response.setContentType("text/plain;charset=UTF-8"); PrintWriter writer; try { writer = response.getWriter(); writer.write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"); writer.flush(); } catch (IOException e) { e.printStackTrace(); }