package com.ruoyi.order.controller.miniapp; import com.alibaba.fastjson.JSONObject; import com.github.binarywang.wxpay.bean.ecommerce.*; import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Response; import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyData; import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingNotifyResult; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.ProfitSharingV3Service; import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.v3.auth.Verifier; import com.github.binarywang.wxpay.v3.util.AesUtils; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.web.controller.BaseController; import com.ruoyi.common.redis.service.RedisService; import com.ruoyi.order.domain.dto.WechatPaymentRefundDto; import com.ruoyi.order.domain.dto.WeixinPaymentNotifyDto; import com.ruoyi.order.domain.pojo.account.OrderRefund; import com.ruoyi.order.domain.vo.ProfitSharingNotifyNewResult; import com.ruoyi.order.domain.vo.WeixinPaymentNotifyVo; import com.ruoyi.order.domain.vo.WeixinPaymentRefundVo; import com.ruoyi.order.enums.RefundStatusEnum; import com.ruoyi.order.enums.WxPayNotifyEventTypeEnum; import com.ruoyi.order.enums.dict.IDict; import com.ruoyi.order.service.account.BackMessageService; import com.ruoyi.order.service.account.OrderRefundService; import com.ruoyi.order.service.order.OrderService; import com.ruoyi.order.util.HuiFuTianXiaUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.Collection; import java.util.Enumeration; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; /** * @program: ruoyi * @author: linqingsong * @create: 2023-07-22 17:20 * @description: 成都软思同创科技有限公司 **/ @Api(value = "微信通知控制", tags = "微信通知控制", description = "微信通知控制") @RestController @RequestMapping("/app/notify") @Log4j2 public class NotifyController extends BaseController { public static final String WECHAT_PAY_SERIAL = "Wechatpay-Serial"; public static final String WECHAT_PAY_SIGNATURE = "Wechatpay-Signature"; public static final String WECHAT_PAY_TIMESTAMP = "Wechatpay-Timestamp"; public static final String WECHAT_PAY_NONCE = "Wechatpay-Nonce"; private static final Gson GSON = new GsonBuilder().create(); @Resource private OrderService orderService; @Resource private WxPayService wxService; @Resource private BackMessageService backMessageService; @Resource private OrderRefundService orderRefundService; @Resource private RedisService redisService; @PostMapping(value = "/payNotify") @ApiOperation(value = "微信支付/退款通知") public String payNotify(@RequestBody String notifyData, HttpServletRequest request, HttpServletResponse response) throws WxPayException { // 获取请求头 SignatureHeader signatureHeader = getSignatureHeader(request); log.info("微信支付/退款通知: {}", notifyData); NotifyResponse responseData = GSON.fromJson(notifyData, NotifyResponse.class); String eventType = responseData.getEventType(); int resultType = 1; String resultMessage = notifyData; try { if (WxPayNotifyEventTypeEnum.TRANSACTION_SUCCESS.getCode().equals(eventType)) { PartnerTransactionsNotifyResult notifyResult = wxService.getEcommerceService().parsePartnerNotifyResult(notifyData, signatureHeader); PartnerTransactionsResult result = notifyResult.getResult(); // 支付通知回调 if ("SUCCESS".equals(result.getTradeState())) { orderService.payBack(result, BigDecimal.ONE); } resultMessage = GSON.toJson(notifyResult); } else if (WxPayNotifyEventTypeEnum.REFUND_SUCCESS.getCode().equals(eventType) || WxPayNotifyEventTypeEnum.REFUND_ABNORMAL.getCode().equals(eventType) || WxPayNotifyEventTypeEnum.REFUND_CLOSED.getCode().equals(eventType)) { RefundNotifyResult result = this.parseRefundNotifyResult(notifyData, signatureHeader); orderService.orderRefundBack(result); resultType = 2; resultMessage = GSON.toJson(result); } }catch (Exception e){ e.printStackTrace(); log.info("微信支付/退款通知异常: {}", e.getMessage()); } // 保存支付/退款回调信息 backMessageService.saveBackMessage(resultType, resultMessage); return WxPayNotifyV3Response.success("成功"); } @PostMapping(value = "/profitSharingNotify") @ApiOperation(value = "微信分账通知") public String profitSharingNotify(@RequestBody String notifyData, HttpServletRequest request, HttpServletResponse response) throws WxPayException { ProfitSharingV3Service sharingV3Service = wxService.getProfitSharingV3Service(); // ProfitSharingNotifyResult log.info("微信分账通知: {}", notifyData); // 获取请求头 SignatureHeader signatureHeader = getSignatureHeader(request); ProfitSharingNotifyNewResult notifyResult = getProfitSharingNotifyData(notifyData, signatureHeader); ProfitSharingNotifyResult result = notifyResult.getResult(); String eventType = "-1"; String resultMessage = notifyData; try { eventType = notifyResult.getRawData().getEventType(); if (WxPayNotifyEventTypeEnum.PROFIT_SHARING_SUCCESS.getCode().equals(eventType)) { // 分账通知回调 orderService.profitSharingBack(result); } resultMessage = GSON.toJson(notifyResult); }catch (Exception e){ log.info("微信分账通知通知异常: {}", e.getMessage()); } // 保存记录分账回调信息 backMessageService.saveBackMessage(3, resultMessage); return WxPayNotifyV3Response.success("成功"); } @RequestMapping(value = "/shareNotify", method = RequestMethod.POST) @ApiOperation(value = "微信支付通知") public Map shareNotify(HttpServletRequest request, HttpServletResponse response) { return null; } public ProfitSharingNotifyNewResult getProfitSharingNotifyData(String notifyData, SignatureHeader header) throws WxPayException { if (Objects.nonNull(header) && !this.verifyNotifySign(header, notifyData)) { throw new WxPayException("非法请求,头部信息验证失败"); } ProfitSharingNotifyData response = GSON.fromJson(notifyData, ProfitSharingNotifyData.class); ProfitSharingNotifyData.Resource resource = response.getResource(); String cipherText = resource.getCipherText(); String associatedData = resource.getAssociatedData(); String nonce = resource.getNonce(); String apiV3Key = this.wxService.getConfig().getApiV3Key(); try { String result = AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key); ProfitSharingNotifyResult profitSharingResult = GSON.fromJson(result, ProfitSharingNotifyResult.class); ProfitSharingNotifyNewResult notifyResult = new ProfitSharingNotifyNewResult(); notifyResult.setRawData(response); notifyResult.setResult(profitSharingResult); return notifyResult; } catch (GeneralSecurityException | IOException e) { throw new WxPayException("解析报文异常!", e); } } private RefundNotifyResult parseRefundNotifyResult(String notifyData, SignatureHeader header) throws WxPayException { NotifyResponse response = GSON.fromJson(notifyData, NotifyResponse.class); NotifyResponse.Resource resource = response.getResource(); String cipherText = resource.getCiphertext(); String associatedData = resource.getAssociatedData(); String nonce = resource.getNonce(); String apiV3Key = this.wxService.getConfig().getApiV3Key(); try { String result = AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key); RefundNotifyResult notifyResult = GSON.fromJson(result, RefundNotifyResult.class); notifyResult.setRawData(response); return notifyResult; } catch (GeneralSecurityException | IOException e) { throw new WxPayException("解析报文异常!", e); } } private boolean verifyNotifySign(SignatureHeader header, String data) throws WxPayException { String beforeSign = String.format("%s\n%s\n%s\n", header.getTimeStamp(), header.getNonce(), data); Verifier verifier = this.wxService.getConfig().getVerifier(); if (verifier == null) { throw new WxPayException("证书检验对象为空"); } return verifier.verify(header.getSerialNo(), beforeSign.getBytes(StandardCharsets.UTF_8), header.getSigned()); } private SignatureHeader getSignatureHeader(HttpServletRequest request){ SignatureHeader signatureHeader = new SignatureHeader(); signatureHeader.setSerialNo(request.getHeader(WECHAT_PAY_SERIAL)); signatureHeader.setSigned(request.getHeader(WECHAT_PAY_SIGNATURE)); signatureHeader.setNonce(request.getHeader(WECHAT_PAY_NONCE)); signatureHeader.setTimeStamp(request.getHeader(WECHAT_PAY_TIMESTAMP)); log.info("timeStamp:{} nonce : {} signed:{} serialNo:{}", signatureHeader.getTimeStamp(), signatureHeader.getNonce(), signatureHeader.getSigned(), signatureHeader.getSerialNo()); return signatureHeader; } @RequestMapping(value = "/wechatPaymentCallback", method = RequestMethod.POST) @ApiOperation(value = "微信支付通知(汇付天下)") public void wechatPaymentCallback(WeixinPaymentNotifyDto dto, HttpServletResponse response){ R r = HuiFuTianXiaUtil.weixinPaymentNotify(dto); if(r.getCode() == 200){ WeixinPaymentNotifyVo data = r.getData(); String transStat = data.getTransStat(); if("S".equals(transStat)){ PartnerTransactionsResult transaction = new PartnerTransactionsResult(); transaction.setOutTradeNo(data.getReqSeqId()); //防止回调频繁导致时间处理异常 Object cacheObject = redisService.getCacheObject(data.getReqSeqId()); if(null == cacheObject){ redisService.setCacheObject(data.getReqSeqId(), "", 10L, TimeUnit.SECONDS); }else{ return; } //开始处理业务数据 orderService.payBack(transaction, data.getFeeFlag() == 1 ? BigDecimal.ZERO : new BigDecimal(data.getFeeAmount())); response.setStatus(200); PrintWriter out = null; try { out = response.getWriter(); out.print("RECV_ORD_ID_" + data.getReqSeqId()); out.flush(); } catch (IOException e) { e.printStackTrace(); }finally { out.close(); } } } } @RequestMapping(value = "/wechatPaymentRefundCallback", method = RequestMethod.POST) @ApiOperation(value = "微信支付退款通知(汇付天下)") public void wechatPaymentRefundCallback(WechatPaymentRefundDto dto, HttpServletResponse response){ R r = HuiFuTianXiaUtil.weixinPaymentRefundNotify(dto); if(r.getCode() == 200){ WeixinPaymentRefundVo data = r.getData(); String transStat = data.getTransStat(); if("S".equals(transStat)){ //开始处理业务数据 String reqSeqId = data.getReqSeqId(); OrderRefund orderRefund = orderRefundService.getById(reqSeqId); if(null != orderRefund && orderRefund.getRefundStatus() != 2){ orderRefund.setWxRefundId(data.getPartyOrderId()); orderRefund.setBackTime(data.getTransDate() + data.getTransTime()); orderRefund.setRefundStatus(2); orderRefundService.saveOrUpdate(orderRefund); } response.setStatus(200); PrintWriter out = null; try { out = response.getWriter(); out.print("RECV_ORD_ID_" + data.getReqSeqId()); out.flush(); } catch (IOException e) { e.printStackTrace(); }finally { out.close(); } } } } }