上次写了一篇微信支付的文章,倒也还行就是逻辑上面有点欠妥当,这次针对这个问题我用struts2写了一套用于微信验证并能处理各种消息以及H5支付的一套程序,也针对分享到朋友圈这个功能使用了微信的jssdk,感触特别深,废话不多说下面就开始分享我的心得
一 微信如何和struts2整合
针对这个问题,网上基本没有这方面的资料,因此本人也就特别想针对这方面写篇博客,网上没资料其实道理很简单,首先java开发微信应用本身就比较少,市场都被php占领了;还有就是struts2的原因,目前市面上大家熟知的springmvc大大的挤压了struts的空间。所以.......
1 struts2整合微信的注意事项
struts2本身其实就是一个控制器,微信开发首先就是服务器配置那个部分的验证(使用GET请求)以及各种常用消息的接收和回复(POST)请求,因此只需在struts2里写方法暴露给微信服务器访问就OK,这个方法只要区分出GET和POST两种请求就行,GET请求就调用微信验证的相应方法,POST请求就调用消息处理的方法。这样就搞定了,来看下我的方法
package com.debug.weixin.action;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.debug.weixin.service.CoreService;
import com.debug.weixin.util.MessageUtil;
import com.debug.weixin.util.SignUtil;
import com.opensymphony.xwork2.ActionContext;
public class WeixinAction {
public void execute() {
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
// 判断请求方法是get还是post
String method = request.getMethod().trim();
if ("get".equalsIgnoreCase(method)) {
wxCheck(request, response);
} else if ("post".equalsIgnoreCase(method)) {
try {
processRequest(request, response);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void wxCheck(HttpServletRequest request, HttpServletResponse response) {
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
PrintWriter out = null;
try {
out = response.getWriter();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
}
out.close();
out = null;
}
public void processRequest(HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
String encryptType = request.getParameter("encrypt_type");
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 响应消息
try{
PrintWriter out = response.getWriter();
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
Map requestMap = null;
if("aes".equals(encryptType)){
requestMap=MessageUtil.parseXmlCrypt(request);
String respXml=CoreService.processRequest(requestMap);
respXml=MessageUtil.getWxCrypt().encryptMsg(respXml,timestamp,nonce);
//System.out.println(respXml);
out.print(respXml);
}else{
requestMap=MessageUtil.parseXml(request);
String respXml=CoreService.processRequest(requestMap);
//respXml=MessageUtil.getWxCrypt().encryptMsg(respXml,timestamp,nonce);
out.print(respXml);
//System.out.println(respXml);
}
// 调用核心业务类接收消息、处理消息
//String respMessage = CoreService.processRequest(request);
out.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
二 struts2版微信实现H5支付
微信H5支付其实是3种支付方式之中,最简单的一种,难点是取得openId和生成微信支付签名2个部分,下面我拆分开来说一下
为了简化操作我写了一个表单,只需填写订单号,提交之后取得openId之后直接调用统一下单接口跳转到支付确认界面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
提交订单
package com.debug.weixin.action;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import org.apache.struts2.ServletActionContext;
import com.debug.weixin.pojo.WeixinOauth2Token;
import com.debug.weixin.util.AdvancedUtil;
import com.debug.weixin.util.CommonUtil;
import com.debug.weixin.util.ConfigUtil;
import com.debug.weixin.util.PayCommonUtil;
import com.debug.weixin.util.XMLUtil;
public class OrderAction extends BaseAction{
public String orderIndex(){
//HttpServletRequest request = ServletActionContext.getRequest();
//HttpServletResponse response = ServletActionContext.getResponse();
return "orderIndex";
}
public void payConfirm() throws Exception{
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
String orderNo=request.getParameter("orderNo");
response.sendRedirect(initOpenId("http://chenwill3.imwork.net/StrutsWX/order/order_h5Pay.action?orderNo="+orderNo));
}
public void paySuccess() throws Exception{
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
//这里处理订单
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);
}
outSteam.close();
inStream.close();
String result = new String(outSteam.toByteArray(),"utf-8");//获取微信调用我们notify_url的返回信息
Map
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
H5订单支付
public String initOpenId(String destUrl) throws Exception {
String re = URLEncoder.encode(destUrl, "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", ConfigUtil.APPID);
url = url.replace("REDIRECT_URI", re);
return url;
}
public String getH5PayStr(String result,HttpServletRequest request) throws Exception{
Map map = XMLUtil.doXMLParse(result);
SortedMap params = new TreeMap();
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;
}
第一个方法是按照获取openid的API写的方法,这个不解释,理解不了只能去看微信开发文档了;第二个方法是拼接用于H5支付的json串
关于支付来最后看下运行截图:
下面就是支付时的截图
到这里支付相关的代码就完结了
三 分享到朋友圈功能(使用JSSDK)
难点:取得AccessToken、取得JSAPITicket、生成jssdk签名
1 取得jsapiticket
accessToken的获取之前已经写过博客,这里直接进入取得jsapiticket的流程,java代码如下
public static JSAPITicket getJsApiTicket(String accessToken){
String requestUrl="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
requestUrl=requestUrl.replace("ACCESS_TOKEN", accessToken);
JSONObject json=CommonUtil.httpsRequest(requestUrl, "GET", null);
JSAPITicket js=new JSAPITicket();
js.setTicket(json.getString("ticket"));
js.setExpires_in(json.getInt("expires_in"));
return js;
}
package com.debug.weixin.pojo;
public class JSAPITicket {
private String ticket;
private int expires_in;
public String getTicket() {
return ticket;
}
public void setTicket(String ticket) {
this.ticket = ticket;
}
public int getExpires_in() {
return expires_in;
}
public void setExpires_in(int expiresIn) {
expires_in = expiresIn;
}
}
微信提供了一个生成签名的方法,我这边使用的代码和微信提供的形式上有点差异,但效果是一样的,下面是生成签名的方法
package com.debug.weixin.util;
import java.util.Arrays;
import java.util.UUID;
import java.util.Map;
import java.util.HashMap;
import java.util.Formatter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
public class JSSDKSign {
public static Map sign(String jsapi_ticket, String url) {
Map ret = new HashMap();
String nonce_str = create_nonce_str();
String timestamp = create_timestamp();
String[] paramArr = new String[] { "jsapi_ticket=" + jsapi_ticket,
"timestamp=" + timestamp, "noncestr=" + nonce_str, "url=" + url};
Arrays.sort(paramArr);
// 将排序后的结果拼接成一个字符串
String content = paramArr[0].concat("&"+paramArr[1]).concat("&"+paramArr[2])
.concat("&"+paramArr[3]);
String gensignature = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
// 对拼接后的字符串进行 sha1 加密
byte[] digest = md.digest(content.toString().getBytes());
gensignature = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
ret.put("url", url);
ret.put("jsapi_ticket", jsapi_ticket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", Long.parseLong(timestamp));
ret.put("signature", gensignature);
return ret;
}
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
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;
}
private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
private static String create_nonce_str() {
return UUID.randomUUID().toString();
}
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}
3 展示界面代码如下
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
分享到朋友圈
出现这样的alert说明jssdk配置成功了,为了保险起见一般还要判断下微信浏览器是否支持分享到朋友圈
接下来就是点右上方的分享到朋友圈测功能了
输入内容点发送就OK
最后打开朋友圈就能看到分享的东西了
一般情况下这种场景是用在点分享到朋友圈要分享值得内容的功能,正常情况或没特别定制性要求的应用可能用不到,使用jssdk要注意配置这里:
如果是微信支付要配置网页账号和微信支付开发配置
微信支付开发配置一般如下