最近一段时间换了工作,虽说时间不长但也算收获颇丰,不仅学到了新的知识而且眼界也比之前开阔了。这次就分享下这段时间做微信开发的心得和体会。

一   java微信开发应具备的前提条件

1  掌握xml解析工具Dom4j、Jdom中的任意一种

微信所有的消息处理都是xml,因此xml的解析就显得尤为重要,这集中体现在文本消息、图文消息这两个部分

2 掌握JSON开发工具类如json-lib

json数据的处理在微信开发集中体现在自定义菜单接口、获取Access_Token、Oauth2.0网页授权等常用接口,此外第三方接口也会使用到如百度翻译、百度词典等。

3 掌握xstream

xstream的用途集中体现在java对象转xml字符串这个方面,使用xstream主要是为了最大程度地发挥java面向对象的特点。

4 熟悉MD5和SHA-1加密算法

加密算法 主要用于微信验证签名和生成签名(微信支付)两个部分

5 掌握HTTPConnection和HTTPSConnecion

这个部分一帮的第二点配合使用以达到最佳效果

6 掌握常用数据库

7 能熟练使用linux操作系统

这7点是2每个java微信开发者都必须具备的,如果这几点都没办法掌握,微信定制开发你就高攀不起。

二   下面是几个常用工具类

1 微信消息处理工具类

package com.debug.weixin.util;import java.io.InputStream;
import java.io.Writer;
import java.util.*;import javax.servlet.http.HttpServletRequest;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import com.debug.weixin.message.resp.Article;
import com.debug.weixin.message.resp.MusicMessage;
import com.debug.weixin.message.resp.NewsMessage;
import com.debug.weixin.message.resp.TextMessage;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;public class MessageUtil {/** * 返回消息类型:文本 */  public static final String PICTUREHOST = "http://xxxx";  /** * 返回消息类型:文本 */  public static final String RESP_MESSAGE_TYPE_TEXT = "text";  /** * 返回消息类型:音乐 */  public static final String RESP_MESSAGE_TYPE_MUSIC = "music";  /** * 返回消息类型:图文 */  public static final String RESP_MESSAGE_TYPE_NEWS = "news";  /** * 请求消息类型:文本 */  public static final String REQ_MESSAGE_TYPE_TEXT = "text";  /** * 请求消息类型:图片 */  public static final String REQ_MESSAGE_TYPE_IMAGE = "image";  /** * 请求消息类型:链接 */  public static final String REQ_MESSAGE_TYPE_LINK = "link";  /** * 请求消息类型:地理位置 */  public static final String REQ_MESSAGE_TYPE_LOCATION = "location";  /** * 请求消息类型:音频 */  public static final String REQ_MESSAGE_TYPE_VOICE = "voice";  /** * 请求消息类型:推送 */  public static final String REQ_MESSAGE_TYPE_EVENT = "event";  /** * 事件类型:subscribe(订阅) */  public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";  /** * 事件类型:unsubscribe(取消订阅) */  public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";  /** * 事件类型:CLICK(自定义菜单点击事件) */  public static final String EVENT_TYPE_CLICK = "CLICK";  /** * 解析微信发来的请求(XML) *  * @param request * @return * @throws Exception */  @SuppressWarnings("unchecked")  public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {  // 将解析结果存储在HashMap中  Map<String, String> map = new HashMap<String, String>();  // 从request中取得输入流  InputStream inputStream = request.getInputStream();  // 读取输入流  SAXReader reader = new SAXReader();  Document document = reader.read(inputStream);  // 得到xml根元素  Element root = document.getRootElement();  // 得到根元素的所有子节点  List<Element> elementList = root.elements();  // 遍历所有子节点  for (Element e : elementList)  map.put(e.getName(), e.getText());  // 释放资源  inputStream.close();  inputStream = null;  return map;  }  /** * 文本消息对象转换成xml *  * @param textMessage 文本消息对象 * @return xml */  public static String textMessageToXml(TextMessage textMessage) {  xstream.alias("xml", textMessage.getClass());  return xstream.toXML(textMessage);  }  /** * 音乐消息对象转换成xml *  * @param musicMessage 音乐消息对象 * @return xml */  public static String musicMessageToXml(MusicMessage musicMessage) {  xstream.alias("xml", musicMessage.getClass());  return xstream.toXML(musicMessage);  }  /** * 图文消息对象转换成xml *  * @param newsMessage 图文消息对象 * @return xml */  public static String newsMessageToXml(NewsMessage newsMessage) {  xstream.alias("xml", newsMessage.getClass());  xstream.alias("item", new Article().getClass());  return xstream.toXML(newsMessage);  }  /** * 扩展xstream,使其支持CDATA块 *  * @date 2013-05-19 */  private static XStream xstream = new XStream(new XppDriver() {  public HierarchicalStreamWriter createWriter(Writer out) {  return new PrettyPrintWriter(out) {  // 对所有xml节点的转换都增加CDATA标记  boolean cdata = true;  @SuppressWarnings("unchecked")  public void startNode(String name, Class clazz) {  super.startNode(name, clazz);  }  protected void writeText(QuickWriter writer, String text) {  if (cdata) {  writer.write("<![CDATA[");  writer.write(text);  writer.write("]]>");  } else {  writer.write(text);  }  }  };  }  });
}

2  签名验证工具类

package com.debug.weixin.util;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;public class SignUtil {// 与接口配置信息中的Token要一致private static String token = "xxxxxx";/*** 验证签名* * @param signature* @param timestamp* @param nonce* @return*/public static boolean checkSignature(String signature, String timestamp, String nonce) {String[] arr = new String[] { token, timestamp, nonce };// 将token、timestamp、nonce三个参数进行字典序排序Arrays.sort(arr);StringBuilder content = new StringBuilder();for (int i = 0; i < arr.length; i++) {content.append(arr[i]);}MessageDigest md = null;String tmpStr = null;try {md = MessageDigest.getInstance("SHA-1");// 将三个参数字符串拼接成一个字符串进行sha1加密byte[] digest = md.digest(content.toString().getBytes());tmpStr = byteToStr(digest);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}content = null;// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;}/*** 将字节数组转换为十六进制字符串* * @param byteArray* @return*/private static String byteToStr(byte[] byteArray) {String strDigest = "";for (int i = 0; i < byteArray.length; i++) {strDigest += byteToHexStr(byteArray[i]);}return strDigest;}/*** 将字节转换为十六进制字符串* * @param mByte* @return*/private static String byteToHexStr(byte mByte) {char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };char[] tempArr = new char[2];tempArr[0] = Digit[(mByte >>> 4) & 0X0F];tempArr[1] = Digit[mByte & 0X0F];String s = new String(tempArr);return s;}
}

3 xml解析工具类

package com.debug.weixin.util;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;public class XMLUtil {/*** 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。* @param strxml* @return* @throws JDOMException* @throws IOException*/public static Map doXMLParse(String strxml) throws JDOMException, IOException {strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");if(null == strxml || "".equals(strxml)) {return null;}Map m = new HashMap();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 = XMLUtil.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(XMLUtil.getChildrenText(list));}sb.append(value);sb.append("</" + name + ">");}}return sb.toString();}}

4  微信自定义菜单工具类

package com.debug.weixin.util;import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;import com.debug.weixin.pojo.AccessToken;
import com.debug.weixin.pojo.Menu;import net.sf.json.JSONException;
import net.sf.json.JSONObject;public class WeixinUtil {public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";// 菜单创建(POST) 限100(次/天)  public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"; // 菜单删除public static String menu_delete_url = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN"; /** * 发起https请求并获取结果 *  * @param requestUrl 请求地址 * @param requestMethod 请求方式(GET、POST) * @param outputStr 提交的数据 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) */  public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {  JSONObject jsonObject = null;  StringBuffer buffer = new StringBuffer();  try {  // 创建SSLContext对象,并使用我们指定的信任管理器初始化  TrustManager[] tm = { new MyX509TrustManager() };  SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");  sslContext.init(null, tm, new java.security.SecureRandom());  // 从上述SSLContext对象中得到SSLSocketFactory对象  SSLSocketFactory ssf = sslContext.getSocketFactory();  URL url = new URL(requestUrl);  HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();  httpUrlConn.setSSLSocketFactory(ssf);  httpUrlConn.setDoOutput(true);  httpUrlConn.setDoInput(true);  httpUrlConn.setUseCaches(false);  // 设置请求方式(GET/POST)  httpUrlConn.setRequestMethod(requestMethod);  if ("GET".equalsIgnoreCase(requestMethod))  {httpUrlConn.connect();  }// 当有数据需要提交时  if (null != outputStr) {  OutputStream outputStream = httpUrlConn.getOutputStream();  // 注意编码格式,防止中文乱码  outputStream.write(outputStr.getBytes("UTF-8"));  outputStream.close();  }  // 将返回的输入流转换成字符串  InputStream inputStream = httpUrlConn.getInputStream();  InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");  BufferedReader bufferedReader = new BufferedReader(inputStreamReader);  String str = null;  while ((str = bufferedReader.readLine()) != null) {  buffer.append(str);  }  bufferedReader.close();  inputStreamReader.close();  // 释放资源  inputStream.close();  inputStream = null;  httpUrlConn.disconnect();  jsonObject = JSONObject.fromObject(buffer.toString());  } catch (ConnectException ce) {  ce.printStackTrace();// log.error("Weixin server connection timed out.");  } catch (Exception e) {  //log.error("https request error:{}", e); e.printStackTrace();}  return jsonObject;  }  /** * 获取access_token *  * @param appid 凭证 * @param appsecret 密钥 * @return */  public static AccessToken getAccessToken(String appid, String appsecret) {  AccessToken accessToken = null;  String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);  JSONObject jsonObject = httpRequest(requestUrl, "GET", null);  // 如果请求成功  if (null != jsonObject) {  try {  accessToken = new AccessToken();  accessToken.setToken(jsonObject.getString("access_token"));  accessToken.setExpiresIn(jsonObject.getInt("expires_in"));  } catch (JSONException e) {  accessToken = null;  // 获取token失败  //log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); System.out.println("获取token失败"+jsonObject.getInt("errcode")+"," +jsonObject.getString("errmsg"));}  }  return accessToken;  }/** * 创建菜单 *  * @param menu 菜单实例 * @param accessToken 有效的access_token * @return 0表示成功,其他值表示失败 */  public static int createMenu(Menu menu, String accessToken) {  int result = 0;  // 拼装创建菜单的url  String url = menu_create_url.replace("ACCESS_TOKEN", accessToken);  // 将菜单对象转换成json字符串  String jsonMenu = JSONObject.fromObject(menu).toString();  // 调用接口创建菜单  JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);  if (null != jsonObject) {  if (0 != jsonObject.getInt("errcode")) {  result = jsonObject.getInt("errcode");  //log.error("创建菜单失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));  System.out.println("创建菜单失败"+jsonObject.getInt("errcode")+","+ jsonObject.getString("errmsg"));}  }  return result;  }  public static void removeAllMenu(String accessToken){// 拼装创建菜单的url  String url = menu_delete_url.replace("ACCESS_TOKEN", accessToken);  // 调用接口创建菜单  JSONObject jsonObject = httpRequest(url, "POST", null);  if (null != jsonObject) {  //if (0 != jsonObject.getInt("errcode")) {  System.out.println(jsonObject.getInt("errcode")+","+ jsonObject.getString("errmsg"));//}}}
}

httpRequest这个类比较常用,单独提取到一个类里也是非常OK的。

5 MD5工具类(主要用于微信支付)

package com.debug.weixin.util;
import java.security.MessageDigest;
public class MD5Util {private static String byteArrayToHexString(byte b[]) {StringBuffer resultSb = new StringBuffer();for (int i = 0; i < b.length; i++)resultSb.append(byteToHexString(b[i]));return resultSb.toString();}private static String byteToHexString(byte b) {int n = b;if (n < 0)n += 256;int d1 = n / 16;int d2 = n % 16;return hexDigits[d1] + hexDigits[d2];}public static String MD5Encode(String origin, String charsetname) {String resultString = null;try {resultString = new String(origin);MessageDigest md = MessageDigest.getInstance("MD5");if (charsetname == null || "".equals(charsetname))resultString = byteArrayToHexString(md.digest(resultString.getBytes()));elseresultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));} catch (Exception exception) {}return resultString;}private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5","6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}

6  微信统一下单工具类(主要用于微信支付)

package com.debug.weixin.util;import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;public class PayCommonUtil {public static String CreateNoncestr(int length) {String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";String res = "";for (int i = 0; i < length; i++) {Random rd = new Random();res += chars.indexOf(rd.nextInt(chars.length() - 1));}return res;}public static String CreateNoncestr() {String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";String res = "";for (int i = 0; i < 16; i++) {Random rd = new Random();res += chars.charAt(rd.nextInt(chars.length() - 1));}return res;}/*** @author 李欣桦* @date 2014-12-5下午2:29:34* @Description:sign签名* @param characterEncoding 编码格式* @param parameters 请求参数* @return*/public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){StringBuffer sb = new StringBuffer();  Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)  Iterator it = es.iterator();  while(it.hasNext()) {  Map.Entry entry = (Map.Entry)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=" + ConfigUtil.API_KEY);  String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();  return sign;  }/*** @author 李欣桦* @date 2014-12-5下午2:32:05* @Description:将请求参数转换为xml格式的string* @param parameters  请求参数* @return*/public static String getRequestXml(SortedMap<Object,Object> parameters){StringBuffer sb = new StringBuffer();sb.append("<xml>");Set es = 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 ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");}else {sb.append("<"+k+">"+v+"</"+k+">");}}sb.append("</xml>");return sb.toString();}/*** @author 李欣桦* @date 2014-12-3上午10:17:43* @Description:返回给微信的参数* @param return_code 返回编码* @param return_msg  返回信息* @return*/public static String setXML(String return_code, String return_msg) {return "<xml><return_code><![CDATA[" + return_code+ "]]></return_code><return_msg><![CDATA[" + return_msg+ "]]></return_msg></xml>";}
}

这个工具类是参考csdn上一位大神的博客,与其说参考不如说是复制粘贴过来的,没修改注释主要是为了尊重原创作者,在此也真诚感谢这位作者的无私奉献。

7 证书信任管理器(主要用于HttpsConnection)

关于这个部分我也专门写过一篇博客的,这里就直接上代码了

package com.debug.weixin.util;import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;import javax.net.ssl.X509TrustManager;/** * 证书信任管理器(用于https请求) * @date 2013-08-08 */
public class MyX509TrustManager implements X509TrustManager{public void checkClientTrusted(X509Certificate[] arg0, String arg1)throws CertificateException {}public void checkServerTrusted(X509Certificate[] arg0, String arg1)throws CertificateException {}public X509Certificate[] getAcceptedIssuers() {return null;}}

三  微信支付部分容易出错几个细节点

这个部分主要是对csdn上“情本寂寞”这位大神(就上面的那位李欣桦)的博客做补充说明。

1  对Oauth2.0网页授权获取openid的补充

这个地方是支付环节最容易出错也是最困难的部分,我给出了几个方法以供参考

Oauth2.0网页授权第一步:

@RequestMapping("/wxIndex")
public void wxIndex(HttpServletRequest request, HttpServletResponse response)throws Exception {String orderNo=request.getParameter("orderNo");String re = URLEncoder.encode(ServerConfig.SERVERDOMAIN+"/waphulai/order/wxOAuth.do?orderNo="+orderNo, "UTF-8");String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";url = url.replace("APPID", "xxxxxx");url = url.replace("REDIRECT_URI", re);response.sendRedirect(url);}

Oauth2.0网页授权第二步(直接获取openid):

 @RequestMapping("/wxOAuth")public String wxOAuth(HttpServletRequest request,HttpServletResponse response) throws Exception {request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");String code = request.getParameter("code");String orderNo=request.getParameter("orderNo");if (!"authdeny".equals("code")) {WeixinOauth2Token w = AdvancedUtil.getOauth2AccessToken("xxxxx", "xxxxxx",code);// 网页授权接口访问凭证String accessToken = w.getAccessToken();// 用户标识String openId = w.getOpenId();// 获取用户信息/*SNSUserInfo snsUser = AdvancedUtil.getSNSUserInfo(accessToken,openId);request.setAttribute("snsUserInfo", snsUser);*/Order orderInfo=orderService.getOrderByNo(orderNo);SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();parameters.put("appid", ConfigUtil.APPID);parameters.put("mch_id", ConfigUtil.MCH_ID);parameters.put("device_info", "1000");  parameters.put("body", "洗车订单");parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());parameters.put("out_trade_no", orderInfo.getOrder_no());parameters.put("total_fee", String.valueOf(orderInfo.getOrder_money()*100));parameters.put("spbill_create_ip", request.getRemoteAddr());parameters.put("notify_url", ConfigUtil.NOTIFY_URL);parameters.put("trade_type", "JSAPI");parameters.put("openid", openId);String sign = PayCommonUtil.createSign("UTF-8", parameters);//System.out.println("我 的签名是:"+sign);  parameters.put("sign", sign);String requestXML = PayCommonUtil.getRequestXml(parameters);String result = CommonUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL,"POST", requestXML);System.out.println("----------------------------------");System.out.println(result);System.out.println("----------------------------------");System.out.println(String.valueOf(orderInfo.getOrder_money()*100)+"----------------------------------");request.setAttribute("orderNo", orderInfo.getOrder_no());request.setAttribute("totalPrice", orderInfo.getOrder_money());request.setAttribute("unifiedOrder",getH5PayStr(result,request));}return "/weixin/wxPay.ftl";}

这里我编写了一个工具类来获取,工具类如下:

package com.debug.weixin.util;import net.sf.json.JSONArray;
import net.sf.json.JSONObject;import com.debug.weixin.pojo.SNSUserInfo;
import com.debug.weixin.pojo.WeixinOauth2Token;public class AdvancedUtil {public static WeixinOauth2Token getOauth2AccessToken(String appId,String appSecret,String code){WeixinOauth2Token wat=null;//拼接请求地址String requestUrl="https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";requestUrl=requestUrl.replace("APPID", appId);requestUrl=requestUrl.replace("SECRET", appSecret);requestUrl=requestUrl.replace("CODE", code);//获取网页授权凭证JSONObject jsonObject=WeixinUtil.httpRequest(requestUrl, "GET", null);if(jsonObject!=null){try{wat=new WeixinOauth2Token();wat.setAccessToken(jsonObject.getString("access_token"));wat.setExpiresIn(jsonObject.getInt("expires_in"));wat.setRefreshToken(jsonObject.getString("refresh_token"));wat.setOpenId(jsonObject.getString("openid"));wat.setScope(jsonObject.getString("scope"));}catch(Exception e){wat=null;int errorCode=jsonObject.getInt("errcode");String errorMsg=jsonObject.getString("errmsg");System.out.println("获取网页授权凭证失败:"+errorCode+","+errorMsg);}}return wat;}public static WeixinOauth2Token refreshOauth2AccessToken(String appId,String refreshToken){WeixinOauth2Token wat=null;//拼接请求地址String requestUrl="https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";requestUrl=requestUrl.replace("APPID", appId);requestUrl=requestUrl.replace("REFRESH_TOKEN", refreshToken);//获取网页授权凭证JSONObject jsonObject=WeixinUtil.httpRequest(requestUrl, "GET", null);if(jsonObject!=null){try{wat=new WeixinOauth2Token();wat.setAccessToken(jsonObject.getString("access_token"));wat.setExpiresIn(jsonObject.getInt("expires_in"));wat.setRefreshToken(jsonObject.getString("refresh_token"));wat.setOpenId(jsonObject.getString("openid"));wat.setScope(jsonObject.getString("scope"));}catch(Exception e){wat=null;int errorCode=jsonObject.getInt("errcode");String errorMsg=jsonObject.getString("errmsg");System.out.println("刷新网页授权凭证失败:"+errorCode+","+errorMsg);}}return wat;}public static SNSUserInfo getSNSUserInfo(String accessToken,String openId){SNSUserInfo snsUserInfo=null;String requestUrl="https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";requestUrl=requestUrl.replace("ACCESS_TOKEN", accessToken);requestUrl=requestUrl.replace("OPENID", openId);//通过网页获取用户信息JSONObject jsonObject=WeixinUtil.httpRequest(requestUrl, "GET", null);if(jsonObject!=null){try{snsUserInfo=new SNSUserInfo();snsUserInfo.setOpenId(jsonObject.getString("openid"));snsUserInfo.setNickname(jsonObject.getString("nickname"));//性别(1男  2女 0未知)snsUserInfo.setSex(jsonObject.getInt("sex"));snsUserInfo.setCountry(jsonObject.getString("country"));snsUserInfo.setProvince(jsonObject.getString("province"));snsUserInfo.setCity(jsonObject.getString("city"));snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));snsUserInfo.setPrivilegeList(JSONArray.toList(jsonObject.getJSONArray("privilege")));}catch(Exception e){snsUserInfo=null;int errorCode=jsonObject.getInt("errcode");String errorMsg=jsonObject.getString("errmsg");System.out.println("获取用户信息失败:"+errorCode+","+errorMsg);}}return snsUserInfo;}
}

2 拼接H5微信支付相关参数的方法

public String getH5PayStr(String result,HttpServletRequest request) throws Exception{Map<String, String> map = XMLUtil.doXMLParse(result);SortedMap<Object,Object> params = new TreeMap<Object,Object>();params.put("appId", ConfigUtil.APPID);params.put("timeStamp", Long.toString(new Date().getTime()));params.put("nonceStr", PayCommonUtil.CreateNoncestr());params.put("package", "prepay_id="+map.get("prepay_id"));params.put("signType", ConfigUtil.SIGN_TYPE);String paySign =  PayCommonUtil.createSign("UTF-8", params);params.put("packageValue", "prepay_id="+map.get("prepay_id"));    //这里用packageValue是预防package是关键字在js获取值出错params.put("paySign", paySign);                                                          //paySign的生成规则和Sign的生成规则一致params.put("sendUrl", ConfigUtil.SUCCESS_URL);                               //付款成功后跳转的页面String userAgent = request.getHeader("user-agent");char agent = userAgent.charAt(userAgent.indexOf("MicroMessenger")+15);params.put("agent", new String(new char[]{agent}));//微信版本号,用于前面提到的判断用户手机微信的版本是否是5.0以上版本。String json = JSONObject.fromObject(params).toString();return json;}

这个方法也是参考大神的博客改出来的,相比之前大神的博客,只是把流程理得更清晰明了

3 对支付成功微信通知的请求方法的修改

        @RequestMapping("/paySuccess")public void paySuccess(HttpServletRequest request,HttpServletResponse response) throws Exception {InputStream inStream = request.getInputStream();ByteArrayOutputStream outSteam = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len = 0;while ((len = inStream.read(buffer)) != -1) {outSteam.write(buffer, 0, len);}System.out.println("~~~~~~~~~~~~~~~~付款成功~~~~~~~~~");outSteam.close();inStream.close();String result  = new String(outSteam.toByteArray(),"utf-8");//获取微信调用我们notify_url的返回信息Map<Object, Object> map = XMLUtil.doXMLParse(result);for(Object keyValue : map.keySet()){System.out.println(keyValue+"="+map.get(keyValue));}if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {//TODO 对数据库的操作String orderNo=map.get("out_trade_no").toString();boolean ressult=orderService.updateOrderStatus(orderNo, 2);if(ressult){System.out.println("数据库订单状态修改成功");}else{System.out.println("数据库订单状态修改失败");}response.getWriter().write(PayCommonUtil.setXML("SUCCESS", ""));   //告诉微信服务器,我收到信息了,不要在调用回调action了System.out.println("-------------"+PayCommonUtil.setXML("SUCCESS", ""));}}

这个方法是微信支付的终点或者说是最后一步,我对大神对微信支付文档的阅读用四个字概括就是“相当到位”。为了解释这段代码为什么这样写,请参考下面的链接

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

三  使用微信支付容易被坑到的几个细节点

1 有关微信支付开发配置部分

2 Oauth2.0网页版授权请记得配置回调页面域名

3 支付参数的问题点

这一步的问题主要在total_fee这个参数上面,这里应该是一个整型的字符串如“3500”、“1000”;但不能是“3500.0”或“3500.00”这种,且单位是分而不是元;其他参数应该不会出问题。

如果这些步骤都OK那么微信支付就不会有问题了,其他部分请参考如下链接

微信支付参考博客:http://blog.csdn.net/u011160656/article/details/41759195

消息处理参考博客:http://blog.csdn.net/lyq8479

看了这两位作者的博客之后个人觉得微信的常规开发应该是不成问题了。

java微信开发需具备的条件相关推荐

  1. Java后端开发需具备什么技术?这几个部分你需要关注

    Java后端开发需具备什么技术?对于初学Java并且有志于后端开发的同学来说,需要重点关注以下几个部分: Java开发普遍需要: 1.Java基础 2.数据库 MYSQL/SQLServer/Orac ...

  2. 同城跑腿APP开发需具备哪些功能?

    移动互联网的飞速发展加上人们生活水平的提高,生活工作闲暇之余,人们不愿意为买药.送文件.取东西.送花.排队等小事浪费时间或者是根本没有时间去处理类似的事情.这个时候就想如果能够花钱请人来替我做这些事就 ...

  3. 多商户商城小程序源码开发需具备哪些功能?

    随着电商的进一步发展,传统企业为了更好的占领市场也纷纷向电商市场迈进,着手打造属于自己的商城系统.多商户商城系统是一种多商户.多商品.多支付的电子商务平台,功能丰富,涵盖多个行业,能够满足多种商家和用 ...

  4. java微信开发-消息接收和自动回复

    0.前提条件 1.需要一个公众平台帐号(测试帐号也可以) 2.平台需要被人关注 1.导入jar commons-beanutils-1.8.0.jar commons-collections-3.1. ...

  5. 【Java Web开发学习】Spring4条件化的bean

    [Java Web开发学习]Spring4条件化的bean 转载:https://www.cnblogs.com/yangchongxing/p/9071960.html Spring4引入了@Con ...

  6. Java微信开发_00_资源汇总贴

    1.微信公众平台技术文档(https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432) 2.微信企业号开发接口文档(ht ...

  7. java微信开发API解析(二)-获取消息和回复消息

    java微信开发API解析(二)-获取消息和回复消息 说明 * 本演示样例依据微信开发文档:http://mp.weixin.qq.com/wiki/home/index.html最新版(4/3/20 ...

  8. 英语本科 国外跨计算机,跨专业考研需具备哪些条件,如英语,计算机的过级情况...

    跨专业考研需具备哪些条件,如英语,计算机的过级情况以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 跨专业考研需具备哪些条 ...

  9. 自媒体助手软件开发需具备哪些功能?

    自媒体助手软件开发需具备哪些功能?        1.多平台多账号管理.        用户可以在单独的平台上管理其他平台的账号,不需要登录多个平台,为用户减少了大量的时间,与此同时可以记忆账号和密码 ...

  10. Java微信开发入门

    前言: 1.先简单介绍一下微信公众号的开发者模式和编辑模式,玩过微信公众号的同志都知道,在微信公众号后台功能栏里面可以编辑消息自动回复,关键字回复,自定义菜单等功能,这些都是在视图页面下编辑的,叫编辑 ...

最新文章

  1. Linux中/proc目录下文件详解
  2. Python可视化(matplotlib)在图像中添加文本和标记(Text and Annotation)
  3. TF之LiR:基于tensorflow实现机器学习之线性回归算法
  4. 笔记本电脑锁_小雷问答丨3000-3500 价格的笔记本电脑怎么选?
  5. VC使用编译时间作为版本号
  6. ft232r usb uart驱动 win7_新电脑想装WIN7,技术员让装WIN10,不是忽悠你,是有原因的...
  7. IList类转换成DataTable
  8. MySQL数据检索+查询+全文本搜索
  9. 【机器学习】 - Keras学习 - TensorBoard模块 - 可视化模型训练过程神器
  10. 阿里十年DBA经验产品经理:真的不要再有一起删库跑路事件了
  11. java byreference_Java中各种引用(Reference)解析
  12. oracle .net 中文,C# 连接Oracle 中文乱码问题解决办法
  13. python 怎么将数字转大写_python 数字转换为大写
  14. 「HNOI 2009」图的同构记数
  15. python中步长_python步长什么意思
  16. 理性分析三星盖乐世炸弹7
  17. itext7学习笔记杂谈系列2——在itext7中添加中文(其他字体)和字体相关事
  18. golang sync.Map和map+mutex性能比较
  19. 3dsmax UVW展开,然后在 BodyPaint 3D 中进行绘制
  20. cad画1000线太长了_为什么在CAD里面我画50变成了1000?

热门文章

  1. JAVA-pdfBox-纯文件流实现PDF文件加水印后转PDF图片文件下载
  2. 微信开放平台 公众号第三方平台开发C#
  3. 这3种管理者是“企业毒瘤”,须根除
  4. Macbook 删除被锁定的应用
  5. 北林信息学院计算机导师名单,北京林业大学信息学院计算机应用技术导师介绍:刘文萍...
  6. MPI编程(4)—集合通信MPI_Bcast、MPI_Gather、MPI_Scatter、MPI_Reduce
  7. 如何使用xposed强制开启android webview debug模式
  8. 2018SCAU校赛题解
  9. TSU-求最大最小数
  10. 计算机主机内的零件有什么用,ROM和RAM分别是什么?有什么区别?与电脑的什么配件的作用是一? 爱问知识人...