最近客栈订房,当然是需要微信支付的,官方微信支付文档什么的,只想说 去你妈的文档
so, 经过两天不懈的努力和网上大牛文档,终于做出来了,下面具体来说说,希望能对各位看客有点帮助
放大招,直接上代码
1. Configure.java 文件
1 package com.kzb.website.core.wechat.common; 2 3 import java.util.Calendar; 4 import java.util.Date; 5 6 import org.apache.commons.lang3.StringUtils; 7 8 public class Configure { 9 10 private static String token = null; 11 private static Date tokenTime = null; 12 private static String jsapiTicket = null; 13 private static Date jsapiTicketTime = null; 14 15 public static String MD5 = "MD5"; 16 public static String EMPTY = ""; 17 public static String SUCCESS = "SUCCESS"; 18 public static String HEX_FORMAT = "%02x"; 19 public static String TRADE_TYPE = "JSAPI"; 20 public static String MIDDLE_LINE = "-"; 21 public static String CHARTSET_UTF8 = "UTF-8"; 22 23 public static String NOTIFY_SUCCESS = "<xml>\n<return_code><![CDATA[SUCCESS]]></return_code>\n<return_msg><![CDATA[OK]]></return_msg>\n</xml>"; 24 public static String NOTIFY_FAIL = "<xml>\n<return_code><![CDATA[FAIL]]></return_code>\n<return_msg><![CDATA[ERROR]]></return_msg>\n</xml>"; 25 26 private static String key = "API密钥(32位,在微信商户平台下的API安全栏下)"; 27 // 微信分配的公众号ID(开通公众号之后可以获取到) 28 private static String appID = "XXXXXXXXXXXXXX"; 29 private static String appSecret = "XXXXXXXXXXXXXX"; 30 // 微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到) 31 private static String mchID = "1349019501"; 32 // 机器IP 33 private static String ip = "127.0.0.1"; 34 35 // 以下是几个API的路径: 36 // 统一下单 37 public static String UNIFIEDORDER_API = "https://api.mch.weixin.qq.com/pay/unifiedorder"; 38 // access_token API 39 public static String TOKEN_API = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"; 40 // 临时票据API 41 public static String TICKET_API = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"; 42 // 微信OPENID API 43 public static String OPENID_API = "https://api.weixin.qq.com/sns/oauth2/access_token"; 44 45 public static void setKey(String key) { 46 Configure.key = key; 47 } 48 49 public static void setAppID(String appID) { 50 Configure.appID = appID; 51 } 52 53 public static void setMchID(String mchID) { 54 Configure.mchID = mchID; 55 } 56 57 public static void setIp(String ip) { 58 Configure.ip = ip; 59 } 60 61 public static String getKey(){ 62 return key; 63 } 64 65 public static String getAppid(){ 66 return appID; 67 } 68 69 public static String getMchid(){ 70 return mchID; 71 } 72 73 public static String getIP(){ 74 return ip; 75 } 76 77 public static String getAppSecret() { 78 return appSecret; 79 } 80 81 public static void setAppSecret(String appSecret) { 82 Configure.appSecret = appSecret; 83 } 84 85 public static String getToken() { 86 return token; 87 } 88 89 public static void setToken(String token) { 90 Configure.token = token; 91 Configure.tokenTime = new Date(); 92 } 93 94 public static String getJsapiTicket() { 95 return jsapiTicket; 96 } 97 98 public static void setJsapiTicket(String jsapiTicket) { 99 Configure.jsapiTicket = jsapiTicket; 100 Configure.jsapiTicketTime = new Date(); 101 } 102 103 public static boolean checkToken() { 104 if (!StringUtils.isEmpty(Configure.token)) { 105 Calendar calendar = Calendar.getInstance(); 106 calendar.setTime(tokenTime); 107 calendar.add(Calendar.SECOND, 7200); 108 return calendar.before(new Date()); 109 } 110 return true; 111 } 112 113 public static boolean checkJsapiTicket() { 114 if (!StringUtils.isEmpty(Configure.jsapiTicket)) { 115 Calendar calendar = Calendar.getInstance(); 116 calendar.setTime(jsapiTicketTime); 117 calendar.add(Calendar.SECOND, 7200); 118 return calendar.before(new Date()); 119 } 120 return true; 121 } 122 }
2. SignMD5.java文件
1 package com.kzb.website.core.wechat.common; 2 3 import java.io.UnsupportedEncodingException; 4 import java.security.MessageDigest; 5 import java.security.NoSuchAlgorithmException; 6 import java.util.Formatter; 7 import java.util.UUID; 8 9 import org.springframework.security.crypto.password.PasswordEncoder; 10 11 public class SignMD5 implements PasswordEncoder { 12 public static String byteToHex(final byte[] hash) { 13 Formatter formatter = new Formatter(); 14 for (byte b : hash) { 15 formatter.format(Configure.HEX_FORMAT, b); 16 } 17 String result = formatter.toString(); 18 formatter.close(); 19 return result; 20 } 21 22 @Override 23 public String encode(CharSequence charSequence) { 24 try { 25 MessageDigest crypt = MessageDigest.getInstance(Configure.MD5); 26 crypt.reset(); 27 crypt.update(charSequence.toString().getBytes(Configure.CHARTSET_UTF8)); 28 return byteToHex(crypt.digest()); 29 } catch (NoSuchAlgorithmException e) { 30 e.printStackTrace(); 31 } catch (UnsupportedEncodingException e) { 32 e.printStackTrace(); 33 } 34 return Configure.EMPTY; 35 } 36 37 @Override 38 public boolean matches(CharSequence charSequence, String encodedPassword) { 39 return encode(charSequence).equals(encodedPassword); 40 } 41 42 public String createNonceStr() { 43 return UUID.randomUUID().toString().replaceAll(Configure.MIDDLE_LINE,Configure.EMPTY); 44 } 45 46 public String createTimeStamp() { 47 return Long.toString(System.currentTimeMillis() / 1000); 48 } 49 50 }
3. WechatNotify.java文件
1 package com.kzb.website.core.wechat.common; 2 3 import java.io.Serializable; 4 5 import javax.xml.bind.annotation.XmlRootElement; 6 7 @XmlRootElement(name="xml") 8 public class WechatNotify implements Serializable{ 9 10 private static final long serialVersionUID = 8047707600433551023L; 11 private String appid; 12 private String attach; 13 private String bank_type; 14 private String fee_type; 15 private String is_subscribe; 16 private String mch_id; 17 private String nonce_str; 18 private String openid; 19 private String out_trade_no; 20 private String result_code; 21 private String return_code; 22 private String sign; 23 private String sub_mch_id; 24 private String time_end; 25 private String total_fee; 26 private String trade_type; 27 private String transaction_id; 28 29 public String getAppid() { 30 return appid; 31 } 32 33 public void setAppid(String appid) { 34 this.appid = appid; 35 } 36 37 public String getAttach() { 38 return attach; 39 } 40 41 public void setAttach(String attach) { 42 this.attach = attach; 43 } 44 45 public String getBank_type() { 46 return bank_type; 47 } 48 49 public void setBank_type(String bank_type) { 50 this.bank_type = bank_type; 51 } 52 53 public String getFee_type() { 54 return fee_type; 55 } 56 57 public void setFee_type(String fee_type) { 58 this.fee_type = fee_type; 59 } 60 61 public String getIs_subscribe() { 62 return is_subscribe; 63 } 64 65 public void setIs_subscribe(String is_subscribe) { 66 this.is_subscribe = is_subscribe; 67 } 68 69 public String getMch_id() { 70 return mch_id; 71 } 72 73 public void setMch_id(String mch_id) { 74 this.mch_id = mch_id; 75 } 76 77 public String getNonce_str() { 78 return nonce_str; 79 } 80 81 public void setNonce_str(String nonce_str) { 82 this.nonce_str = nonce_str; 83 } 84 85 public String getOpenid() { 86 return openid; 87 } 88 89 public void setOpenid(String openid) { 90 this.openid = openid; 91 } 92 93 public String getOut_trade_no() { 94 return out_trade_no; 95 } 96 97 public void setOut_trade_no(String out_trade_no) { 98 this.out_trade_no = out_trade_no; 99 } 100 101 public String getResult_code() { 102 return result_code; 103 } 104 105 public void setResult_code(String result_code) { 106 this.result_code = result_code; 107 } 108 109 public String getReturn_code() { 110 return return_code; 111 } 112 113 public void setReturn_code(String return_code) { 114 this.return_code = return_code; 115 } 116 117 public String getSign() { 118 return sign; 119 } 120 121 public void setSign(String sign) { 122 this.sign = sign; 123 } 124 125 public String getSub_mch_id() { 126 return sub_mch_id; 127 } 128 129 public void setSub_mch_id(String sub_mch_id) { 130 this.sub_mch_id = sub_mch_id; 131 } 132 133 public String getTime_end() { 134 return time_end; 135 } 136 137 public void setTime_end(String time_end) { 138 this.time_end = time_end; 139 } 140 141 public String getTotal_fee() { 142 return total_fee; 143 } 144 145 public void setTotal_fee(String total_fee) { 146 this.total_fee = total_fee; 147 } 148 149 public String getTrade_type() { 150 return trade_type; 151 } 152 153 public void setTrade_type(String trade_type) { 154 this.trade_type = trade_type; 155 } 156 157 public String getTransaction_id() { 158 return transaction_id; 159 } 160 161 public void setTransaction_id(String transaction_id) { 162 this.transaction_id = transaction_id; 163 } 164 }
4. WechatPayModel.java文件
1 package com.kzb.website.core.wechat.common; 2 3 import javax.xml.bind.annotation.XmlRootElement; 4 5 import org.springframework.security.crypto.password.PasswordEncoder; 6 7 @XmlRootElement(name = "XML") 8 public class WechatPayModel { 9 10 private String appid; 11 private String mch_id; 12 private String nonce_str; 13 private String body; 14 private String out_trade_no; 15 private String total_fee; 16 private String spbill_create_ip; 17 private String notify_url; 18 private String trade_type; 19 private String openid; 20 private String sign; 21 22 public String getAppid() { 23 return appid; 24 } 25 26 public void setAppid(String appid) { 27 this.appid = appid; 28 } 29 30 public String getMch_id() { 31 return mch_id; 32 } 33 34 public void setMch_id(String mch_id) { 35 this.mch_id = mch_id; 36 } 37 38 public String getNonce_str() { 39 return nonce_str; 40 } 41 42 public void setNonce_str(String nonce_str) { 43 this.nonce_str = nonce_str; 44 } 45 46 public String getOut_trade_no() { 47 return out_trade_no; 48 } 49 50 public void setOut_trade_no(String out_trade_no) { 51 this.out_trade_no = out_trade_no; 52 } 53 54 public String getTotal_fee() { 55 return total_fee; 56 } 57 58 public void setTotal_fee(String total_fee) { 59 this.total_fee = total_fee; 60 } 61 62 public String getSpbill_create_ip() { 63 return spbill_create_ip; 64 } 65 66 public void setSpbill_create_ip(String spbill_create_ip) { 67 this.spbill_create_ip = spbill_create_ip; 68 } 69 70 public String getNotify_url() { 71 return notify_url; 72 } 73 74 public void setNotify_url(String notify_url) { 75 this.notify_url = notify_url; 76 } 77 78 public String getTrade_type() { 79 return trade_type; 80 } 81 82 public void setTrade_type(String trade_type) { 83 this.trade_type = trade_type; 84 } 85 86 public String getBody() { 87 return body; 88 } 89 90 public void setBody(String body) { 91 this.body = body; 92 } 93 94 public String getSign() { 95 return sign; 96 } 97 98 public void setSign(String sign) { 99 this.sign = sign; 100 } 101 102 public String getOpenid() { 103 return openid; 104 } 105 106 public void setOpenid(String openid) { 107 this.openid = openid; 108 } 109 110 public void sign(PasswordEncoder encoder) { 111 StringBuilder stringBuilder = new StringBuilder(); 112 stringBuilder.append("appid=").append(getAppid()); 113 stringBuilder.append("&body=").append(getBody()); 114 stringBuilder.append("&mch_id=").append(getMch_id()); 115 stringBuilder.append("&nonce_str=").append(getNonce_str()); 116 stringBuilder.append("¬ify_url=").append(getNotify_url()); 117 stringBuilder.append("&openid=").append(getOpenid()); 118 stringBuilder.append("&out_trade_no=").append(getOut_trade_no()); 119 stringBuilder.append("&spbill_create_ip=").append(getSpbill_create_ip()); 120 stringBuilder.append("&total_fee=").append(getTotal_fee()); 121 stringBuilder.append("&trade_type=").append(getTrade_type()); 122 stringBuilder.append("&key=").append(Configure.getKey()); 123 this.sign = encoder.encode(stringBuilder.toString()); 124 } 125 126 @Override 127 public String toString() { 128 return "<xml>" + 129 "<appid><![CDATA[" + appid + "]]></appid>" + 130 "<body><![CDATA[" + body + "]]></body>" + 131 "<mch_id><![CDATA[" + mch_id + "]]></mch_id>" + 132 "<nonce_str><![CDATA[" + nonce_str + "]]></nonce_str>" + 133 "<notify_url><![CDATA[" + notify_url + "]]></notify_url>" + 134 "<openid><![CDATA[" + openid + "]]></openid>" + 135 "<out_trade_no><![CDATA[" + out_trade_no + "]]></out_trade_no>" + 136 "<spbill_create_ip><![CDATA[" + spbill_create_ip + "]]></spbill_create_ip>" + 137 "<trade_type><![CDATA[" + trade_type + "]]></trade_type>" + 138 "<total_fee><![CDATA[" + total_fee + "]]></total_fee>" + 139 "<sign><![CDATA[" + sign + "]]></sign>" + 140 "</xml>"; 141 } 142 }
5. WechatUtil.java文件
1 package com.kzb.website.core.wechat.common; 2 3 import java.io.DataInputStream; 4 import java.io.IOException; 5 6 import javax.servlet.http.HttpServletRequest; 7 8 import com.kzb.website.core.utils.XMLUtil; 9 10 public class WechatUtil { 11 12 /** 13 * 微信回调成功后 将 xml 转换为 WechatNotify 对象 14 * 15 * @param request 16 * @return WechatNotify 对象 17 */ 18 public static WechatNotify getNotifyBean(HttpServletRequest request){ 19 try { 20 DataInputStream in = new DataInputStream(request.getInputStream()); 21 byte[] dataOrigin = new byte[request.getContentLength()]; 22 // 根据长度,将消息实体的内容读入字节数组dataOrigin中 23 in.readFully(dataOrigin); 24 // 关闭数据流 25 in.close(); 26 // 从字节数组中得到表示实体的字符串 27 String xml = new String(dataOrigin); 28 // 将 xml 转换为 WechatNotify 对象 29 Object object = XMLUtil.xmlToBean(WechatNotify.class, xml); 30 if (object != null && object instanceof WechatNotify) { 31 WechatNotify notify = (WechatNotify) object; 32 return notify; 33 } else {return null;} 34 } catch (IOException e) { 35 e.printStackTrace(); 36 return null; 37 } 38 } 39 }
6. XMLUtil.java文件
1 package com.kzb.website.core.utils; 2 3 import java.io.StringReader; 4 5 import javax.xml.bind.JAXBContext; 6 import javax.xml.bind.JAXBException; 7 import javax.xml.bind.Unmarshaller; 8 9 public class XMLUtil { 10 11 /** 12 * 将 XML 字符串转换为 Java 对象 13 * 14 * @param clazz 要转换对象的 class 15 * @param xml 待转换的 xml 16 * @return 转换后的对象 17 */ 18 public static Object xmlToBean(Class<?> clazz, String xml) { 19 try { 20 JAXBContext jc = JAXBContext.newInstance(clazz); 21 Unmarshaller us = jc.createUnmarshaller(); 22 return us.unmarshal(new StringReader(xml)); 23 } catch (JAXBException e) { 24 e.printStackTrace(); 25 return null; 26 } 27 } 28 }
7. pay_choose.jsp 页面,这个页面是一个支付选择画面,用来获取code,和回调到支付画面
1 <%@ page language="java" contentType="text/html; charset=UTF-8"%> 2 <!DOCTYPE html> 3 <html> 4 <head> 5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> 7 <title>支付选择画面</title> 8 <meta name="keywords" content="关键字,关键字"> 9 <meta name="description" content=""> 10 <style type="text/css"> 11 *{margin: 0;padding: 0;} 12 body{font-size:12px;font-family:"微软雅黑";color:#666;} 13 </style> 14 </head> 15 <body> 16 <div style="margin:40px auto;width:400px;"> 17 <a href="javascript:wechat();">微信支付</a><br/><br/><br/> 18 <a href="javascript:ali();">支付宝支付</a> 19 </div> 20 <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> 21 <script type="text/javascript"> 22 function wechat(){ 23 var redirect_uri = "http://www.xxxxx.com/wxpay/payment.htm?hotelId=14501"; 24 var href = "https://open.weixin.qq.com/connect/oauth2/authorize?redirect_uri=" + encodeURIComponent(redirect_uri); 25 href += "&appid=wx73997299f6dba700&response_type=code&scope=snsapi_base"; 26 window.location.href = href; 27 } 28 function ali(){ 29 30 } 31 </script> 32 </body> 33 </html>
8. wechat_pay.jsp 页面
1 <%@ page language="java" contentType="text/html; charset=UTF-8"%> 2 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 <!DOCTYPE html> 4 <html> 5 <head> 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> 8 <title>微信支付</title> 9 <meta name="keywords" content="关键字,关键字"> 10 <meta name="description" content=""> 11 <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> 12 <script type="text/javascript" charset="UTF-8" src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> 13 <script type="text/javascript"> 14 function pay(){ 15 //config 16 var appId = $(‘#appId‘).val(); 17 var timeStamp = $(‘#timeStamp‘).val(); 18 var nonceStr = $(‘#nonceStr‘).val(); 19 var signature = $(‘#signature‘).val(); 20 21 var signType = $(‘#signType‘).val(); 22 var pk = $(‘#package‘).val(); 23 var paySign = $(‘#paySign‘).val(); 24 25 wx.config({ 26 debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 27 appId: appId, // 必填,公众号的唯一标识 28 timestamp: timeStamp , // 必填,生成签名的时间戳 29 nonceStr: nonceStr, // 必填,生成签名的随机串 30 signature: signature,// 必填,签名,见附录1 31 jsApiList: [‘chooseWXPay‘] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 32 }); 33 34 wx.ready(function(res){ 35 // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。 36 wx.chooseWXPay({ 37 timestamp: timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 38 nonceStr: nonceStr, // 支付签名随机串,不长于 32 位 39 package: pk, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***) 40 signType: signType, // 签名方式,默认为‘SHA1‘,使用新版支付需传入‘MD5‘ 41 paySign: paySign, // 支付签名 42 success: function (res) { 43 location.href = ‘http://www.xxxxxxx.net/success.htm‘; 44 }, 45 cancel: function (res) { 46 location.href = ‘http://www.xxxxxxx.net/success.htm‘; 47 }, 48 fail: function (res) { 49 location.href = ‘http://www.xxxxxxx.net/success.htm‘; 50 } 51 }); 52 }); 53 54 wx.error(function (res) { 55 alert("error:" + JSON.stringify(res)); 56 }); 57 } 58 </script> 59 </head> 60 <body onload="pay()"> 61 <h1>正在进行微信支付.... 62 <br/> 63 <small>请不要关闭</small> 64 </h1> 65 <input id="appId" type="hidden" value="${start.appId}" /><br/> 66 <input id="timeStamp" type="hidden" value="${start.timestamp}" /><br/> 67 <input id="nonceStr" type="hidden" value="${start.nonceStr}" /><br/> 68 <input id="signature" type="hidden" value="${start.signature}" /><br/> 69 70 <input id="signType" type="hidden" value="${pay.signType}" /><br/> 71 <input id="package" type="hidden" value="${pay.packageStr}" /><br/> 72 <input id="paySign" type="hidden" value="${pay.paySign}" /><br/> 73 </body> 74 </html>
9. WechatPayService.java 页面
1 package com.kzb.website.web.wechat; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import org.apache.commons.lang3.StringUtils; 7 import org.springframework.stereotype.Service; 8 import org.springframework.ui.Model; 9 import org.springframework.web.client.RestTemplate; 10 11 import com.alibaba.fastjson.JSONArray; 12 import com.fasterxml.jackson.databind.JsonNode; 13 import com.fasterxml.jackson.dataformat.xml.XmlMapper; 14 import com.kzb.website.core.wechat.common.Configure; 15 import com.kzb.website.core.wechat.common.SignMD5; 16 import com.kzb.website.core.wechat.common.WechatPayModel; 17 18 @Service 19 public class WechatPayService { 20 SignMD5 encoder = new SignMD5(); 21 RestTemplate restTemplate = new RestTemplate(); 22 23 /** 24 * 微信支付统一下单接口 25 * 26 * @param notifyUrl 支付成功后回调路径 27 * @param openId 用户的 openId 28 * @param body 商品描述 29 * @param total 支付金额(单位分) 30 * @param out_trade_no 订单唯一订单号 31 * @return 32 */ 33 public String unifiedorder(String notifyUrl, String openId, String body, String total, String out_trade_no) { 34 WechatPayModel xml = new WechatPayModel(); 35 xml.setAppid(Configure.getAppid()); 36 xml.setMch_id(Configure.getMchid()); 37 xml.setNonce_str(encoder.createNonceStr()); 38 xml.setBody(body); 39 xml.setOut_trade_no(out_trade_no); 40 xml.setTotal_fee(total); 41 xml.setSpbill_create_ip(Configure.getIP()); 42 xml.setNotify_url(notifyUrl); 43 xml.setTrade_type(Configure.TRADE_TYPE); 44 xml.setOpenid(openId); 45 xml.sign(encoder); 46 return restTemplate.postForObject(Configure.UNIFIEDORDER_API, xml, String.class); 47 } 48 49 /** 50 * 调起微信支付 51 * 52 * @param model 53 * @param res 预支付订单 字符串 54 * @param url 微信支付 url 55 */ 56 public void wechatPay(Model model, String res,String url) { 57 try { 58 Map<String, String> start = new HashMap<>(); 59 StringBuilder startSign = new StringBuilder(); 60 61 Map<String, String> pay = new HashMap<>(); 62 StringBuilder paySign = new StringBuilder(); 63 XmlMapper xmlMapper = new XmlMapper(); 64 JsonNode node = xmlMapper.readTree(res); 65 if (StringUtils.equals(node.get("return_code").asText(), Configure.SUCCESS)) { 66 // 得到的预支付订单,重新生成微信支付参数 67 String prepay_id = node.get("prepay_id").asText(); 68 String jsapi_ticket = jsapiTicket(); 69 // 生成 微信支付 config 参数 70 start.put("appId", Configure.getAppid()); 71 start.put("nonceStr", encoder.createNonceStr()); 72 start.put("timestamp", encoder.createTimeStamp()); 73 // 生成 config 签名 74 startSign.append("jsapi_ticket=").append(jsapi_ticket); 75 startSign.append("&noncestr=").append(start.get("nonceStr")); 76 startSign.append("×tamp=").append(start.get("timestamp")); 77 startSign.append("&url=").append(url); 78 start.put("signature", encoder.encode(startSign.toString())); 79 80 // config信息验证后会执行ready方法的参数 81 pay.put("signType", Configure.MD5); 82 pay.put("packageStr", "prepay_id=" + prepay_id); 83 // 生成支付签名 84 paySign.append("appId=").append(start.get("appId")); 85 paySign.append("&nonceStr=").append(start.get("nonceStr")); 86 paySign.append("&package=").append(pay.get("packageStr")); 87 paySign.append("&signType=").append(pay.get("signType")); 88 paySign.append("&timeStamp=").append(start.get("timestamp")); 89 paySign.append("&key=").append(Configure.getKey()); 90 pay.put("paySign", encoder.encode(paySign.toString())); 91 // 将微信支参数放入 model 对象中以便前端使用 92 model.addAttribute("start", start); 93 model.addAttribute("pay", pay); 94 } 95 } catch (Exception e) { 96 model.addAttribute("wechatMessage", "微信授权失败!"); 97 e.printStackTrace(); 98 } 99 } 100 101 /** 102 * 微信授权,获取 access_token 103 * 104 * @return access_token 105 */ 106 public String getToken() { 107 if (Configure.checkToken()) { 108 // 声明 获取 access_token 路径 109 StringBuilder tokenBuilder = new StringBuilder(); 110 tokenBuilder.append(Configure.TOKEN_API); 111 tokenBuilder.append("&appid=").append(Configure.getAppid()); 112 tokenBuilder.append("&secret=").append(Configure.getAppSecret()); 113 // 获取 token 114 Map<?, ?> token = restTemplate.getForObject(tokenBuilder.toString(), Map.class); 115 Configure.setToken((String) token.get("access_token")); 116 } 117 return Configure.getToken(); 118 } 119 120 /** 121 * 获取微信 JSAPI 支付的临时票据 122 * 123 * @return 临时票据 124 */ 125 public String jsapiTicket() { 126 if (Configure.checkJsapiTicket()) { 127 // 声明 获取临时票据路径 128 StringBuilder ticketBuilder = new StringBuilder(); 129 ticketBuilder.append(Configure.TICKET_API); 130 ticketBuilder.append("?access_token=").append(getToken()); 131 ticketBuilder.append("&type=jsapi"); 132 // 获取 临时票据 133 Map<?, ?> jsapiTicket = restTemplate.getForObject(ticketBuilder.toString(), Map.class); 134 Configure.setJsapiTicket((String) jsapiTicket.get("ticket")); 135 } 136 return Configure.getJsapiTicket(); 137 } 138 139 /** 140 * 获取用的 OPENID 141 * 142 * @param code 微信认证回调的 code 143 * @return 144 */ 145 public String takeOpenId(String code) { 146 // 声明 获取OPENID路径 147 StringBuilder openidBuilder = new StringBuilder(); 148 openidBuilder.append(Configure.OPENID_API); 149 openidBuilder.append("?appid=").append(Configure.getAppid()); 150 openidBuilder.append("&secret=").append(Configure.getAppSecret()); 151 openidBuilder.append("&code=").append(code); 152 openidBuilder.append("&grant_type=authorization_code"); 153 // 获取 OPENID 154 String res = restTemplate.getForObject(openidBuilder.toString(), String.class); 155 Map<?, ?> map = JSONArray.parseObject(res, Map.class); 156 return String.valueOf(map.get("openid")); 157 } 158 }
10. WechatPayController.java 页面
1 package com.kzb.website.web.wechat; 2 3 import java.util.Date; 4 5 import javax.servlet.http.HttpServletRequest; 6 7 import org.apache.commons.lang3.StringUtils; 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.stereotype.Controller; 10 import org.springframework.ui.Model; 11 import org.springframework.web.bind.annotation.RequestMapping; 12 import org.springframework.web.bind.annotation.RequestMethod; 13 import org.springframework.web.bind.annotation.RequestParam; 14 import org.springframework.web.bind.annotation.ResponseBody; 15 16 import com.kzb.website.core.wechat.common.Configure; 17 import com.kzb.website.core.wechat.common.WechatNotify; 18 import com.kzb.website.core.wechat.common.WechatUtil; 19 20 @RequestMapping("wxpay") 21 @Controller 22 public class WechatPayController { 23 24 @Autowired 25 private PayService payService; 26 27 @RequestMapping("payment.htm") 28 public String payment(@RequestParam(required = false) String code, Model model, HttpServletRequest request){ 29 String openId = payService.takeOpenId(code); 30 if (StringUtils.isEmpty(openId)) { 31 return "pay/wxpay_error"; 32 } 33 String notify = "http://www.xxxxxx.net/wxpay/notify.htm"; 34 String body = "商品支付内容"; 35 String total = "1"; // 单位分 36 String out_trade_no = new Date().getTime() + ""; 37 38 String res = payService.unifiedorder(notify, openId, body, total, out_trade_no); 39 40 String url = "http://www.xxxxxx.net/wxpay/payment.htm"; 41 payService.wechatPay(model,res,url); 42 return "pay/wxpay"; 43 } 44 45 /** 46 * 微信支付成功后的回调函数 47 * 48 * @param request 49 * @return 50 */ 51 @RequestMapping(value="notify.htm",method=RequestMethod.POST) 52 @ResponseBody 53 public String wechatNotify(HttpServletRequest request){ 54 // 从 request 对象中获取 WechatNotify 对象 55 WechatNotify notify = WechatUtil.getNotifyBean(request); 56 // 如果 notify 对象不为空 并且 result_code 和 return_code 都为 ‘SUCCESS‘ 则表示支付成功 57 if (notify != null 58 && StringUtils.equals(notify.getResult_code(), Configure.SUCCESS) 59 && StringUtils.equals(notify.getReturn_code(), Configure.SUCCESS)) { 60 61 return Configure.NOTIFY_SUCCESS; 62 } 63 return Configure.NOTIFY_FAIL; 64 } 65 }
现在就只需要放入服务器测试啦,赶快行动起来,试试吧
下面还有几篇来说说微信支付中遇到的那些 操蛋 的问题吧