】
/** * 统一下单 * @param userId * @param activityId * @param openId * @param orderNo * @param money * @param describe * @param detail * @return */ private WxappPayDto prePay(String userId,String activityId,String openId,String orderNo,String money,String describe,String detail){ money = String.valueOf(Long.valueOf(money.substring( 0 , money.length()- 1 ))* 100 ); String currTime = PayUtils.getCurrTime(); //8位日期 String strTime = currTime.substring( 8 , currTime.length()); //四位随机数 String strRandom = PayUtils.buildRandom( 4 ) + "" ; //10位序列号,可以自行调整。 String nonceStr = strTime + strRandom; //这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。 String notifyUrl = baseUrl+ "/notify" ; //附加数据,以一定格式保存userId和activityId。原样返回。 String attach = userId+ "#wx#" +activityId; SortedMap<String, String> packageParams = new TreeMap<String, String>(); packageParams.put( "appid" , appId); packageParams.put( "attach" , attach); //附加数据 packageParams.put( "body" , describe); //商品描述 packageParams.put( "detail" , detail); packageParams.put( "mch_id" , mchId); //商户号 packageParams.put( "nonce_str" , nonceStr); //随机数 packageParams.put( "notify_url" , notifyUrl); packageParams.put( "openid" , openId); packageParams.put( "out_trade_no" , orderNo); //商户订单号 packageParams.put( "spbill_create_ip" , spBillCreateIp); //订单生成的机器 IP packageParams.put( "total_fee" , money); //总金额 packageParams.put( "trade_type" , "JSAPI" ); String sign = PayUtils.createSign(packageParams,key); String xml= "<xml>" + "<appid>" +appId+ "</appid>" + "<attach>" +attach+ "</attach>" + "<body><![CDATA[" +describe+ "]]></body>" + "<detail><![CDATA[" +detail+ "]]></detail>" + "<mch_id>" +mchId+ "</mch_id>" + "<nonce_str>" +nonceStr+ "</nonce_str>" + "<sign>" +sign+ "</sign>" + "<notify_url>" +notifyUrl+ "</notify_url>" + "<openid>" +openId+ "</openid>" + "<out_trade_no>" +orderNo+ "</out_trade_no>" + "<spbill_create_ip>" +spBillCreateIp+ "</spbill_create_ip>" + "<total_fee>" +money+ "</total_fee>" + "<trade_type>JSAPI</trade_type>" + "</xml>" ; String prepay_id= "" ; try { prepay_id = PayUtils.getPayNo(createOrderURL, xml); if (prepay_id.equals( "" )){ //错误提示 System.out.println( "统一支付接口获取预支付订单出错" ); } } catch (Exception e1) { e1.printStackTrace(); } SortedMap<String, String> finalpackage = new TreeMap<String, String>(); String timestamp = PayUtils.getTimeStamp(); String packages = "prepay_id=" +prepay_id; finalpackage.put( "appId" , appId); finalpackage.put( "nonceStr" , nonceStr); finalpackage.put( "package" , packages); finalpackage.put( "signType" , "MD5" ); finalpackage.put( "timeStamp" , timestamp); String finalsign = PayUtils.createSign(finalpackage,key); WxappPayDto dto = new WxappPayDto(); dto.setNonceStr(nonceStr); dto.setPackage_(packages); dto.setPaySign(finalsign); dto.setSignType( "MD5" ); dto.setTimeStamp(timestamp); return dto; } |
/** * 支付完成通知 * @param request * @param response * @return * @throws Exception */ @RequestMapping (value = "/notify" ,method = RequestMethod.POST) public String notify(HttpServletRequest request, HttpServletResponse response) throws Exception { BufferedReader br = new BufferedReader( new InputStreamReader((ServletInputStream)request.getInputStream())); String line = null ; StringBuilder sb = new StringBuilder(); while ((line = br.readLine())!= null ){ sb.append(line); } //解析并给微信发回收到通知确认 Map map = PayUtils.doXMLParse(sb.toString()); String returnCode = map.get( "return_code" ).toString(); if (returnCode.equals( "SUCCESS" )){ String resultCode = map.get( "result_code" ).toString(); if (resultCode.equals( "SUCCESS" )){ SortedMap<String, String> packageParams = new TreeMap<String, String>(); packageParams.put( "appid" , map.get( "appid" ).toString()); packageParams.put( "attach" , map.get( "attach" ).toString()); packageParams.put( "bank_type" , map.get( "bank_type" ).toString()); packageParams.put( "cash_fee" , map.get( "cash_fee" ).toString()); packageParams.put( "fee_type" , map.get( "fee_type" ).toString()); packageParams.put( "is_subscribe" , map.get( "is_subscribe" ).toString()); packageParams.put( "mch_id" , map.get( "mch_id" ).toString()); packageParams.put( "nonce_str" , map.get( "nonce_str" ).toString()); packageParams.put( "openid" , map.get( "openid" ).toString()); packageParams.put( "out_trade_no" , map.get( "out_trade_no" ).toString()); packageParams.put( "result_code" , map.get( "result_code" ).toString()); packageParams.put( "return_code" , map.get( "return_code" ).toString()); packageParams.put( "time_end" , map.get( "time_end" ).toString()); packageParams.put( "total_fee" , map.get( "total_fee" ).toString()); packageParams.put( "trade_type" , map.get( "trade_type" ).toString()); packageParams.put( "transaction_id" , map.get( "transaction_id" ).toString()); String sign = PayUtils.createSign(packageParams,key); String originSign = map.get( "sign" ).toString(); if (sign.equals(originSign)){ //签名一致,保存支付流水 String xml= "<xml>" + "<return_code>SUCCESS</return_code>" + "<return_msg>OK</return_msg>" + "</xml>" ; ShopPayLog payLog = new ShopPayLog(); payLog.setCreatedAt( new Date()); payLog.setSource(Source.WeiXin); DecimalFormat df = new DecimalFormat( "######0.00" ); payLog.setTotalFee(String.valueOf(df.format((Double.valueOf(map.get( "total_fee" ).toString())/ 100 )))); payLog.setTradeNo(map.get( "out_trade_no" ).toString()); payLog.setTransactionId(map.get( "transaction_id" ).toString()); String attach = map.get( "attach" ).toString(); //userId+"#wx#"+activityId payLog.setUserId(attach.split( "#wx#" )[ 0 ]); payLog = wxappPayService.save(payLog); WxappUser user = wxappUserService.find(Long.valueOf(attach.split( "#wx#" )[ 0 ])); WxappActivity activity = wxappActivityService.find(Long.valueOf(attach.split( "#wx#" )[ 1 ])); WxappActivityApply activityApply = wxappActivityApplyService.findActivityApplyByUserAndActivity(user, activity); //在活动申请表中关联上支付流水的id activityApply.setPayLogId(String.valueOf(payLog.getId())); wxappActivityApplyService.save(activityApply); return xml; } else { String xml= "<xml>" + "<return_code>FAIL</return_code>" + "<return_msg>签名不一致</return_msg>" + "</xml>" ; return xml; } } else { String xml= "<xml>" + "<return_code>FAIL</return_code>" + "<return_msg>支付通知失败</return_msg>" + "</xml>" ; return xml; } } else { String xml= "<xml>" + "<return_code>FAIL</return_code>" + "<return_msg>支付通知失败</return_msg>" + "</xml>" ; return xml; } } |
PayUtils.java
import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.KeyStore; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import javax.net.ssl.SSLContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; import com.pro.profwxappapi.api.PayApi; @SuppressWarnings ( "deprecation" ) public class PayUtils { private static Object Server; @SuppressWarnings ( "deprecation" ) public static DefaultHttpClient httpclient; private static SortedMap parameters; static { httpclient = new DefaultHttpClient(); httpclient = (DefaultHttpClient) HttpClientConnectionManager.getSSLInstance(httpclient); parameters = new TreeMap(); } /** * 把对象转换成字符串 * * @param obj * @return String 转换成字符串,若对象为null,则返回空字符串. */ public static String toString(Object obj) { if (obj == null ) return "" ; return obj.toString(); } /** * 把对象转换为int数值. * * @param obj * 包含数字的对象. * @return int 转换后的数值,对不能转换的对象返回0。 */ public static int toInt(Object obj) { int a = 0 ; try { if (obj != null ) { a = Integer.parseInt(obj.toString()); } } catch (Exception e) { e.printStackTrace(); } return a; } /** * 获取从1970年开始到现在的秒数 * * @param date * @return */ public static String getTimeStamp() { long seconds = System.currentTimeMillis() / 1000 ; return String.valueOf(seconds); } /** * 获取当前时间 yyyyMMddHHmmss * @return String */ public static String getCurrTime() { Date now = new Date(); SimpleDateFormat outFormat = new SimpleDateFormat( "yyyyMMddHHmmss" ); String s = outFormat.format(now); return s; } /** * 获取当前日期 yyyyMMdd * @param date * @return String */ public static String formatDate(Date date) { SimpleDateFormat formatter = new SimpleDateFormat( "yyyyMMdd" ); String strDate = formatter.format(date); return strDate; } /** * 取出一个指定长度大小的随机正整数. * @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)); } /** * 获取编码字符集 * @param request * @param response * @return String */ public static String getCharacterEncoding(HttpServletRequest request, HttpServletResponse response) { if ( null == request || null == response) { return "utf-8" ; } String enc = request.getCharacterEncoding(); if ( null == enc || "" .equals(enc)) { enc = response.getCharacterEncoding(); } if ( null == enc || "" .equals(enc)) { enc = "utf-8" ; } return enc; } public static String URLencode(String content) { String URLencode; URLencode = replace(Server.equals(content), "+" , "%20" ); return URLencode; } private static String replace( boolean equals, String string, String string2) { return null ; } /** * 获取unix时间,从1970-01-01 00:00:00开始的秒数 * @param date * @return long */ public static long getUnixTime(Date date) { if ( null == date) { return 0 ; } return date.getTime() / 1000 ; } public static String QRfromGoogle(String chl) { int widhtHeight = 300 ; String EC_level = "L" ; int margin = 0 ; String QRfromGoogle; chl = URLencode(chl); QRfromGoogle = "http://chart.apis.google.com/chart?chs=" + widhtHeight + "x" + widhtHeight + "&cht=qr&chld=" + EC_level + "|" + margin + "&chl=" + chl; return QRfromGoogle; } /** * 时间转换成字符串 * @param date 时间 * @param formatType 格式化类型 * @return String */ public static String date2String(Date date, String formatType) { SimpleDateFormat sdf = new SimpleDateFormat(formatType); return sdf.format(date); } public static String getNonceStr() { Random random = new Random(); return MD5Utils.MD5Encode(String.valueOf(random.nextInt( 10000 )), "UTF-8" ); } /** * 创建签名SHA1 * @param signParams * @return * @throws Exception */ public static String createSHA1Sign(SortedMap<String, String> signParams) throws Exception { StringBuffer sb = new StringBuffer(); Set es = signParams.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); sb.append(k + "=" + v + "&" ); // 要采用URLENCODER的原始值! } String params = sb.substring( 0 , sb.lastIndexOf( "&" )); return getSha1(params); } /** * Sha1签名 * @param str * @return */ public static String getSha1(String str) { if (str == null || str.length() == 0 ) { return null ; } char hexDigits[] = { '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' }; try { MessageDigest mdTemp = MessageDigest.getInstance( "SHA1" ); mdTemp.update(str.getBytes( "UTF-8" )); byte [] md = mdTemp.digest(); int j = md.length; char buf[] = new char [j * 2 ]; int k = 0 ; for ( int i = 0 ; i < j; i++) { byte byte0 = md[i]; buf[k++] = hexDigits[byte0 >>> 4 & 0xf ]; buf[k++] = hexDigits[byte0 & 0xf ]; } return new String(buf); } catch (Exception e) { e.printStackTrace(); return null ; } } /** * 获得预支付订单号 * @param url * @param xmlParam * @return */ public static String getPayNo(String url, String xmlParam) { String prepay_id = "" ; try { String jsonStr = postWithXmlParams(url, xmlParam); if (jsonStr.indexOf( "FAIL" ) != - 1 ) { return prepay_id; } Map<String, Object> map = doXMLParse(jsonStr); prepay_id = (String) map.get( "prepay_id" ); System.out.println( "prepay_id:" + prepay_id); } catch (Exception e) { e.printStackTrace(); } return prepay_id; } /** * 发送请求 * @param url 请求路径 * @param xmlParams xml字符串 * @return */ public static String postWithXmlParams(String url, String xmlParams) { HttpPost httpost = HttpClientConnectionManager.getPostMethod(url); try { httpost.setEntity( new StringEntity(xmlParams, "UTF-8" )); HttpResponse response = httpclient.execute(httpost); return EntityUtils.toString(response.getEntity(), "UTF-8" ); } catch (Exception e) { e.printStackTrace(); return "" ; } } /** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map doXMLParse(String strxml) throws Exception { if ( null == strxml || "" .equals(strxml)) { return null ; } Map m = new HashMap(); InputStream in = String2Inputstream(strxml); 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; } /** * 获取子结点的xml * @param children * @return String */ 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(); } public static InputStream String2Inputstream(String str) { return new ByteArrayInputStream(str.getBytes()); } public String getParameter(String parameter) { String s = (String) this .parameters.get(parameter); return ( null == s) ? "" : s; } /** * 特殊字符处理 * @param src * @return * @throws UnsupportedEncodingException */ public String UrlEncode(String src) throws UnsupportedEncodingException { return URLEncoder.encode(src, "UTF-8" ).replace( "+" , "%20" ); } /** * 获取package的签名包 * @param packageParams * @param key * @return * @throws UnsupportedEncodingException */ public String genPackage(SortedMap<String, String> packageParams, String key) throws UnsupportedEncodingException { String sign = createSign(packageParams, key); StringBuffer sb = new StringBuffer(); Set es = packageParams.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); sb.append(k + "=" + UrlEncode(v) + "&" ); } // 去掉最后一个& String packageValue = sb.append( "sign=" + sign).toString(); return packageValue; } /** * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 */ public static String createSign(SortedMap<String, String> packageParams, String key) { StringBuffer sb = new StringBuffer(); Set es = packageParams.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if ( null != v && ! "" .equals(v) && ! "sign" .equals(k) && ! "key" .equals(k)) { sb.append(k + "=" + v + "&" ); } } sb.append( "key=" + key); System.out.println( "md5:" + sb.toString()); String sign = MD5Utils.MD5Encode(sb.toString(), "UTF-8" ).toUpperCase(); System.out.println( "packge签名:" + sign); return sign; } /** * 创建package签名 */ public boolean createMd5Sign(String signParams) { StringBuffer sb = new StringBuffer(); Set es = this .parameters.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.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 + "&" ); } } // 算出摘要 String sign = MD5Utils.MD5Encode(sb.toString(), "utf-8" ).toLowerCase(); String paySign = this .getParameter( "sign" ).toLowerCase(); return paySign.equals(sign); } /** * 输出XML * @return */ public String parseXML() { StringBuffer sb = new StringBuffer(); sb.append( "<xml>" ); Set es = this .parameters.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if ( null != v && ! "" .equals(v) && ! "appkey" .equals(k)) { sb.append( "<" + k + ">" + getParameter(k) + "</" + k + ">\n" ); } } sb.append( "</xml>" ); return sb.toString(); } public static String post(String url, String xmlParam){ StringBuilder sb = new StringBuilder(); try { KeyStore keyStore = KeyStore.getInstance( "PKCS12" ); FileInputStream instream = new FileInputStream( new File(PayApi. class .getClassLoader().getResource( "apiclient_cert.p12" ).getPath())); try { keyStore.load(instream, "1344023801" .toCharArray()); } finally { instream.close(); } // 证书 SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keyStore, "1344023801" .toCharArray()) .build(); // 只允许TLSv1协议 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[] { "TLSv1" }, null , SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); //创建基于证书的httpClient,后面要用到 CloseableHttpClient client = HttpClients.custom() .setSSLSocketFactory(sslsf) .build(); HttpPost httpPost = new HttpPost(url); //退款接口 StringEntity reqEntity = new StringEntity(xmlParam); // 设置类型 reqEntity.setContentType( "application/x-www-form-urlencoded" ); httpPost.setEntity(reqEntity); CloseableHttpResponse response = client.execute(httpPost); try { HttpEntity entity = response.getEntity(); System.out.println(response.getStatusLine()); if (entity != null ) { BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(entity.getContent(), "UTF-8" )); String text= "" ; while ((text = bufferedReader.readLine()) != null ) { sb.append(text); } } EntityUtils.consume(entity); } catch (Exception e){ e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); } finally { httpclient.close(); } return sb.toString(); } } |
这是一年级小孩子写的代码
流程清晰