<%
String basePath = request.getScheme() + "://"+ request.getServerName() + request.getContextPath()+ "/";
%>
<html>
<body>
....
<div class="zdx3"><button onclick="pay()">共需支付${sumPrice }元 确认支付button>div>
body>
html>
<script>
function pay() {
var url="<%=basePath%>wechat/pay?money=${sumPrice}"; //注意此处的basePath是没有端口号的域名地址。如果包含:80,在提交给微信时有可能会提示 “redirect_uri参数错误” 。
//money为订单需要支付的金额
//state中存放的为商品订单号
var weixinUrl="https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri="+encodeURI(url)+"&response_type=code&scope=snsapi_userinfo&state=${orderId}#wechat_redirect";
window.location.href=encodeURI(weixinUrl);
}
script>
二.后台处理支付功能代码
(包含两部分:
1.处理支付信息,通过微信接口生成订单号,返回支付页面
2.提供一个微信支付完成后的回调接口)
第1部分代码:
/**
* 用户提交支付,获取微信支付订单接口
*/
@RequestMapping(value="/pay")
public ModelAndView pay(HttpServletRequest request,HttpServletResponse response){
ModelAndView mv = new ModelAndView();
String GZHID = "wxfd7c065eee11112222";// 微信公众号id
String GZHSecret = "b5b3a627f5d1f8888888888888";// 微信公众号密钥id
String SHHID = "111111111";// 财付通商户号
String SHHKEY = "mmmmmmmmmmmmmmm";// 商户号对应的密钥
/*------1.获取参数信息------- */
//商户订单号
String out_trade_no= request.getParameter("state");
//价格
String money = request.getParameter("money");
//金额转化为分为单位
String finalmoney = WeChat.getMoney(money);
//获取用户的code
String code = request.getParameter("code");
/*------2.根据code获取微信用户的openId和access_token------- */
//注: 如果后台程序之前已经得到了用户的openId 可以不需要这一步,直接从存放openId的位置或session中获取就可以。
//toPay.jsp页面中提交的url路径也就不需要再经过微信重定向。写成:http://localhost:8080/项目名/wechat/pay?money=${sumPrice}&state=${orderId}
String openid=null;
try {
List<Object> list = accessToken(code);
openid=list.get(1).toString();
} catch (IOException e) {
logger.error("根据code获取微信用户的openId出现错误", e);
mv.setViewName("error");
return mv;
}
/*------3.生成预支付订单需要的的package数据------- */
//随机数
String nonce_str= MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
//订单生成的机器 IP
String spbill_create_ip = request.getRemoteAddr();
//交易类型 :jsapi代表微信公众号支付
String trade_type = "JSAPI";
//这里notify_url是 微信处理完支付后的回调的应用系统接口url。
String notify_url ="http://69a6a38e.ngrok.natapp.cn/heyi-console/wechat/weixinNotify";
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", GZHID);
packageParams.put("mch_id", SHHID);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", "费用");
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", finalmoney);
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
packageParams.put("openid", openid);
/*------4.根据package数据生成预支付订单号的签名sign------- */
RequestHandler reqHandler = new RequestHandler(request, response);
reqHandler.init( GZHID, GZHSecret, SHHKEY);
String sign = reqHandler.createSign(packageParams);
/*------5.生成需要提交给统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 的xml数据-------*/
String xml="" +
"" + GZHID+""+
"" + SHHID+""+
"" +nonce_str+""+
"" +sign+""+
"+"费用"+"]]>"+
"" +out_trade_no+""+
"" +finalmoney+""+
"" +spbill_create_ip+""+
"" +notify_url+""+
"" +trade_type+""+
"" +openid+""+
"";
/*------6.调用统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 生产预支付订单----------*/
String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String prepay_id="";
try {
prepay_id = GetWxOrderno.getPayNo(createOrderURL, xml);
if(prepay_id.equals("")){
mv.addObject("ErrorMsg", "支付错误");
mv.setViewName("error");
return mv;
}
} catch (Exception e) {
logger.error("统一支付接口获取预支付订单出错", e);
mv.setViewName("error");
return mv;
}
/*将prepay_id存到库中*/
PageData p = new PageData();
p.put("shopId", out_trade_no);
p.put("prePayId", prepay_id);
activityService.updatePrePayId(p);
/*------7.将预支付订单的id和其他信息生成签名并一起返回到jsp页面 ------- */
nonce_str= MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String packages = "prepay_id="+prepay_id;
finalpackage.put("appId", GZHID);
finalpackage.put("timeStamp", timestamp);
finalpackage.put("nonceStr", nonce_str);
finalpackage.put("package", packages);
finalpackage.put("signType", "MD5");
String finalsign = reqHandler.createSign(finalpackage);
mv.addObject("appid", GZHID);
mv.addObject("timeStamp", timestamp);
mv.addObject("nonceStr", nonce_str);
mv.addObject("packageValue", packages);
mv.addObject("paySign", finalsign);
mv.addObject("success","ok");
mv.setViewName("wechat/pay");
return mv;
}
第2部分代码:
/**
* 提交支付后的微信异步返回接口
*/
@RequestMapping(value="/weixinNotify")
public void weixinNotify(HttpServletRequest request, HttpServletResponse response){
String out_trade_no=null;
String return_code =null;
try {
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 resultStr = new String(outSteam.toByteArray(),"utf-8");
logger.info("支付成功的回调:"+resultStr);
Map<String, Object> resultMap = parseXmlToList(resultStr);
String result_code = (String) resultMap.get("result_code");
String is_subscribe = (String) resultMap.get("is_subscribe");
String transaction_id = (String) resultMap.get("transaction_id");
String sign = (String) resultMap.get("sign");
String time_end = (String) resultMap.get("time_end");
String bank_type = (String) resultMap.get("bank_type");
out_trade_no = (String) resultMap.get("out_trade_no");
return_code = (String) resultMap.get("return_code");
request.setAttribute("out_trade_no", out_trade_no);
//通知微信.异步确认成功.必写.不然微信会一直通知后台.八次之后就认为交易失败了.
response.getWriter().write(RequestHandler.setXML("SUCCESS", ""));
} catch (Exception e) {
logger.error("微信回调接口出现错误:",e);
try {
response.getWriter().write(RequestHandler.setXML("FAIL", "error"));
} catch (IOException e1) {
e1.printStackTrace();
}
}
if(return_code.equals("SUCCESS")){
//支付成功的业务逻辑
}else{
//支付失败的业务逻辑
}
}
三.微信app中具体支付的jsp页面
<html>
<head>
<script src="js/jquery-1.8.2.min.js" type="text/javascript">script>
head>
<body onload="javascript:pay();">
<script type="text/javascript">
function pay(){
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
}
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId" : "${appid}", //公众号名称,由商户传入
"timeStamp": "${timeStamp}", //时间戳,自1970年以来的秒数
"nonceStr" : "${nonceStr}", //随机串
"package" : "${packageValue}",
"signType" : "MD5", //微信签名方式:
"paySign" : "${paySign}" //微信签名
},function(res){
if(res.err_msg == "get_brand_wcpay_request:ok"){
alert("微信支付成功!");
}else if(res.err_msg == "get_brand_wcpay_request:cancel"){
alert("用户取消支付!");
}else{
alert("支付失败!");
}
});
}
script>
body>
html>
其他需要用到的相关类和方法:
金额 元转分:
/**
* 元转换成分
* @param money
* @return
*/
public static String getMoney(String amount) {
if(amount==null){
return "";
}
// 金额转化为分为单位
String currency = amount.replaceAll("\\$|\\¥|\\,", ""); //处理包含, ¥ 或者$的金额
int index = currency.indexOf(".");
int length = currency.length();
Long amLong = 0l;
if(index == -1){
amLong = Long.valueOf(currency+"00");
}else if(length - index >= 3){
amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));
}else if(length - index == 2){
amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);
}else{
amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");
}
return amLong.toString();
}
通过微信用户code获取用户的openId:
/**
* 通过微信用户的code换取网页授权access_token
* @return
* @throws IOException
* @throws
*/
public List
MD5提取摘要:
/**
* MD5加密
*/
public class MD5 {
private MD5() {}
/**
* 对传入的数据提取摘要
* @param buffer
* @return
*/
public final static String getMessageDigest(byte[] buffer) {
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(buffer);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
}
解析微信回调的xml数据:
/**
* description: 解析微信通知xml
*
* @param xml
* @return
* @author ex_yangxiaoyi
* @see
*/
@SuppressWarnings({ "unused", "rawtypes", "unchecked" })
private static Map parseXmlToList(String xml) {
Map retMap = new HashMap();
try {
StringReader read = new StringReader(xml);
// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
InputSource source = new InputSource(read);
// 创建一个新的SAXBuilder
SAXBuilder sb = new org.jdom.input.SAXBuilder.SAXBuilder();
// 通过输入源构造一个Document
Document doc = (Document) sb.build(source);
Element root = doc.getRootElement();// 指向根节点
List es = root.getChildren();
if (es != null && es.size() != 0) {
for (Element element : es) {
retMap.put(element.getName(), element.getValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return retMap;
}