JAVA微信公众号开发之公众号内H5调微信支付
?
微信公眾號內(nèi)調(diào)用微信支付接口wx.chooseWXPay,需要引用JSSDK,配置config,這個步驟在我的另一篇調(diào)用地圖接口博客有詳細說明,這里就不介紹了,然后微信公眾號里還提供了wx.onBridgeReady,這個就不需要配置直接調(diào)用就好了,可能是前者是對后者的封裝吧。
?
首先把公眾號的域名授權(quán)和微信支付目錄授權(quán)配置好,支付目錄要配置到調(diào)用支付的頁面當(dāng)前文件夾,否則會導(dǎo)致調(diào)不出支付頁面。
然后就是開發(fā)步驟:
?
這是需要傳遞的參數(shù);
?
wx.chooseWXPay({
? ? timestamp: 0, // 支付簽名時間戳,注意微信jssdk中的所有使用timestamp字段均為小寫。但最新版的支付后臺生成簽名使用的timeStamp字段名需大寫其中的S字符
? ? nonceStr: '', // 支付簽名隨機串,不長于 32 位
? ? package: '', // 統(tǒng)一支付接口返回的prepay_id參數(shù)值,提交格式如:prepay_id=***)
? ? signType: '', // 簽名方式,默認(rèn)為'SHA1',使用新版支付需傳入'MD5'
? ? paySign: '', // 支付簽名
? ? success: function (res) {
? ? ? ? // 支付成功后的回調(diào)函數(shù)
? ? }
});
1、時間戳隨機串就不說了,獲取prepayid
?
public static String unifiedorder(String spbill_create_ip,String total_fee,String body,String out_trade_no,String openid) throws Exception {SortedMap<String,String> parameters = new TreeMap<String,String>();parameters.put("appid", ConfigUtil.APPID);parameters.put("mch_id", ConfigUtil.MCH_ID);//商戶號parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());parameters.put("body", body);//訂單詳情parameters.put("out_trade_no", out_trade_no);//訂單號,可使用隨機字符串parameters.put("total_fee", total_fee);//金額。單位分parameters.put("spbill_create_ip",spbill_create_ip);//客戶端ipparameters.put("notify_url", ConfigUtil.NOTIFY_URL);//回調(diào)地址parameters.put("trade_type", "JSAPI");parameters.put("openid", openid);String sign = PayUtil.generateSignature(parameters,ConfigUtil.API_KEY);//商戶密鑰,自行設(shè)置parameters.put("sign", sign);//簽名String requestXML = PayCommonUtil.getRequestXml(parameters);System.out.println(requestXML.toString());String result =CommonUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL, "POST", requestXML);//統(tǒng)一支付訂單路徑System.out.println(result.toString());Map<String, String> map=new HashMap<String, String>();try {map = XMLUtil.doXMLParse(result);} catch (JDOMException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}//解析微信返回的信息,以Map形式存儲便于取值return map.get("prepay_id").toString();}?
/*** * @Description:將請求參數(shù)轉(zhuǎn)換為xml格式的string* @param parameters 請求參數(shù)* @return*/ public static String getRequestXml(SortedMap<String,String> parameters){StringBuffer sb = new StringBuffer();sb.append("<xml>");Set es = parameters.entrySet();Iterator it = es.iterator();while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next();String k = (String)entry.getKey();String v = (String)entry.getValue();if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");}else {sb.append("<"+k+">"+v+"</"+k+">");}}sb.append("</xml>");return sb.toString(); } private static Logger log = LoggerFactory.getLogger(CommonUtil.class); /** * 發(fā)送https請求 * @param requestUrl 請求地址 * @param requestMethod 請求方式(GET、POST) * @param outputStr 提交的數(shù)據(jù) * @return 返回微信服務(wù)器響應(yīng)的信息 */ public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) { try { // 創(chuàng)建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 conn = (HttpsURLConnection) url.openConnection(); conn.setSSLSocketFactory(ssf); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 設(shè)置請求方式(GET/POST) conn.setRequestMethod(requestMethod); conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); // 當(dāng)outputStr不為null時向輸出流寫數(shù)據(jù) if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); // 注意編碼格式 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 從輸入流讀取返回內(nèi)容 InputStream inputStream = conn.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; conn.disconnect(); return buffer.toString(); } catch (ConnectException ce) { log.error("連接超時:{}", ce); } catch (Exception e) { log.error("https請求異常:{}", e); } return null; }public static String urlEncodeUTF8(String source){String result = source;try {result = java.net.URLEncoder.encode(source,"utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return result;} public class MyX509TrustManager implements X509TrustManager{// 檢查客戶端證書public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 檢查服務(wù)器端證書public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 返回受信任的X509證書數(shù)組public X509Certificate[] getAcceptedIssuers() {return null;} }?
//獲得客戶端ipString ip = req.getHeader("X-Forwarded-For");if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){//多次反向代理后會有多個ip值,第一個ip才是真實ipint index = ip.indexOf(",");if(index != -1){ip= ip.substring(0,index);}}ip = req.getHeader("X-Real-IP");if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){ip = req.getHeader("X-Forwarded-For");}ip = req.getRemoteAddr();?
?
?
簽名生成方式:
?
第一步,設(shè)所有發(fā)送或者接收到的數(shù)據(jù)為集合M,將集合M內(nèi)非空參數(shù)值的參數(shù)按照參數(shù)名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特別注意以下重要規(guī)則:
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并對stringSignTemp進行MD5運算,再將得到的字符串所有字符轉(zhuǎn)換為大寫,得到sign值signValue。
◆ key設(shè)置路徑:微信商戶平臺(pay.weixin.qq.com)-->賬戶設(shè)置-->API安全-->密鑰設(shè)置
簽名需要參數(shù):
?
* 生成簽名. 注意,若含有sign_type字段,必須和signType參數(shù)保持一致。** @param data 待簽名數(shù)據(jù)* @param key API密鑰* @param signType 簽名方式* @return 簽名*/public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {Set<String> keySet = data.keySet();String[] keyArray = keySet.toArray(new String[keySet.size()]);Arrays.sort(keyArray);StringBuilder sb = new StringBuilder();for (String k : keyArray) {if (k.equals(ConfigUtil.FIELD_SIGN)) {continue;}if (data.get(k).trim().length() > 0) // 參數(shù)值為空,則不參與簽名sb.append(k).append("=").append(data.get(k).trim()).append("&");}sb.append("key=").append(key);if (SignType.MD5.equals(signType)) {return MD5(sb.toString()).toUpperCase();}/* else if (SignType.HMACSHA256.equals(signType)) {return HMACSHA256(sb.toString(), key);}*/else {throw new Exception(String.format("Invalid sign_type: %s", signType));}} /*** 生成 MD5** @param data 待處理數(shù)據(jù)* @return MD5結(jié)果*/public static String MD5(String data) throws Exception {java.security.MessageDigest md = MessageDigest.getInstance("MD5");byte[] array = md.digest(data.getBytes("UTF-8"));StringBuilder sb = new StringBuilder();for (byte item : array) {sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}return sb.toString().toUpperCase();}?
/*** 生成 HMACSHA256* @param data 待處理數(shù)據(jù)* @param key 密鑰* @return 加密結(jié)果* @throws Exception*/public static String HMACSHA256(String data, String key) throws Exception {Mac sha256_HMAC = Mac.getInstance("HmacSHA256");SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");sha256_HMAC.init(secret_key);byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));StringBuilder sb = new StringBuilder();for (byte item : array) {sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}return sb.toString().toUpperCase();}
2、獲取prepayid后,形成支付簽名,然后把需要參數(shù)傳給前端,前端引用JSSDK的config配置中簽名與此簽名規(guī)則不一樣,注意。
?
SortedMap<String,String> signParams = new TreeMap<String,String>(); signParams.put("appId", ConfigUtil.APPID); signParams.put("nonceStr",nonceStr); signParams.put("package", "prepay_id="+prepayId); signParams.put("timeStamp", String.valueOf(timeStamp)); signParams.put("signType", "MD5"); // 生成支付簽名,要采用URLENCODER的原始值進行SHA1算法! String sign= PayUtil.generateSignature(signParams,ConfigUtil.API_KEY, SignType.MD5);
注意,所有訂單參數(shù)都為字符串類型!
?
3、當(dāng)用戶支付完成后,微信服務(wù)器會返給服務(wù)器填寫的那個回調(diào)地址notify_url狀態(tài),判定支付是否成功,我們接收到成功后,判斷簽名是否正確,正確后再返回微信SUCCESS即完成整個流程,如果不返給微信狀態(tài),則微信會每隔一段時間就給服務(wù)器返狀態(tài),直到服務(wù)器返回微信成功,沒有回應(yīng)的話一共8次結(jié)束。
?
public String processResponseXml(HttpServletRequest req,HttpServletResponse resp) throws Exception {String RETURN_CODE = "return_code";String return_code;Map<String, String> respData = MessageUtil.xmlToMap(req);Map<String,String> result = new HashMap<String,String>(); result.put("return_code", ConfigUtil.FAIL);if (respData.containsKey(RETURN_CODE)) {return_code = respData.get(RETURN_CODE); }else {throw new Exception(String.format("No `return_code` in XML"));}if (return_code.equals(ConfigUtil.FAIL)) {result.remove("return_code");result.put("return_code", ConfigUtil.FAIL);}else if (return_code.equals(ConfigUtil.SUCCESS)) {if (this.isResponseSignatureValid(respData)) {result.remove("return_code");result.put("return_code", ConfigUtil.SUCCESS);//}}}}}else {throw new Exception(String.format("Invalid sign value in XML")); }}else {throw new Exception(String.format("return_code value %s is invalid in XML", return_code));}String resultMSG = PayUtil.mapToXml(result);return resultMSG;} /*** 判斷xml數(shù)據(jù)的sign是否有效,必須包含sign字段,否則返回false。** @param reqData 向wxpay post的請求數(shù)據(jù)* @return 簽名是否有效* @throws Exception*/public boolean isResponseSignatureValid(Map<String, String> reqData) throws Exception {// 返回數(shù)據(jù)的簽名方式和請求中給定的簽名方式是一致的return PayUtil.isSignatureValid(reqData, ConfigUtil.API_KEY, SignType.MD5);}?
/*** @Description:返回給微信的參數(shù)* @param return_code 返回編碼* @param return_msg 返回信息* @return*/ public static String setXML(String return_code, String return_msg) {return "<xml><return_code><![CDATA[" + return_code+ "]]></return_code><return_msg><![CDATA[" + return_msg+ "]]></return_msg></xml>"; } /*** 判斷簽名是否正確,必須包含sign字段,否則返回false。** @param data Map類型數(shù)據(jù)* @param key API密鑰* @param signType 簽名方式* @return 簽名是否正確* @throws Exception*/public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {if (!data.containsKey(ConfigUtil.FIELD_SIGN) ) {return false;}String sign = data.get(ConfigUtil.FIELD_SIGN);return generateSignature(data, key, signType).equals(sign);}?
/*** 將Map轉(zhuǎn)換為XML格式的字符串** @param data Map類型數(shù)據(jù)* @return XML格式的字符串* @throws Exception*/public static String mapToXml(Map<String, String> data) throws Exception {DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();org.w3c.dom.Document document = documentBuilder.newDocument();org.w3c.dom.Element root = document.createElement("xml");document.appendChild(root);for (String key: data.keySet()) {String value = data.get(key);if (value == null) {value = "";}value = value.trim();org.w3c.dom.Element filed = document.createElement(key);filed.appendChild(document.createTextNode(value));root.appendChild(filed);}TransformerFactory tf = TransformerFactory.newInstance();Transformer transformer = tf.newTransformer();DOMSource source = new DOMSource(document);transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");transformer.setOutputProperty(OutputKeys.INDENT, "yes");StringWriter writer = new StringWriter();StreamResult result = new StreamResult(writer);transformer.transform(source, result);String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");try {writer.close();}catch (Exception ex) {}return output;} /*** XML格式字符串轉(zhuǎn)換為Map** @param strXML XML字符串* @return XML數(shù)據(jù)轉(zhuǎn)換后的Map* @throws Exception*/public static Map<String, String> xmlToMap(String strXML) throws Exception {try {Map<String, String> data = new HashMap<String, String>();DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));org.w3c.dom.Document doc = documentBuilder.parse(stream);doc.getDocumentElement().normalize();NodeList nodeList = doc.getDocumentElement().getChildNodes();for (int idx = 0; idx < nodeList.getLength(); ++idx) {Node node = nodeList.item(idx);if (node.getNodeType() == Node.ELEMENT_NODE) {org.w3c.dom.Element element = (org.w3c.dom.Element) node;data.put(element.getNodeName(), element.getTextContent());}}try {stream.close();} catch (Exception ex) {// do nothing}return data;} catch (Exception ex) {PayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);throw ex;}}?
到這里,微信公眾號內(nèi)支付就完成了,親測可用。
?
?
最近在整理一些資源工具,放在網(wǎng)站分享?http://tools.maqway.com
歡迎關(guān)注公眾號:麻雀唯伊 , 不定時更新資源文章,生活優(yōu)惠,或許有你想看的
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的JAVA微信公众号开发之公众号内H5调微信支付的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STM32 CRH和CRL记录
- 下一篇: 【USB网络摄像头】基于mjpeg-st