對開發測試成功後的源碼以及在開發中遇到的問題作一下簡要說明javascript
開發以前的準備工做:css
參數:開發以前你要知道請求每一個接口地址的參數所表明的含義 //下面四個是必須知道的參數html
appid //公衆號appid前端
appsecret //公衆號密鑰java
mch_id //商戶id算法
key //商戶密鑰json
appid和appsecret 在微信公衆號中獲取,mch_id和key在公衆號對應的微信商戶號中獲取api
設置支付受權目錄微信
開發以前先要熟悉一下業務流程 app
業務流程時序圖
開發流程能夠分爲三步:
1, 受權,獲取openid
2, 調用統一下單API,獲取預支付信息
3, 在頁面調用微信支付js而且完成支付
一, 獲取openid
1, 受權
調用微信OAuth2.0網頁受權
類 : GetCodeServlet.java //getCode
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String appid = ""; //公衆號id String state = "0"; String scope = "snsapi_base"; String redirect_uri = "http://cmy.tunnel.qydev.com/wx_cmy/pay"; //重定向的url,就是受權後要跳轉的地址 String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appid+"&redirect_uri="+redirect_uri+"&response_type=code&scope="+scope+"&state="+state+"#wechat_redirect"; //System.out.println("code === "+ url.toString()); response.sendRedirect(url.toString()); }
scope:應用受權做用域
snsapi_base:不彈出受權頁面,直接跳轉,只能獲取用戶openid
snsapi_userinfo:彈出受權頁面,可經過openid拿到暱稱、性別、所在地
2, 獲取用戶微信OpenId
類:PayServlet.java //pay 重定向以後的url
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 獲取openid */
//網頁受權後獲取傳遞的參數
String code = request.getParameter("code");
String appid = ""; String appsecret = ""; String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appid+"&secret="+secret+"&code="+code+"&grant_type=authorization_code"; //接口地址 //System.out.println("pay code === "+ url.toString()); String requestMethod = "GET"; String outputStr = ""; String httpRequest = WeixinUtil.httpRequest(url, requestMethod, outputStr); JSONObject obj = JSONObject.fromObject(httpRequest); String openid = obj.get("openid").toString(); //System.out.println("openid是"+openid);
WeixinUtil.httpRequest(url, requestMethod, outputStr);
/** * 發起https請求並獲取結果 * * @param requestUrl 請求地址 * @param requestMethod 請求方式(GET、POST) * @param outputStr 提交的數據 * @return JSONObject(經過JSONObject.get(key)的方式獲取json對象的屬性值) */ public static String httpRequest(String requestUrl, String requestMethod, String outputStr) { StringBuffer buffer = new StringBuffer(); 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 httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // 設置請求方式(GET/POST) httpUrlConn.setRequestMethod(requestMethod); if ("GET".equalsIgnoreCase(requestMethod)) httpUrlConn.connect(); // 當有數據須要提交時 if (null != outputStr) { OutputStream outputStream = httpUrlConn.getOutputStream(); // 注意編碼格式,防止中文亂碼 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 將返回的輸入流轉換成字符串 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 釋放資源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); } catch (ConnectException ce) { System.out.println("---CMY---Weixin server connection timed out."); } catch (Exception e) { System.out.println("---CMY---https request error:{}"); } return buffer.toString(); }
二, 調用統一下單API,獲取預支付信息
在獲得openid以後,調用統一下單接口
經過openid獲取prepay_id
//-------------統一下單--------------- /** * 預支付 */ RequestHandler reqHandler = new RequestHandler(request, response); reqHandler.init(com.bean.Constants.appid, com.bean.Constants.appsecret, com.bean.Constants.key); //獲取openId後調用統一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder String requestXml=""; try { requestXml = CommonUtil.getSign(request, response,reqHandler, request.getRemoteAddr(), openid); //獲取 請求參數 的xml, } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } //.out.println(requestXml); String createURL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //統一下單接口url String prepay_id=""; try { prepay_id = new GetWxOrderno().getPayNo(createURL, requestXml); //獲取 prepay_id if(prepay_id.equals("")){ System.out.println("----------- 你好,我是分割線 ------------"); response.sendRedirect("error.jsp"); } } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } System.out.println("--------------------- 超級華麗分割線 -----------------------"); System.out.println("訂單id是"+prepay_id);
CommonUtil.getSign(request, response,reqHandler, request.getRemoteAddr(), openid);
在這個方法裏,生成請求參數的簽名,並把簽名放到請求參數拼接成的xml裏
/** * 請求參數 * @throws Exception */ public static String getSign(HttpServletRequest request, HttpServletResponse response ,RequestHandler reqHandler,String ip,String openId) throws Exception { String appid = ""; String mch_id= ""; //商戶id String nonce_str = getNonce_str(); //隨機字符串 //商戶訂單號 本身定義 String out_trade_no = "A"+TenpayUtil.getCurrTime(); String notify_url ="http://cmy.tunnel.qydev.com/wx_cmy/notify"; //接收微信支付異步通知回調地址 String trade_type = "JSAPI"; String openid = openId; //上面取到的openid SortedMap<String, String> packageParams = new TreeMap<String, String>(); packageParams.put("appid", appid); packageParams.put("mch_id", mch_id); packageParams.put("nonce_str", nonce_str); packageParams.put("body", "my_xn_iphone"); packageParams.put("attach", "WeiXin_zf"); packageParams.put("out_trade_no", out_trade_no); //這裏寫的金額爲1 分到時修改 packageParams.put("total_fee", "1"); packageParams.put("spbill_create_ip", ip); packageParams.put("notify_url", notify_url); packageParams.put("trade_type", trade_type); packageParams.put("openid", openid); String sign = reqHandler.createSign(packageParams); //經過map獲取簽名,這塊也能夠把參數拼接成字符串 以後進行sign=MD5(stringSignTemp).toUpperCase();生成簽名,能夠參照開發文檔--簽名算法 //生成請求參數的xml,這個也能夠調用字符串或map【前提是 把剛生成的sign已經添加到字符串裏或添加到map裏了】轉成xml的方法生成xml String xml="<xml>"+ "<appid>"+appid+"</appid>"+ "<mch_id>"+mch_id+"</mch_id>"+ "<nonce_str>"+nonce_str+"</nonce_str>"+ "<sign>"+sign+"</sign>"+ "<body><![CDATA[my_xn_iphone]]></body>"+ "<attach>WeiXin_zf</attach>"+ "<out_trade_no>"+out_trade_no+"</out_trade_no>"+ //金額,這裏寫的1 分到時修改 "<total_fee>"+1+"</total_fee>"+ //"<total_fee>"+finalmoney+"</total_fee>"+ "<spbill_create_ip>"+ip+"</spbill_create_ip>"+ "<notify_url>"+notify_url+"</notify_url>"+ "<trade_type>"+trade_type+"</trade_type>"+ "<openid>"+openid+"</openid>"+ "</xml>"; return xml; }
createSign(SortedMap<String, String> packageParams);
1 /** 2 * 建立md5摘要,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。 3 */ 4 public String createSign(SortedMap<String, String> packageParams) { 5 StringBuffer sb = new StringBuffer(); 6 Set es = packageParams.entrySet(); 7 Iterator it = es.iterator(); 8 while (it.hasNext()) { 9 Map.Entry entry = (Map.Entry) it.next(); 10 String k = (String) entry.getKey(); 11 String v = (String) entry.getValue(); 12 if (null != v && !"".equals(v) && !"sign".equals(k) 13 && !"key".equals(k)) { 14 sb.append(k + "=" + v + "&"); 15 } 16 } 17 sb.append("key=" + this.getKey()); 18 System.out.println("md5 sb:" + sb+"key="+this.getKey()); 19 String sign = MD5Util.MD5Encode(sb.toString(), this.charset) 20 .toUpperCase(); 21 System.out.println("簽名:" + sign); 22 return sign; 23 24 }
三, 在頁面調用微信支付js而且完成支付
獲取到prepay_id,
SortedMap<String, String> finalpackage = new TreeMap<String, String>(); String appid = ""; //不用寫你應該也是到是啥了 String timestamp = Sha1Util.getTimeStamp(); String nonceStr2 = CommonUtil.getNonce_str(); String prepay_id2 = "prepay_id="+prepay_id; //拼接packages String packages = prepay_id2; finalpackage.put("appId", appid); finalpackage.put("timeStamp", timestamp); finalpackage.put("nonceStr", nonceStr2); finalpackage.put("package", packages); finalpackage.put("signType", "MD5"); String finalsign = reqHandler.createSign(finalpackage); //生成攜有以上參數的簽名 //請求前端頁面調用微信支付js並完成支付 response.sendRedirect("pay.jsp?appid="+appid2+"&timeStamp="+timestamp+"&nonceStr="+nonceStr2+"&package="+packages+"&sign="+finalsign); }
pay.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String appId = request.getParameter("appid"); String timeStamp = request.getParameter("timeStamp"); String nonceStr = request.getParameter("nonceStr"); String packageValue = request.getParameter("package"); String paySign = request.getParameter("sign"); %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>微信支付</title> <meta name="viewport" content="width=device-width; initial-scale=1.0"> //h5適應手機 <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <br><br><br> <div style="text-align:center;"> <h2>xxx __ 微信支付 </h2> <h3>價格: 0.01元</h3> </div> <div style="text-align:center;"> <input type="button" value="確認支付" onclick="callpay()"> </div> </body> <script type="text/javascript"> function callpay(){ WeixinJSBridge.invoke('getBrandWCPayRequest',{ "appId" : "<%=appId%>", "timeStamp" : "<%=timeStamp%>", "nonceStr" : "<%=nonceStr%>", "package" : "<%=packageValue%>", "signType" : "MD5", "paySign" : "<%=paySign%>" },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("支付失敗!"); } }) } </script> </html>
生成時間戳以及隨即字符串的方法
public static String getTimeStamp() { return String.valueOf(System.currentTimeMillis() / 1000); } public static String getNonce_str(){ String currTime = TenpayUtil.getCurrTime(); //8位日期 String strTime = currTime.substring(8, currTime.length()); //四位隨機數 String strRandom = TenpayUtil.buildRandom(4) + ""; //10位序列號,能夠自行調整。 String str = strTime + strRandom; return str ; }
開發中遇到的問題:
開發中須要注意的問題:
1,全部簽名中攜帶的key都是商戶密鑰
2,若是你的簽名經過了微信支付接口簽名校驗工具校驗成功 ,那麼你就別懷疑了,從新申請一個商戶密鑰
3,若是你的簽名未經過了微信支付接口簽名校驗工具校驗,那你就檢查一下,你參與簽名的參數是否按參數名ASCII碼未按升序排列,或者是生成MD5字符串沒有toUpperCase轉換爲大寫