項目目前使用了微信公衆平臺的支付接口,遇到不少坑。其實仍是以爲微信接口的API太散亂,作完了一套以後再看下,發現基本都有說明。沒有java版本的demo。下面是本身的一些步驟思路;前端
2.準備工做:java
3.步驟詳解git
$("#wxsubmit").on("click",function(){ if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(); } function onBridgeReady(){ $$.ajax({ type: "get", async:"false", url: "pay?total_fee=" + pay_total_fee, //請求後臺支付方法 dataType: "json", success:function(result){ var data = result.data; if(null == data.appId || data.code == "-100"){ alert(data.msg); } //獲得預支付ID、其餘必須值傳遞給微信 WeixinJSBridge.invoke('getBrandWCPayRequest',{ "appId" : data.appId.trim(), "timeStamp" : data.timeStamp.trim(), "nonceStr" : data.nonceStr.trim(), "package" : data.package.trim(), "signType" :"MD5".trim(), "paySign" : data.sign.trim() },function(res){ // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回 ok,但並不保證它絕對可靠。 if(res.err_msg == "get_brand_wcpay_request:ok" ) { view.router.back(); } }) } }); } });
支付方法:ajax
/** * 微信支付接口 * * @param request * @param total_fee 支付金額 * @return * @throws Exception */ @ResponseBody @RequestMapping(value = "pay") public Map<String, Object> pay(HttpServletRequest request, @RequestParam(required = true) String total_fee) throws Exception { String appid = "********"; String key = "********"; //商戶號key String mch_id = "********"; //商戶號 String openid = "********";// 用戶在商戶appid下的惟一標識 String uuid = UUIDUtil.create32UUID(); //做爲隨機值串、商戶訂單號使用 String product_name = "付款"; String totalFee = 1;//獲取提交的商品價格(分) Map<String, Object> sign; //用於兩次MD5簽名結果 //參與簽名的map Map<String, Object> signParams = new HashMap<>(); signParams.put("attach", compId); //微信附帶參數,給什麼返回什麼 signParams.put("appid", appid); signParams.put("mch_id", mch_id); signParams.put("nonce_str", uuid); signParams.put("body", product_name); //商品描述 signParams.put("out_trade_no", uuid); //商戶訂單號 signParams.put("total_fee", totalFee); //商品總金額,以分爲單位 signParams.put("spbill_create_ip", request.getRemoteAddr()); //訂單生成的機器IP,指用戶瀏覽器端IP signParams.put("notify_url", prop.get("wx.NOTIFY_URL")); //通知地址 signParams.put("trade_type", prop.get("wx.TRADE_TYPE")); //交易類型 signParams.put("openid", openid); //MD5簽名 sign = createSign(signParams, key); //組裝xml字符串 StringBuffer xmlStr = new StringBuffer(); xmlStr.append("<xml>"); for (String str : new ArrayList<>(sign.keySet())) { xmlStr.append("<" + str + "><![CDATA[" + sign.get(str) + "]]></" + str + ">"); } xmlStr.append("</xml>"); try { //與微信支付創建鏈接,獲取預支付ID(prepay_id),以便支付使用 HttpURLConnection conn = (HttpURLConnection) new URL((String) prop.get("wx.URL")).openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); //是否進行輸入輸出 默認爲false BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream()); //向輸出流中添加數據 buffOutStr.write(xmlStr.toString().getBytes("UTF8")); buffOutStr.flush(); buffOutStr.close(); //獲取微信返回xml字符串 String sa = getStrFormInStream(conn.getInputStream()); //將xml轉換爲Map Map<String, Object> map = XmlToMap.getMap(sa); //預支付ID,返回的code正確(根據微信文檔return_code和result_code都爲SUCCESS的時候纔會返回code_url、prepay_id) if (!CollectionUtils.isEmpty(map) && "SUCCESS".equals(map.get("return_code")) && "SUCCESS".equals(map.get("result_code"))) { //設置支付參數 Map<String, Object> params = new TreeMap<>(); params.put("appId", appid); params.put("nonceStr", map.get("nonce_str").toString().trim()); params.put("package", "prepay_id=" + map.get("prepay_id").toString().trim()); params.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000).trim()); params.put("signType", "MD5"); //第二次簽名 sign.clear(); sign = createSign(params, key); //返回結果到前臺,並調用微信支付頁面 return Response.success(sign); } else { return Response.failure("獲取預支付ID失敗"); } } catch (Exception e) { } }
公用方法(支付方法中用到的通用方法):spring
/** * 微信支付結果通知 * @param request * @param response * @throws Exception */ @SystemLog(module = "微信支付結果通知", methods = "/wx/notifyUrl.json", description = "", level = "1", type = "1") @RequestMapping(value = "/wx/notifyUrl.json", method = {RequestMethod.GET, RequestMethod.POST}) public void notifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception { //獲取微信返回結果xml字符串,並轉換爲map String result = getStrFormInStream(request.getInputStream()); Map<String, Object> map = XmlToMap.getMap(result); saleService.pay(payRecordService, (String) map.get("attach"), (String) map.get("out_trade_no")); //TODO 測試是否有8次 通知 -- 像一張表中加入一條數據 8條數據 response.getWriter().print("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"); } /** * 將輸入流中的字符轉換爲utf8編碼的字符串 * * @param inStream 輸入流 * @return uft8編碼字符串 * @throws IOException */ private String getStrFormInStream(InputStream inStream) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(inStream)); String line; StringBuffer sb = new StringBuffer(); while ((line = reader.readLine()) != null) { sb.append(line); } inStream.close(); return new String(sb.toString().getBytes("gbk"), "utf-8"); } /** * 建立md5摘要,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。 * * @param params * @param key * @return */ private Map<String, Object> createSign(Map<String, Object> params, String key) { List<String> list = new ArrayList<String>(params.keySet()); Collections.sort(list); StringBuffer rs = new StringBuffer(); for (String str : list) { String value = (String) params.get(str); if (null != value && !"".equals(value) && !"sign".equals(value) && !"key".equals(value)) { rs.append("&" + str + "=" + value); logger.info(" " + str + " : " + value); } } params.put("sign", MD5Util.MD5Encode((rs.append("&key=") + key).toString().replaceFirst("&", ""), "UTF8").toUpperCase()); return params; }
基礎類(公共方法中用的的類):json
MD5加密類:瀏覽器
package ******; import java.security.MessageDigest; /** * @author JGS * @description MD5加密 * @created 2016-09-08 15:49 */ public class MD5Util { private static String byteArrayToHexString(byte b[]) { StringBuffer resultSb = new StringBuffer(); for (int i = 0; i < b.length; i++) resultSb.append(byteToHexString(b[i])); return resultSb.toString(); } private static String byteToHexString(byte b) { int n = b; if (n < 0) n += 256; int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } public static String MD5Encode(String origin, String charsetname) { String resultString = null; try { resultString = new String(origin); MessageDigest md = MessageDigest.getInstance("MD5"); if (charsetname == null || "".equals(charsetname)) resultString = byteArrayToHexString(md.digest(resultString.getBytes())); else { resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname))); } } catch (Exception exception) { } return resultString; } private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; }
解析xml微信
package *****; import com.suncd.dev.util.BeanUtil; import org.springframework.util.StringUtils; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import java.io.IOException; import java.io.StringReader; import java.util.HashMap; import java.util.Map; /** * @author JGS * @description 解析xml(key=xml的不解析) * @created 2016-09-12 11:12 */ public class XmlToMap extends DefaultHandler { private StringBuffer buf; private String str; private static Map<String, Object> map = new HashMap<>(); public XmlToMap() { super(); } public void startDocument() throws SAXException { buf = new StringBuffer(); System.out.println("*******開始解析XML*******"); } public void endDocument() throws SAXException { map.remove("xml"); System.out.println("*******XML解析結束*******"); } public void endElement(String namespaceURI, String localName, String fullName) throws SAXException { str = buf.toString(); String value = str; if (!StringUtils.isEmpty(buf) && !fullName.equals("xml")) { map.put(fullName, value); } buf.delete(0, buf.length()); } public void characters(char[] chars, int start, int length) throws SAXException { //將元素內容累加到StringBuffer中 buf.append(chars, start, length); System.out.println(buf.toString()); } /** * 將xml字符串轉換爲對象 * * @param str 轉換的XML字符串 * @param beanClass 對象Class * @return * @throws javax.xml.parsers.ParserConfigurationException * @throws org.xml.sax.SAXException * @throws java.io.IOException */ public static <T> T getObject(String str, Class<T> beanClass) throws IOException, SAXException, ParserConfigurationException { return (T) BeanUtil.map2Bean(getMap(str), beanClass); } public static Map getMap(String str) throws ParserConfigurationException, SAXException, IOException { XmlToMap xmlMap = new XmlToMap(); SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(new StringReader(str)), xmlMap); return map; } }