一、最近又做了微信公眾號支付,前一次做支付沒有好好記錄,這次又浪費了不少時間,故完整的記錄下,下次就可以直接用了。
- 1、准備工作(微信公眾號、微信商戶號申請)
- 2、域名購買、域名備案(微信支付必須是備案的域名,測試環境支付測試不了)
- 測試環境能測試授權等功能,掃描關注可獲得微信管方測試、app_id、app_secret 有了兩個就可以了
二、准備工作續
第一步:開通微信公眾號支付功能后,就可以獲得app_id、app_secret,這個地方還需要設置一個ip白名單,代碼所放置的服務器ip地址
第二步:設置回調域名,必須是備案的域名,域名去掉 http

第三步:設置調起微信支付h5頁面的地址,在微信商戶平台設置
以上工作准備好了,接下來開始上代碼。
微信支付重點在簽名的生成,這塊錯一點就很麻煩。
主代碼:
package com.snp.app.controller; import com.alibaba.fastjson.JSONObject; import com.snp.app.domain.VO.PayVO; import com.snp.common.controller.BaseController; import com.snp.common.utils.ConfigUtil; import com.snp.common.utils.StringUtil; import com.snp.order.dao.FinancialClaimOrderDao; import com.snp.order.domain.FinancialClaimOrderDO; import com.snp.userManager.domain.UserWechatDO; import com.snp.userManager.service.UserWechatService; import com.snp.wechat.config.WechatConfig; import com.snp.wechat.model.bean.*; import com.snp.wechat.utils.AdvancedUtil; import com.snp.wechat.utils.PayUtils; import com.snp.wechat.utils.WeiXinOrderUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Map; /** * 支付控制器 */ @RestController @RequestMapping("/interface/pay") public class PayController extends BaseController{ private static Logger logger = LoggerFactory.getLogger(PayController.class); @Autowired private WeiXinOrderUtil weiXinOrderUtil; @Autowired private FinancialClaimOrderDao financialClaimOrderDao; @Autowired private WechatConfig wechatConfig; @Autowired private UserWechatService userWechatService; @ResponseBody @RequestMapping(value = "/createWechatOrder", method ={ RequestMethod.GET,RequestMethod.POST}) public JSONObject createWechatOrder(PayVO payVO, HttpServletRequest request, HttpServletResponse response) throws Exception{ payVO.setPayAmount("1");//1分錢 payVO.setRobbingOrderNo(PayUtils.getTransferNo16()); //ResultBody result = new ResultBody(); logger.info("saveFinancialClaimOrder{}支付金額:"+payVO.getPayAmount()); logger.info("saveFinancialClaimOrder{}訂單號:"+payVO.getRobbingOrderNo()); UserWechatDO userWechatDO = userWechatService.getUserWechatByUserId(ConfigUtil.getUserId(request)); WechatOrder wechatOrder = new WechatOrder(); wechatOrder.setOpenid(userWechatDO.getWechatOpenid()); wechatOrder.setOrderName("押金"); //PayUtils.getTransferNo16() wechatOrder.setOut_trade_no(payVO.getRobbingOrderNo()); wechatOrder.setNonce_str(PayUtils.getNonceStr()); //wechatOrder.setTotal_fee(MoneyUtil.getOrderMoney(payVO.getPayAmount())); wechatOrder.setTotal_fee(Integer.parseInt(payVO.getPayAmount())); //請求微信下單 並返回參數 String resultOrderStr = weiXinOrderUtil.buildWechatOrderParam(wechatOrder,request); JSONObject returnJson=JSONObject.parseObject(resultOrderStr); //更新 openID prepay_id 到搶單表中 FinancialClaimOrderDO finaClaimDO = new FinancialClaimOrderDO(); finaClaimDO.setOpenId(userWechatDO.getWechatOpenid()); finaClaimDO.setPrepayId(returnJson.get("pg").toString()); //financialClaimOrderDao.update(finaClaimDO); logger.info("createWechatOrder{}支付結果:"+resultOrderStr); //result.setData(resultOrderStr); return returnJson; } //支付之前先去下單 獲取用戶openID @RequestMapping(value = "/getWechatOpenId", method = RequestMethod.GET) public void getWechatOpenId(PayVO payVO,HttpServletRequest request,HttpServletResponse response) throws IOException{ //靜默授權 只能獲得openid //獲得微信公眾號的唯一標識 String appID = wechatConfig.getWechatAppId(); String appSecret = wechatConfig.getWechatAppSecret(); // 用戶同意授權后,能獲取到code String code = request.getParameter("code"); WeixinOauth2Token weixinOauth2Token = AdvancedUtil.getOauth2AccessToken(appID, appSecret, code); String openId = null; String accessToken = null; if(null!=weixinOauth2Token){ openId = weixinOauth2Token.getOpenId(); accessToken = weixinOauth2Token.getAccessToken(); } UserWechatDO userWechatDO = new UserWechatDO(); userWechatDO.setUserId(ConfigUtil.getUserId(request)); userWechatDO.setWechatOpenid(openId); userWechatDO.setAccessToken(accessToken); userWechatService.save(userWechatDO); //獲取回調域名 String url = wechatConfig.getRedirectUri(); url=url+"/index.html"; System.out.println(url); response.sendRedirect(url); } /** * 支付下單之前 先去判斷是否需要靜默授權獲取openID * @param request * @param response * @throws IOException */ @RequestMapping(value = "/saveFinancialClaimOrder", method = {RequestMethod.GET,RequestMethod.POST}) public void saveFinancialClaimOrder(PayVO payVO,HttpServletRequest request,HttpServletResponse response) throws Exception{ //查詢數據庫 如果沒有記錄就是第一次登錄,如果有數據判斷token有沒有失效 if(ConfigUtil.getUserId(request) == 0L){ throw new Exception("用戶id為空"); } UserWechatDO userWechatDO = userWechatService.getUserWechatByUserId(ConfigUtil.getUserId(request)); int isLoginFirst = 0; if(StringUtil.isEmpty(userWechatDO)){//首次支付 靜默登錄 isLoginFirst = 1; }else{ //第二次登錄 支付這里不用檢查token失效 openid唯一的不會失效 isLoginFirst = 0; } //獲取回調域名 String url = wechatConfig.getRedirectUri(); //獲得微信公眾號的唯一標識 String appID = wechatConfig.getWechatAppId(); //微信本地中轉站 去獲取token String reUrl = wechatConfig.getReUrl(); reUrl = reUrl+"?userId="+ConfigUtil.getUserId(request); //微信靜默授權地址 String accreditUrlBase = wechatConfig.getAccreditUrlBase(); if(isLoginFirst == 1){ accreditUrlBase=accreditUrlBase.replace("Redirect_UI", reUrl).replace("appId", appID); response.sendRedirect(accreditUrlBase);//未授權的用戶繼續授權 } //token effective 支付頁面 url=url+"/index.html"; System.out.println(url); response.sendRedirect(url); } /** * 提交支付后的微信異步返回接口 * @throws IOException */ @RequestMapping(value="/weixinNotify") public void weixinNotify(HttpServletRequest request, HttpServletResponse response) throws IOException{ String out_trade_no=null; String return_code =null; try { String resultNotify = weiXinOrderUtil.getOrderReturnStream(request); Map<String, String> resultMap = PayUtils.getH5PayMap(resultNotify,request); System.out.println("支付回調參數:"+resultMap); out_trade_no = resultMap.get("out_trade_no"); return_code = resultMap.get("return_code"); //通知微信.異步確認成功.必寫.不然微信會一直通知后台.八次之后就認為交易失敗了. response.getWriter().write(PayUtils.setXML("SUCCESS", "")); } catch (Exception e) { logger.error("微信回調接口出現錯誤:",e); try { response.getWriter().write(PayUtils.setXML("FAIL", "error")); } catch (IOException e1) { e1.printStackTrace(); } } FinancialClaimOrderDO fClaimDO = financialClaimOrderDao.getFinanClaimByOrderNo(out_trade_no); if(return_code.equals("SUCCESS")){ //支付成功的業務邏輯 fClaimDO.setStatus(1); }else{ //支付失敗的業務邏輯 fClaimDO.setStatus(-1); } financialClaimOrderDao.update(fClaimDO); } }
頁面:
<!DOCTYPE html> <html> <head> <script src="/js/jquery-1.4.4.min.js"></script> <script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> <meta charset="UTF-8"> <title>微信安全支付</title> </head> <body> <script type="text/javascript"> window.onload=function() { var totalfees = window.location.search; totalfees=totalfees.substring(11,12); $.ajax({ type : "POST", url : '/interface/pay/createWechatOrder?userId=4', success : function(data) { var appId = data.appId; var timeStamp = data.timeStamp; var nonceStr = data.nonceStr; var packages = "prepay_id=" + data.pg; var signType = "MD5"; var paySign = data.paySign; if (data.result == "success") { pay(); function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId" : appId, //公眾號名稱,由商戶傳入 "timeStamp": timeStamp+"", //時間戳,自1970年以來的秒數 "nonceStr" : nonceStr, //隨機串 "package" : packages, "signType" : signType, //微信簽名方式: "paySign" : paySign //微信簽名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { alert("支付成功"); // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功后返回 ok,但並不保證它絕對可靠。 //回到用戶訂單列表 // window.location.href="http://wx.ooklady.com/wechat/order/orderlist"; }else if (res.err_msg == "get_brand_wcpay_request:cancel") { alert("支付過程中用戶取消"); }else{ //支付失敗 alert(JSON.stringify(res)) } } ); } //喚起微信支付 function pay(){ 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(); } } } } }); } </script> </body> </html>
工具類:
package com.snp.wechat.utils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.snp.wechat.config.WechatConfig; import com.snp.wechat.model.bean.WechatOrder; import com.snp.wechat.model.bean.WeixinOauth2Token; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; /** * 微信公用工具類 */ @Component public class WeiXinOrderUtil { @Autowired private WechatConfig wechatConfig; /** * 組裝統一下單參數 * @return */ public String buildWechatOrderParam(WechatOrder wechatOrder,HttpServletRequest request) throws Exception{ String payKey = wechatConfig.getPaykey(); String signType = wechatConfig.getSignType(); SortedMap<String, String> signMap = new TreeMap<String, String>(); long timeStamp = PayUtils.getUnixTime(new Date()); wechatOrder.setAppid(wechatConfig.getWechatAppId()); wechatOrder.setTrade_type(wechatConfig.getTradeType()); //構建參數返回前台 請求支付接口 String prepayId = createWechatOrder(wechatOrder,request); signMap.put("appId",wechatOrder.getAppid()); signMap.put("timeStamp", timeStamp+""); signMap.put("nonceStr", wechatOrder.getNonce_str()); signMap.put("package", "prepay_id="+prepayId); signMap.put("signType", signType); String paySign = PayUtils.getSign(signMap, payKey); signMap.put("pg", prepayId); signMap.put("paySign", paySign); signMap.put("result", "success"); String json = JSON.toJSONString(signMap); JSONObject returnJson=JSONObject.parseObject(json); return returnJson.toJSONString(); } /** * 創建下單簽名 包含商品信息 * @return */ public String createWechatSign(WechatOrder wechatOrder){ String mch_id = wechatConfig.getMchId(); String notify_url = wechatConfig.getNotifyUrl(); String device_info = wechatConfig.getDeviceInfo(); String payKey = wechatConfig.getPaykey(); //將商品信息打包 SortedMap<String, String> parameters = new TreeMap<String, String>(); parameters.put("appid", wechatOrder.getAppid());//公眾號id 這地方一定要小寫並跟下面xml文件對應都是小寫 parameters.put("mch_id",mch_id);//商戶ID parameters.put("device_info", device_info); parameters.put("body", wechatOrder.getOrderName());//名稱 parameters.put("trade_type", wechatOrder.getTrade_type()); parameters.put("nonce_str", wechatOrder.getNonce_str());//隨機數 parameters.put("notify_url", notify_url); parameters.put("out_trade_no", wechatOrder.getOut_trade_no()); parameters.put("total_fee", wechatOrder.getTotal_fee()+""); // parameters.put("spbill_create_ip", spbill_create_ip ); parameters.put("openid", wechatOrder.getOpenid()); //根據上述的數據生成預支付訂單號的前面sign return PayUtils.getSign(parameters, payKey); } /** * 創建微信訂單 請求微信接口下單 * @return */ public String createWechatOrder(WechatOrder wechatOrder,HttpServletRequest request) throws Exception{ String mch_id = wechatConfig.getMchId(); String notify_url = wechatConfig.getNotifyUrl(); String device_info = wechatConfig.getDeviceInfo(); String createOrderURL = wechatConfig.getCreateOrderUrl(); //生成統一支付接口數據 String xml = "<xml>"+ "<appid>"+wechatOrder.getAppid()+"</appid>"+ "<body>"+wechatOrder.getOrderName()+"</body>"+ "<device_info>"+device_info+"</device_info>"+ "<mch_id>"+mch_id+"</mch_id>"+ "<nonce_str>"+wechatOrder.getNonce_str()+"</nonce_str>"+ "<notify_url>"+notify_url+"</notify_url>"+ "<openid>"+wechatOrder.getOpenid()+"</openid>"+ "<out_trade_no>"+wechatOrder.getOut_trade_no()+"</out_trade_no>"+ "<total_fee>"+wechatOrder.getTotal_fee()+"</total_fee>"+ "<trade_type>"+wechatOrder.getTrade_type()+"</trade_type>"+ "<sign>"+createWechatSign(wechatOrder)+"</sign>"+ "</xml>"; //調用統一支付接口 String result = PayUtils.httpsRequest(createOrderURL, "POST", xml); System.out.println("-----------------------------統一下單結果---------------------------"); System.out.println(result); Map<String, String> resultMap = null; resultMap=PayUtils.getH5PayMap(result,request); return resultMap.get("prepay_id"); //預支付ID,保存到數據庫中 } /** * 解析微信支付回調的結果 * @return */ public static String getOrderReturnStream(HttpServletRequest request) throws IOException { InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inStream.close(); return new String(outSteam.toByteArray(),"utf-8"); } /** * 獲取 WeixinOauth2Token * @return */ public WeixinOauth2Token getWeixinOauth2Token(String code){ //獲得微信公眾號的唯一標識 String appId = wechatConfig.getWechatAppId(); String appSecret = wechatConfig.getWechatAppSecret(); return AdvancedUtil.getOauth2AccessToken(appId, appSecret, code); } }
package com.snp.wechat.utils; import com.snp.common.utils.StringUtil; import com.snp.userManager.domain.UserWechatDO; import com.snp.wechat.model.bean.WeixinOauth2Token; import org.apache.commons.lang3.StringUtils; import com.alibaba.fastjson.JSONObject; import java.util.Date; /** * @author yuwei * 工具類 * 2016年12月21日 下午1:58:38 */ public class AdvancedUtil { public AdvancedUtil() { super(); // TODO Auto-generated constructor stub } /** * 獲取網頁授權憑證 * * @param appId 公眾賬號的唯一標識 * @param appSecret 公眾賬號的密鑰 * @param code * @return * @return WeixinAouth2Token */ public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) { WeixinOauth2Token wat = null; // 拼接請求地址 String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"; requestUrl = requestUrl.replace("APPID", appId); requestUrl = requestUrl.replace("SECRET", appSecret); requestUrl = requestUrl.replace("CODE", code); // 獲取網頁授權憑證 JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null); if (null != jsonObject) { try { wat = new WeixinOauth2Token(); wat.setAccessToken(jsonObject.getString("access_token")); wat.setExpiresIn(jsonObject.getInteger("expires_in")); wat.setRefreshToken(jsonObject.getString("refresh_token")); wat.setOpenId(jsonObject.getString("openid")); wat.setScope(jsonObject.getString("scope")); } catch (Exception e) { wat = null; int errorCode = jsonObject.getInteger("errcode"); String errorMsg = jsonObject.getString("errmsg"); System.out.println(errorCode); System.out.println(errorMsg); // log.error("獲取網頁授權憑證失敗 errcode:{} errmsg:{}", errorCode, errorMsg); } } return wat; } /** * 通過網頁授權獲取用戶信息 * * @param accessToken 網頁授權接口調用憑證 * @param openId 用戶標識 * @return SNSUserInfo */ public static UserWechatDO getWechatInfo(String accessToken, String openId,String refreshToken) { UserWechatDO userWechatDO = null; // 拼接請求地址 String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID"; requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId); // 通過網頁授權獲取用戶信息 JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null); if (!StringUtil.isEmpty(jsonObject)) { try { userWechatDO = new UserWechatDO(); // 用戶的標識 userWechatDO.setWechatOpenid(jsonObject.getString("openid")); // 昵稱 String nickname = jsonObject.getString("nickname"); nickname = filterEmoji(nickname); userWechatDO.setNickName(nickname); // 性別(1是男性,2是女性,0是未知) userWechatDO.setSex(jsonObject.getInteger("sex")); // 用戶所在國家 userWechatDO.setCountry(jsonObject.getString("country")); // 用戶所在省份 userWechatDO.setProvince(jsonObject.getString("province")); // 用戶所在城市 userWechatDO.setCity(jsonObject.getString("city")); // 用戶頭像 userWechatDO.setHeadImgUrl(jsonObject.getString("headimgurl")); //UnicodeID userWechatDO.setWechatUnionid(jsonObject.getString("unionid")); //userWechatDO.setWin(0); //userWechatDO.setLose(0); //查詢時間 // Date date = new Date(); // player.setJoinTime(date); //首次授權時間 userWechatDO.setCreateTime(new Date()); //更新時間 userWechatDO.setUpdateTime(new Date()); userWechatDO.setLasttIme(new Date()); //憑證保存 userWechatDO.setAccessToken(accessToken); //刷新憑證 userWechatDO.setRefreshToken(refreshToken); // 用戶特權信息 // snsUserInfo.setPrivilegeList(JSONArray.parseObject(jsonObject.getJSONArray("privilege"), List.class)); } catch (Exception e) { userWechatDO = null; int errorCode = jsonObject.getInteger("errcode"); String errorMsg = jsonObject.getString("errmsg"); System.out.println(errorCode); System.out.println(errorMsg); // log.error("獲取用戶信息失敗 errcode:{} errmsg:{}", errorCode, errorMsg); } } return userWechatDO; } //檢驗憑證是否失效 @SuppressWarnings("unused") public static boolean judgeToken(String accessToken, String openId){ // 拼接請求地址 String requestUrl = "https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID"; requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId); // 通過網頁授權獲取用戶信息 JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null); int errorCode = jsonObject.getInteger("errcode"); String errorMsg = jsonObject.getString("errmsg");//正確返回OK errorMsg = errorMsg.toUpperCase(); if(errorMsg.equals("OK")){ return true; } return false; } //去掉ios特殊字符 public static String filterEmoji(String source) { if (StringUtils.isBlank(source)) { return source; } StringBuilder buf = null; int len = source.length(); for (int i = 0; i < len; i++) { char codePoint = source.charAt(i); if (isNotEmojiCharacter(codePoint)) { if (buf == null) { buf = new StringBuilder(source.length()); } buf.append(codePoint); } } if (buf == null) { return source; } else { if (buf.length() == len) { buf = null; return source; } else { return buf.toString(); } } } //判斷特殊字符串 private static boolean isNotEmojiCharacter(char codePoint) { return (codePoint == 0x0) || (codePoint == 0x9) || (codePoint == 0xA) || (codePoint == 0xD) || ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) || ((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) || ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF)); } }
以上就是主要代碼。代碼寫的還不夠好,不喜勿噴