当前位置:   article > 正文

微信支付踩坑血泪史(JAVA -V3版本)_weixin-java-pay

weixin-java-pay


背景介绍

最近第一次接触到微信支付,踩了很多坑,赶紧写下来,全是心酸和眼泪。话不多说。开始展示。

项目中使用的接入方式是JSAPI(参考 产品中心 - 微信支付商户平台),当然也可以使用其他接入方式,根据需求来。

微信支付提供了两种对接方式(SDK,工具 | 微信支付商户平台文档中心)。一种是SDK,一种是Client,我个人偏向于SDK,因为它更简单,基本都封装好了。

项目中我们选择的非官方SDK binarywang,没什么别的原因,主要是更方便。

官网:Binary Wang

GitHub:GitHub - binarywang/weixin-java-pay-demo: 基于Spring Boot 和 WxJava 实现的微信支付Java后端Demo

一、接入前准备

接入前需要创建账号等步骤,可以参考官方文档:JSAPI支付-接入前准备 | 微信支付商户平台文档中心​​​​​​​x

二、引入依赖

  1. <dependency>
  2. <groupId>com.github.binarywang</groupId>
  3. <artifactId>weixin-java-mp</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>com.github.binarywang</groupId>
  7. <artifactId>weixin-java-pay</artifactId>
  8. </dependency>

三、编写配置文件

开发环境下可以把秘钥存到本地,线上建议放到服务器。

因为支付会有回调和退款等操作,本地接口不能被访问。建议使用内网穿透工具进行测试(Sunny-Ngrok内网转发内网穿透 - 国内内网映射服务器

  1. wechat-pay:
  2. #商户id
  3. mchId: 111111
  4. #公众号appid
  5. appid: wxce9662d47d419qwvw
  6. #APIv3密钥
  7. apiV3key: oLV98ZUF1Kdsfwefgwq7nCazqpew
  8. #商户证书序列号
  9. serialNo: 234235r598C803FersvdvB380A4F9CA77C8538AE7F
  10. #商户私钥
  11. privateKeyPath: E:/Project/devOps/key/apiclient_key.pem
  12. privateCertPath: E:/Project/devOps/key/apiclient_cert.pem
  13. #接收结果通知地址
  14. notifyUrl: http://howfhu.frfe.idcwfwgye.com/notify/pay/wx
  15. #退款通知地址
  16. refundNotifyUrl: http://hu.free.idefengye.com/notify/refund/wx

四、数据库设计

订单表


退款表 支付单表

 订单与支付单和退款单关系为一对多 不同用户扫码会产生多个支付记录,同样退款有多个退款记录。业务代码需要做支付幂等,同一笔不允许重复支付,重复支付需要调用退款接口。

 五、业务代码

 1.控制层

  1. @Api(tags = "订单管理")
  2. @RestController
  3. public class OrderController {
  4. @Autowired
  5. private OrderService orderService;
  6. @Operation(summary = "提交订单")
  7. @PostMapping("/order/create")
  8. public BizResult<String> createOrder(@Valid @RequestBody OrderCreateReq req) {
  9. String orderNO = orderService.createOrder(req);
  10. return BizResult.create(orderNO);
  11. }
  12. @Operation(summary = "订单详情")
  13. @GetMapping("/order/detail")
  14. public BizResult<OrderRsp> orderDetail(@RequestParam String orderNo) {
  15. OrderRsp orderRsp = orderService.orderDetail(orderNo);
  16. return BizResult.create(orderRsp);
  17. }
  18. @Operation(summary = "轮询订单")
  19. @GetMapping("/order/polling")
  20. public BizResult<Boolean> pollingOrder(@RequestParam String orderNo) {
  21. Boolean result = orderService.pollingOrder(orderNo);
  22. return BizResult.create(result);
  23. }
  24. @Operation(summary = "订单列表")
  25. @PostMapping("/order/list")
  26. public BizResult<PageData<OrderListRsp>> queryOrderList(@RequestBody OrderListReq req) {
  27. req.check();
  28. PageData<OrderListRsp> pageData = orderService.queryOrderList(req);
  29. return BizResult.create(pageData);
  30. }
  31. }

2.业务层

  1. @Slf4j
  2. @Service
  3. public class OrderService {
  4. /**
  5. * 创建订单
  6. */
  7. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  8. public String createOrder(OrderCreateReq req)
  9. //判断用户是否短时间多次提交
  10. // 加锁 RLock rLock
  11. try {
  12. //查询商品是否存在 不存在处理异常
  13. OrderDO orderDO = OrderConverter.init(req, traceCodeGoodsDO, merchantDO, orderTypeEnum);
  14. orderMapper.insert(orderDO);
  15. return orderDO.getOrderNo();
  16. } finally {
  17. if (rLock.isHeldByCurrentThread()) {
  18. rLock.unlock();
  19. }
  20. }
  21. }
  22. /**
  23. * 查询订单详情
  24. */
  25. public OrderRsp orderDetail(String orderNo) {
  26. if (StringUtils.isBlank(orderNo)) {
  27. throw BizException.create(BaseErrorCodeEnum.PARAMS_ERROR, "订单号不存在");
  28. }
  29. //查询订单
  30. OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
  31. if (Objects.isNull(orderDO)) {
  32. throw BizException.create(BizErrorCodeEnum.ORDER_NOT_EXITS);
  33. }
  34. return OrderConverter.toOrderRsp(orderDO);
  35. }
  36. /**
  37. * 订单轮询 前端轮询这个接口 判断当前订单状态 进行响应状态修改
  38. */
  39. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  40. public Boolean pollingOrder(String orderNo) {
  41. if (StringUtils.isBlank(orderNo)) {
  42. throw BizException.create(BaseErrorCodeEnum.PARAMS_ERROR, "订单号不存在");
  43. }
  44. OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
  45. if (Objects.isNull(orderDO)) {
  46. throw BizException.create(BizErrorCodeEnum.ORDER_NOT_EXITS);
  47. }
  48. //状态支付中 将状态改为未支付
  49. if (Objects.equals(orderDO.getStatus(), OrderStatusEnum.PAYING.getCode())) {
  50. orderMapper.updateStatus(orderNo, OrderStatusEnum.UNPAID.getCode(), orderDO.getStatus());
  51. return false;
  52. }
  53. //未支付
  54. if (Objects.equals(orderDO.getStatus(), OrderStatusEnum.UNPAID.getCode())) {
  55. return false;
  56. }
  57. String payNo = orderDO.getPayNo();
  58. if (StringUtils.isBlank(payNo)) {
  59. return false;
  60. }
  61. //查询支付单是否存在
  62. PaymentDO paymentDO = paymentMapper.findByPayNo(payNo);
  63. if (Objects.isNull(paymentDO)) {
  64. log.error("轮询订单,支付单不存在。orderNo:{},payNo:{}", orderDO, payNo);
  65. return false;
  66. }
  67. if (!Objects.equals(orderDO.getStatus(), OrderStatusEnum.UNPAID.getCode()) && !Objects.equals(orderDO.getStatus(), OrderStatusEnum.PAYING.getCode())) {
  68. // 查询需要取消支付单记录
  69. List<PaymentDO> paymentList = paymentMapper.queryPaymentList(orderDO.getOrderNo(), PayStatusEnum.UNPAID.getCode(), paymentDO.getPayNo());
  70. if (ObjectUtil.isNotEmpty(paymentList)) {
  71. // 修改支付单取消状态
  72. paymentMapper.cancelPay(orderDO.getOrderNo(), PayStatusEnum.CANCEL.getCode(), paymentDO.getPayNo(), PayStatusEnum.UNPAID.getCode());
  73. }
  74. return true;
  75. }
  76. PayStatusEnum payStatusEnum = LocalEnumUtils.findByCodeWithoutDefault(PayStatusEnum.class, paymentDO.getStatus());
  77. if (Objects.equals(payStatusEnum, PayStatusEnum.CANCEL)
  78. || Objects.equals(payStatusEnum, PayStatusEnum.FAIL)) {
  79. //取消支付或支付失败,可重新扫码创建支付单
  80. return false;
  81. } else if (Objects.equals(payStatusEnum, PayStatusEnum.UNPAID)) {
  82. //支付单待支付,查询第三方渠道
  83. WxPayOrderQueryV3Result wxPayOrderQueryV3Result = null;
  84. try {
  85. WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request();
  86. request.setOutTradeNo(paymentDO.getPayNo());
  87. wxPayOrderQueryV3Result = wxManager.getWxPayService().queryOrderV3(request);
  88. } catch (WxPayException e) {
  89. log.error("查询微信支付订单失败。payNo:{}", paymentDO.getPayNo(), e);
  90. return false;
  91. }
  92. //查询第三方支付信息并更新订单
  93. if (orderManager.queryOrderByWx(wxPayOrderQueryV3Result, orderDO, paymentDO, payStatusEnum)) {
  94. producerHelper.sendOrderMsg(orderDO);
  95. return true;
  96. }
  97. } else {
  98. //其他状态,更新订单状态。
  99. int orgOrderStatus = orderDO.getStatus();
  100. OrderStatusEnum tarOrderStatus = OrderStatusEnum.PAID;
  101. if (Objects.equals(payStatusEnum, PayStatusEnum.REFUND)) {
  102. tarOrderStatus = OrderStatusEnum.REFUND;
  103. orderDO.setRefundTime(paymentDO.getRefundTime());
  104. orderDO.setRefundAmount(paymentDO.getRefundAmount());
  105. }
  106. orderDO.setFinishTime(paymentDO.getPayTime());
  107. orderDO.setStatus(tarOrderStatus.getCode());
  108. orderDO.setUpdateTime(new Date());
  109. int i = orderMapper.updateStatusByOrderNo(orderDO, orgOrderStatus);
  110. if (i > 0) {
  111. producerHelper.sendOrderMsg(orderDO);
  112. }
  113. return true;
  114. }
  115. return false;
  116. }
  117. /**
  118. * jsapi下单并返回前端唤起参数
  119. */
  120. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  121. public OrderJsApiRsq jsapiPay(JsAPIReq req) {
  122. String orderNo = req.getOrderNo();
  123. String openId = req.getOpenid();
  124. // 加锁 RLock lock
  125. if (lock == null) {
  126. throw BizException.create(CommonErrorCodeEnum.RESUBMIT_ERROR, "订单支付中");
  127. }
  128. //查询订单
  129. OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
  130. if (Objects.isNull(orderDO)) {
  131. throw BizException.create(BizErrorCodeEnum.ORDER_NOT_EXITS);
  132. }
  133. Integer orgStatus = orderDO.getStatus();
  134. //校验重复支付
  135. if (Objects.equals(orgStatus, OrderStatusEnum.PAID.getCode())) {
  136. throw BizException.create(BizErrorCodeEnum.ORDER_PAY, "订单已支付,请勿重复支付");
  137. }
  138. // 查询支付单,没有则新增
  139. // 对比openid,如果一样直接发起支付,不一样新增支付单
  140. PaymentDO paymentDO = paymentMapper.findByPayNo(orderDO.getPayNo());
  141. if (Objects.isNull(paymentDO) || !Objects.equals(paymentDO.getOpenid(), req.getOpenid())) {
  142. //插入支付单数据
  143. paymentMapper.insert(paymentDO);
  144. //更新订单号中支付单号
  145. // 幂等更新
  146. int i = orderMapper.updateStatusByOrderNo(orderDO, orgStatus);
  147. if (i == 0) {
  148. throw BizException.create(BizErrorCodeEnum.ORDER_PAY, "订单已支付");
  149. }
  150. }
  151. WxPayUnifiedOrderV3Request request = WxPayConverter.toWxPayUnifiedOrderV3Request(orderDO, paymentDO.getPayNo(), openId);
  152. WxPayUnifiedOrderV3Result.JsapiResult result = null;
  153. try {
  154. result = wxManager.getWxPayService().createOrderV3(TradeTypeEnum.JSAPI, request);
  155. } catch (WxPayException e) {
  156. log.error("JSAPI下单支付异常,orderNo:{}", orderNo, e);
  157. throw BizException.create(BizErrorCodeEnum.ORDER_UNIFIED_ERROR, "支付唤起失败");
  158. } finally {
  159. if (lock.isHeldByCurrentThread()) {
  160. lock.unlock();
  161. }
  162. }
  163. return WxPayConverter.toOrderJsApiRsq(result);
  164. }
  165. /**
  166. * 支付通知
  167. */
  168. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  169. public OrderPayNotify payNotify(JSONObject jsonObject, HttpServletRequest request, HttpServletResponse response) {
  170. log.info("微信支付结果通知:{}", jsonObject);
  171. //获取请求头
  172. SignatureHeader header = PayUtil.getWXRequestHeader(request);
  173. //请求体
  174. //解析回调通知
  175. WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = getWxPayOrderNotifyV3Result(jsonObject, header);
  176. WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
  177. WxPayTradeStateEnum wxPayStatus = WxPayTradeStateEnum.findByCode(result.getTradeState());
  178. //中间态,直接返回,继续通知
  179. if (Objects.equals(wxPayStatus, WxPayTradeStateEnum.NOT_PAY)
  180. || Objects.equals(wxPayStatus, WxPayTradeStateEnum.ACCEPT)
  181. || Objects.equals(wxPayStatus, WxPayTradeStateEnum.USER_PAYING)) {
  182. log.error("微信支付回调,中间态继续通知,payNo:{}", wxPayOrderNotifyV3Result.getResult().getOutTradeNo());
  183. response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
  184. return OrderPayNotify.fail();
  185. }
  186. //加锁RLock lock
  187. if (lock == null) {
  188. throw BizException.create(BizErrorCodeEnum.PAY_FAIL);
  189. }
  190. try {
  191. //查询支付单
  192. PaymentDO paymentDO
  193. log.info("支付单结果解析:{},支付单号{}", paymentDO, paymentDO.getPayNo());
  194. //查询订单
  195. OrderDO orderDO
  196. log.info("订单结果解析:{},支付单号{}", orderDO, orderDO.getOrderNo());
  197. Integer status = paymentDO.getStatus();
  198. //订单已支付 订单状态不等于未支付和支付中
  199. if (!Objects.equals(orderDO.getStatus(), OrderStatusEnum.UNPAID.getCode())) {
  200. if (!Objects.equals(orderDO.getStatus(), OrderStatusEnum.PAYING.getCode())) {
  201. //订单已支付 判断是否同一支付单, 是则表示同一笔订单重复支付
  202. if (!Objects.equals(orderDO.getPayNo(), paymentDO.getPayNo())) {
  203. //支付通知状态
  204. RefundDO refundDO = null;
  205. if (Objects.equals(wxPayStatus, WxPayTradeStateEnum.SUCCESS)) {
  206. //重复支付,新增退款记录
  207. refundDO = RefundConverter.init(paymentDO);
  208. refundMapper.insert(refundDO);
  209. paymentDO.setRefundTime(refundDO.getRefundTime());
  210. paymentDO.setRefundAmount(refundDO.getRefundAmount());
  211. //申请退款
  212. WxPayRefundV3Request wxPayRefundV3Request = WxPayConverter.toWxPayRefundV3(refundDO);
  213. wxPayRefundV3Request.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());
  214. WxPayRefundV3Result wxPayRefundV3Result = wxManager.getWxPayRefundService().refundV3(wxPayRefundV3Request);
  215. log.info("申请退款结果解析:{}", wxPayRefundV3Result);
  216. }
  217. //更新支付单
  218. paymentDO.setStatus(PayStatusEnum.REFUNDING.getCode());
  219. paymentMapper.updatePayStatus(paymentDO, status);
  220. response.setStatus(HttpStatus.SC_OK);
  221. return OrderPayNotify.success();
  222. }
  223. response.setStatus(HttpStatus.SC_OK);
  224. return OrderPayNotify.success();
  225. }
  226. }
  227. //未支付订单
  228. //更新订单和支付单状态
  229. if (orderManager.payNotify(result, orderDO, paymentDO, wxPayStatus)) {
  230. //生成码包
  231. producerHelper.sendOrderMsg(orderDO);
  232. }
  233. response.setStatus(HttpStatus.SC_OK);
  234. return OrderPayNotify.success();
  235. } catch (Exception e) {
  236. log.error("微信回调结果异常,第三方流水号{},异常支付单号{}", result.getTransactionId(), wxPayOrderNotifyV3Result.getResult().getOutTradeNo(), e);
  237. response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
  238. return OrderPayNotify.fail();
  239. } finally {
  240. if (lock.isHeldByCurrentThread()) {
  241. lock.unlock();
  242. }
  243. }
  244. }
  245. /**
  246. * 退款通知
  247. */
  248. @Transactional(rollbackFor = Exception.class)
  249. public OrderPayNotify payRefundNotify(JSONObject jsonObject, HttpServletRequest request, HttpServletResponse response) {
  250. //获取请求头
  251. SignatureHeader header = PayUtil.getWXRequestHeader(request);
  252. //解析回调
  253. WxPayRefundNotifyV3Result wxPayRefundNotifyV3Result = getWxPayRefundNotifyV3Result(jsonObject, header);
  254. WxPayRefundNotifyV3Result.DecryptNotifyResult result = wxPayRefundNotifyV3Result.getResult();
  255. log.info("微信退款结果通知:{}", result);
  256. WxPayRefundStateEnum wxPayRefundStateEnum = WxPayRefundStateEnum.findByCode(result.getRefundStatus());
  257. //退款关闭或者退款异常,直接返回,继续通知
  258. if (Objects.equals(wxPayRefundStateEnum, WxPayRefundStateEnum.CLOSED) || Objects.equals(wxPayRefundStateEnum, WxPayRefundStateEnum.ABNORMAL)
  259. ) {
  260. log.error("微信退款异常回调,退款单号,refundNo:{}", result.getOutRefundNo());
  261. response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
  262. return OrderPayNotify.fail();
  263. }
  264. //加锁 RLock lock
  265. if (lock == null) {
  266. throw BizException.create(BizErrorCodeEnum.REFUND_NOTIFY_FAIL);
  267. }
  268. try {
  269. //查询退款单
  270. RefundDO refundDo
  271. log.info("微信退款单解析:{},退款单号", refundDo, refundDo.getRefundNo());
  272. //查询支付单
  273. PaymentDO payment
  274. log.info("支付单结果解析:{},支付单号{}", payment, payment.getPayNo());
  275. //查询订单
  276. OrderDO order
  277. log.info("订单结果解析:{},订单号{}", order, order.getOrderNo());
  278. Integer paymentStatus = payment.getStatus();
  279. Integer orderStatus = order.getStatus();
  280. Integer refundStatus = refundDo.getStatus();
  281. //退款单状态退款中 并且退款通知成功 更新支付单和退款单
  282. if (Objects.equals(refundStatus, RefundStateEnum.REFUNDING.getCode())) {
  283. //更新退款单
  284. if (!Objects.equals(refundStatus, RefundStateEnum.SUCCESS.getCode())) {
  285. refundDo = RefundConverter.toRefund(wxPayRefundNotifyV3Result, refundDo);
  286. int r = refundMapper.updateStatus(refundDo, refundStatus);
  287. if (r == 0) {
  288. throw BizException.create(BizErrorCodeEnum.REFUND_UPDATE_FAIL, "退款单更新失败");
  289. }
  290. }
  291. //更新支付单
  292. payment.setStatus(PayStatusEnum.REFUND.getCode());
  293. payment.setRefundTime(DateUtil.parse(result.getSuccessTime()));
  294. BigDecimal refundAmount = LocalMoneyUtils.fen2yuan(Long.valueOf(result.getAmount().getRefund()));
  295. payment.setPayAmount(refundAmount);
  296. int p = paymentMapper.updatePayStatus(payment, paymentStatus);
  297. if (p == 0) {
  298. throw BizException.create(BizErrorCodeEnum.PAYMENT_UPDATE_FAIL, "支付单状态更新失败");
  299. }
  300. response.setStatus(HttpStatus.SC_OK);
  301. return OrderPayNotify.success();
  302. }
  303. response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
  304. return OrderPayNotify.fail();
  305. } catch (Exception e) {
  306. response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
  307. return OrderPayNotify.fail();
  308. } finally {
  309. if (lock.isHeldByCurrentThread()) {
  310. lock.unlock();
  311. }
  312. }
  313. }
  314. /**
  315. * 订单列表
  316. */
  317. public PageData<OrderListRsp> queryOrderList(OrderListReq req, MerchantContext loginUser) {
  318. Long count = orderMapper.orderTotal(req, loginUser.getMerchantId());
  319. if (count <= 0) {
  320. return PageData.create(Lists.newArrayList(), count, req.getPage(), req.getSize());
  321. }
  322. List<OrderDO> orderList = orderMapper.queryOrderList(req, loginUser.getMerchantId());
  323. List<OrderListRsp> OrderRsp = OrderConverter.toOrderListReq(orderList);
  324. return PageData.create(OrderRsp, count, req.getPage(), req.getSize());
  325. }
  326. /**
  327. * 关闭订单
  328. */
  329. @Transactional(rollbackFor = Exception.class)
  330. public boolean closeOrder(String orderNo) throws WxPayException {
  331. //查询订单是否已支付
  332. OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
  333. if (!OrderStatusEnum.UNPAID.getCode().equals(orderDO.getStatus())) {
  334. return false;
  335. }
  336. Date expireTime = orderDO.getExpireTime();
  337. Date date = new Date();
  338. if (expireTime.compareTo(date) > 0) {
  339. //时间未到
  340. return false;
  341. }
  342. log.info("订单超期关闭,单号为{}", orderDO.getOrderNo());
  343. orderDO.setCloseTime(date);
  344. orderDO.setUpdateTime(date);
  345. orderDO.setStatus(OrderStatusEnum.CLOSED.getCode());
  346. int i = orderMapper.updateStatusByOrderNo(orderDO, OrderStatusEnum.UNPAID.getCode());
  347. if (i == 0) {
  348. //订单状态以改变
  349. return false;
  350. }
  351. //关闭支付单
  352. PaymentDO paymentDO = paymentMapper.findByPayNo(orderDO.getPayNo());
  353. if (Objects.isNull(paymentDO)) {
  354. return false;
  355. }
  356. //更新支付单
  357. Integer payStatus = paymentDO.getStatus();
  358. paymentDO.setStatus(PayStatusEnum.CANCEL.getCode());
  359. paymentDO.setUpdateTime(date);
  360. paymentMapper.updatePayStatus(paymentDO, payStatus);
  361. //第三方渠道关单
  362. WxPayOrderCloseV3Request request = new WxPayOrderCloseV3Request();
  363. request.setOutTradeNo(paymentDO.getPayNo());
  364. wxManager.getWxPayService().closeOrderV3(request);
  365. return true;
  366. }
  367. /**
  368. * 解析微信支付回调通知
  369. */
  370. private WxPayOrderNotifyV3Result getWxPayOrderNotifyV3Result(JSONObject jsonObject, SignatureHeader header) {
  371. WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = null;
  372. try {
  373. wxPayOrderNotifyV3Result = wxManager.getWxPayService().parseOrderNotifyV3Result(jsonObject.toJSONString(), header);
  374. log.info("微信支付结果解析:{}", wxPayOrderNotifyV3Result.getResult());
  375. } catch (Exception e) {
  376. log.info("微信支付结果异常解析:{},异常信息{}", wxPayOrderNotifyV3Result.getResult(), e);
  377. }
  378. return wxPayOrderNotifyV3Result;
  379. }
  380. /**
  381. * 解析微信退款回调通知
  382. */
  383. private WxPayRefundNotifyV3Result getWxPayRefundNotifyV3Result(JSONObject jsonObject, SignatureHeader header) {
  384. WxPayRefundNotifyV3Result wxPayRefundNotifyV3Result = null;
  385. try {
  386. wxPayRefundNotifyV3Result = wxManager.getWxPayRefundService().parseRefundNotifyV3Result(jsonObject.toJSONString(), header);
  387. log.info("微信退款结果解析:{}", wxPayRefundNotifyV3Result.getResult());
  388. } catch (WxPayException e) {
  389. log.info("微信退款结果异常解析:{},异常信息{}", wxPayRefundNotifyV3Result.getResult(), e);
  390. }
  391. return wxPayRefundNotifyV3Result;
  392. }
  393. }

3.manager

  1. @Slf4j
  2. @Component
  3. public class WxManager {
  4. @Autowired
  5. WxPayProperties wxPayProperties;
  6. @Resource
  7. private RedissonClient redissonClient;
  8. @Autowired
  9. private WxMpProperties wxMpProperties;
  10. public WxMpService getWxMpService() {
  11. WxMpRedissonConfigImpl config = new WxMpRedissonConfigImpl(redissonClient, CommonConstants.NAMESPACE);
  12. config.setAppId(wxMpProperties.getAppId());
  13. config.setSecret(wxMpProperties.getSecret());
  14. WxMpService service = new WxMpServiceImpl();
  15. service.setWxMpConfigStorage(config);
  16. return service;
  17. }
  18. public WxPayService getWxPayService() {
  19. WxPayConfig payConfig = new WxPayConfig();
  20. payConfig.setAppId(wxPayProperties.getAppid());
  21. payConfig.setMchId(wxPayProperties.getMchId());
  22. payConfig.setPrivateKeyPath(wxPayProperties.getPrivateKeyPath());
  23. payConfig.setNotifyUrl(wxPayProperties.getNotifyUrl());
  24. payConfig.setApiV3Key(wxPayProperties.getApiV3key());
  25. payConfig.setPrivateCertPath(wxPayProperties.getPrivateCertPath());
  26. WxPayService wxPayService = new WxPayServiceImpl();
  27. wxPayService.setConfig(payConfig);
  28. return wxPayService;
  29. }
  30. public WxPayService getWxPayRefundService() {
  31. WxPayConfig payConfig = new WxPayConfig();
  32. payConfig.setAppId(wxPayProperties.getAppid());
  33. payConfig.setMchId(wxPayProperties.getMchId());
  34. payConfig.setPrivateKeyPath(wxPayProperties.getPrivateKeyPath());
  35. payConfig.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());
  36. payConfig.setApiV3Key(wxPayProperties.getApiV3key());
  37. payConfig.setPrivateCertPath(wxPayProperties.getPrivateCertPath());
  38. WxPayService wxPayService = new WxPayServiceImpl();
  39. wxPayService.setConfig(payConfig);
  40. return wxPayService;
  41. }
  42. }
  1. @Slf4j
  2. @Component
  3. public class OrderManager {
  4. @Autowired
  5. OrderMapper orderMapper;
  6. @Autowired
  7. PaymentMapper paymentMapper;
  8. @Autowired
  9. WxManager wxManager;
  10. /**
  11. * 查询第三方支付,更新订单
  12. *
  13. * @param orderDO
  14. * @param paymentDO
  15. */
  16. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  17. public Boolean queryOrderByWx(WxPayOrderQueryV3Result wxPayOrderQueryV3Result, OrderDO orderDO, PaymentDO paymentDO, PayStatusEnum payStatusEnum) {
  18. //未支付 支付中 等待扣款 直接返回
  19. WxPayTradeStateEnum wxPayTradeStateEnum = WxPayTradeStateEnum.findByCode(wxPayOrderQueryV3Result.getTradeState());
  20. if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.NOT_PAY)
  21. || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.ACCEPT)
  22. || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.USER_PAYING)) {
  23. return false;
  24. }
  25. if (!wxPayOrderQueryV3Result.getPayer().getOpenid().equals(paymentDO.getOpenid())) {
  26. //记录日志,更新openid
  27. log.error("查询微信支付,openid与本地不一致,paymentDO:{},payer:{}", paymentDO, wxPayOrderQueryV3Result.getPayer());
  28. }
  29. //更新支付单状态
  30. paymentDO = PaymentConverter.toPaymentByWxPayOrderQueryV3Result(wxPayOrderQueryV3Result, paymentDO);
  31. int p = paymentMapper.updatePayStatus(paymentDO, payStatusEnum.getCode());
  32. if (p == 0) {
  33. throw BizException.create(BizErrorCodeEnum.PAYMENT_UPDATE_FAIL, "支付单状态更新失败");
  34. }
  35. //已关闭 已撤销 支付失败 不更新订单状态 可重新支付
  36. if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.CLOSED)
  37. || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.REVOKED)
  38. || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.PAY_ERROR)) {
  39. return false;
  40. }
  41. //更新订单状态 幂等
  42. int orgOrderStatus = orderDO.getStatus();
  43. orderDO = OrderConverter.toOrderByPayment(paymentDO, orderDO, payStatusEnum);
  44. int o = orderMapper.updateStatusByOrgStatus(orderDO, orgOrderStatus);
  45. if (o == 0) {
  46. throw BizException.create(BizErrorCodeEnum.ORDER_UPDATE_FAIL, "订单状态更新失败");
  47. }
  48. return true;
  49. }
  50. /**
  51. * 支付回调 更新订单
  52. */
  53. public Boolean payNotify(WxPayOrderNotifyV3Result.DecryptNotifyResult result, OrderDO order, PaymentDO payment, WxPayTradeStateEnum wxPayTradeStateEnum) {
  54. if (!result.getPayer().getOpenid().equals(payment.getOpenid())) {
  55. //记录日志
  56. log.error("查询微信支付,openid与本地不一致,paymentDO:{},payer:{}", payment, result.getPayer());
  57. }
  58. PayStatusEnum payStatusEnum = LocalEnumUtils.findByCodeWithoutDefault(PayStatusEnum.class, payment.getStatus());
  59. Integer orderStatus = order.getStatus();
  60. //非 未支付 代扣款 支付中 更新支付单信息
  61. if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.NOT_PAY)
  62. || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.ACCEPT)
  63. || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.USER_PAYING)) {
  64. return false;
  65. }
  66. //更新支付单
  67. payment = PaymentConverter.fromWxPayOrderNotifyV3Result(result, payment);
  68. int p = paymentMapper.updatePayStatus(payment, payStatusEnum.getCode());
  69. if (p == 0) {
  70. throw BizException.create(BizErrorCodeEnum.PAYMENT_UPDATE_FAIL, "支付单状态更新失败");
  71. }
  72. //已关闭 已撤销 支付失败 不更新订单状态 可重新支付
  73. if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.CLOSED)
  74. || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.REVOKED)
  75. || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.PAY_ERROR)) {
  76. return false;
  77. }
  78. PayStatusEnum statusEnum = LocalEnumUtils.findByCodeWithoutDefault(PayStatusEnum.class, payment.getStatus());
  79. //更新订单状态 幂等
  80. order = OrderConverter.toOrderByPayment(payment, order, statusEnum);
  81. int o = orderMapper.updateStatusByOrgStatus(order, orderStatus);
  82. if (o == 0) {
  83. throw BizException.create(BizErrorCodeEnum.ORDER_UPDATE_FAIL, "订单状态更新失败");
  84. }
  85. return true;
  86. }
  87. }

4.Properties

  1. @Data
  2. @Component
  3. @Configuration
  4. @ConfigurationProperties(prefix = "wechat-pay")
  5. public class WxPayProperties {
  6. /**
  7. * 商户号
  8. */
  9. private String mchId;
  10. /**
  11. * 证书解密的密钥
  12. */
  13. private String apiV3key;
  14. /**
  15. * 商户私钥文件
  16. */
  17. private String privateKeyPath;
  18. /**
  19. * apiclient_cert.pem证书文件
  20. */
  21. private String privateCertPath;
  22. /**
  23. * 商户证书序列号
  24. */
  25. private String serialNo;
  26. /**
  27. * 赋选供应链 服务号appid
  28. */
  29. private String appid;
  30. /**
  31. * 支付回调通知地址
  32. */
  33. private String notifyUrl;
  34. /**
  35. * 退款回调通知地址
  36. */
  37. private String refundNotifyUrl;
  38. }

总结

以上就是核心业务代码,涉及到具体业务mapper没有放上来,但是核心的处理步骤已经体现出来了。主要是对订单状态需要仔细判断,不同操作会引起状态的变更。业务中,当多个人扫同一个码,后续扫码成功会调用退款接口。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/874155
推荐阅读
相关标签
  

闽ICP备14008679号