1. 準備工做javascript
----> 微信提供的appid、 appSecret、payKey、 MchId、tokenphp
這些比較容易獲取,過程此處省略.....html
----->配置微信的OAuth2.0網頁受權回調頁面的域名。前端
這個比較難找,我看文檔看了1天沒有找到,鬱悶致死。 最後登陸到微信公衆號點左側菜單,基本全部的菜單都點了一遍才被我發現,當時哭的心都有了。因此直接上圖,明確位置。以下圖:只須要點修改把本身的域名放進去就行。如m.baidu.com 或者 baidu.comjava
-----> 配置微信公衆號支付的受權目錄git
這個比較好找,直接上圖:直接點修改,頁面給的有提示。json
以上工做都準備完畢 就剩下些代碼了。api
2. 功能實現數組
實現流程微信
--> 統一下單API查看微信提供的文檔 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
--> 頁面受權獲取openid
受權文檔 http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
甭看他們囉嗦 直接拉到頁面的中部看目錄部分。走到目錄中的第二步就能獲取用戶的openid
第一步:重定向用戶受權的URL。
控制器重定向一下地址:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
參數:
這裏要注意的是 redirect_uri。 這個地址中的域名必定要是上面準備工做【配置微信的OAuth2.0網頁受權回調頁面的域名】提到的域名。
注意:只有參數scope=snsapi_userinfo 的時候纔會出現須要用戶點擊受權的頁面,其它的不出現。
在這個回調地址的控制器中獲取返回的參數code。實現代碼以下:
public static AccessTokenOAuth getWeiXinOAuthAccessToken(String code){ AccessTokenOAuth token = null; StringBuffer sb = new StringBuffer(WeiXinConfig.getOAuthAccessTokenURL); sb.append("?appid=").append(WeiXinConfig.AppId).append("&secret=").append(WeiXinConfig.AppSecret); sb.append("&code=").append(code).append("&grant_type=authorization_code"); try { URL url = new URL(sb.toString()); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); InputStream is =url.openStream(); //轉換返回值 String returnStr = SendMsgUtil.convertStreamToString(is); // 返回結果爲{"access_token":"ACCESS_TOKEN","expires_in":7200} Gson gson = new Gson(); token = gson.fromJson(returnStr, AccessTokenOAuth.class); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return token; }
AccessTokenOAuth 的實體代碼以下:
public class AccessTokenOAuth implements Serializable { /** * */ private static final long serialVersionUID = -9011346947427899815L; private String access_token; //網頁受權接口調用憑證,注意:此access_token與基礎支持的access_token不一樣 private Long expires_in; private String refresh_token; private String openid; private String scope; private String errcode; private String errmsg; public String getAccess_token() { return access_token; } public void setAccess_token(String access_token) { this.access_token = access_token; } public Long getExpires_in() { return expires_in; } public void setExpires_in(Long expires_in) { this.expires_in = expires_in; } public String getRefresh_token() { return refresh_token; } public void setRefresh_token(String refresh_token) { this.refresh_token = refresh_token; } public String getOpenid() { return openid; } public void setOpenid(String openid) { this.openid = openid; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } public String getErrcode() { return errcode; } public void setErrcode(String errcode) { this.errcode = errcode; } public String getErrmsg() { return errmsg; } public void setErrmsg(String errmsg) { this.errmsg = errmsg; } }
此時就能夠從token中獲取到用戶的openid
請求的參數trade_type = JSAPI Openid = 獲取用戶的openid
微信的統一下單API返回結果中 若是return_code="SUCCESS" 獲取下單成功,
而後根據返回的參數生成簽名 此簽名不一樣與統一下單 的簽名,此簽名主要用於頁面經過JS調用微信的H5支付請求。
組裝簽名的代碼:
Map<String,String> paramMap = new HashMap<String,String>(); paramMap.put("appId", WeiXinConfig.AppId); paramMap.put("timeStamp", String.valueOf(new Date().getTime())); paramMap.put("nonceStr", WeiXinConfig.getRandomStr()); paramMap.put("package", "prepay_id="+returnXML.get("prepay_id")); paramMap.put("signType", "MD5"); String _signData = SignUtil.genSignData(JSON.parseObject(JSON.toJSONString(paramMap))); _signData +="&key="+WeiXinConfig.PayKey; String _sign = SignUtil.addSignMD5(_signData);
簽名須要進行MD5加密。
微信簽名工具類
public class SignUtil { private static String token = "XXXXXXX"; //在微信公衆平臺配置 /** * 驗證簽名 * * @param signature * @param timestamp * @param nonce * @return */ public static boolean checkSignature(String signature, String timestamp, String nonce) { String[] arr = new String[] { token, timestamp, nonce }; // 將token、timestamp、nonce三個參數進行字典序排序 Arrays.sort(arr); StringBuilder content = new StringBuilder(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); } MessageDigest md = null; String tmpStr = null; try { md = MessageDigest.getInstance("SHA-1"); // 將三個參數字符串拼接成一個字符串進行sha1加密 byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } content = null; // 將sha1加密後的字符串可與signature對比,標識該請求來源於微信 return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false; } /** * 獲取jsapi的簽名 * @param jsapiTicket * @return */ public static String getSignature(String jsapiTicket){ String[] arr = new String[] {jsapiTicket}; Arrays.sort(arr); StringBuilder content = new StringBuilder(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); } MessageDigest md = null; String tmpStr = null; try { md = MessageDigest.getInstance("SHA-1"); // 將三個參數字符串拼接成一個字符串進行sha1加密 byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return tmpStr; } /** * 將字節數組轉換爲十六進制字符串 * * @param byteArray * @return */ private static String byteToStr(byte[] byteArray) { String strDigest = ""; for (int i = 0; i < byteArray.length; i++) { strDigest += byteToHexStr(byteArray[i]); } return strDigest; } /** * 將字節轉換爲十六進制字符串 * * @param mByte * @return */ 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; } /** * 簽名字符串 * @param sign_src 須要簽名的字符串 * @return */ public static String addSignMD5(String sign_src){ if (sign_src == null) return ""; try { String md5 = DigestUtils.md5Hex(getContentBytes(sign_src, "UTF-8")).toUpperCase(); return md5; }catch (Exception e){ return ""; } } /** * 校驗MD5簽名 * @param text 須要簽名的字符串 * @param sign 簽名結果 * @return */ public static boolean verifySignMD5(String text, String sign) { String mysign = DigestUtils.md5Hex(getContentBytes(text, "UTF-8")).toUpperCase(); if(mysign.equals(sign)) { return true; } else { return false; } } private static byte[] getContentBytes(String content, String charset) { if (charset == null || "".equals(charset)) { return content.getBytes(); } try { return content.getBytes(charset); } catch (UnsupportedEncodingException e) { throw new RuntimeException("MD5簽名過程當中出現錯誤,指定的編碼集不對,您目前指定的編碼集是:" + charset); } } public static String genSignData(JSONObject jsonObject) { StringBuffer content = new StringBuffer(); // 按照key作首字母升序排列 List<String> keys = new ArrayList<String>(jsonObject.keySet()); Collections.sort(keys, String.CASE_INSENSITIVE_ORDER); for (int i = 0; i < keys.size(); i++) { String key = (String) keys.get(i); if ("sign".equals(key)) { continue; } String value = jsonObject.getString(key); // 空串不參與簽名 if (isnull(value)) { continue; } content.append((i == 0 ? "" : "&") + key + "=" + value); } String signSrc = content.toString(); if (signSrc.startsWith("&")) { signSrc = signSrc.replaceFirst("&", ""); } return signSrc; } public static boolean isnull(String str) { if (null == str || str.equalsIgnoreCase("null") || str.equals("")) { return true; } else return false; } }
最後一步在頁面經過js發起微信的H5支付請求
WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId":appId, //公衆號名稱,由商戶傳入 "timeStamp":timeStamp, //時間戳,自1970年以來的秒數 "nonceStr" :nonceStr, //隨機串 "package":paypackage, "signType":"MD5", //微信簽名方式: "paySign":sign //微信簽名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { Util.alert("支付成功!!",5000); }else if(res.err_msg=="get_brand_wcpay_request:cancel"){//支付過程當中用戶取消 Util.alert("您取消了支付!!",5000); }else if(res.err_msg=="get_brand_wcpay_request:fail"){//支付失敗 Util.alert("支付失敗!!",-1); } // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回 ok,但並不保證它絕對可靠。 } );