Java实现公众号H5 微信支付
2016-09-26 16:37
295 查看
最近由于有微信项目使用到了微信支付这功能,我原本以为跟调用其它js接口一样,非常简单,实则不然,因为微信团队留下了很多天坑,提供的文档全是老版的,下载下来无法使用,导致这个支付功能 害我整整调了一天,全是微信团队留下的坑,有些签名并不是下载事例所说那样,接下来我将分享通过自身努力实现的微信支付成果:
第一步:微信支付配置文件 ConfigUtil
/**
* 微信支付配置文件
* @author Mark
*
*/
public class ConfigUtil {
/**
* 服务号相关信息
*/
public final static String APPID = “”;//服务号的appid
public final static String APP_SECRECT = “”;//服务号的appSecrect
public final static String TOKEN = ” “;//服务号的配置token
public final static String MCH_ID = “”;//开通微信支付分配的商户号
public final static String API_KEY = “”;//商户API密钥 自行去商户平台设置
public final static String SIGN_TYPE = “MD5”;//签名加密方式
//微信支付统一接口的回调action
public final static String NOTIFY_URL = “”; //用于告知微信服务器 调用成功
}
第二步:通用工具类 CommonUtil (用于请求接口)
/**
* 通用工具类
* @author Mark
*/
public class CommonUtil {
private static Logger log = LoggerFactory.getLogger(CommonUtil.class);
/**
* 发送https请求
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return 返回微信服务器响应的信息
*/
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
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 conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
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 ce) {
log.error(“连接超时:{}”, ce);
} catch (Exception e) {
log.error(“https请求异常:{}”, e);
}
return null;
}
}
第三步:MD5处理 MD5Util
/**
* MD5处理
* @author Mark
*
*/
public class MD5Util {
}
第四步:信任管理器 微信的信任证书 MyX509TrustManager
/**
* 信任管理器
* @author Mark
*/
public class MyX509TrustManager implements X509TrustManager {
}
第五步:微信H5支付 签名 随机码 时间戳等管理 PayCommonUtil
/**
* 微信H5支付 签名 随机码 时间戳等管理
* @author Mark
*
*/
public class PayCommonUtil {
}
第六步:调用微信H5支付统一下单接口 得到预支付ID WxPayUtil
/**
* 调用微信H5支付统一下单接口 得到预支付ID
* @author Mark
*
*/
public class WxPayUtil {
}
第七步:xml管理 XMLUtil
/**
* xml管理
* @author Mark
*
*/
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\”“);
}
第八步:接下来是Controller中调用,方式如下:
@ApiOperation(value = “微信支付调用”, httpMethod = “POST”)
@RequestMapping(“/couponsConfirm”)
public String couponsConfirm(Model m,@RequestParam(“openid”)String openid,@RequestParam(“orderNo”)String orderNo) {
//openid可通过微信高级接口oath2.0网页授权接口获取到用户信息,此接口本文中就不提供了,如果有需要,请留言。
m.addAttribute(“openid”, openid);
//orderNo是你的商品订单号,自行生成的随机订单号,但是要保证随机性,不能有重复订单号。
m.addAttribute(“orderNo”, orderNo);
// String userAgent = request.getHeader(“user-agent”);
// char agent = userAgent.charAt(userAgent.indexOf(“MicroMessenger”)+15);
// m.addAttribute(“agent”, new String(new char[]{agent}));//微信版本号,用于前面提到的判断用户手机微信的版本是否是5.0以上版本。
最后一步:页面调用
WeixinJSBridge.invoke(‘getBrandWCPayRequest’,{
“appId”:(“#appid”).val(),
“timeStamp”:(“#timestamp”).val(),
“nonceStr”:(“#nonceStr”).val(),
“package”:(“#packageValue”).val(),
“signType”:”MD5”,
“paySign”:$(“#paySign”).val()
},function(res){
WeixinJSBridge.log(res.err_msg);
if(res.err_msg == “get_brand_wcpay_request:ok”){
alert(“支付成功”);
}else if(res.err_msg == “get_brand_wcpay_request:cancel”){
alert(“取消支付!”)
到此,所有需要的代码都已提供,各位在借鉴的时候,记得也要做些内容修改,ConfigUtil中的一些参数,需要各位自行提供。其它的都可以直接使用,微信支付整体并不难,难点就在于微信签名,很多人调试微信支付,都被这个微信签名给难倒了,这也是微信团队的留下的一天坑。
**最后一句:如果各位在借鉴中,发现缺失什么,可以私信找我要,也可以留言。如果觉得我写的好,就来犒劳我一下吧
第一步:微信支付配置文件 ConfigUtil
/**
* 微信支付配置文件
* @author Mark
*
*/
public class ConfigUtil {
/**
* 服务号相关信息
*/
public final static String APPID = “”;//服务号的appid
public final static String APP_SECRECT = “”;//服务号的appSecrect
public final static String TOKEN = ” “;//服务号的配置token
public final static String MCH_ID = “”;//开通微信支付分配的商户号
public final static String API_KEY = “”;//商户API密钥 自行去商户平台设置
public final static String SIGN_TYPE = “MD5”;//签名加密方式
//微信支付统一接口的回调action
public final static String NOTIFY_URL = “”; //用于告知微信服务器 调用成功
/** * 微信基础接口地址 */ //获取token接口(GET) public final static String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; //oauth2授权接口(GET) public final static String OAUTH2_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"; //刷新access_token接口(GET) public final static String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN"; // 菜单创建接口(POST) public final static String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"; // 菜单查询(GET) public final static String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN"; // 菜单删除(GET) public final static String MENU_DELETE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN"; /** * 微信支付接口地址 */ //微信支付统一接口(POST) public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //微信退款接口(POST) public final static String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund"; //订单查询接口(POST) public final static String CHECK_ORDER_URL = "https://api.mch.weixin.qq.com/pay/orderquery"; //关闭订单接口(POST) public final static String CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder"; //退款查询接口(POST) public final static String CHECK_REFUND_URL = "https://api.mch.weixin.qq.com/pay/refundquery"; //对账单接口(POST) public final static String DOWNLOAD_BILL_URL = "https://api.mch.weixin.qq.com/pay/downloadbill"; //短链接转换接口(POST) public final static String SHORT_URL = "https://api.mch.weixin.qq.com/tools/shorturl"; //接口调用上报接口(POST) public final static String REPORT_URL = "https://api.mch.weixin.qq.com/payitil/report";
}
第二步:通用工具类 CommonUtil (用于请求接口)
/**
* 通用工具类
* @author Mark
*/
public class CommonUtil {
private static Logger log = LoggerFactory.getLogger(CommonUtil.class);
/**
* 发送https请求
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return 返回微信服务器响应的信息
*/
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
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 conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
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 ce) {
log.error(“连接超时:{}”, ce);
} catch (Exception e) {
log.error(“https请求异常:{}”, e);
}
return null;
}
public static String urlEncodeUTF8(String source){ String result = source; try { result = java.net.URLEncoder.encode(source,"utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; }
}
第三步:MD5处理 MD5Util
/**
* MD5处理
* @author Mark
*
*/
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())); else resultString = 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" };
}
第四步:信任管理器 微信的信任证书 MyX509TrustManager
/**
* 信任管理器
* @author Mark
*/
public class MyX509TrustManager implements X509TrustManager {
// 检查客户端证书 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 检查服务器端证书 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 返回受信任的X509证书数组 public X509Certificate[] getAcceptedIssuers() { return null; }
}
第五步:微信H5支付 签名 随机码 时间戳等管理 PayCommonUtil
/**
* 微信H5支付 签名 随机码 时间戳等管理
* @author Mark
*
*/
public class PayCommonUtil {
/** * 获取支付随机码 * @return */ public static String create_nonce_str() { return UUID.randomUUID().toString(); } /** * 获取微信支付时间戳 * @return */ public static String create_timestamp() { return Long.toString(System.currentTimeMillis() / 1000); } /** * 获取预支付ID时 获取随机码 * @param length * @return */ 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; } /** * 获取预支付ID时 获取随机码 * @param length * @return */ 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 Mark * @Description:sign签名 * @param characterEncoding 编码格式 * @param parameters 请求参数 * @return */ public static String createSign(SortedMap<Object,Object> parameters){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet(); 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(),"UTF-8").toUpperCase(); return sign; } /** * @author Mark * @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 Mark * @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>"; }
}
第六步:调用微信H5支付统一下单接口 得到预支付ID WxPayUtil
/**
* 调用微信H5支付统一下单接口 得到预支付ID
* @author Mark
*
*/
public class WxPayUtil {
@SuppressWarnings("unchecked") public static String unifiedorder(String body,String out_trade_no,String openid) { SortedMap<Object,Object> parameters = new TreeMap<Object,Object>(); parameters.put("appid", ConfigUtil.APPID); parameters.put("mch_id", ConfigUtil.MCH_ID); parameters.put("nonce_str", PayCommonUtil.CreateNoncestr()); parameters.put("body", body); parameters.put("out_trade_no", out_trade_no); parameters.put("total_fee", "1"); parameters.put("spbill_create_ip","113.57.246.11"); parameters.put("notify_url", ConfigUtil.NOTIFY_URL); parameters.put("trade_type", "JSAPI"); parameters.put("openid", openid); String sign = PayCommonUtil.createSign(parameters); parameters.put("sign", sign); String requestXML = PayCommonUtil.getRequestXml(parameters); System.out.println(requestXML.toString()); String result =CommonUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL, "POST", requestXML); System.out.println(result.toString()); Map<String, String> map=new HashMap<String, String>(); try { map = XMLUtil.doXMLParse(result); } catch (JDOMException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }//解析微信返回的信息,以Map形式存储便于取值 return map.get("prepay_id").toString(); }
}
第七步:xml管理 XMLUtil
/**
* xml管理
* @author Mark
*
*/
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(); }
}
第八步:接下来是Controller中调用,方式如下:
@ApiOperation(value = “微信支付调用”, httpMethod = “POST”)
@RequestMapping(“/couponsConfirm”)
public String couponsConfirm(Model m,@RequestParam(“openid”)String openid,@RequestParam(“orderNo”)String orderNo) {
//openid可通过微信高级接口oath2.0网页授权接口获取到用户信息,此接口本文中就不提供了,如果有需要,请留言。
m.addAttribute(“openid”, openid);
//orderNo是你的商品订单号,自行生成的随机订单号,但是要保证随机性,不能有重复订单号。
m.addAttribute(“orderNo”, orderNo);
String timeStamp=PayCommonUtil.create_timestamp(); String nonceStr=PayCommonUtil.create_nonce_str(); m.addAttribute("appid", ConfigUtil.APPID); m.addAttribute("timestamp", timeStamp); m.addAttribute("nonceStr", nonceStr); m.addAttribute("openid",openid); String prepayId=WxPayUtil.unifiedorder("外卖下单",orderNo, openid);
// String userAgent = request.getHeader(“user-agent”);
// char agent = userAgent.charAt(userAgent.indexOf(“MicroMessenger”)+15);
// m.addAttribute(“agent”, new String(new char[]{agent}));//微信版本号,用于前面提到的判断用户手机微信的版本是否是5.0以上版本。
SortedMap<Object,Object> signParams = new TreeMap<Object,Object>(); signParams.put("appId", ConfigUtil.APPID); signParams.put("nonceStr",nonceStr); signParams.put("package", "prepay_id="+prepayId); signParams.put("timeStamp", timeStamp); signParams.put("signType", "MD5"); // 生成支付签名,要采用URLENCODER的原始值进行SHA1算法! String sign= PayCommonUtil.createSign(signParams); m.addAttribute("paySign", sign); m.addAttribute("packageValue", "prepay_id="+prepayId); return "跳转到你的支付页面"; }
最后一步:页面调用
WeixinJSBridge.invoke(‘getBrandWCPayRequest’,{
“appId”:(“#appid”).val(),
“timeStamp”:(“#timestamp”).val(),
“nonceStr”:(“#nonceStr”).val(),
“package”:(“#packageValue”).val(),
“signType”:”MD5”,
“paySign”:$(“#paySign”).val()
},function(res){
WeixinJSBridge.log(res.err_msg);
if(res.err_msg == “get_brand_wcpay_request:ok”){
alert(“支付成功”);
}else if(res.err_msg == “get_brand_wcpay_request:cancel”){
alert(“取消支付!”)
}else{ alert("支付失败!") }
到此,所有需要的代码都已提供,各位在借鉴的时候,记得也要做些内容修改,ConfigUtil中的一些参数,需要各位自行提供。其它的都可以直接使用,微信支付整体并不难,难点就在于微信签名,很多人调试微信支付,都被这个微信签名给难倒了,这也是微信团队的留下的一天坑。
**最后一句:如果各位在借鉴中,发现缺失什么,可以私信找我要,也可以留言。如果觉得我写的好,就来犒劳我一下吧
相关文章推荐
- Java实现公众号H5 微信支付
- Java微信支付开发之公众号支付(微信内H5调起支付)
- 微信支付-公众号支付(java实现)
- 微信支付-公众号支付(java实现)
- 微信支付-公众号支付(java实现)
- 微信支付-公众号支付(java实现)
- 微信公众号开发《五》基于Java实现微信支付(公众号支付)简单教程
- 【支付宝,微信支付必备】Java实现url参数按照参数名Unicode码从小到大排序(字典序)
- 微信支付java 服务端实现
- Java实现JsApi方式的微信支付
- 微信支付如何实现内置浏览器的H5页面支付
- Java 从零开始实现微信支付(后台)
- java微信支付实现流程
- Java微信公众平台开发(九)——关键字回复以及客服接口实现(该公众号暂时无法提供服务解决方案)
- 微信公众账号开发接口实现 - java servlet
- 微信支付 JAVA实现
- .NET微信支付(H5仅限公众号支付)
- Java微信支付(公众号,扫码)
- 微信支付 JAVA实现
- Java通过JsApi方式实现微信支付