<正则吃饺子> :关于微信支付的简单总结说明(一)
关于支付,一直想参与开发,现在根据项目中已有及参见的微信开发文档,将自己对于微信开发的流程进行简单的总结,以备后用和帮助后来者。
一、相关官方文档
微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
二、参考博文
(待添加,等我找找..)
三、自己参看文档时候的简单文档整理,
ps:只为了方便自己记忆和联想
四、根据官方文档中,标记后台主要做的工作流程
五、参看项目代码时候,整理的demo,来源于网络
(1)MD5Util ---- 生成签名时候使用
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; } }
六、结合项目,微信支付流程解析
主要为微信支付官方文档提供的流程中,对应于 4、5、6、10、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"; }
注意:文章中所列出的代码不一定是最好的,只提供流程思路,方便理解和记忆。具体的实现方式,自己把握。比如,在生成支付签名时候,有多种方式来实现。
愿无岁月可回首 且以深情共白头