Selaa lähdekoodia

fix: 支付/退款回调增加签名校验(对齐 his_java 验签逻辑)

Co-authored-by: Cursor <cursoragent@cursor.com>
15376779826 3 viikkoa sitten
vanhempi
commit
f3d2c95324

+ 1 - 1
fs-admin/src/main/java/com/fs/store/controller/FsStorePaymentController.java

@@ -156,7 +156,7 @@ public class FsStorePaymentController extends BaseController
 
 
     @ApiOperation("支付手动通知")
-//    @PreAuthorize("@ss.hasPermi('store:storePayment:payNotify')")
+@PreAuthorize("@ss.hasPermi('store:storePayment:payNotify')")
     @PostMapping("/returnPayStatus")
     public R returnPayStatus(@RequestBody FsStorePayment fsStorePayment) throws Exception
     {

+ 52 - 14
fs-api/src/main/java/com/fs/app/controller/PayController.java

@@ -21,8 +21,9 @@ import com.fs.pay.pay.domain.OrderCallback;
 import com.fs.pay.pay.domain.OrderResult;
 import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.pay.service.PayService;
-import com.fs.pay.pay.util.PayUtil;
+import com.fs.pay.service.IPayNotifyVerifyService;
 import com.fs.pay.service.dto.PayNotifyDTO;
+import com.alibaba.fastjson.TypeReference;
 import com.fs.pay.service.dto.RefundNotifyDTO;
 import com.fs.pay.service.dto.TradeOrder;
 import com.fs.store.domain.FsStorePayment;
@@ -99,13 +100,18 @@ public class PayController {
     @Autowired
     private ICompanyUserService companyUserService;
 
+    @Autowired
+    private IPayNotifyVerifyService payNotifyVerifyService;
+
     @ApiOperation("台州银行第三方支付回调")
     @PostMapping(value="/tzBkPayNotify")
     public String tzBkPayNotify( @RequestBody String  decryptForm ) throws Exception {
 
-        TzConfigInfoDTO tzConfigInfoDTO = TzConfigUtils.getConfig();
-
-        String decrypt = CallBackUtil.decrypt( tzConfigInfoDTO.getAppSecret(),decryptForm);
+        String decrypt = payNotifyVerifyService.decryptTzBankCallback(decryptForm);
+        if (StringUtils.isBlank(decrypt)) {
+            logger.warn("台州银行支付回调验签失败");
+            return "fail";
+        }
         logger.info("台州银行第三方支付回调接口被调用,请求参数: {}", decrypt);
 
         String filteredString = decrypt.replaceAll("[^\\x20-\\x7E]", "");
@@ -125,9 +131,11 @@ public class PayController {
     @ApiOperation("台州银行第三方退款回调")
     @PostMapping(value="/tzRefundNotify")
     public String tzRefundNotify(@RequestBody String  decryptForm) throws Exception {
-        TzConfigInfoDTO tzConfigInfoDTO = TzConfigUtils.getConfig();
-
-        String decrypt = CallBackUtil.decrypt( tzConfigInfoDTO.getAppSecret(),decryptForm);
+        String decrypt = payNotifyVerifyService.decryptTzBankCallback(decryptForm);
+        if (StringUtils.isBlank(decrypt)) {
+            logger.warn("台州银行退款回调验签失败");
+            return "fail";
+        }
         logger.info("台州银行第三方退款回调,请求参数: {}", JSON.toJSONString(decrypt));
 
 //        if(dto.getRefundOrdStatus().equals("90")){
@@ -144,9 +152,14 @@ public class PayController {
 
     @ApiOperation("易宝第三方支付回调")
     @PostMapping(value="/ybPayNotify")
-    public String ybPayNotify(@RequestBody OrderCallback o) throws Exception {
-        logger.info("易宝支付回调: {} ",o);
-        //验证
+    public String ybPayNotify(@RequestBody String body) throws Exception {
+        Map<String, String> params = JSON.parseObject(body, new TypeReference<Map<String, String>>() {});
+        if (!payNotifyVerifyService.verifyYibaoCallback(params)) {
+            logger.warn("易宝支付回调签名校验失败");
+            return "fail";
+        }
+        OrderCallback o = JSON.parseObject(body, OrderCallback.class);
+        logger.info("易宝支付回调: {} ", o);
         if(o.getState().equals("0")){
             OrderQueryDTO orderQueryDTO = new OrderQueryDTO();
             orderQueryDTO.setLowOrderId(o.getLowOrderId());
@@ -167,7 +180,10 @@ public class PayController {
     @ApiOperation("汇付第三方支付回调")
     @PostMapping(value="/hfPayNotify")
     public String hfPayNotify(@RequestParam String resp_desc, @RequestParam String resp_code, @RequestParam String sign, @RequestParam String resp_data) throws Exception {
-        //验证
+        if (!payNotifyVerifyService.verifyHuifuCallback(resp_data, sign)) {
+            logger.warn("汇付支付回调签名校验失败");
+            return "fail";
+        }
         HuiFuResult o = JSON.parseObject(resp_data, HuiFuResult.class);
         logger.info("汇付支付回调:"+o);
         if(o.getResp_code().equals("00000000")){
@@ -187,25 +203,42 @@ public class PayController {
 
     @RequestMapping("/payOnlineNotifyUrl")
     public String asyncHuFuOnlinePay(@RequestParam String resp_desc,@RequestParam String resp_code,@RequestParam String sign,@RequestParam String resp_data) {
+        if (!payNotifyVerifyService.verifyHuifuCallback(resp_data, sign)) {
+            logger.warn("汇付在线支付回调签名校验失败");
+            return "fail";
+        }
         HuiFuOnlinePay huFuResult = JSON.parseObject(resp_data, HuiFuOnlinePay.class);
         return "ok";
     }
     @RequestMapping("/onlineRefund")
     public String onlineRefund(@RequestParam String resp_desc,@RequestParam String resp_code,@RequestParam String sign,@RequestParam String resp_data) {
+        if (!payNotifyVerifyService.verifyHuifuCallback(resp_data, sign)) {
+            logger.warn("汇付在线退款回调签名校验失败");
+            return "fail";
+        }
         HuiFuOnlineRefund huFuResult = JSON.parseObject(resp_data, HuiFuOnlineRefund.class);
         return "ok";
     }
 
     @RequestMapping("/refundNotifyUrl")
     public String refundMessage(@RequestParam String resp_desc,@RequestParam String resp_code,@RequestParam String sign,@RequestParam String resp_data) {
-        System.out.println(resp_data);
+        if (!payNotifyVerifyService.verifyHuifuCallback(resp_data, sign)) {
+            logger.warn("汇付退款消息回调签名校验失败");
+            return "fail";
+        }
+        logger.info("汇付退款消息回调: {}", resp_data);
         return "ok";
     }
 
 
     @ApiOperation("小雨点第三方支付回调")
     @PostMapping(value="/payNotify")
-    public String payNotify(HttpServletRequest request,PayNotifyDTO dto) throws Exception {
+    public String payNotify(@RequestBody String body) throws Exception {
+            if (!payNotifyVerifyService.verifyHcPayCallback(body)) {
+                logger.warn("小雨点支付回调签名校验失败");
+                return "fail";
+            }
+            PayNotifyDTO dto = JSON.parseObject(body, PayNotifyDTO.class);
             if("SUCCESS".equals(dto.getOrderStatus())) {
                 //订单号
                 String tradeNo = dto.getMerchOrderNo();
@@ -231,7 +264,12 @@ public class PayController {
 
     @ApiOperation("小雨点第三方退款回调")
     @PostMapping(value="/refundNotify")
-    public String refundNotify(HttpServletRequest request, RefundNotifyDTO dto) throws Exception {
+    public String refundNotify(@RequestBody String body) throws Exception {
+        if (!payNotifyVerifyService.verifyHcPayCallback(body)) {
+            logger.warn("小雨点退款回调签名校验失败");
+            return "fail";
+        }
+        RefundNotifyDTO dto = JSON.parseObject(body, RefundNotifyDTO.class);
         if("SUCCESS".equals(dto.getOrderStatus())) {
             String[] orderIds=dto.getOrigMerchOrderNo().split("-");
             FsStorePayment storePayment=null;

+ 30 - 4
fs-service-system/src/main/java/com/fs/pay/pay/util/PayUtil.java

@@ -83,11 +83,40 @@ public class PayUtil {
 
 
 
+    /**
+     * 校验易宝/聚合支付回调签名
+     */
+    public static boolean verifySign(Map<String, String> formData)
+    {
+        SysConfigMapper sysConfigMapper = SpringUtils.getBean(SysConfigMapper.class);
+        SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("store.pay");
+        FsPayConfig fsPayConfig = new Gson().fromJson(sysConfig.getConfigValue(), FsPayConfig.class);
+        return verifySign(formData, fsPayConfig.getYbKey());
+    }
+
+    public static boolean verifySign(Map<String, String> formData, String secretyKey)
+    {
+        if (formData == null || !formData.containsKey("sign"))
+        {
+            return false;
+        }
+        String receivedSign = formData.get("sign");
+        if (StringUtils.isBlank(receivedSign))
+        {
+            return false;
+        }
+        String calculatedSign = sign(formData, secretyKey);
+        return calculatedSign != null && calculatedSign.equals(receivedSign);
+    }
+
     public static String sign(Map<String, String> formData) {
         SysConfigMapper sysConfigMapper= SpringUtils.getBean(SysConfigMapper.class);
         SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("store.pay");
         FsPayConfig fsPayConfig = new Gson().fromJson(sysConfig.getConfigValue(), FsPayConfig.class);
-        String secretyKey= fsPayConfig.getYbKey();
+        return sign(formData, fsPayConfig.getYbKey());
+    }
+
+    public static String sign(Map<String, String> formData, String secretyKey) {
         if (formData == null) {
             throw new IllegalArgumentException("数据不能为空");
         } else if (formData.isEmpty()) {
@@ -113,10 +142,7 @@ public class PayUtil {
                 stringToSign.deleteCharAt(stringToSign.length() - 1);
             }
             stringToSign.append("&key="+secretyKey);
-            String hash = Md5Utils.hash(stringToSign.toString());
-
             String signature = Md5Utils.hash(stringToSign.toString());
-
             return signature.toUpperCase();
         }
     }

+ 39 - 0
fs-service-system/src/main/java/com/fs/pay/service/IPayNotifyVerifyService.java

@@ -0,0 +1,39 @@
+package com.fs.pay.service;
+
+import java.util.Map;
+
+/**
+ * 第三方支付/退款异步回调签名校验(对齐 his_java 验签逻辑)
+ */
+public interface IPayNotifyVerifyService
+{
+    /**
+     * 易宝支付回调验签(MD5,密钥来自 store.pay 配置 ybKey)
+     */
+    boolean verifyYibaoCallback(Map<String, String> params);
+
+    /**
+     * 易宝支付回调验签(按 appId 匹配密钥,无多小程序配置时回退默认 ybKey)
+     */
+    boolean verifyYibaoCallback(Map<String, String> params, String appId);
+
+    /**
+     * 汇付天下异步回调验签(RSA,遍历已配置公钥对 resp_data 验签)
+     */
+    boolean verifyHuifuCallback(String respData, String sign);
+
+    /**
+     * 汇付天下异步回调验签(指定 appId 时使用对应公钥,否则遍历全部公钥)
+     */
+    boolean verifyHuifuCallback(String respData, String sign, String appId);
+
+    /**
+     * 小雨点/账户支付异步回调验签(MD5,密钥来自 fs-config.payKey)
+     */
+    boolean verifyHcPayCallback(String rawJsonBody);
+
+    /**
+     * 台州银行支付/退款回调验签(解密成功即视为合法,对齐 his_java CallBackUtil.decrypt)
+     */
+    String decryptTzBankCallback(String decryptForm);
+}

+ 197 - 0
fs-service-system/src/main/java/com/fs/pay/service/impl/PayNotifyVerifyServiceImpl.java

@@ -0,0 +1,197 @@
+package com.fs.pay.service.impl;
+
+import com.fs.common.config.FSSysConfig;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.spring.SpringUtils;
+import com.fs.huifuPay.sdk.opps.core.BasePay;
+import com.fs.huifuPay.sdk.opps.core.utils.RsaUtils;
+import com.fs.pay.pay.util.PayUtil;
+import com.fs.pay.service.IPayNotifyVerifyService;
+import com.fs.store.domain.FsPayConfig;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
+import com.google.gson.Gson;
+import com.hc.openapi.tool.HcClient;
+import com.hc.openapi.tool.HcGateway;
+import com.tzbank.util.CallBackUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Service
+public class PayNotifyVerifyServiceImpl implements IPayNotifyVerifyService
+{
+    private static final Logger log = LoggerFactory.getLogger(PayNotifyVerifyServiceImpl.class);
+
+    private static final String PAY_CONFIG_KEY = "store.pay";
+
+    @Autowired
+    private FSSysConfig fsSysConfig;
+
+    @Override
+    public boolean verifyYibaoCallback(Map<String, String> params)
+    {
+        return verifyYibaoCallback(params, null);
+    }
+
+    @Override
+    public boolean verifyYibaoCallback(Map<String, String> params, String appId)
+    {
+        if (params == null || !params.containsKey("sign"))
+        {
+            return false;
+        }
+        String receivedSign = params.get("sign");
+        if (StringUtils.isBlank(receivedSign))
+        {
+            return false;
+        }
+        FsPayConfig payConfig = loadPayConfig();
+        if (payConfig == null || StringUtils.isBlank(payConfig.getYbKey()))
+        {
+            log.warn("易宝支付回调验签失败:未配置 ybKey");
+            return false;
+        }
+        try
+        {
+            String calculatedSign = PayUtil.sign(params, payConfig.getYbKey());
+            return calculatedSign != null && calculatedSign.equals(receivedSign);
+        }
+        catch (Exception e)
+        {
+            log.warn("易宝支付回调验签异常", e);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean verifyHuifuCallback(String respData, String sign)
+    {
+        return verifyHuifuCallback(respData, sign, null);
+    }
+
+    @Override
+    public boolean verifyHuifuCallback(String respData, String sign, String appId)
+    {
+        if (StringUtils.isBlank(respData) || StringUtils.isBlank(sign))
+        {
+            return false;
+        }
+        List<String> publicKeys = collectHuifuPublicKeys(appId);
+        if (publicKeys.isEmpty())
+        {
+            log.error("汇付公钥未配置");
+            return false;
+        }
+        for (String publicKey : publicKeys)
+        {
+            if (verifyWithPublicKey(respData, sign, publicKey))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean verifyHcPayCallback(String rawJsonBody)
+    {
+        if (StringUtils.isBlank(rawJsonBody))
+        {
+            return false;
+        }
+        if (StringUtils.isBlank(fsSysConfig.getPayKey()))
+        {
+            log.warn("小雨点支付回调验签失败:未配置 fs-config.payKey");
+            return false;
+        }
+        try
+        {
+            HcClient hcClient = HcGateway.getOpenApiClientService();
+            return hcClient.verificationSign(rawJsonBody, fsSysConfig.getPayKey());
+        }
+        catch (Exception e)
+        {
+            log.warn("小雨点支付回调验签异常", e);
+            return false;
+        }
+    }
+
+    @Override
+    public String decryptTzBankCallback(String decryptForm)
+    {
+        if (StringUtils.isBlank(decryptForm))
+        {
+            return null;
+        }
+        FsPayConfig payConfig = loadPayConfig();
+        if (payConfig == null || StringUtils.isBlank(payConfig.getAppSecret()))
+        {
+            log.warn("台州银行回调验签失败:未配置 appSecret");
+            return null;
+        }
+        try
+        {
+            return CallBackUtil.decrypt(payConfig.getAppSecret(), decryptForm);
+        }
+        catch (Exception e)
+        {
+            log.warn("台州银行回调解密失败", e);
+            return null;
+        }
+    }
+
+    private List<String> collectHuifuPublicKeys(String appId)
+    {
+        Set<String> keys = new LinkedHashSet<>();
+        FsPayConfig payConfig = loadPayConfig();
+        if (payConfig != null && StringUtils.isNotBlank(payConfig.getHfRsaPublicKey()))
+        {
+            keys.add(payConfig.getHfRsaPublicKey());
+        }
+        if (StringUtils.isNotBlank(BasePay.HUIFU_DEFAULT_PUBLIC_KEY))
+        {
+            keys.add(BasePay.HUIFU_DEFAULT_PUBLIC_KEY);
+        }
+        if (StringUtils.isNotBlank(appId))
+        {
+            log.debug("当前项目未配置多小程序汇付公钥,appId={} 将使用默认公钥列表验签", appId);
+        }
+        return new ArrayList<>(keys);
+    }
+
+    private boolean verifyWithPublicKey(String respData, String sign, String publicKey)
+    {
+        if (StringUtils.isBlank(publicKey))
+        {
+            return false;
+        }
+        try
+        {
+            return RsaUtils.verify(respData, publicKey, sign);
+        }
+        catch (Exception e)
+        {
+            log.warn("汇付验签异常: {}", e.getMessage());
+            return false;
+        }
+    }
+
+    private FsPayConfig loadPayConfig()
+    {
+        SysConfigMapper sysConfigMapper = SpringUtils.getBean(SysConfigMapper.class);
+        SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey(PAY_CONFIG_KEY);
+        if (sysConfig == null || StringUtils.isBlank(sysConfig.getConfigValue()))
+        {
+            return null;
+        }
+        return new Gson().fromJson(sysConfig.getConfigValue(), FsPayConfig.class);
+    }
+}

+ 48 - 33
fs-user-app/src/main/java/com/fs/app/controller/PayController.java

@@ -2,19 +2,19 @@ package com.fs.app.controller;
 
 
 import com.alibaba.fastjson.JSON;
-import com.fs.common.core.controller.BaseController;
-import com.fs.erp.service.IErpOrderService;
+import com.alibaba.fastjson.TypeReference;
 import com.fs.huifuPay.domain.HuiFuResult;
 import com.fs.pay.pay.domain.OrderCallback;
 import com.fs.pay.pay.domain.OrderResult;
 import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.pay.service.PayService;
-import com.fs.pay.pay.util.PayUtil;
-import com.fs.store.domain.*;
-import com.fs.store.service.*;
+import com.fs.pay.service.IPayNotifyVerifyService;
 import com.fs.pay.service.dto.PayNotifyDTO;
 import com.fs.pay.service.dto.RefundNotifyDTO;
 import com.fs.pay.service.dto.TradeOrder;
+import com.fs.store.domain.FsStorePayment;
+import com.fs.store.service.IFsStoreOrderService;
+import com.fs.store.service.IFsStorePaymentService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
@@ -22,7 +22,6 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
-import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
@@ -44,16 +43,19 @@ public class PayController {
     @Autowired
     private PayService ybPayService;
 
+    @Autowired
+    private IPayNotifyVerifyService payNotifyVerifyService;
+
 
     @ApiOperation("易宝第三方支付回调")
     @PostMapping(value="/ybPayNotify")
-    public String ybPayNotify(@RequestBody Map<String, String> params, @RequestBody OrderCallback o) {
-        //验证
-        String sign = PayUtil.sign(params);
-        String sg = params.get("sign");
-        if (!sg.equals(sign)){
+    public String ybPayNotify(@RequestBody String body) {
+        Map<String, String> params = JSON.parseObject(body, new TypeReference<Map<String, String>>() {});
+        if (!payNotifyVerifyService.verifyYibaoCallback(params)) {
+            logger.warn("易宝支付回调签名校验失败");
             return "fail";
         }
+        OrderCallback o = JSON.parseObject(body, OrderCallback.class);
         OrderQueryDTO orderQueryDTO = new OrderQueryDTO();
         orderQueryDTO.setLowOrderId(o.getLowOrderId());
         OrderResult orderResult = ybPayService.getOrder(orderQueryDTO);
@@ -68,10 +70,14 @@ public class PayController {
         }
         return "SUCCESS";
     }
+
     @ApiOperation("汇付第三方支付回调")
     @PostMapping(value="/hfPayNotify")
     public String hfPayNotify(@RequestParam String resp_desc, @RequestParam String resp_code, @RequestParam String sign, @RequestParam String resp_data) {
-        //验证
+        if (!payNotifyVerifyService.verifyHuifuCallback(resp_data, sign)) {
+            logger.warn("汇付支付回调签名校验失败");
+            return "fail";
+        }
         HuiFuResult o = JSON.parseObject(resp_data, HuiFuResult.class);
         logger.info("汇付支付回调:"+o);
         if(o.getResp_code().equals("00000000")){
@@ -84,7 +90,6 @@ public class PayController {
                 case "payment":
                     return storePaymentService.payConfirm(order[1],o.getHf_seq_id(),o.getOut_trans_id(),o.getParty_order_id());
             }
-
         }
         return "ok";
     }
@@ -92,32 +97,42 @@ public class PayController {
 
     @ApiOperation("小雨点第三方支付回调")
     @PostMapping(value="/payNotify")
-    public String payNotify(@RequestBody PayNotifyDTO dto) {
-            if("SUCCESS".equals(dto.getOrderStatus())) {
-                //订单号
-                String tradeNo = dto.getMerchOrderNo();
-                List<TradeOrder> tradeOrders= JSON.parseArray(dto.getTradeOrders(),TradeOrder.class);
-                if(tradeOrders!=null&&tradeOrders.size()>0){
-                    for(TradeOrder tradeOrder:tradeOrders){
-                        String[] order=tradeOrder.getSubMerchOrderNo().split("-");
-                        switch (order[0]) {
-                            case "store":
-                                orderService.payConfirm(1,null,order[1], tradeNo,dto.getBankTransactionId(),dto.getBankSerialNo());
-                                return "success";
-                            case "store_remain":
-                                orderService.payRemainConfirm( order[1], tradeNo,dto.getBankTransactionId(),dto.getBankSerialNo());
-                                return "success";
-                            case "payment":
-                                return storePaymentService.payConfirm(order[1], tradeNo,dto.getBankTransactionId(),dto.getBankSerialNo());
-                        }
+    public String payNotify(@RequestBody String body) {
+        if (!payNotifyVerifyService.verifyHcPayCallback(body)) {
+            logger.warn("小雨点支付回调签名校验失败");
+            return "fail";
+        }
+        PayNotifyDTO dto = JSON.parseObject(body, PayNotifyDTO.class);
+        if("SUCCESS".equals(dto.getOrderStatus())) {
+            String tradeNo = dto.getMerchOrderNo();
+            List<TradeOrder> tradeOrders= JSON.parseArray(dto.getTradeOrders(),TradeOrder.class);
+            if(tradeOrders!=null&&tradeOrders.size()>0){
+                for(TradeOrder tradeOrder:tradeOrders){
+                    String[] order=tradeOrder.getSubMerchOrderNo().split("-");
+                    switch (order[0]) {
+                        case "store":
+                            orderService.payConfirm(1,null,order[1], tradeNo,dto.getBankTransactionId(),dto.getBankSerialNo());
+                            return "success";
+                        case "store_remain":
+                            orderService.payRemainConfirm( order[1], tradeNo,dto.getBankTransactionId(),dto.getBankSerialNo());
+                            return "success";
+                        case "payment":
+                            return storePaymentService.payConfirm(order[1], tradeNo,dto.getBankTransactionId(),dto.getBankSerialNo());
                     }
                 }
             }
-            return "success";
+        }
+        return "success";
     }
+
     @ApiOperation("小雨点第三方退款回调")
     @PostMapping(value="/refundNotify")
-    public String refundNotify(@RequestBody RefundNotifyDTO dto) throws Exception {
+    public String refundNotify(@RequestBody String body) throws Exception {
+        if (!payNotifyVerifyService.verifyHcPayCallback(body)) {
+            logger.warn("小雨点退款回调签名校验失败");
+            return "fail";
+        }
+        RefundNotifyDTO dto = JSON.parseObject(body, RefundNotifyDTO.class);
         if("SUCCESS".equals(dto.getOrderStatus())) {
             String[] orderIds=dto.getOrigMerchOrderNo().split("-");
             FsStorePayment storePayment=null;