1. 微信官方文档
微信支付开发api文档
2. 初步流程
首先一个最简单的支付功能,大体分为三步 
- 预支付,后台系统跟微信后台交互,给小程序返回参数
统一下单 - 小程序拿到参数进行预支付
- 用户确认支付,//跟微信后台交互,这一步我们不需要管
- 微信后台回调我们的后台系统的接口, 处理我们自己的业务逻辑
回调处理支付结果
3.代码
这里用到了github上的一个依赖,相当于支付的工具包
dependency groupIdcom.github.javen205/groupId artifactIdIJPay/artifactId version1.1.3/version/dependency这边从前端小程序传过来的参数有: orderId – 我们系统这边的订单id openId – 小程序传过来的用户标识
我们配置文件中配置的固定参数有: appid – 小程序id mchId – 商户号 notify_url – 支付成功以后回调的我们写的接口地址 partnerKey – 商户平台设置的密钥key
封装返回结果的vo对象AjaxJson
/** * $.ajax后需要接受的JSON * * @author * */public class AjaxJson implements Serializable { /** * */ private static final long serialVersionUID = 1L; private boolean success = true;// 是否成功 private String msg = "操作成功";// 提示信息 private Object obj = null;// 其他信息 private ConcurrentMapString, Object attributes;// 其他参数 private String errorCode;// 错误码 private Integer totalSize;// 错误码 getter...setter方法}控制层
- 统一下单
/*** 小程序微信支付的第一步,统一下单* @return * @author zgd * @time 2018年7月9日17:53:35 */ @RequestMapping("/createUnifiedOrder") @ResponseBody public AjaxJson createUnifiedOrder(HttpServletRequest request) { AjaxJson aj = new AjaxJson(); aj.setSuccess(false); String orderId = request.getParameter("orderId"); //接受参数(openid) String openid = request.getParameter("openid"); if (StringUtils.isAnyBlank(orderId,openid)){ aj.setMsg("支付失败,支付所需参数缺失"); return aj; } //这里调用service层根据订单id获取订单数据,这里省略不表 MapString, String mapBasic = getOrderInfoById(orderId); if (mapBasic == null) { aj.setMsg("支付失败,暂时无法获取到您的订单数据,请稍后再试"); return aj; } String return_msg = "统一订单失败";try { //支付金额 **金额不能有小数点,单位是分!!** BigDecimal price = new BigDecimal(mapBasic.get("payAmount").toString()); BigDecimal beishu = new BigDecimal("100"); BigDecimal priceFee=price.multiply(beishu); //商家订单号 String orderNo = mapBasic.get("orderNo").toString(); //创建 时间戳 String timeStamp = Long.valueOf(System.currentTimeMillis()).toString(); //生成32位随机数 UUID uuid = UUID.randomUUID(); String nonceStr = uuid.toString().replaceAll("-",""); //商品描述 String body = "XX商城-支付订单"; //创建hashmap(用户获得签名) SortedMapString, String paraMap = new TreeMapString, String(); //设置请求参数(小程序ID) paraMap.put("appid", appid); //设置请求参数(商户号) paraMap.put("mch_id", mchId); //设置请求参数(随机字符串) paraMap.put("nonce_str", nonceStr); //设置请求参数(商品描述) paraMap.put("body", body); //设置请求参数(商户订单号) paraMap.put("out_trade_no", orderNo); //设置请求参数(总金额) paraMap.put("total_fee", priceFee.toBigInteger().toString()); //设置请求参数(终端IP) 如果是springmvc,或者能获取到request的servlet,用下面这种 paraMap.put("spbill_create_ip", request.getRemoteAddr()); //设置请求参数(通知地址) paraMap.put("notify_url",notify_url); //设置请求参数(交易类型) paraMap.put("trade_type", String.valueOf(WxPayApi.TradeType.JSAPI)); //设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置成'JSAPI'则必须传入openid) paraMap.put("openid", openid); //MD5运算生成签名,这里是第一次签名,用于调用统一下单接口 String sign = PaymentKit.createSign(paraMap, partnerKey); paraMap.put("sign", sign); //统一下单,向微信api发送数据 logger.info("微信小程序统一下单发送的数据: "+paraMap.toString()); String xmlResult = WxPayApi.pushOrder(false, paraMap); logger.info("微信小程序统一下单接受返回的结果: "+xmlResult); //转成xml MapString, String map = PaymentKit.xmlToMap(xmlResult); //返回状态码 String return_code = (String) map.get("return_code"); return_msg = return_msg+", "+ (String) map.get("return_msg"); //返回给小程序端需要的参数 MapString, String returnMap = new HashMapString, String(); if(Constants.SUCCESS.equals(return_code)){ //返回的预付单信息 returnMap.put("appId",appid); returnMap.put("nonceStr", nonceStr); String prepay_id = (String) map.get("prepay_id"); returnMap.put("package", "prepay_id=" + prepay_id); returnMap.put("signType","MD5"); //这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误 returnMap.put("timeStamp", timeStamp); //拼接签名需要的参数 //再次签名,这个签名用于小程序端调用wx.requesetPayment方法 String paySign = PaymentKit.createSign(returnMap, partnerKey).toUpperCase(); returnMap.put("paySign", paySign); aj.setObj(returnMap); aj.setMsg("操作成功"); aj.setSuccess(true); return aj; }else{ aj.setMsg(getMsgByCode(return_code)); logger.error(Thread.currentThread().getStackTrace()[1].getMethodName()+""+return_msg); } } catch (Exception e) { logger.error(Thread.currentThread().getStackTrace()[1].getMethodName() +"发生的异常是: ",e); aj.setMsg("微信支付下单失败,请稍后再试"); } return aj; }getMsgByCode
/** * 判断返回的return_code,给前端相应的提示 * @param return_code * @return * @author zgd * @time 2018年7月9日17:53:13 */ private String getMsgByCode(String return_code) { switch (return_code){ case "NOTENOUGH":return "您的账户余额不足"; case "ORDERPAID":return "该订单已支付完成,请勿重复支付"; case "ORDERCLOSED":return "当前订单已关闭,请重新下单"; case "SYSTEMERROR":return "系统超时,请重新支付"; case "OUT_TRADE_NO_USED":return "请勿重复提交该订单"; default:return "网络正在开小差,请稍后再试"; } }2.回调
比如上面配置的notify_url是http://系统的ip和端口/wxPay/notify
@RequestMapping("/wxPay")public class WxPayController{ private String paternerKey = "XXXXXX"; @RequestMapping("/notify") public void notify(HttpServletRequest request){ //获取所有的参数 StringBuffer sbf=new StringBuffer(); // 支付结果通用通知文档: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 String xmlMsg = HttpKit.readData(request); System.out.println("支付通知="+xmlMsg); MapString, String params = PaymentKit.xmlToMap(xmlMsg); String result_code = params.get("result_code"); //校验返回来的支付结果,根据已经配置的密钥 if(PaymentKit.verifyNotify(params, paternerKey)){ //Constants.SUCCESS="SUCCESS" if ((Constants.SUCCESS).equals(result_code)) { 校验通过. 更改订单状态为已支付, 修改库存 } } }}拼接参数为字符串的工具方法
public static String readData(HttpServletRequest request) { BufferedReader br = null; try { StringBuilder ret; br = request.getReader(); String line = br.readLine(); if (line != null) { ret = new StringBuilder(); ret.append(line); } else { return ""; } while ((line = br.readLine()) != null) { ret.append('').append(line); } return ret.toString(); } catch (IOException e) { throw new RuntimeException(e); } finally { if (br != null) { try {br.close();} catch (IOException e) {LogKit.error(e.getMessage(), e);} } } }













