前言:最近要完成一個關於微信h5支付的需求,因此就花了一段時間來研究微信關於微信關於h5方面的相關文檔,官方提供的文檔內容至關複雜,並且相對內容比較分散。並且在整個支付流程中的問題也是情況百出,不少東西都沒有給出詳細的說明,我是在查詢了大量的博客纔將這個流程走通。一句話就是,這裏的坑很是多,下面我將詳細的介紹相應的流程,以及應該避免那些坑,本人也是初學者望各位大神多提意見。php
一:token服務器驗證(僅支持80端口):html
咱們須要在微信公衆號的的管理界面設置一個路徑和token值,微信服務器會訪問該路徑而且傳遞一個CheckModel對象過來,咱們要作的是將該對象裏面的echostr返回過去,微信僅支持訪問80端口,因此若是使用tomcat的容器須要去修改server.xml文件(詳情百度tomcat默認80端口訪問server.xml配置)。java
完成服務器token驗證代碼:(WeixinPayConstants類中有微信服務號相關的信息,在最後我會將相關代碼給出來)web
@Controller @RequestMapping("/token") public class TokenController { @Autowired private TokenService tokenService; /** * 開發者模式token校驗 * @param tokenModel * @throws ParseException * @throws IOException */ @RequestMapping(value = "/check", method = RequestMethod.GET, produces = "text/plain") public @ResponseBody String validate(CheckModel tokenModel, HttpServletRequest httpServletRequest) throws ParseException, IOException { String wxToken = WeixinPayConstants.token; tokenModel.setSignature(httpServletRequest.getParameter("signature")); tokenModel.setTimestamp(Long.valueOf(httpServletRequest.getParameter("timestamp"))); tokenModel.setNonce(Long.valueOf(httpServletRequest.getParameter("nonce"))); tokenModel.setEchostr(httpServletRequest.getParameter("echostr")); tokenModel.setEchostr(httpServletRequest.getParameter("echostr")); return tokenService.validate(wxToken, tokenModel); }
Validate方法:ajax
@Service public class TokenService { /** * 微信開發者驗證 * @param wxToken * @param tokenModel * @return */ @Transactional public String validate(String wxToken, CheckModel tokenModel){ String signature = tokenModel.getSignature(); Long timestamp = tokenModel.getTimestamp(); Long nonce =tokenModel.getNonce(); String echostr = tokenModel.getEchostr(); if(signature!=null&×tamp!=null&nonce!=null) { String[] str = {wxToken, timestamp+"", nonce+""}; Arrays.sort(str); // 字典序排序 String bigStr = str[0] + str[1] + str[2]; // SHA1加密 String digest = EncoderHandler.encode("SHA1", bigStr).toLowerCase(); // 確認請求來至微信 if (digest.equals(signature)) { //最好此處將echostr存起來,之後每次校驗消息來源都須要用到 return echostr; } } return "error"; }
CheckModel實體:算法
public class CheckModel extends ErrorCodeModel{ String signature; Long timestamp; Long nonce; String echostr; public String getSignature() { return signature; } public void setSignature(String signature) { this.signature = signature; } public Long getTimestamp() { return timestamp; } public void setTimestamp(Long timestamp) { this.timestamp = timestamp; } public Long getNonce() { return nonce; } public void setNonce(Long nonce) { this.nonce = nonce; } public String getEchostr() { return echostr; } public void setEchostr(String echostr) { this.echostr = echostr; } }
EncoderHandler類:數據庫
public class EncoderHandler { private static final String ALGORITHM = "MD5"; private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * encode string * * @param algorithm * @param str * @return String */ public static String encode(String algorithm, String str) { if (str == null) { return null; } try { MessageDigest messageDigest = MessageDigest.getInstance(algorithm); messageDigest.update(str.getBytes()); return getFormattedText(messageDigest.digest()); } catch (Exception e) { throw new RuntimeException(e); } } /** * encode By MD5 * * @param str * @return String */ public static String encodeByMD5(String str) { if (str == null) { return null; } try { MessageDigest messageDigest = MessageDigest.getInstance(ALGORITHM); messageDigest.update(str.getBytes()); return getFormattedText(messageDigest.digest()); } catch (Exception e) { throw new RuntimeException(e); } } /** * Takes the raw bytes from the digest and formats them correct. * * @param bytes * the raw bytes from the digest. * @return the formatted bytes. */ private static String getFormattedText(byte[] bytes) { int len = bytes.length; StringBuilder buf = new StringBuilder(len * 2); // 把密文轉換成十六進制的字符串形式 for (int j = 0; j < len; j++) { buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); buf.append(HEX_DIGITS[bytes[j] & 0x0f]); } return buf.toString(); }
當你寫好這個功能的時候,在微信公衆號後臺管理:開發——》基本配置——》輸入token驗證地址,點擊提交就會有相應的提示。json
2、微信網頁調起js sdk網頁支付流程:後端
對頁面受權—》發起支付—》回調—》根據相應返回參數判斷支付結果進行頁面跳轉api
1. 咱們在任何一個頁面要調起微信的支付接口都要先對這個頁面進行受權,其次就是要在服務號的管理平臺上對支付路徑進行註冊以下圖。(在相應的頁面須要調用微信的js文件 )<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
完對受權路徑的添加以後咱們就能夠進入代碼的編寫了:
1.1 在進入要調起支付頁面的時候,頁面經過ajax請求後端接口,而且獲得相關信息:
後端方法實現:
/** * 獲得獲取微信權限所需的相關參數 * * @param request * @return */ //返回js權限相關的信息給前臺 @ResponseBody @RequestMapping(value = "/getWeiXinConfig", method = RequestMethod.POST) public String getWeiXinConfig(HttpServletRequest request) { // 處理JsAPI_ticket 問題(存儲數據庫) String jsapi_ticket = getJsapiTicket(); logger.info("獲取到的信息:------------:" + JSON.toJSON(jsapi_ticket)); String url = request.getParameter("url"); long timastamp = WeiXinDateUtil.getNowTimeStamp(); WeiXinConfig weiXinConfig = new WeiXinConfig(); weiXinConfig.setApp_id(WeixinPayConstants.appid); weiXinConfig.setTimestamp(String.valueOf(timastamp)); weiXinConfig.setNonceStr(RandomUtil.generateString(20)); //設置config模塊的簽名 SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("jsapi_ticket", jsapi_ticket); parameters.put("timestamp", timastamp); parameters.put("noncestr", RandomUtil.generateString(20)); parameters.put("url", url); String sign = WeiXinCrateSign.createSign("utf-8", parameters); weiXinConfig.setSignature(sign); String jsonString = JSON.toJSONString(weiXinConfig); return jsonString; }
獲取jsapi_ticket:
/** * 從數據庫中查詢出當前的JsAPI_ticket或者從網絡中獲取 * * @return */ private String getJsapiTicket() { WeiXinJurisdictionConfig weiXinJurisdictionConfig = weiXinJurisdictionConfigService.selectByPrimaryKey(1); logger.info("數據庫獲取到的微信JsAPITicket信息:------------:" + JSON.toJSON(weiXinJurisdictionConfig)); if (weiXinJurisdictionConfig.getJsapiTicket() == "0") { String jsapi = GetJsAPITicket.getJsAPITicket(); logger.info("微信服務器獲取到的微信JsAPITicket信息:------------:" + jsapi); weiXinJurisdictionConfig.setJsapiTicket(jsapi); logger.info("修改了JsAPITicket以後的實體:------------:" + weiXinJurisdictionConfig); weiXinJurisdictionConfigService.updateByPrimaryKeySelective(weiXinJurisdictionConfig); return jsapi; } else { return weiXinJurisdictionConfig.getJsapiTicket(); } }
WeiXinJurisdictionConfig類:
public class WeiXinJurisdictionConfig { private Integer id; private String jsapiTicket; private String accessToken; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getJsapiTicket() { return jsapiTicket; } public void setJsapiTicket(String jsapiTicket) { this.jsapiTicket = jsapiTicket == null ? null : jsapiTicket.trim(); } public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken == null ? null : accessToken.trim(); } }
從網絡獲取jsapi_ticket:
public class GetJsAPITicket { /** * 獲取JsAPIticket * @return */ public static String getJsAPITicket(){ String access_token = GetAccessToken.getAccess_token(); String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ access_token +"&type=jsapi"; String jsAPITicketString = GetAccessToken.getJsonString(url); JsAPITicket jsAPITicket = JSON.parseObject(jsAPITicketString,JsAPITicket.class); System.out.println("jsapitiket: "+jsAPITicket.getTicket()); return jsAPITicket.getTicket(); } }
JsAPITicket類:
public class JsAPITicket { private String ticket; private int expiresIn; public String getTicket() { return ticket; } public void setTicket(String ticket) { this.ticket = ticket; } public int getExpiresIn() { return expiresIn; } public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; } }
根據以上代碼可得要想獲取jsapi_ticket首先要獲取AccessToken:
/** * Created by Administrator on 2016/8/19. */ public class GetAccessToken { /** * 獲取Accesstoken * @return access_token */ public static String getAccess_token() { AccessToken accessToken = null; String jsonString = null; String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + WeixinPayConstants.appid+ "&secret=" + WeixinPayConstants.appsecret; jsonString = getJsonString(url); accessToken = JSON.parseObject(jsonString,AccessToken.class); System.out.println("access_token:---------------- "+accessToken.getAccess_token()); return accessToken.getAccess_token(); } /** * 獲取json字符串經過get方式 * @param url 地址 * @return */ public static String getJsonString(String url){ String message = null; try { URL urlGet = new URL(url); HttpURLConnection http = (HttpURLConnection) urlGet .openConnection(); http.setRequestMethod("GET"); // 必須是get方式請求 http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); http.setDoOutput(true); http.setDoInput(true); System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 鏈接超時30秒 System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 讀取超時30秒 http.connect(); InputStream is = http.getInputStream(); int size = is.available(); byte[] jsonBytes = new byte[size]; is.read(jsonBytes); message = new String(jsonBytes, "UTF-8"); is.close(); } catch (Exception e) { e.printStackTrace(); } return message; } }
AccessToken類:
/** * Created by Administrator on 2016/8/19. */ public class AccessToken { private String access_token; private String expires_in; public String getAccess_token() { return access_token; } public void setAccess_token(String access_token) { this.access_token = access_token; } public String getExpires_in() { return expires_in; } public void setExpires_in(String expires_in) { this.expires_in = expires_in; } }
當以上步驟都走完的時候這是前臺的ajax 的success中就會,調起wx.config()(注意裏面的參數的名的大小寫,以及有那些參數,還有須要調起的接口須要在jsApiList中列出來,詳情參考下面的代碼或微信官方文檔);對該頁面進行受權,當受權成功的時候就會執行wx.ready();
2. // 注意:全部的JS接口只能在公衆號綁定的域名下調用,公衆號開發者須要先登陸微信公衆平臺進入「公衆號設置」的「功能設置」裏填寫「JS接口安全域名」。 // 若是發如今 Android 不能分享自定義內容,請到官網下載最新的包覆蓋安裝,Android 自定義分享接口需升級至 6.0.2.58 版本及以上。 // 完整 JS-SDK 文檔地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html $(function () { $.ajax({ url: path+"/weiXin/weiXinPay/getWeiXinConfig", type: "POST", data: {url: window.location.href}, success: function (data) { var weixinConfig = JSON.parse(data); wx.config({ appId: weixinConfig.app_id, timestamp: weixinConfig.timestamp, nonceStr: weixinConfig.nonceStr, signature: weixinConfig.signature, jsApiList: [// 全部要調用的 API 都要加到這個列表中 "chooseWXPay" ] }); wx.ready(function () { $.ajax({ url: path+"/weiXin/weiXinPay/getOrder", type: "POST", data: {}, success: function (data) { if (data != null) { var weixinPay = JSON.parse(data); wx.chooseWXPay({ timestamp: weixinPay.timeStamp, // 支付簽名時間戳,注意微信jssdk中的全部使用timestamp字段均爲小寫。但最新版的支付後臺生成簽名使用的timeStamp字段名需大寫其中的S字符 nonceStr: weixinPay.nonceStr, // 支付簽名隨機串,不長於 32 位 package: weixinPay.package, // 統一支付接口返回的prepay_id參數值,提交格式如:prepay_id=***) signType: weixinPay.signType, // 簽名方式,默認爲'SHA1',使用新版支付需傳入'MD5' paySign: weixinPay.paySign, // 支付簽名 success: function (res) { // 支付成功後的回調函數 根據相應的返回參數判斷支付結果,而且進行相應的頁面跳轉 }, error: function (res) { alert(res + "失敗!") } }); }else{ alert("出錯了!"); } }, error: function (data) { alert(data + "REEOR") } }) }) }, error: function (data) { alert(data + "REEOR") } }) })
當執行wx.ready() 時整個流程是:生成一個訂單—》調用微信的獲取點單id的接口—》獲取到訂單id,而後將一系列的相關簽名和其餘參數傳遞到頁面—》調起微信的相關控件,輸入密碼完成支付。
2.1 根據上面的ajax請求訪問後端獲取訂單信息的相關接口:
後端處理代碼:
/** * 獲取微信支付所需的相關參數 * * @param httpServletRequest * @param session * @return * @throws Exception */ @ResponseBody @RequestMapping(value = "/getOrder", method = RequestMethod.POST) public String getOrderInfo(HttpServletRequest httpServletRequest, HttpSession session) throws Exception { String orderXml;//微信下單xml String orderInfo;//訂單的簡介 String nonce_str;//32位之內隨機字符串 String out_trade_no;//商戶訂單號 int total_fee; String jsonString = null; getWeiXinOpenIdReturn(session);//獲取用戶的openid存放到session中 logger.info("session獲取出來的訂單信息:------------>>>>>>>saleProduct:" + JSON.toJSON(session)); // 下單所用到真實的信息,從系統session獲取 WeiXinOrderNeedInfo weiXinOrderNeedInfo = (WeiXinOrderNeedInfo) session.getAttribute("weiXinOrderNeedInfo"); if (weiXinOrderNeedInfo != null) { logger.info("session獲取出來的訂單信息:------------>>>>>>>saleProduct:" + JSON.toJSON(weiXinOrderNeedInfo)); orderInfo = "菜小樂支付訂單";// 訂單的簡介 nonce_str = weiXinOrderNeedInfo.getNonce_str(); out_trade_no = weiXinOrderNeedInfo.getOut_trade_no();// 商戶訂單號(商戶本身系統內生成的訂單號32位之內) total_fee = weiXinOrderNeedInfo.getTotal_fee();// 總金額,單位默認爲分 String openid = String.valueOf(session.getAttribute("openId"));// 用戶的openid WeiXinGetOrder weiXinGetOrder = new WeiXinGetOrder(); weiXinGetOrder.setAppid(WeixinPayConstants.appid); weiXinGetOrder.setBody(orderInfo); weiXinGetOrder.setMch_id(WeixinPayConstants.mch_id); weiXinGetOrder.setNonce_str(nonce_str); weiXinGetOrder.setNotify_url(WeixinPayConstants.notify_url);// 接收微信支付異步通知回調地址,通知url必須爲直接可訪問的url,不能攜帶參數。 weiXinGetOrder.setOpenid(openid); weiXinGetOrder.setOut_trade_no(out_trade_no); weiXinGetOrder.setSpbill_create_ip(GetClientIp.getIpAddr(httpServletRequest)); //用戶頁面的ip weiXinGetOrder.setTrade_type("JSAPI"); weiXinGetOrder.setTotal_fee(total_fee); //設置並獲得簽名 SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("appid", WeixinPayConstants.appid); parameters.put("body", orderInfo); parameters.put("mch_id", WeixinPayConstants.mch_id); parameters.put("nonce_str", nonce_str); parameters.put("notify_url", WeixinPayConstants.notify_url);// 接收微信支付異步通知回調地址,通知url必須爲直接可訪問的url,不能攜帶參數。 parameters.put("openid", openid); parameters.put("out_trade_no", out_trade_no); parameters.put("spbill_create_ip", GetClientIp.getIpAddr(httpServletRequest));//用戶頁面的ip parameters.put("trade_type", "JSAPI"); parameters.put("total_fee", total_fee); String sign = WeiXinCrateSign.createSign("utf-8", parameters); System.out.println("簽名------------------" + sign); weiXinGetOrder.setSign(sign); orderXml = getOrderXml(weiXinGetOrder); System.out.println("直接字符串輸出" + orderXml); //獲取相關的訂單信息,注意獲得的是xml格式的信息,須要在下面對其解析 String stringObject = CommonUtil.httpsRequest(WeixinPayConstants.createOrderURL, "POST", orderXml).toString(); System.out.println("返回的xmlstring" + stringObject); //將map對象轉換成xml格式數據 Map map = ObjectAndXmlUtil.doXMLParse(stringObject); String prepay_id = String.valueOf(map.get("prepay_id")); logger.info("-----統一下單接口的訂單id:" + JSON.toJSON(map.get("prepay_id"))); WeiXinPay weiXinPay = new WeiXinPay(); String nonceStr = RandomUtil.generateString(15); String timaStamp = String.valueOf(WeiXinDateUtil.getNowTimeStamp()); weiXinPay.setPackage("prepay_id=" + prepay_id); weiXinPay.setTimeStamp(timaStamp);// weiXinPay.setNonceStr(nonceStr); weiXinPay.setAppId(WeixinPayConstants.appid); weiXinPay.setSignType("MD5"); SortedMap<Object, Object> parameter = new TreeMap<Object, Object>(); parameter.put("timeStamp", timaStamp); parameter.put("appId", WeixinPayConstants.appid); parameter.put("nonceStr", nonceStr); parameter.put("signType", "MD5"); parameter.put("package", weiXinPay.getPackage()); weiXinPay.setPaySign(WeiXinCrateSign.createSign("utf-8", parameter)); jsonString = JSON.toJSONString(weiXinPay); } return jsonString; }
每個訂單都對應一個微信用戶,因此在此以前咱們要獲取用戶的openid:
/** * 獲取微信用戶的openid * * @param session */ public void getWeiXinOpenIdReturn(HttpSession session) { String code = String.valueOf(session.getAttribute("returnCode")); System.out.println("code值----------" + code); String oauth_url = WeixinPayConstants.oauth_url.replace("APPID", WeixinPayConstants.appid).replace("SECRET", WeixinPayConstants.appsecret).replace("CODE", code); logger.info("oauth_url:" + oauth_url); JSONObject jsonObject = CommonUtil.httpsRequestToJsonObject(oauth_url, "POST", null); logger.info("jsonObject:" + jsonObject); Object errorCode = jsonObject.get("errcode"); if (errorCode != null) { logger.info("code不合法"); } else { String openId = jsonObject.getString("openid"); logger.info("openId:" + openId); logger.info("openId:" + openId); session.setAttribute("openId", openId); System.out.println("openid-----------" + openId); } }
具體獲取openid的相關代碼:
public class CommonUtil { /** * 獲取用戶openid的網絡實現 * @param requestUrl * @param requestMethod * @param outputStr * @return */ public static JSONObject httpsRequestToJsonObject(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; try { StringBuffer buffer = httpsRequest(requestUrl, requestMethod, outputStr); jsonObject = JSONObject.fromObject(buffer.toString()); } catch (ConnectException ce) { System.out.println("鏈接超時:" + ce.getMessage()); } catch (Exception e) { System.out.println("https請求異常:" + e.getMessage()); } return jsonObject; } /** * /獲取openid的post方法 * @param requestUrl * @param requestMethod * @param output * @return * @throws NoSuchAlgorithmException * @throws KeyManagementException * @throws IOException */ public static StringBuffer httpsRequest(String requestUrl, String requestMethod, String output) throws NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException, IOException{ URL url = new URL(requestUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod(requestMethod); if (null != output) { OutputStream outputStream = connection.getOutputStream(); outputStream.write(output.getBytes("UTF-8")); outputStream.close(); } // 從輸入流讀取返回內容 InputStream inputStream = connection.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; connection.disconnect(); return buffer; }
將要下單的信息轉換爲xml格式的數據(要在每條數據中加上<![CDATA]>,騰訊官方文檔是沒有加的,可是接口又須要加上,當初我在這裏也卡了很長一段時間):
/** * 將要下單的訂單信息轉化成xml * * @param weiXinGetOrder * @return */ private String getOrderXml(WeiXinGetOrder weiXinGetOrder) { String data = "<xml>" + "<appid>" + "<![CDATA[" + weiXinGetOrder.getAppid() + "]]>" + "</appid>" + "<body>" + "<![CDATA[" + weiXinGetOrder.getBody() + "]]>" + "</body>" + "<mch_id>" + "<![CDATA[" + weiXinGetOrder.getMch_id() + "]]>" + "</mch_id>" + "<nonce_str>" + "<![CDATA[" + weiXinGetOrder.getNonce_str() + "]]>" + "</nonce_str>" + "<notify_url>" + "<![CDATA[" + weiXinGetOrder.getNotify_url() + "]]>" + "</notify_url>" + "<openid>" + "<![CDATA[" + weiXinGetOrder.getOpenid() + "]]>" + "</openid>" + "<out_trade_no>" + "<![CDATA[" + weiXinGetOrder.getOut_trade_no() + "]]>" + "</out_trade_no>" + "<spbill_create_ip>" + "<![CDATA[" + weiXinGetOrder.getSpbill_create_ip() + "]]>" + "</spbill_create_ip>" + "<total_fee>" + "<![CDATA[" + weiXinGetOrder.getTotal_fee() + "]]>" + "</total_fee>" + "<trade_type>" + "<![CDATA[" + weiXinGetOrder.getTrade_type() + "]]>" + "</trade_type>" + "<sign>" + "<![CDATA[" + weiXinGetOrder.getSign() + "]]>" + "</sign>" + "</xml>";//將實體轉化成xml(string) return data; }
ObjectAndXmlUtil類(進行實體與xml數據之間的相互轉化):
public class ObjectAndXmlUtil { /** * 將微信xml轉化成實體 * * @param xmlStr * * @return */ public static WeiXinGetOrder XMLStringToBeanWeiXinGetOrder(String xmlStr) { XStream xstream = new XStream(); WeiXinGetOrder weiXinGetOrder = (WeiXinGetOrder) xstream.fromXML(xmlStr); return weiXinGetOrder; } public static InputStream String2Inputstream(String str) { return new ByteArrayInputStream(str.getBytes()); } /** * 獲取子結點的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(getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } /** * 解析xml,返回第一級元素鍵值對。若是第一級元素有子節點,則此節點的值是子節點的xml數據。 * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map doXMLParse(String strxml) throws Exception { if (null == strxml || "".equals(strxml)) { return null; } Map map = new HashMap(); InputStream in = String2Inputstream(strxml); 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 = getChildrenText(children); } map.put(k, v); } //關閉流 in.close(); return map;
} }
WeiXinGetOrder類,相關參數在代碼中有相關說明:
/** * Created by Administrator on 2016/8/18. */ @XmlRootElement public class WeiXinGetOrder implements Serializable { private String appid;//公衆號id private String mch_id;//商戶號 private String nonce_str;//很多於32位隨機字符串 private String sign;//簽名 private String body;//商品的描述(瀏覽器打開的移動網頁的主頁title名-商品概述) private String out_trade_no;//商戶訂單號(商戶本身系統內生成的訂單號32位之內) private int total_fee;//總金額,單位默認爲分 private String spbill_create_ip;//提交用戶終端的ip private String notify_url;//接收微信支付異步通知回調地址,通知url必須爲直接可訪問的url,不能攜帶參數。 private String trade_type;//交易類型 取值以下:JSAPI private String openid;//交易類型 取值以下:JSAPI public String getOpenid() { return openid; } public void setOpenid(String openid) { this.openid = openid; } public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public String getMch_id() { return mch_id; } public void setMch_id(String mch_id) { this.mch_id = mch_id; } public String getNonce_str() { return nonce_str; } public void setNonce_str(String nonce_str) { this.nonce_str = nonce_str; } public String getNotify_url() { return notify_url; } public void setNotify_url(String notify_url) { this.notify_url = notify_url; } public String getOut_trade_no() { return out_trade_no; } public void setOut_trade_no(String out_trade_no) { this.out_trade_no = out_trade_no; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public String getSpbill_create_ip() { return spbill_create_ip; } public void setSpbill_create_ip(String spbill_create_ip) { this.spbill_create_ip = spbill_create_ip; } public int getTotal_fee() { return total_fee; } public void setTotal_fee(int total_fee) { this.total_fee = total_fee; } public String getTrade_type() { return trade_type; } public void setTrade_type(String trade_type) { this.trade_type = trade_type; } }
在上面的實體類中有一個屬性是sign:我在這裏也給出微信的簽名算法(官方的簽名規定:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3):
@SuppressWarnings("unchecked") //api 密鑰 private static String Key = WeixinPayConstants.partnerkey; /** * 微信簽名算法 * @param characterEncoding 編碼格式 * @param parameters 簽名所涉及到數據 * @return sign 簽名 */ 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=" + Key); String sign = WeiXinMD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; }
後端的相關步驟執行完以後,程序會執行到上面給出來的ajax的success方法中,經過判斷返回值就能夠判斷是否支付成功,並進行先關的頁面跳轉:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
結束:因爲本人也是新手,這些代碼很low僅僅是簡單的實現了支付的功能而已,後期有時間在來重構這些代碼了。