package com.imooc.config; import com.lly835.bestpay.config.WxPayH5Config; import com.lly835.bestpay.service.impl.BestPayServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public class WechatPayConfig { @Autowired private WechatAccountConfig accountConfig; @Bean public BestPayServiceImpl bestPayService() { BestPayServiceImpl bestPayService = new BestPayServiceImpl(); bestPayService.setWxPayH5Config(wxPayH5Config()); return bestPayService; } @Bean public WxPayH5Config wxPayH5Config() { WxPayH5Config wxPayH5Config = new WxPayH5Config(); wxPayH5Config.setAppId(accountConfig.getMpAppId()); wxPayH5Config.setAppSecret(accountConfig.getMpAppSecret()); wxPayH5Config.setMchId(accountConfig.getMchId()); wxPayH5Config.setMchKey(accountConfig.getMchKey()); wxPayH5Config.setKeyPath(accountConfig.getKeyPath()); wxPayH5Config.setNotifyUrl(accountConfig.getNotifyUrl()); return wxPayH5Config; } }
package com.imooc.service; import com.imooc.dto.OrderDTO; import com.lly835.bestpay.model.PayResponse; import com.lly835.bestpay.model.RefundResponse; public interface PayService { PayResponse create(OrderDTO orderDTO); PayResponse notify(String notifyData); RefundResponse refund(OrderDTO orderDTO); }
package com.imooc.service.impl; import com.imooc.dto.OrderDTO; import com.imooc.enums.ResultEnum; import com.imooc.exception.SellException; import com.imooc.service.OrderService; import com.imooc.service.PayService; import com.imooc.utils.JsonUtil; import com.imooc.utils.MathUtil; import com.lly835.bestpay.enums.BestPayTypeEnum; import com.lly835.bestpay.model.PayRequest; import com.lly835.bestpay.model.PayResponse; import com.lly835.bestpay.model.RefundRequest; import com.lly835.bestpay.model.RefundResponse; import com.lly835.bestpay.service.impl.BestPayServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @Slf4j public class PayServiceImpl implements PayService { private static final String ORDER_NAME = "微信点餐订单"; @Autowired private BestPayServiceImpl bestPayService; @Autowired private OrderService orderService; @Override public PayResponse create(OrderDTO orderDTO) { PayRequest payRequest = new PayRequest(); payRequest.setOpenid(orderDTO.getBuyerOpenid()); payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue()); payRequest.setOrderId(orderDTO.getOrderId()); payRequest.setOrderName(ORDER_NAME); payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5); log.info("【微信支付】发起支付, request={}", JsonUtil.toJson(payRequest)); PayResponse payResponse = bestPayService.pay(payRequest); log.info("【微信支付】发起支付, response={}", JsonUtil.toJson(payResponse)); return payResponse; } @Override public PayResponse notify(String notifyData) { //1. 验证签名 //2. 支付的状态 //3. 支付金额 //4. 支付人(下单人 == 支付人) // bestPayService.asyncNotify 已经验证了签名和支付状态 PayResponse payResponse = bestPayService.asyncNotify(notifyData); log.info("【微信支付】异步通知, payResponse={}", JsonUtil.toJson(payResponse)); //查询订单 OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId()); //判断订单是否存在 if (orderDTO == null) { log.error("【微信支付】异步通知, 订单不存在, orderId={}", payResponse.getOrderId()); throw new SellException(ResultEnum.ORDER_NOT_EXIST); } //判断金额是否一致(0.10 0.1) if (!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) { log.error("【微信支付】异步通知, 订单金额不一致, orderId={}, 微信通知金额={}, 系统金额={}", payResponse.getOrderId(), payResponse.getOrderAmount(), orderDTO.getOrderAmount()); throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR); } //修改订单的支付状态 orderService.paid(orderDTO); return payResponse; } /** * 退款 * @param orderDTO */ @Override public RefundResponse refund(OrderDTO orderDTO) { RefundRequest refundRequest = new RefundRequest(); refundRequest.setOrderId(orderDTO.getOrderId()); refundRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue()); refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5); log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest)); RefundResponse refundResponse = bestPayService.refund(refundRequest); log.info("【微信退款】response={}", JsonUtil.toJson(refundResponse)); return refundResponse; } }
package com.imooc.controller; import com.imooc.dto.OrderDTO; import com.imooc.enums.ResultEnum; import com.imooc.exception.SellException; import com.imooc.service.OrderService; import com.imooc.service.PayService; import com.lly835.bestpay.model.PayResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import java.util.Map; @Controller @RequestMapping("/pay") public class PayController { @Autowired private OrderService orderService; @Autowired private PayService payService; @GetMapping("/create") public ModelAndView create(@RequestParam("orderId") String orderId, @RequestParam("returnUrl") String returnUrl, Map<String, Object> map) { //1. 查询订单 OrderDTO orderDTO = orderService.findOne(orderId); if (orderDTO == null) { throw new SellException(ResultEnum.ORDER_NOT_EXIST); } //2. 发起支付 PayResponse payResponse = payService.create(orderDTO); map.put("payResponse", payResponse); map.put("returnUrl", returnUrl); return new ModelAndView("pay/create", map); } /** * 微信异步通知 * @param notifyData */ @PostMapping("/notify") public ModelAndView notify(@RequestBody String notifyData) { payService.notify(notifyData); //返回给微信处理结果 return new ModelAndView("pay/success"); } }
// create <script> function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId":"${payResponse.appId}", //公众号名称,由商户传入 "timeStamp":"${payResponse.timeStamp}", //时间戳,自1970年以来的秒数 "nonceStr":"${payResponse.nonceStr}", //随机串 "package":"${payResponse.packAge}", "signType":"MD5", //微信签名方式: "paySign":"${payResponse.paySign}" //微信签名 }, function(res){ // if(res.err_msg == "get_brand_wcpay_request:ok" ) { // } // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 location.href = "${returnUrl}"; } ); } 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>
// success <xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> </xml>
附
微信一旦成功发起了支付,这时,你在数据库改价格,发现再一次调用接口,还是原先未改的价格。这是因为一旦成功发起,微信服务器就会记录该订单信息,所以需要重新修改订单号等相关信息。
订单状态业务处理后需要返回给微信处理结果,否则微信会定时调用异步回调方法。
模板引擎:Freemarker,之所以要用这个,没有前后端分离,是因为前端可能反复需要写这么一段JSSDK,当然前端也可以去封装,这里只不过也可以在后端封装中间这个过程。