關於支付,一直想參與開發,如今根據項目中已有及參見的微信開發文檔,將本身對於微信開發的流程進行簡單的總結,以備後用和幫助後來者。php
微信支付官方文檔:https://pay.weixin.qq.com/wiki/doc/api/index.htmlhtml
(待添加,等我找找..)前端
ps:只爲了方便本身記憶和聯想java
(1)MD5Util ---- 生成簽名時候使用git
package com.weixin.test; import java.security.MessageDigest; /** * 微信測試MD5 * @author Administrator * */ 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" }; }
(2)PayTest算法
package com.weixin.test; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; public class PayTest { // http://mch.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3 private static String Key = "192006250b4c09247ec02edce69f6a2d"; /** * @param args */ public static void main(String[] args) { System.out.println(">>>模擬微信支付<<<"); System.out.println("==========華麗的分隔符=========="); // 微信api提供的參數 String appid = "wxd930ea5d5a258f4f"; String mch_id = "10000100"; String device_info = "1000"; String body = "test"; String nonce_str = "ibuaiVcKdpRxkhJA"; SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("appid", appid); parameters.put("mch_id", mch_id); parameters.put("device_info", device_info); parameters.put("body", body); parameters.put("nonce_str", nonce_str); String characterEncoding = "UTF-8"; String weixinApiSign = "9A0A8659F005D6984697E2CA0A9CF3B7"; System.out.println("微信的簽名是:" + weixinApiSign); String mySign = createSign(characterEncoding, parameters); System.out.println("我 的簽名是:" + mySign); if (weixinApiSign.equals(mySign)) { System.out.println("恭喜你成功了~"); } else { System.out.println("不行啊,再接再礪~"); } String userAgent = "Mozilla/5.0(iphone;CPU iphone OS 5_1_1 like Mac OS X) AppleWebKit/534.46(KHTML,like Geocko) Mobile/9B206 MicroMessenger/5.0"; char agent = userAgent.charAt(userAgent.indexOf("MicroMessenger") + 15); System.out.println("微信的版本號:" + new String(new char[] { agent })); } /** * 微信支付簽名算法sign * @param characterEncoding * @param parameters * @return */ @SuppressWarnings("unchecked") public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) { StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();// 全部參與傳參的參數按照accsii排序(升序) Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); Object v = entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + Key); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } }
6、結合項目,微信支付流程解析api
主要爲微信支付官方文檔提供的流程中,對應於 四、五、六、十、11微信
(1)網頁內請求生成支付訂單。主要工做就是前端(app)在將商品添加到購物車後下單購買或者當即購買時,在後臺系統中所生成的一個未支付訂單。網絡
下面只是項目中一個業務處理,能夠忽略,僅作參考和方便記憶。微信開發
/** * 生成訂單 * * @param order * @param baseUser * @return */ @Override public Response<String> createRechargeOrder(RechargeOrder order, BaseUser baseUser) { Response<String> response = new Response<>(); try { if (baseUser == null) { throw new BusinessException(ResultCode.ERROR_USER_UNLOGIN.getName()); } if (order == null) { throw new ArgumentException(ResultCode.ERROR_ORDER_SAVE_NULL.getName()); } if (!checkAmount(order.getActuralAmount(), order.getRechargeAmount())) { throw new ArgumentException("支付金額異常"); } order.setCustomerName(baseUser.getName()); order.setCustomerId(baseUser.getId()); if("customer".equals(baseUser.getType())){ Response<MemberCustomerUser> res = customerService.getCustomerById(baseUser.getId()); if (null != res && res.isSuccess()) { order.setCustomerPhone(res.getResult().getMobilePhone()); } }else if("shop".equals(baseUser.getType())){ MemberShopUser user = new MemberShopUser(); user.setId(baseUser.getId()); Response<MemberShopUser> res = terminalShopService.getShopUserById(user); if (null != res && res.isSuccess()) { order.setCustomerPhone(res.getResult().getMobilePhone()); } } if (StringUtils.isBlank(order.getSource())) { order.setSource(""); } //訂單狀態:01未支付 order.setOrderCode("10"); order.setOrderName("未支付"); //付款狀態:待付款 order.setPayStatus("0"); rechargeOrderManager.createRechargeOrder(order); //記錄訂單狀態日誌 if (order.getOrderId() != null && !"".equals(order.getOrderId())) { ConvenienceOrderLog orderLog = new ConvenienceOrderLog(); orderLog.setOrderId(String.valueOf(order.getOrderId())); orderLog.setStatusCode(10); orderLog.setStatusName("未支付"); orderLog.setStatusUserId(baseUser.id); orderLog.setStatusUser(baseUser.name); rechargeOrderManager.saveConvenienceOrderLog(orderLog); } response.setResult(String.valueOf(order.getOrderId())); } catch (ArgumentException a) { //參數異常不寫log日誌 response.setError(a.getMessage()); } catch (BusinessException b) { response.setError(b.getMessage()); log.error("保存訂單異常!緣由:{}", Throwables.getStackTraceAsString(b)); } catch (Exception e) { response.setError(e.getMessage()); log.error("保存訂單異常!緣由:{}", Throwables.getStackTraceAsString(e)); } return response; }
(2)調用統一下單api,交易請求,獲取微信後臺返回的交易支付預付單,對返回信息處理後,返回給前端(app)
/** * 微信支付 交易請求,生成預付單 -====== 公衆號 * * @param orderId * @param baseUser * @return */ @Override public Response<Map<String, String>> weChatPayRequest(String orderId, String openId, BaseUser baseUser) { Response<Map<String, String>> response = new Response<>(); Map<String, String> map = new HashMap<>(); try { if (Strings.isNullOrEmpty(orderId)) { throw new ArgumentException("交易編號爲空"); } if (Strings.isNullOrEmpty(openId)) { throw new ArgumentException("openId爲空"); } if (baseUser == null) { throw new BusinessException(ResultCode.ERROR_USER_UNLOGIN.getName()); } Response<String> res = this.getOrderAmount(orderId, "1", baseUser); int amount = 0; if (res.isSuccess() && res.getResult() != null) { amount = new BigDecimal(res.getResult()).multiply(new BigDecimal(100)).intValue(); } else { throw new BusinessException(res.getError()); } //生成請求參數XML--公衆號 String xml = this.createWeChatPayXml(orderId, openId, amount); //微信支付統一下單接口 String payRequestURL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; ClientCustomSSL clientCustomSSL = new ClientCustomSSL(); //發起請求 String result = clientCustomSSL.doPayRequest(payRequestURL, xml); result = result.replaceAll("\n", "").replaceAll("\r", "").replaceAll("\t", ""); log.error(result); //解析返回結果,從新處理以發給前端(app) Document doc = DocumentHelper.parseText(result); Map<String, Object> resultMap = XmlMapHandle.Dom2Map(doc); if (((String) resultMap.get("return_code")).equalsIgnoreCase("SUCCESS") && ((String) resultMap.get("result_code")).equals("SUCCESS")) { map.put("appid", (String) resultMap.get("appid")); map.put("mch_id", (String) resultMap.get("mch_id")); map.put("prepay_id", (String) resultMap.get("prepay_id")); map.put("package", "prepay_id=" + resultMap.get("prepay_id")); Long l = new Date().getTime() / 1000; Integer timestamp = Integer.parseInt(l.toString()); map.put("timestamp", timestamp.toString()); map.put("nonceStr", "aaronlovem" + timestamp.toString()); // createPaySignStr() ---- 生成支付簽名-公衆號,注意 :須要跟發起支付時候的,生成簽名的參數規則一致。總之,須要跟前端提交時候,微信生成的簽名一致。 String paySign = createPaySignStr(map.get("nonceStr").toString(), map.get("package"), map.get("timestamp")); map.put("paySign", paySign); map.put("openId", openId); response.setResult(map); } else { response.setSuccess(false); response.setError((String) resultMap.get("return_msg")); } } catch (ArgumentException a) { response.setError(a.getMessage()); } catch (BusinessException b) { response.setError(b.getMessage()); log.error("weChatPayRequest 異常!緣由:{}", Throwables.getStackTraceAsString(b)); } catch (RuntimeException c) { response.setError(c.getMessage()); log.error("weChatPayRequest 異常!緣由:{}", Throwables.getStackTraceAsString(c)); } catch (Exception e) { response.setError("微信支付異常!" + Throwables.getStackTraceAsString(e)); log.error("weChatPayRequest 異常!緣由:{}", Throwables.getStackTraceAsString(e)); } //返回給前端,微信支付訂單信息 return response; }
一個生成簽名的方式,還有其餘方式,僅作記錄,以下:
/** * 生成支付簽名-公衆號 * * @return */ public String createPaySignStr(String nonceStr, String packageStr, String timestamp) throws Exception { String[] arr = new String[5]; arr[0] = "appId=suwxb1d7b09dsaf81c92eb"; arr[1] = "nonceStr=" + nonceStr; arr[2] = "package=" + packageStr; arr[3] = "signType=MD5"; arr[4] = "timeStamp=" + timestamp; Arrays.sort(arr); String param = ""; for (int i = 0; i < arr.length; i++) { param += arr[i] + "&"; } param += "key=我是key呵呵呵呵呵"; log.error("微信支付請求參數:" + param); return MD5Util.MD5Encode(param, null).toUpperCase(); } /** * 生成支付簽名-B端、C端 * * @param noncestr * @param packagestr * @param prepayid * @param timestamp * @return */ public String createWeChatGoPayXmlForAPP(String noncestr, String packagestr, String prepayid, String timestamp, String type) { String appId = ""; String partnerId = ""; String key = ""; if ("B2C".equals(type)) { appId = "suwxb1d7bf09af81c92eb"; partnerId = "1322218523201"; key = "我是key"; } else if ("B2B".equals(type)) { appId = "suwxb1d7b09af81c92eb"; partnerId = "1352713d383902"; key = "我是key啊"; } String[] arr = new String[6]; arr[0] = "appid=" + appId; arr[1] = "noncestr=" + noncestr; arr[2] = "package=" + packagestr; arr[3] = "partnerid=" + partnerId; arr[4] = "prepayid=" + prepayid; arr[5] = "timestamp=" + timestamp; Arrays.sort(arr); String param = ""; for (int i = 0; i < arr.length; i++) { param += arr[i] + "&"; } param += "key=" + key; log.error("微信支付請求參數:" + param); return MD5Util.MD5Encode(param, null).toUpperCase(); }
b、c的交易請求,app支付:大致與公衆號支付相似,參數不一致
/** * 微信支付 交易請求,生成預付單,C端 * * @param orderId * @param baseUser * @return */ @Override public Response<Map<String, String>> weChatPayRequestForC(String orderId, BaseUser baseUser) { Response<Map<String, String>> response = new Response<Map<String, String>>(); Map<String, String> map = new HashMap<String, String>(); try { if (Strings.isNullOrEmpty(orderId)) { throw new ArgumentException("交易編號爲空"); } Response<String> res = this.getOrderAmount(orderId, "1", baseUser); int amount = 0; if (res.isSuccess() && res.getResult() != null) { amount = new BigDecimal(res.getResult()).multiply(new BigDecimal(100)).intValue(); } else { throw new BusinessException(res.getError()); } String xml = this.createWeChatPayXmlForAPP(orderId, amount, "B2C"); String payRequestURL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; ClientCustomSSL clientCustomSSL = new ClientCustomSSL(); String result = clientCustomSSL.doPayRequest(payRequestURL, xml); result = result.replaceAll("\n", "").replaceAll("\r", "").replaceAll("\t", ""); log.error(result); Document doc = DocumentHelper.parseText(result); Map<String, Object> resultMap = XmlMapHandle.Dom2Map(doc); if (((String) resultMap.get("return_code")).equalsIgnoreCase("SUCCESS") && ((String) resultMap.get("result_code")).equals("SUCCESS")) { Map<String, String> param = new HashMap<String, String>(); param.put("appid", (String) resultMap.get("appid")); param.put("partnerid", (String) resultMap.get("mch_id")); param.put("prepayid", (String) resultMap.get("prepay_id")); param.put("package", "Sign=WXPay"); Long l = new Date().getTime() / 1000; Integer timestamp = Integer.parseInt(l.toString()); param.put("noncestr", "xxxxxB2Capp" + timestamp.toString()); param.put("timestamp", timestamp.toString()); param.put("sign", this.createWeChatGoPayXmlForAPP(param.get("noncestr").toString(), param.get("package").toString(), param.get("prepayid").toString(), param.get("timestamp").toString(), "B2C")); response.setResult(param); } else { response.setSuccess(false); response.setError((String) resultMap.get("return_msg")); } } catch (ArgumentException a) { response.setError(a.getMessage()); } catch (BusinessException b) { response.setError(b.getMessage()); log.error("weChatPayRequest 異常!緣由:{}", Throwables.getStackTraceAsString(b)); } catch (RuntimeException c) { response.setError(c.getMessage()); log.error("weChatPayRequest 異常!緣由:{}", Throwables.getStackTraceAsString(c)); } catch (Exception e) { response.setError("微信支付異常!" + Throwables.getStackTraceAsString(e)); log.error("weChatPayRequest 異常!緣由:{}", Throwables.getStackTraceAsString(e)); } return response; } @Override public Response<Map<String, String>> weChatPayRequestForB(String orderId, BaseUser baseUser) { Response<Map<String, String>> response = new Response<Map<String, String>>(); Map<String, String> map = new HashMap<String, String>(); try { if (Strings.isNullOrEmpty(orderId)) { throw new ArgumentException("交易編號爲空"); } Response<String> res = this.getOrderAmount(orderId, "1", baseUser); int amount = 0; if (res.isSuccess() && res.getResult() != null) { amount = new BigDecimal(res.getResult()).multiply(new BigDecimal(100)).intValue(); } else { throw new BusinessException(res.getError()); } String xml = this.createWeChatPayXmlForAPP(orderId, amount, "B2B"); String payRequestURL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; ClientCustomSSL clientCustomSSL = new ClientCustomSSL(); String result = clientCustomSSL.doPayRequest(payRequestURL, xml); result = result.replaceAll("\n", "").replaceAll("\r", "").replaceAll("\t", ""); log.error(result); Document doc = DocumentHelper.parseText(result); Map<String, Object> resultMap = XmlMapHandle.Dom2Map(doc); if (((String) resultMap.get("return_code")).equalsIgnoreCase("SUCCESS") && ((String) resultMap.get("result_code")).equals("SUCCESS")) { Map<String, String> param = new HashMap<String, String>(); param.put("appid", (String) resultMap.get("appid")); param.put("partnerid", (String) resultMap.get("mch_id")); param.put("prepayid", (String) resultMap.get("prepay_id")); param.put("package", "Sign=WXPay"); Long l = new Date().getTime() / 1000; Integer timestamp = Integer.parseInt(l.toString()); param.put("noncestr", "xxxxB2Bapp" + timestamp.toString()); param.put("timestamp", timestamp.toString()); param.put("sign", this.createWeChatGoPayXmlForAPP(param.get("noncestr").toString(), param.get("package").toString(), param.get("prepayid").toString(), param.get("timestamp").toString(), "B2B")); response.setResult(param); } else { response.setSuccess(false); response.setError((String) resultMap.get("return_msg")); } } catch (ArgumentException a) { response.setError(a.getMessage()); } catch (BusinessException b) { response.setError(b.getMessage()); log.error("weChatPayRequest 異常!緣由:{}", Throwables.getStackTraceAsString(b)); } catch (RuntimeException c) { response.setError(c.getMessage()); log.error("weChatPayRequest 異常!緣由:{}", Throwables.getStackTraceAsString(c)); } catch (Exception e) { response.setError("微信支付異常!" + Throwables.getStackTraceAsString(e)); log.error("weChatPayRequest 異常!緣由:{}", Throwables.getStackTraceAsString(e)); } return response; }
(3)告知微信處理通知支付結果。此爲,咱們在後臺發起微信支付請求生成預訂單時候,傳入的回調函數的方法。微信後臺在處理完微信支付結果後,會發起請求,調用咱們已經準備好的回調函數地址。
回調函數中,能夠具體處理下咱們商戶系統中的一些業務,好比更新訂單狀態,提醒消息等等.....處理完成,返回微信後臺處理結果。
對應於微信支付下單api接口參數中的,以下:
/** * 微信支付回調函數 * * @param weChatPayReturn * @return */ @Override public String getWeChatPayReturnForOrder(WeChatPayReturn weChatPayReturn) { log.error("微信普通訂單回調,參數爲:" + weChatPayReturn.toString()); System.out.println("微信普通訂單回調,參數爲:" + weChatPayReturn.toString()); boolean flag = false; try { if (null != weChatPayReturn) { //保存微信回調信息 rechargeOrderManager.saveRechargeOrderWeChatPayInfo(weChatPayReturn); if (true) { //判斷該筆訂單是否在商戶網站中已經作過處理 //若是沒有作過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序 //若是有作過處理,不執行商戶的業務程序 flag = rechargeOrderManager.getWeChatPayReturnForOrder(weChatPayReturn); if (flag) { String successStr = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; return successStr; } else { return "fail"; } } } else { throw new ArgumentException("微信回調對象爲空!"); } } catch (ArgumentException a) { //參數異常不寫log日誌 log.error("微信回調對象異常!緣由:{}", Throwables.getStackTraceAsString(a)); } catch (BusinessException b) { log.error("微信回調對象異常!緣由:{}", Throwables.getStackTraceAsString(b)); } catch (Exception e) { log.error("微信回調對象異常!緣由:{}", Throwables.getStackTraceAsString(e)); } return "fail"; }
注意:文章中所列出的代碼不必定是最好的,只提供流程思路,方便理解和記憶。具體的實現方式,本身把握。好比,在生成支付簽名時候,有多種方式來實現。