| | |
| | | package com.ruoyi.order.controller.miniapp; |
| | | |
| | | import com.ruoyi.common.core.domain.R; |
| | | 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.web.controller.BaseController; |
| | | import com.ruoyi.order.domain.dto.AppSureOrderDto; |
| | | import com.ruoyi.order.domain.dto.WXPayNotifyDto; |
| | | import com.ruoyi.order.domain.vo.AppSureOrderVo; |
| | | import com.ruoyi.order.domain.vo.ProfitSharingNotifyNewResult; |
| | | import com.ruoyi.order.enums.WxPayNotifyEventTypeEnum; |
| | | import com.ruoyi.order.service.account.BackMessageService; |
| | | import com.ruoyi.order.service.order.OrderService; |
| | | import com.wechat.pay.contrib.apache.httpclient.notification.Notification; |
| | | import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler; |
| | | import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest; |
| | | import com.wechat.pay.java.core.Config; |
| | | import com.wechat.pay.java.core.RSAAutoCertificateConfig; |
| | | import com.wechat.pay.java.core.RSAConfig; |
| | | import com.wechat.pay.java.core.cipher.PrivacyEncryptor; |
| | | import com.wechat.pay.java.core.notification.NotificationConfig; |
| | | import com.wechat.pay.java.core.notification.NotificationParser; |
| | | import com.wechat.pay.java.core.notification.RequestParam; |
| | | import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import net.bytebuddy.asm.Advice; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestMethod; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | 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.BufferedReader; |
| | | import java.io.IOException; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.util.HashMap; |
| | | import java.security.GeneralSecurityException; |
| | | import java.util.Collection; |
| | | import java.util.Enumeration; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @program: ruoyi |
| | |
| | | @Api(value = "微信通知控制", tags = "微信通知控制", description = "微信通知控制") |
| | | @RestController |
| | | @RequestMapping("/app/notify") |
| | | @Log4j2 |
| | | public class NotifyController extends BaseController { |
| | | /** 商户号 */ |
| | | public static String merchantId = ""; |
| | | /** 商户API私钥路径 */ |
| | | public static String privateKeyPath = ""; |
| | | /** 商户证书序列号 */ |
| | | public static String merchantSerialNumber = ""; |
| | | /** 微信支付平台证书路径 */ |
| | | public static String wechatPayCertificatePath = ""; |
| | | /** 商户APIV3密钥 */ |
| | | public static String apiV3key = "..."; |
| | | |
| | | 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; |
| | | |
| | | @RequestMapping(value = "/payNotify", method = RequestMethod.POST) |
| | | @ApiOperation(value = "微信支付通知") |
| | | public Map payNotify(HttpServletRequest request, HttpServletResponse response) { |
| | | Config config = |
| | | new RSAConfig.Builder() |
| | | .merchantId(merchantId) |
| | | .privateKeyFromPath(privateKeyPath) |
| | | .merchantSerialNumber(merchantSerialNumber) |
| | | .wechatPayCertificatesFromPath(wechatPayCertificatePath) |
| | | .build(); |
| | | @Resource |
| | | private WxPayService wxService; |
| | | @Resource |
| | | private BackMessageService backMessageService; |
| | | |
| | | PrivacyEncryptor encryptor = config.createEncryptor(); |
| | | String wechatPayCertificateSerialNumber = encryptor.getWechatpaySerial(); |
| | | @PostMapping(value = "/payNotify") |
| | | @ApiOperation(value = "微信支付/退款通知") |
| | | public String payNotify(@RequestBody String notifyData, HttpServletRequest request, HttpServletResponse response) throws WxPayException { |
| | | |
| | | |
| | | String nonoc=request.getHeader("Wechatpay-Nonce"); |
| | | String signature=request.getHeader("Wechatpay-Signature"); |
| | | String timestamp=request.getHeader("Wechatpay-Timestamp"); |
| | | String serial=request.getHeader("Wechatpay-Serial"); |
| | | String signatureType=request.getHeader("Wechatpay-Signature-Type"); |
| | | |
| | | String requestBody=""; |
| | | |
| | | try { |
| | | BufferedReader reader = request.getReader(); |
| | | String line = ""; |
| | | StringBuffer inputString = new StringBuffer(); |
| | | while ( (line = reader.readLine()) != null ) { |
| | | inputString.append(line); |
| | | } |
| | | if(inputString!=null && !"".equals(inputString)) { |
| | | requestBody = inputString.toString(); |
| | | } |
| | | reader.close(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | Enumeration<String> headerNames = request.getHeaderNames(); |
| | | while (headerNames.hasMoreElements()){ |
| | | String name = headerNames.nextElement(); |
| | | log.info("request headerNames: {}", name); |
| | | } |
| | | |
| | | RequestParam requestParam = new RequestParam.Builder() |
| | | .serialNumber(wechatPayCertificateSerialNumber) |
| | | .nonce(nonoc) |
| | | .signature(signature) |
| | | .timestamp(timestamp) |
| | | .body(requestBody) |
| | | .build(); |
| | | |
| | | NotificationConfig notifiConfig = new RSAAutoCertificateConfig.Builder() |
| | | .merchantId(merchantId) |
| | | .privateKeyFromPath(privateKeyPath) |
| | | .merchantSerialNumber(merchantSerialNumber) |
| | | .apiV3Key(apiV3key) |
| | | .build(); |
| | | |
| | | NotificationParser parser = new NotificationParser(notifiConfig); |
| | | |
| | | Transaction transaction = parser.parse(requestParam, Transaction.class); |
| | | |
| | | |
| | | String tradeStateEnum=transaction.getTradeState().toString(); |
| | | if(tradeStateEnum.equals("success")){ |
| | | orderService.payBack(transaction); |
| | | Collection<String> headerNames1 = response.getHeaderNames(); |
| | | for (String name : headerNames1) { |
| | | log.info("request headerNames: {}", name); |
| | | } |
| | | |
| | | Map map=new HashMap(); |
| | | map.put("code","SUCCESS"); |
| | | map.put("message","成功"); |
| | | // 获取请求头 |
| | | SignatureHeader signatureHeader = getSignatureHeader(request); |
| | | |
| | | return map; |
| | | log.info("微信支付/退款通知: {}", notifyData); |
| | | |
| | | NotifyResponse responseData = GSON.fromJson(notifyData, NotifyResponse.class); |
| | | String eventType = responseData.getEventType(); |
| | | |
| | | int resultType = 1; |
| | | String resultMessage = ""; |
| | | 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); |
| | | } |
| | | 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 = wxService.getEcommerceService().parseRefundNotifyResult(notifyData, signatureHeader); |
| | | orderService.orderRefundBack(result); |
| | | resultType = 2; |
| | | resultMessage = GSON.toJson(result); |
| | | } |
| | | // 保存支付/退款回调信息 |
| | | 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 = notifyResult.getRawData().getEventType(); |
| | | if(WxPayNotifyEventTypeEnum.PROFIT_SHARING_SUCCESS.getCode().equals(eventType)){ |
| | | // 分账通知回调 |
| | | orderService.profitSharingBack(result); |
| | | } |
| | | |
| | | String resultMessage = GSON.toJson(notifyResult); |
| | | // 保存记录分账回调信息 |
| | | backMessageService.saveBackMessage(3, resultMessage); |
| | | |
| | | return WxPayNotifyV3Response.success("成功"); |
| | | } |
| | | |
| | | @RequestMapping(value = "/shareNotify", method = RequestMethod.POST) |
| | |
| | | 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 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; |
| | | } |
| | | } |