赞
踩
最近在做订单支付,既然都做到这里,突然想起了博客,那么就来记录一波
1.应用场景:学生志愿填报推荐,线上付款。
2.数据流程
学生登录 - 数据采集 - 生成订单 - 前端发起支付 - 后端处理数据(调用微信接口,返回数据给前端) - 生成支付单 - 等待回调 - 处理回调数据 - 更改订单状态 - 生成推荐数据 (整体业务流程就是这样,但是主要写支付逻辑这一块)
3.代码展示
1)引入微信支付集成jar
<!--引入微信支付--> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-pay</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-miniapp</artifactId> <version>3.5.0</version> </dependency>
2)编写微信相关配置类
/** * 微信相关配置 */ @Data @Component @ConfigurationProperties(prefix = "wechat") public class WechatAccountConfig { // 小程序 appId private String mpAppId; // 小程序 appSecret private String mpAppSecret; // 微信支付ip private String ip; // 微信支付回调地址 private String wechatPayCallback; // 微信退款回调地址 private String weRefundCallback; , // 授权类型,此处只需填写 authorization_code private String grantType; // 微信登录认证地址,登录凭证校验 private String authCode2Session; // 微信token获取地址 private String accessToken; // 商户密钥 private String mchKey; //商户号 private String mchId; // 支付证书路径 private String keyPath; }
3)application.yml 文件配置
wechat: mpAppId: 。。。。 mpAppSecret: 。。。。 validateUrl: 。。。。 validateData: 。。。。 ip: 。。。。(按理说是获取支付用户的ip地址,微信那边必传但是不重要) wechatPayCallback: /weChat/pay/notify weRefundCallback: http:/weChat/pay/wxRefundNotify grantType: 。。。。 mchKey: 。。。。 mchId: 。。。。 keyPath: 。。。。
4)MallApplication 启动类增加微信相关
@Bean public WxMpService wxMpService(){ WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); return wxMpService; } @Bean public WxMpConfigStorage wxMpConfigStorage(){ WxMpInMemoryConfigStorage wxMpConfigStorage = new WxMpInMemoryConfigStorage(); wxMpConfigStorage.setAppId(wechatAccountConfig.getMpAppId()); wxMpConfigStorage.setSecret(wechatAccountConfig.getMpAppSecret()); return wxMpConfigStorage; } @Bean public WxPayConfig wxPayConfig(){ WxPayConfig payConfig = new WxPayConfig(); payConfig.setSignType(WxPayConstants.SignType.MD5); payConfig.setNotifyUrl(wechatAccountConfig.getWechatPayCallback()); payConfig.setAppId(wechatAccountConfig.getMpAppId()); payConfig.setKeyPath(wechatAccountConfig.getKeyPath()); payConfig.setMchId(wechatAccountConfig.getMchId()); payConfig.setMchKey(wechatAccountConfig.getMchKey()); return payConfig; } @Bean public WxPayService wxPayService(){ WxPayService wxPayService = new WxPayServiceImpl(); wxPayService.setConfig(wxPayConfig()); return wxPayService; }
5)重点来了 WeChatServerDto 支付相关
@Slf4j @Service public class WeChatServerDto { @Autowired private WechatAccountConfig config; @Autowired private WxPayConfig wxPayConfig; @Autowired private WxPayService wxPayService; @Autowired private OrderServiceDto orderServiceDto; /** * 发起支付 方法前后处理订单逻辑 * @param reqDto * @return */ public WxPayMpOrderResult payMpOrder(PayReqDto reqDto) throws SoaException { WxPayMpOrderResult unOrder = null; String tradeType = null; try { switch (reqDto.getPaymentType()){ case APP_WECHAT: tradeType = WxPayConstants.TradeType.APP; break; case SMALL_PROGRAM: tradeType = WxPayConstants.TradeType.JSAPI; if (StringUtils.isEmpty(reqDto.getOpenId())){ throw new SoaException("openid不能为空"); } WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest(); wxPayConfig.setKeyPath(config.getKeyPath()); wxPayConfig.setMchKey(config.getMchKey()); wxPayConfig.setMchId(config.getMchId()); wxPayConfig.setAppId(config.getMpAppId()); // JSAPI支付openid必须传 orderRequest.setOpenid(reqDto.getOpenId()); orderRequest.setBody("商品购买"); orderRequest.setAppid(config.getMpAppId()); orderRequest.setMchId(config.getMchId()); //NATIVE -- 处理回调 orderRequest.setAttach(reqDto.getEPayScene().name()); // 商户订单号 orderRequest.setOutTradeNo(reqDto.getOrderNo()); // 支付币种 人名币 orderRequest.setFeeType("CNY"); // 支付金额 单位分 orderRequest.setTotalFee(reqDto.getPayMoney()); // 终端ip orderRequest.setSpbillCreateIp(config.getIp()); // 微信支付回调地址 orderRequest.setNotifyUrl(config.getWechatPayCallback()); //支付类型 orderRequest.setTradeType(tradeType); log.info("微信支付订单{}创建参数: {}", tradeType, JSONObject.toJSONString(orderRequest)); unOrder = wxPayService.createOrder(orderRequest); log.info("微信支付订单{}创建返回结果: {}", tradeType, JSONObject.toJSONString(unOrder)); break; default: break; } }catch (Exception e){ throw new SoaException("发起支付失败!"); } return unOrder; } /** * 微信退款 方法前后处理订单逻辑 * @param refundReqDto * @return */ private WxPayRefundResult refund(RefundReqDto refundReqDto) throws SoaException { WxPayRefundResult wxPayRefundResult = null; try { wxPayConfig.setKeyPath(config.getKeyPath()); wxPayConfig.setMchKey(config.getMchKey()); wxPayConfig.setMchId(config.getMchId()); wxPayConfig.setAppId(config.getMpAppId()); WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest(); wxPayRefundRequest.setAppid(config.getMpAppId()); wxPayRefundRequest.setMchId(config.getMchId()); //设置微信订单号 wxPayRefundRequest.setTransactionId(refundReqDto.getTransactionId()); //订单号(自己的) wxPayRefundRequest.setOutTradeNo(refundReqDto.getOrderNo()); // 微信订单号,唯一,一个号码只能退一次,分批次退款需要 wxPayRefundRequest.setOutRefundNo(refundReqDto.getRefundNo()); //订单金额 单位分 wxPayRefundRequest.setTotalFee(refundReqDto.getTotalAmount()); //退款金额 单位分 wxPayRefundRequest.setRefundFee(refundReqDto.getAmount()); //退款货币种类 wxPayRefundRequest.setRefundFeeType("CNY"); // 退款原因 wxPayRefundRequest.setRefundDesc(refundReqDto.getRemark()); // 退款回调路径 wxPayRefundRequest.setNotifyUrl(config.getWeRefundCallback()); log.info("微信退款请求对象-->{}", wxPayRefundRequest.toString()); log.info("微信配置对象-->{}", wxPayConfig.toString()); log.info("微信退款订单号{}: 退款单号{}", refundReqDto.getOrderNo(), refundReqDto.getRefundNo()); wxPayRefundResult = wxPayService.refund(wxPayRefundRequest); log.info("微信退款订单{}:创建返回结果: {}", refundReqDto.getOrderNo(), JSONObject.toJSONString(wxPayRefundResult)); } catch (Exception e) { e.printStackTrace(); log.error("微信退款订单{}创建失败!退款单号:{},原因:{}",refundReqDto.getOrderNo(), refundReqDto.getRefundNo(), e.getMessage()); throw new SoaException("微信支付订单创建失败!订单号:" + refundReqDto.getOrderNo()); } return wxPayRefundResult; } /** * 微信支付回调 * @param xmlResult */ public void payNotify(String xmlResult) throws Exception{ //转化xml数据 WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlResult); log.info("微信回调报文-->{}", result.toString()); wxPayConfig.setKeyPath(config.getKeyPath()); wxPayConfig.setMchKey(config.getMchKey()); wxPayConfig.setMchId(config.getMchId()); wxPayConfig.setAppId(config.getMpAppId()); wxPayService.parseOrderNotifyResult(xmlResult); // 返回状态值 String returnCode = result.getReturnCode(); String resultCode = result.getResultCode(); if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) { // 回执订单号,付款时候穿入的, String outTradeNo = result.getOutTradeNo(); // 金额 Integer totalFee = result.getTotalFee(); // 最好是验证一下订单金额与微信回调金额是否一致 /*if (new BigDecimal(totalFee).compareTo(order != 0) { throw new JeecgBootException("金额错误"); }*/ // 订单完成时间 Date date = DateUtils.str2Date(result.getTimeEnd(), DateUtils.yyyymmddhhmmss); // 微信支付单号,可以理解为流水号,必须存好,退款时候需要 String transactionId = result.getTransactionId(); // 支付时候传入,用于区分具体逻辑 String attach = result.getAttach(); switch (EPayScene.valueOf(attach)){ case PAYMENT_PRODUCT: // 处理订单逻辑,支付单逻辑 orderServiceDto.payNotify(outTradeNo,totalFee,date,transactionId); break; default: break; } }else { throw new SoaException("微信支付回调失败"); } } /** * 微信退款回调 * @param xmlResult */ public void payWxRefundNotify(String xmlResult) throws SoaException { WxPayRefundNotifyResult wxPayRefundNotifyResult = WxPayRefundNotifyResult.fromXML(xmlResult, WxPayRefundNotifyResult.class); String returnCode = wxPayRefundNotifyResult.getReturnCode(); if ("SUCCESS".equals(returnCode)) { WxPayRefundNotifyResult.ReqInfo reqInfo = wxPayRefundNotifyResult.getReqInfo(); String transactionId = reqInfo.getTransactionId(); log.info("退款成功:微信订单号-->{}", transactionId); // 根据transactionId查询支付单 // 订单号 String outRefundNo = reqInfo.getOutRefundNo(); // 微信退款单号 String refundId = reqInfo.getRefundId(); // 商户退款单号 String outTradeNo = reqInfo.getOutTradeNo(); // 订单金额 Integer totalFee = reqInfo.getTotalFee(); // 申请退款金额 Integer refundFee = reqInfo.getRefundFee(); // 退款金额 reqInfo.getSettlementRefundFee(); // 退款成功时间 2017-12-15 09:46:01 String successTime = reqInfo.getSuccessTime(); // TODO 处理订单以及支付单逻辑 }else { throw new SoaException("微信支付退款回调失败"); } } }
6)回调Controller
@Slf4j @RestController @RequestMapping("/weChat") public class WeChatController { @Autowired private WeChatServerDto weChatServerDto; /** * 支付回调 * @return */ @RequestMapping(name = "/pay/notify",method = RequestMethod.GET) public String notify(HttpServletRequest request) { try { String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding()); weChatServerDto.payNotify(xmlResult); //处理退款订单 return WxPayNotifyResponse.success("处理成功!"); } catch (Exception e) { log.error(e.getMessage(), e); return WxPayNotifyResponse.fail(e.getMessage()); } } /** * 退款回调 */ @RequestMapping(name = "/pay/wxRefundNotify") public String wxRefundNotify(HttpServletRequest request) { try { String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding()); weChatServerDto.payWxRefundNotify(xmlResult); //处理退款订单 return WxPayNotifyResponse.success("处理成功!"); } catch (Exception e) { log.error(e.getMessage(), e); return WxPayNotifyResponse.fail(e.getMessage()); } } }
总结,发起支付的Dto其实不重要,支付类型本来是打算要做其他支付的,但是项目上用不到。。。。。。
作品原创不喜勿喷,如有雷同纯属巧合
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。