接下来分享一下微信
配置文件
APP_APPID=**** APP_MCH_ID=**** APP_API_KEY=*** APP_SECRET=*** WEB_APPID=*** WEB_SECRET=*** WEB_MCH_ID=*** WEB_API_KEY=**** SENDURL=https://api.mch.weixin.qq.com/pay/unifiedorder PACKAGE=Sign=WXPay TRADE_TYPE_APP=APP TRADE_TYPE_WEB=JSAPI FEE_TYPE=CNY SINTYPE=MD5 NOTIFY_URL= REFUND=https://api.mch.weixin.qq.com/secapi/pay/refund ORDERQUERY=https://api.mch.weixin.qq.com/pay/orderquery APP_CERT=C:\\weixinCert\\appCert\\apiclient_cert.p12 WEB_CERT=C:\\weixinCert\\webCert\\apiclient_cert.p12
这个配置文件有app微信开放平台的和微信商户号的公众号支付
微信的工具类
package com.yuanxinxinxi.yuanxinbuluo.weixin.util; import io.netty.channel.ConnectTimeoutException; 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.OutputStream; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.net.URL; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.List; 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 org.apache.commons.httpclient.ConnectionPoolTimeoutException; import org.apache.http.HttpEntity; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.yuanxinxinxi.yuanxinbuluo.util.MD5Util; /** * 微信工具类 * */ public class WeiXinUtil { private static Logger log = LoggerFactory.getLogger(WeiXinUtil.class); //连接超时时间,默认10秒 private static final int socketTimeout = 10000; //传输超时时间,默认30秒 private static final int connectTimeout = 30000; //请求器的配置 private static RequestConfig requestConfig; //HTTP请求器 private static CloseableHttpClient httpClient; /** * 获取终端ip * @param request * @return */ public static String getIpAddress(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } /** * 获取当前时间14位时间戳 * @param date 时间 * @return 时间戳 */ public static String getDateTime14(Date date){ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); return sdf.format(date); } /** * 获取当前时间10位时间戳 * @param date 时间 * @return 时间戳 */ public static String getDateTime10(Date date){ long time = date.getTime() / 1000; return String.valueOf(time); } /** * 随机字符串生成 * @param length 随机数的长度 * @return */ public static String getRandomString(int length) { //length表示生成字符串的长度 String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 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(); } /** * xml组装 * @param parameters 集合 key value * @return */ public static String getRequestXml(SortedMap<String,Object> parameters){ StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set<Entry<String,Object>> es = parameters.entrySet(); Iterator<Entry<String,Object>> it = es.iterator(); while(it.hasNext()) { Map.Entry<String,Object> entry = (Map.Entry<String,Object>)it.next(); String key = (String)entry.getKey(); String value = String.valueOf(entry.getValue()); if(!value.equals("null")){ if ("attach".equalsIgnoreCase(key)||"body".equalsIgnoreCase(key)||"sign".equalsIgnoreCase(key)) { sb.append("<"+key+">"+"<![CDATA["+value+"]]></"+key+">"); }else { sb.append("<"+key+">"+value+"</"+key+">"); } } } sb.append("</xml>"); return sb.toString(); } /** * 配和xml解析方法使用 * @param children * @return */ @SuppressWarnings("unchecked") public static String getChildrenText(List<Object> children) { StringBuffer sb = new StringBuffer(); if(!children.isEmpty()) { Iterator<Object> it = children.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String name = e.getName(); String value = e.getTextNormalize(); List<Object> list = e.getChildren(); sb.append("<" + name + ">"); if(!list.isEmpty()) { sb.append(getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } /** * xml解析 * @param strxml xml格式字符串 * @return map集合 * @throws JDOMException * @throws IOException */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static SortedMap<String,Object> doXMLParse(String strxml) throws JDOMException, IOException { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if(null == strxml || "".equals(strxml)) { return null; } SortedMap<String,Object> m = new TreeMap<String, Object>(); 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; } /** * 生成签名 * @param characterEncoding 编码 * @param parameters 集合 * @param key 微信APPID * @return sign */ public static String createSign(String characterEncoding,SortedMap<String,Object> parameters,String key){ StringBuffer sb = new StringBuffer(); Set<Entry<String,Object>> es = parameters.entrySet(); Iterator<Entry<String,Object>> it = es.iterator(); while(it.hasNext()) { Map.Entry<String,Object> entry = (Map.Entry<String,Object>)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + key); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } /** * 发送https请求 * @param requestUrl 请求地址 * @param requestMethod 请求方法 * @param outputStr 请求参数 * @return 微信返回xml格式字符串 */ 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) { buffer.append(str); } // 释放资源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; conn.disconnect(); return buffer.toString(); } catch (ConnectException e) { log.error("连接超时:{}"+ e.getMessage()); } catch (Exception e) { log.error("https请求异常:{}"+ e.getMessage()); } return null; } /** * 把空值去掉 * @param map * @return */ public static SortedMap<String,Object> isVlaueNull(SortedMap<String,Object> map){ Iterator<Map.Entry<String,Object>> iterator = map.entrySet().iterator(); while(iterator.hasNext()) { Map.Entry<String,Object> entry = iterator.next(); String key = entry.getKey(); if(map.get(key)==null){ iterator.remove(); } } return map; } /** * 生成签名 * @param map * @return */ public static String jsCreateSign(SortedMap<String,Object> map){ StringBuilder string1Builder = new StringBuilder(); String url = (String) map.get("url"); string1Builder.append("jsapi_ticket=").append(map.get("jsapi_ticket")).append("&") .append("noncestr=").append(map.get("nonceStr")).append("&") .append("timestamp=").append(map.get("timestamp")).append("&") .append("url=").append(url.indexOf("#") >= 0 ? url.substring(0, url.indexOf("#")) : url); String sign = string1Builder.toString(); return SHA1(sign); } /** * 加密算法 * @param decript * @return */ public static String SHA1(String decript) { try { MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1"); digest.update(decript.getBytes()); byte messageDigest[] = digest.digest(); // Create Hex String StringBuffer hexString = new StringBuffer(); // 字节数组转换为 十六进制 数 for (int i = 0; i < messageDigest.length; i++) { String shaHex = Integer.toHexString(messageDigest[i] & 0xFF); if (shaHex.length() < 2) { hexString.append(0); } hexString.append(shaHex); } return hexString.toString(); } catch (NoSuchAlgorithmException e) { log.error(e.getMessage()); } return ""; } /** * 加载证书 * @param path * @throws IOException * @throws KeyStoreException * @throws UnrecoverableKeyException * @throws NoSuchAlgorithmException * @throws KeyManagementException */ private static void initCert(String path,String mch_id) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException { //拼接证书的路径 //path = path + WxConfigure.certLocalPath; KeyStore keyStore = KeyStore.getInstance("PKCS12"); //加载本地的证书进行https加密传输 FileInputStream instream = new FileInputStream(new File(path)); try { keyStore.load(instream, mch_id.toCharArray()); //加载证书密码,默认为商户ID } catch (CertificateException e) { log.error(e.getMessage()); } catch (NoSuchAlgorithmException e) { log.error(e.getMessage()); } finally { instream.close(); } // Trust own CA and all self-signed certs SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keyStore, mch_id.toCharArray()) //加载证书密码,默认为商户ID .build(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); httpClient = HttpClients.custom() .setSSLSocketFactory(sslsf) .build(); //根据默认超时限制初始化requestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build(); } /** * 通过Https往API post xml数据 * @param url API地址 * @param xmlObj 要提交的XML数据对象 * @param path 当前目录,用于加载证书 * @return * @throws IOException * @throws KeyStoreException * @throws UnrecoverableKeyException * @throws NoSuchAlgorithmException * @throws KeyManagementException */ public static String httpsRequest(String url, String xmlObj, String path,String mch_id) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException { //加载证书 initCert(path ,mch_id); String result = null; HttpPost httpPost = new HttpPost(url); //得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别 StringEntity postEntity = new StringEntity(xmlObj, "UTF-8"); httpPost.addHeader("Content-Type", "text/xml"); httpPost.setEntity(postEntity); //设置请求器的配置 httpPost.setConfig(requestConfig); try { org.apache.http.HttpResponse response = httpClient.execute(httpPost); HttpEntity entity = response.getEntity(); result = EntityUtils.toString(entity, "UTF-8"); } catch (ConnectionPoolTimeoutException e) { log.error("http get throw ConnectionPoolTimeoutException(wait time out)"); } catch (ConnectTimeoutException e) { log.error("http get throw ConnectTimeoutException"); } catch (SocketTimeoutException e) { log.error("http get throw SocketTimeoutException"); } catch (Exception e) { log.error("http get throw Exception"); } finally { httpPost.abort(); } return result; } }
public SortedMap<String,Object> encapData(UnifiedorderDto dto){ SortedMap<String,Object> sortedMap =new TreeMap<String, Object>(); String key = ""; //封装数据 //*必要 sortedMap.put("appid", dto.getAppid()); sortedMap.put("mch_id", dto.getMch_id()); sortedMap.put("nonce_str",dto.getNonce_str()); sortedMap.put("body", dto.getBody()); sortedMap.put("out_trade_no", dto.getOut_trade_no()); sortedMap.put("total_fee", dto.getTotal_fee()); sortedMap.put("spbill_create_ip" , dto.getSpbill_create_ip()); sortedMap.put("notify_url",dto.getNotify_url()); sortedMap.put("trade_type",dto.getTrade_type()); //如果是公众号支付那么需要添加一个openid if(sortedMap.get("trade_type").equals(WeiXinConfig.TRADE_TYPE_WEB)){ sortedMap.put("openid",dto.getOpenid()); key = WeiXinConfig.WEB_API_KEY; }else{ key = WeiXinConfig.APP_API_KEY; } // *不必填的数据 sortedMap.put("device_info", dto.getDevice_info()); sortedMap.put("detail", dto.getDetail()); sortedMap.put("attach",dto.getAttach()); sortedMap.put("fee_type",dto.getFee_type()); sortedMap.put("time_start", dto.getTime_start()); sortedMap.put("goods_tag", dto.getGoods_tag()); sortedMap = WeiXinUtil.isVlaueNull(sortedMap); String sign = WeiXinUtil.createSign("utf-8", sortedMap, key); sortedMap.put("sign", sign); return sortedMap; }
package com.yuanxinxinxi.yuanxinbuluo.pay.dto; /** * 统一下单提交为微信的参数 */ public class UnifiedorderDto { private String appid;//微信分配的公众账号ID(企业号corpid即为此appId),例如:wxd678efh567hg6787 private String mch_id;//商户id private String device_info;//终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" private String nonce_str;//随机字符串:数字+大写字母的组合,32位 private String sign;//签名 private String body;//商品或支付单简要描述 private String detail;//商品名称明细列表 private String attach;//附加参数 private String out_trade_no;//商户系统内部的订单号 private String fee_type;//货币类型:符合ISO 4217标准的三位字母代码,默认人民币:CNY private int total_fee;//总金额 private String spbill_create_ip;//APP和网页支付提交[用户端ip],Native支付填调用微信支付API的机器IP。 private String time_start;//订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010 private String time_expire;//订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010;最短失效时间间隔必须大于5分钟[支付宝是30分钟,同样30分钟] private String goods_tag;//商品标记,代金券或立减优惠功能的参数 private String notify_url;//接收微信支付异步通知回调地址 private String trade_type;//交易类型:JSAPI,NATIVE,APP private String product_id;//trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。 private String limit_pay;//no_credit--指定不能使用信用卡支付 private String openid;//trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识 public String getAppid() { return appid; } public String getMch_id() { return mch_id; } public String getDevice_info() { return device_info; } public String getNonce_str() { return nonce_str; } public String getSign() { return sign; } public String getBody() { return body; } public String getDetail() { return detail; } public String getAttach() { return attach; } public String getOut_trade_no() { return out_trade_no; } public String getFee_type() { return fee_type; } public int getTotal_fee() { return total_fee; } public String getSpbill_create_ip() { return spbill_create_ip; } public String getTime_start() { return time_start; } public String getTime_expire() { return time_expire; } public String getGoods_tag() { return goods_tag; } public String getNotify_url() { return notify_url; } public String getTrade_type() { return trade_type; } public String getProduct_id() { return product_id; } public String getLimit_pay() { return limit_pay; } public String getOpenid() { return openid; } public void setAppid(String appid) { this.appid = appid; } public void setMch_id(String mch_id) { this.mch_id = mch_id; } public void setDevice_info(String device_info) { this.device_info = device_info; } public void setNonce_str(String nonce_str) { this.nonce_str = nonce_str; } public void setSign(String sign) { this.sign = sign; } public void setBody(String body) { this.body = body; } public void setDetail(String detail) { this.detail = detail; } public void setAttach(String attach) { this.attach = attach; } public void setOut_trade_no(String out_trade_no) { this.out_trade_no = out_trade_no; } public void setFee_type(String fee_type) { this.fee_type = fee_type; } public void setTotal_fee(int total_fee) { this.total_fee = total_fee; } public void setSpbill_create_ip(String spbill_create_ip) { this.spbill_create_ip = spbill_create_ip; } public void setTime_start(String time_start) { this.time_start = time_start; } public void setTime_expire(String time_expire) { this.time_expire = time_expire; } public void setGoods_tag(String goods_tag) { this.goods_tag = goods_tag; } public void setNotify_url(String notify_url) { this.notify_url = notify_url; } public void setTrade_type(String trade_type) { this.trade_type = trade_type; } public void setProduct_id(String product_id) { this.product_id = product_id; } public void setLimit_pay(String limit_pay) { this.limit_pay = limit_pay; } public void setOpenid(String openid) { this.openid = openid; } }
public SortedMap<String,Object> weiXingUnOrder(HttpServletRequest request, String orderNumber, Integer signId,Integer userid){ SortedMap<String,Object> result = new TreeMap<String, Object>(); SortedMap<String,Object> map = new TreeMap<String, Object>(); UnifiedorderDto dto = null; SortedMap<String,Object> sortedMap = encapData(dto); String outXml = WeiXinUtil.getRequestXml(sortedMap); log.debug("发送微信数据:"+outXml); String requestXml = null; SortedMap<String,Object> resultMap = null; try { requestXml = WeiXinUtil.httpsRequest(WeiXinConfig.SENDURL, "POST", outXml); log.debug("微信返回参数:"+requestXml); resultMap = WeiXinUtil.doXMLParse(requestXml); if(resultMap.get("return_code").equals("SUCCESS")){ //微信返回成功 if(resultMap.get("trade_type").equals(WeiXinConfig.TRADE_TYPE_APP)){ //APP返回 map.put("appid", resultMap.get("appid")); map.put("partnerid", resultMap.get("mch_id")); map.put("prepayid", resultMap.get("prepay_id")); map.put("package", WeiXinConfig.PACKAGE); map.put("noncestr",WeiXinUtil.getRandomString(32)); map.put("timestamp",WeiXinUtil.getDateTime10(new Date())); String sign = WeiXinUtil.createSign("utf-8", map, WeiXinConfig.APP_API_KEY); map.put("sign", sign); result.put("msg","APP支付数据返回成功"); result.put("success", true); result.put("data", map); result.put("uni", dto.getOut_trade_no()); result.put("money", dto.getTotal_fee()); }else if(resultMap.get("trade_type").equals(WeiXinConfig.TRADE_TYPE_WEB)){ //公众号返回 map.put("appId", resultMap.get("appid")); map.put("timeStamp",WeiXinUtil.getDateTime10(new Date())); map.put("nonceStr",WeiXinUtil.getRandomString(32)); map.put("package", "prepay_id="+resultMap.get("prepay_id")); map.put("signType", WeiXinConfig.SINTYPE); String sign = WeiXinUtil.createSign("utf-8",map, WeiXinConfig.WEB_API_KEY); map.put("paySign", sign); result.put("msg","公众号支付数据返回成功"); result.put("success", true); result.put("data", map); result.put("uni", dto.getOut_trade_no()); result.put("money", dto.getTotal_fee()); }else{ result.put("msg","系统错误"); result.put("success", false); } }else{ //微信返回失败 result.put("msg",String.valueOf(resultMap.get("return_msg"))); result.put("success", false); log.debug(String.valueOf(resultMap.get("return_msg"))); } } catch (NullPointerException e){ result.put("msg",resultMap.get("err_code_des")); result.put("success", false); } catch (Exception e) { result.put("msg","服务器未响应"); result.put("success", false); } return result; }
result 返回的是app还有公众号起调的参数,公众号支付有点不一样需要配置wx.config js里面的东西
这个微信返回结果都是xml格式字符串,回调跟上篇支付宝支付差不多