import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.ServletException; import org.jdom.JDOMException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.fastjson.JSON; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.request.AlipayTradePagePayRequest; import com.zqdl.base.BaseController; import com.zqdl.entity.Integral; import com.zqdl.enums.IntegralTypeEnum; import com.zqdl.service.UserService; import com.zqdl.util.CommUtil; import com.zqdl.util.DateUtil; import com.zqdl.util.HttpUtil; import com.zqdl.util.QRCodeUtil; import com.zqdl.util.Result; import com.zqdl.util.XMLUtil; @Controller @RequestMapping(value = "/pay") public class PayController extends BaseController { final static Logger logger = LoggerFactory.getLogger(PayController.class); // 微信相關 @Value("#{configProperties['mch_id']}") private String mch_id; @Value("#{configProperties['appid']}") private String appid; @Value("#{configProperties['notify_url']}") private String notify_url; @Value("#{configProperties['fee_type']}") private String fee_type; @Value("#{configProperties['device_info']}") private String device_info; @Value("#{configProperties['trade_type']}") private String trade_type; @Value("#{configProperties['sign_type']}") private String sign_type; @Value("#{configProperties['api_key']}") private String api_key; @Value("#{configProperties['ufdoder_url']}") private String ufdoder_url; // 支付寶相關 @Value("#{configProperties['app_id']}") private String app_id; @Value("#{configProperties['merchant_private_key']}") private String merchant_private_key; @Value("#{configProperties['alipay_public_key']}") private String alipay_public_key; @Value("#{configProperties['signType']}") private String signType; @Value("#{configProperties['charset']}") private String charset; @Value("#{configProperties['gatewayUrl']}") private String gatewayUrl; @Value("#{configProperties['notifyUrl']}") private String notifyUrl; @Value("#{configProperties['returnUrl']}") private String returnUrl; @Autowired private UserService userService; /** * 微信掃碼支付 * @author maming * @param totalFee */ @RequestMapping(value = "wechat_pay", method = RequestMethod.GET) public void wechatPay(@RequestParam Map<String,String> param) { Map<String, String> params = new HashMap<String, String>(); try { params.put("mch_id", mch_id); params.put("appid", appid); params.put("notify_url", notify_url); params.put("fee_type", fee_type); params.put("device_info", device_info); params.put("trade_type", trade_type); params.put("sign_type", sign_type); String tradeNo = param.get("tradeNo");// 獲取訂單號 params.put("nonce_str", tradeNo); params.put("out_trade_no", tradeNo); params.put("spbill_create_ip", CommUtil.getIpAddress(request)); int totalFee = Integer.valueOf(param.get("totalFee")); params.put("total_fee", String.valueOf(totalFee)); // 將元轉換成分 String description = param.get("description");// 消費描述 params.put("body", description); params.put("product_id", currentUser.getId() + ""); // 商品ID,對應用戶ID params.put("sign", CommUtil.generateSignature(params, api_key, "HMACSHA256")); String requestXML = XMLUtil.mapToXml(params); String resultXML = HttpUtil.postData(ufdoder_url, requestXML); Map<String, String> result = XMLUtil.doXMLParse(resultXML); String codeURL = result.get("code_url"); // 生成驗證碼 QRCodeUtil.createQRCode(response, codeURL); } catch (Exception e) { logger.error("生成微信支付二維碼異常", e); } } /** * * @throws ServletException * @throws IOException */ @RequestMapping(value = "wechat_renotify", method = RequestMethod.GET) public void wechatPay() throws ServletException, IOException { InputStream inputStream; StringBuffer sb = new StringBuffer(); inputStream = request.getInputStream(); String s; BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); while ((s = in.readLine()) != null) { sb.append(s); } in.close(); inputStream.close(); Map<String, String> params = null; try { params = XMLUtil.doXMLParse(sb.toString()); } catch (JDOMException e) { logger.error(e.getMessage()); } String resXml = ""; // 反饋給微信服務器 // 判斷簽名是否正確 if (CommUtil.isTenpaySign("UTF-8", params, api_key)) { // 這里是支付成功 if ("SUCCESS".equals((String) params.get("result_code"))) { // 開始執行自己的業務邏輯 // String mch_id = params.get("mch_id"); // String openid = params.get("openid"); // String is_subscribe = params.get("is_subscribe"); // String out_trade_no = params.get("out_trade_no"); // String total_fee = params.get("total_fee"); // 設置積分記錄參數 // 獲取消費額度 int total_fee = Integer.parseInt(params.get("total_fee")); // 獲取商品ID,對應用戶ID int userId = Integer.parseInt(params.get("product_id")); Integral integral = new Integral(); integral.setIntegral(total_fee); integral.setIntegralType(IntegralTypeEnum.RECHARGE.getCode()); integral.setDescription(String.format("通過微信充值%s積分", total_fee)); integral.setUserId(userId); // 添加積分記錄和修改用戶總積分 userService.modifyUserIntegral(integral); // 結束執行自己的業務邏輯 logger.info("支付成功"); // 通知微信.異步確認成功.必寫.不然會一直通知后台.八次之后就認為交易失敗了. resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; } else { logger.info("支付失敗,錯誤信息:" + params.get("err_code")); resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> "; } } else { logger.info("簽名驗證錯誤"); resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[簽名驗證錯誤]]></return_msg>" + "</xml> "; } BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); } /** * 支付寶支付 */ @RequestMapping(value = "aliPay/{money}", method = RequestMethod.GET) @ResponseBody public String aliPay(@PathVariable("money") String money){ Result rs = new Result(); //獲得初始化的AlipayClient AlipayClient alipayClient = new DefaultAlipayClient ( gatewayUrl, app_id, merchant_private_key, "json", charset, alipay_public_key, signType ); //設置請求參數 AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); // 頁面跳轉同步通知頁面路徑 alipayRequest.setReturnUrl(returnUrl); // 服務器異步通知頁面路徑 alipayRequest.setNotifyUrl(notifyUrl); //請求 String result = ""; try { //商戶訂單號,商戶網站訂單系統中唯一訂單號,必填 String out_trade_no = new String(DateUtil.getOrderNum().getBytes("ISO-8859-1"), "UTF-8"); //付款金額,必填 String total_amount = new String("0.01".getBytes("ISO-8859-1"), "UTF-8"); //訂單名稱,必填 String subject = new String("IphoneX 68G".getBytes("ISO-8859-1"), "UTF-8"); //商品描述,可空 String userId = currentUser.getId() + ""; String body = new String(userId.getBytes("ISO-8859-1"), "UTF-8"); //該筆訂單允許的最晚付款時間,逾期將關閉交易。取值范圍:1m~15d。m-分鍾,h-小時,d-天,1c-當天,可空 String timeout_express = new String("10m".getBytes("ISO-8859-1"), "UTF-8"); //銷售產品碼,可空 String product_code = new String("FAST_INSTANT_TRADE_PAY".getBytes("ISO-8859-1"), "UTF-8"); Map<String, String> maps = new HashMap<String, String>(); //商戶訂單號,商戶網站訂單系統中唯一訂單號,必填 maps.put("out_trade_no", out_trade_no); //付款金額,必填 maps.put("total_amount", total_amount); //訂單名稱,必填 maps.put("subject", subject); //用戶id當做商品描述,可空 maps.put("body", body); //該筆訂單允許的最晚付款時間,逾期將關閉交易。取值范圍:1m~15d。m-分鍾,h-小時,d-天,1c-當天 maps.put("timeout_express", timeout_express); //銷售產品碼,可空 maps.put("product_code", product_code); String bizCon = JSON.toJSONString(maps); alipayRequest.setBizContent(bizCon); result = alipayClient.pageExecute(alipayRequest).getBody(); //輸出 //response.getWriter().write(result); } catch (Exception e) { getServerErrorResult(rs); logger.error("跳轉到支付寶支付頁面異常", e); } return result; } @RequestMapping(value = "ali_renotify", method = RequestMethod.POST) public void ali_renotify(){ try { //獲取支付寶POST過來反饋信息 Map<String, String> params = new HashMap<String, String>(); Map<String, String[]> requestParams = request.getParameterMap(); for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = (String[]) requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } //亂碼解決,這段代碼在出現亂碼時使用 valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8"); params.put(name, valueStr); } boolean signVerified = AlipaySignature.rsaCheckV1(params, alipay_public_key, charset, signType); //調用SDK驗證簽名 if(signVerified) {//驗證成功 //訂單金額 String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8"); //獲取傳入的用戶id String body = new String(request.getParameter("body").getBytes("ISO-8859-1"),"UTF-8"); Integer money = Integer.parseInt(total_amount); Integer userId = Integer.parseInt(body); Integral integral = new Integral(); integral.setIntegral(money); integral.setIntegralType(IntegralTypeEnum.RECHARGE.getCode()); integral.setDescription(String.format("通過支付寶充值%s積分", money)); integral.setUserId(userId); // 添加積分記錄和修改用戶總積分 userService.modifyUserIntegral(integral); logger.info("支付成功"); response.getWriter().write("success"); }else {//驗證失敗 response.getWriter().write("fail"); //調試用,寫文本函數記錄程序運行情況是否正常 //String sWord = AlipaySignature.getSignCheckContentV1(params); //AlipayConfig.logResult(sWord); } } catch (Exception e) { } } }
工具類
public class CommUtil { private static Logger logger = LoggerFactory.getLogger(CommUtil.class); public static String generateUUID() { return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32); } /** * 取出一個指定長度大小的隨機正整數. * * @param length * int 設定所取出隨機數的長度。length小於11 * @return int 返回生成的隨機數。 */ public static int buildRandom(int length) { int num = 1; double random = Math.random(); if (random < 0.1) { random = random + 0.1; } for (int i = 0; i < length; i++) { num = num * 10; } return (int) ((random * num)); } /** * 獲取當前時間 yyyyMMddHHmmss * 生成訂單號 * @return String */ public synchronized static String getNonce_str() { Date now = new Date(); SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss"); String bg = outFormat.format(now); bg.substring(8, bg.length()); String end = String.valueOf(buildRandom(4)); return bg + end; } /** * 生成簽名. 注意,若含有sign_type字段,必須和signType參數保持一致。 * * @param data * 待簽名數據 * @param key * API密鑰 * @param signType * 簽名方式 * @return 簽名 */ public static String generateSignature(final Map<String, String> data, String key, String signType) throws Exception { Set<String> keySet = data.keySet(); String[] keyArray = keySet.toArray(new String[keySet.size()]); Arrays.sort(keyArray); StringBuilder sb = new StringBuilder(); for (String k : keyArray) { if (k.equals("sign")) { continue; } if (data.get(k).trim().length() > 0) // 參數值為空,則不參與簽名 sb.append(k).append("=").append(data.get(k).trim()).append("&"); } sb.append("key=").append(key); if ("MD5".equals(signType)) { return MD5(sb.toString()).toUpperCase(); } else if ("HMACSHA256".equals(signType)) { return HMACSHA256(sb.toString(), key); } else { throw new RuntimeException(String.format("Invalid sign_type: %s", signType)); } } /** * 生成 MD5 * * @param data * 待處理數據 * @return MD5結果 * @throws NoSuchAlgorithmException * @throws UnsupportedEncodingException */ public static String MD5(String data) throws NoSuchAlgorithmException, UnsupportedEncodingException { 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(); } /** * 生成 HMACSHA256 * * @param data * 待處理數據 * @param key * 密鑰 * @return 加密結果 * @throws NoSuchAlgorithmException * @throws UnsupportedEncodingException * @throws InvalidKeyException * @throws Exception */ public static String HMACSHA256(String data, String key) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException{ Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"); sha256_HMAC.init(secret_key); byte[] array = sha256_HMAC.doFinal(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(); } /** * 是否簽名正確,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。 * * @return boolean */ public static boolean isTenpaySign(String characterEncoding, Map<String, String> params, String API_KEY) { StringBuffer sb = new StringBuffer(); Set<Entry<String, String>> es = params.entrySet(); Iterator<Entry<String, String>> it = es.iterator(); while (it.hasNext()) { Entry<String, String> entry = it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if (!"sign".equals(k) && null != v && !"".equals(v)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + API_KEY); // 算出摘要 String mysign = Md5Utils.MD5Encode(sb.toString(), characterEncoding).toLowerCase(); String tenpaySign = ((String) params.get("sign")).toLowerCase(); return tenpaySign.equals(mysign); } /** * 獲取客戶端IP * * @param request * @return */ public static String getIpAddress(HttpServletRequest request) { String ipAddress = request.getHeader("x-forwarded-for"); if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("Proxy-Client-IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknow".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("WL-Proxy-Client-IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getRemoteAddr(); if (ipAddress.equals(Constants.LOCALHOST1) || ipAddress.equals(Constants.LOCALHOST2)) { // 根據網卡獲取本機配置的IP地址 InetAddress inetAddress = null; try { inetAddress = InetAddress.getLocalHost(); } catch (UnknownHostException e) { logger.error(e.getMessage()); } if(inetAddress != null){ ipAddress = inetAddress.getHostAddress(); } } } // 對於通過多個代理的情況,第一個IP為客戶端真實的IP地址,多個IP按照','分割 if (null != ipAddress && ipAddress.length() > 15) { if (ipAddress.indexOf(",") > 0) { ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); } } return ipAddress; } }
public class XMLUtil { /** * 解析xml,返回第一級元素鍵值對。如果第一級元素有子節點,則此節點的值是子節點的xml數據。 * * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map<String, String> doXMLParse(String strxml) throws JDOMException, IOException { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if (null == strxml || "".equals(strxml)) { return null; } Map<String, String> map = 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(); @SuppressWarnings("unchecked") List<Element> list = root.getChildren(); Iterator<Element> it = list.iterator(); while (it.hasNext()) { Element e = it.next(); String k = e.getName(); String v = ""; @SuppressWarnings("unchecked") List<Element> children = e.getChildren(); if (children.isEmpty()) { v = e.getTextNormalize(); } else { v = XMLUtil.getChildrenText(children); } map.put(k, v); } // 關閉流 in.close(); return map; } /** * 將Map轉換為XML格式的字符串 * * @param data * Map類型數據 * @return XML格式的字符串 * @throws ParserConfigurationException * @throws TransformerException * @throws Exception * @throws Exception */ public static String mapToXml(Map<String, String> data) throws ParserConfigurationException, TransformerException{ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); org.w3c.dom.Document document = documentBuilder.newDocument(); org.w3c.dom.Element root = document.createElement("xml"); document.appendChild(root); for (String key : data.keySet()) { String value = data.get(key); if (value == null) { value = ""; } value = value.trim(); org.w3c.dom.Element filed = document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); } TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); String output = writer.getBuffer().toString(); // .replaceAll("\n|\r", // ""); try { writer.close(); } catch (Exception ex) { } return output; } /** * 獲取子結點的xml * * @param children * @return String */ public static String getChildrenText(List<Element> children) { StringBuffer sb = new StringBuffer(); if (!children.isEmpty()) { Iterator<Element> it = children.iterator(); while (it.hasNext()) { Element e = it.next(); String name = e.getName(); String value = e.getTextNormalize(); @SuppressWarnings("unchecked") List<Element> list = e.getChildren(); sb.append("<" + name + ">"); if (!list.isEmpty()) { sb.append(XMLUtil.getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } }
public class HttpUtil { private static Logger logger = Logger.getLogger("HttpUtil"); private final static int CONNECT_TIMEOUT = 5000; // in milliseconds private final static String DEFAULT_ENCODING = "UTF-8"; public static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8"); public static final OkHttpClient client = new OkHttpClient(); public static void httpGet(String urlStr) { Request request = new Request.Builder().url(urlStr).build(); try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { logger.info(response.body().string()); } else { logger.info("服務器端錯誤: " + response); } } catch (IOException e) { logger.error(e.getMessage()); } } public static void httpPost(String urlStr, String json) { RequestBody body = RequestBody.create(MEDIA_TYPE, json); Request request = new Request.Builder().url(urlStr).post(body).build(); try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { logger.info(response.body().string()); } else { logger.info("服務器端錯誤: " + response); } } catch (IOException e) { logger.error(e.getMessage()); } } public static void httpPost(String urlStr, Map<String, String> params) { FormEncodingBuilder builder = new FormEncodingBuilder(); if (params != null && !params.isEmpty()) { Set<String> keys = params.keySet(); for (String key : keys) { builder.add(key, params.get(key)); } } RequestBody body = builder.build(); Request request = new Request.Builder().url(urlStr).post(body).build(); try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { logger.info(response.body().string()); } else { logger.info("服務器端錯誤: " + response); } } catch (IOException e) { logger.error(e.getMessage()); } } public static String postData(String urlStr, String data) { return postData(urlStr, data, null); } public static String postData(String urlStr, String data, String contentType) { BufferedReader reader = null; try { URL url = new URL(urlStr); URLConnection conn = url.openConnection(); conn.setDoOutput(true); conn.setConnectTimeout(CONNECT_TIMEOUT); conn.setReadTimeout(CONNECT_TIMEOUT); if (contentType != null) conn.setRequestProperty("content-type", contentType); OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING); if (data == null) data = ""; writer.write(data); writer.flush(); writer.close(); reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING)); StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line); sb.append("\r\n"); } return sb.toString(); } catch (IOException e) { } finally { try { if (reader != null) reader.close(); } catch (IOException e) { } } return null; } }
二維碼相關操作代碼
public class QRCodeUtil { final static Logger logger = LoggerFactory.getLogger(QRCodeUtil.class); public static void createQRCode(HttpServletResponse response, String codeURL) { // 生成二維碼 Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>(); // 指定糾錯等級 hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); // 指定編碼格式 hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); hints.put(EncodeHintType.MARGIN, 1); try { BitMatrix bitMatrix = new MultiFormatWriter().encode(codeURL, BarcodeFormat.QR_CODE, 200, 200, hints); OutputStream out = response.getOutputStream(); MatrixToImageWriter.writeToStream(bitMatrix, "png", out);// 輸出二維碼 out.flush(); out.close(); } catch (Exception e) { logger.error(e.getMessage()); } } }