无关风月
4 天以前 4742874ad840d7e1e3ac79dc288b38e9a642319d
cloud-server-activity/src/main/java/com/dsh/activity/util/wx/WxPayNotifyController.java
@@ -1,177 +1,177 @@
package com.dsh.activity.util.wx;
import com.dsh.activity.util.wx.WXPaySignatureCertificateUtil;
import com.dsh.activity.util.wx.WxPayAesUtil;
import com.dsh.activity.util.wx.WxV3PayConfig;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/appPayment") // 路径前缀,与常量中配置的回调URL匹配
public class WxPayNotifyController {
    private static final Logger log = LoggerFactory.getLogger(WxPayNotifyController.class);
    private final ObjectMapper objectMapper;
    @Autowired
    public WxPayNotifyController(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
    /**
     * 接收微信支付结果通知
     * URL需要与 WXPayConstants.WECHAT_PAY_NOTIFY_URL 匹配
     */
    @PostMapping("/weChatPayNotify")
    public ResponseEntity<Map<String, String>> handleWeChatPayNotify(HttpServletRequest request, @RequestBody String requestBody) {
        Map<String, String> responseMap = new HashMap<>();
        try {
            // 1. 验证签名 (使用工具类,它会处理证书)
            boolean verifyResult = WXPaySignatureCertificateUtil.verifyNotify(request, requestBody);
            if (!verifyResult) {
                log.error("微信支付通知验签失败!");
                responseMap.put("code", "FAIL");
                responseMap.put("message", "验签失败");
                return new ResponseEntity<>(responseMap, HttpStatus.BAD_REQUEST); // 返回错误状态码
            }
            log.info("微信支付通知验签成功。");
            // 2. 解析通知体
            JsonNode notifyData = objectMapper.readTree(requestBody);
            String eventType = notifyData.get("event_type").asText();
            // 只处理支付成功的通知类型
            if ("TRANSACTION.SUCCESS".equals(eventType)) {
                log.info("处理支付成功通知...");
                JsonNode resourceNode = notifyData.get("resource");
                String associatedData = resourceNode.get("associated_data").asText();
                String nonce = resourceNode.get("nonce").asText();
                String ciphertext = resourceNode.get("ciphertext").asText();
                // 3. 解密关键信息
                String decryptedDataJson = WxPayAesUtil.decrypt(WxV3PayConfig.apiV3Key, associatedData, nonce, ciphertext);
                log.info("解密后的支付通知信息: {}", decryptedDataJson);
                JsonNode decryptedData = objectMapper.readTree(decryptedDataJson);
                // 4. 处理业务逻辑
                String outTradeNo = decryptedData.get("out_trade_no").asText();
                String transactionId = decryptedData.get("transaction_id").asText();
                String tradeState = decryptedData.get("trade_state").asText();
                // ... 获取其他需要的信息,例如支付金额、用户openid等
                // TODO: 在这里实现你的业务逻辑:
                // 1. 根据 outTradeNo 查询你的数据库订单状态。
                // 2. 判断订单是否已经处理过(防止重复处理通知)。
                // 3. 如果订单未处理且 tradeState 为 SUCCESS,则更新订单状态为支付成功。
                // 4. 记录 transactionId (微信支付订单号)。
                // 5. 执行后续业务流程(如发货、增加积分等)。
                // 6. 如果处理失败,可以考虑记录日志并后续重试,但仍需返回成功给微信,避免微信重复通知。
                log.info("业务逻辑处理完成,商户订单号: {}, 微信订单号: {}", outTradeNo, transactionId);
            } else {
                log.warn("收到非支付成功类型的通知: {}", eventType);
                // 其他类型的通知,根据需要处理或忽略
            }
            // 5. 返回成功响应给微信平台
            responseMap.put("code", "SUCCESS");
            responseMap.put("message", "成功");
            return new ResponseEntity<>(responseMap, HttpStatus.OK);
        } catch (Exception e) {
            log.error("处理微信支付通知异常", e);
            responseMap.put("code", "FAIL");
            responseMap.put("message", "处理失败");
            // 即使处理失败,也尽量返回成功给微信,避免重复通知轰炸,然后在后台处理异常。
            // 但如果验签失败,可以返回错误状态码。
            return new ResponseEntity<>(responseMap, HttpStatus.INTERNAL_SERVER_ERROR); // 或者返回OK,根据策略定
        }
    }
    /**
     * 接收微信退款结果通知
     * URL需要与 WXPayConstants.WECHAT_REFUNDS_NOTIFY_URL 匹配
     */
    @PostMapping("/weChatPayRefundsNotify")
    public ResponseEntity<Map<String, String>> handleWeChatRefundsNotify(HttpServletRequest request, @RequestBody String requestBody) {
        Map<String, String> responseMap = new HashMap<>();
        try {
            // 1. 验证签名
            boolean verifyResult = WXPaySignatureCertificateUtil.verifyNotify(request, requestBody);
             if (!verifyResult) {
                log.error("微信退款通知验签失败!");
                responseMap.put("code", "FAIL");
                responseMap.put("message", "验签失败");
                return new ResponseEntity<>(responseMap, HttpStatus.BAD_REQUEST);
            }
            log.info("微信退款通知验签成功。");
            // 2. 解析通知体
            JsonNode notifyData = objectMapper.readTree(requestBody);
            String eventType = notifyData.get("event_type").asText();
             // 处理退款成功或异常的通知
            if ("REFUND.SUCCESS".equals(eventType) || "REFUND.ABNORMAL".equals(eventType) || "REFUND.CLOSED".equals(eventType)) {
                 log.info("处理退款通知,类型: {}", eventType);
                JsonNode resourceNode = notifyData.get("resource");
                String associatedData = resourceNode.get("associated_data").asText();
                String nonce = resourceNode.get("nonce").asText();
                String ciphertext = resourceNode.get("ciphertext").asText();
                // 3. 解密关键信息
                String decryptedDataJson = WxPayAesUtil.decrypt(WxV3PayConfig.apiV3Key, associatedData, nonce, ciphertext);
                log.info("解密后的退款通知信息: {}", decryptedDataJson);
                JsonNode decryptedData = objectMapper.readTree(decryptedDataJson);
                // 4. 处理业务逻辑
                String outTradeNo = decryptedData.get("out_trade_no").asText();
                String outRefundNo = decryptedData.get("out_refund_no").asText();
                String refundStatus = decryptedData.get("refund_status").asText(); // SUCCESS, CLOSED, ABNORMAL
                // ... 获取其他需要的信息,例如退款金额、微信退款单号 refund_id 等
                 // TODO: 在这里实现你的退款业务逻辑:
                // 1. 根据 outRefundNo 查询你的数据库退款单状态。
                // 2. 判断退款单是否已经处理过。
                // 3. 根据 refundStatus 更新退款单状态。
                // 4. 如果退款成功 (SUCCESS),可能需要执行一些操作,如返还库存、通知用户等。
                // 5. 如果退款关闭或异常,也需要记录状态。
                log.info("退款业务逻辑处理完成,商户订单号: {}, 商户退款单号: {}, 退款状态: {}", outTradeNo, outRefundNo, refundStatus);
            } else {
                log.warn("收到非退款类型的通知: {}", eventType);
            }
            // 5. 返回成功响应给微信平台
            responseMap.put("code", "SUCCESS");
            responseMap.put("message", "成功");
            return new ResponseEntity<>(responseMap, HttpStatus.OK);
        } catch (Exception e) {
            log.error("处理微信退款通知异常", e);
            responseMap.put("code", "FAIL");
            responseMap.put("message", "处理失败");
            return new ResponseEntity<>(responseMap, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}
//package com.dsh.activity.util.wx;
//
//import com.dsh.activity.util.wx.WXPaySignatureCertificateUtil;
//import com.dsh.activity.util.wx.WxPayAesUtil;
//import com.dsh.activity.util.wx.WxV3PayConfig;
//import com.fasterxml.jackson.databind.JsonNode;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.http.HttpStatus;
//import org.springframework.http.ResponseEntity;
//import org.springframework.web.bind.annotation.PostMapping;
//import org.springframework.web.bind.annotation.RequestBody;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//
//import javax.servlet.http.HttpServletRequest;
//import java.nio.charset.StandardCharsets;
//import java.util.HashMap;
//import java.util.Map;
//
//@RestController
//@RequestMapping("/api/appPayment") // 路径前缀,与常量中配置的回调URL匹配
//public class WxPayNotifyController {
//
//    private static final Logger log = LoggerFactory.getLogger(WxPayNotifyController.class);
//
//    private final ObjectMapper objectMapper;
//
//    @Autowired
//    public WxPayNotifyController(ObjectMapper objectMapper) {
//        this.objectMapper = objectMapper;
//    }
//
//    /**
//     * 接收微信支付结果通知
//     * URL需要与 WXPayConstants.WECHAT_PAY_NOTIFY_URL 匹配
//     */
//    @PostMapping("/weChatPayNotify")
//    public ResponseEntity<Map<String, String>> handleWeChatPayNotify(HttpServletRequest request, @RequestBody String requestBody) {
//
//        Map<String, String> responseMap = new HashMap<>();
//        try {
//            // 1. 验证签名 (使用工具类,它会处理证书)
//            boolean verifyResult = WXPaySignatureCertificateUtil.verifyNotify(request, requestBody);
//            if (!verifyResult) {
//                log.error("微信支付通知验签失败!");
//                responseMap.put("code", "FAIL");
//                responseMap.put("message", "验签失败");
//                return new ResponseEntity<>(responseMap, HttpStatus.BAD_REQUEST); // 返回错误状态码
//            }
//            log.info("微信支付通知验签成功。");
//
//            // 2. 解析通知体
//            JsonNode notifyData = objectMapper.readTree(requestBody);
//            String eventType = notifyData.get("event_type").asText();
//
//            // 只处理支付成功的通知类型
//            if ("TRANSACTION.SUCCESS".equals(eventType)) {
//                log.info("处理支付成功通知...");
//                JsonNode resourceNode = notifyData.get("resource");
//                String associatedData = resourceNode.get("associated_data").asText();
//                String nonce = resourceNode.get("nonce").asText();
//                String ciphertext = resourceNode.get("ciphertext").asText();
//
//                // 3. 解密关键信息
//                String decryptedDataJson = WxPayAesUtil.decrypt(WxV3PayConfig.apiV3Key, associatedData, nonce, ciphertext);
//                log.info("解密后的支付通知信息: {}", decryptedDataJson);
//                JsonNode decryptedData = objectMapper.readTree(decryptedDataJson);
//
//                // 4. 处理业务逻辑
//                String outTradeNo = decryptedData.get("out_trade_no").asText();
//                String transactionId = decryptedData.get("transaction_id").asText();
//                String tradeState = decryptedData.get("trade_state").asText();
//                // ... 获取其他需要的信息,例如支付金额、用户openid等
//
//                // TODO: 在这里实现你的业务逻辑:
//                // 1. 根据 outTradeNo 查询你的数据库订单状态。
//                // 2. 判断订单是否已经处理过(防止重复处理通知)。
//                // 3. 如果订单未处理且 tradeState 为 SUCCESS,则更新订单状态为支付成功。
//                // 4. 记录 transactionId (微信支付订单号)。
//                // 5. 执行后续业务流程(如发货、增加积分等)。
//                // 6. 如果处理失败,可以考虑记录日志并后续重试,但仍需返回成功给微信,避免微信重复通知。
//
//                log.info("业务逻辑处理完成,商户订单号: {}, 微信订单号: {}", outTradeNo, transactionId);
//
//            } else {
//                log.warn("收到非支付成功类型的通知: {}", eventType);
//                // 其他类型的通知,根据需要处理或忽略
//            }
//
//            // 5. 返回成功响应给微信平台
//            responseMap.put("code", "SUCCESS");
//            responseMap.put("message", "成功");
//            return new ResponseEntity<>(responseMap, HttpStatus.OK);
//
//        } catch (Exception e) {
//            log.error("处理微信支付通知异常", e);
//            responseMap.put("code", "FAIL");
//            responseMap.put("message", "处理失败");
//            // 即使处理失败,也尽量返回成功给微信,避免重复通知轰炸,然后在后台处理异常。
//            // 但如果验签失败,可以返回错误状态码。
//            return new ResponseEntity<>(responseMap, HttpStatus.INTERNAL_SERVER_ERROR); // 或者返回OK,根据策略定
//        }
//    }
//
//
//    /**
//     * 接收微信退款结果通知
//     * URL需要与 WXPayConstants.WECHAT_REFUNDS_NOTIFY_URL 匹配
//     */
//    @PostMapping("/weChatPayRefundsNotify")
//    public ResponseEntity<Map<String, String>> handleWeChatRefundsNotify(HttpServletRequest request, @RequestBody String requestBody) {
//
//        Map<String, String> responseMap = new HashMap<>();
//        try {
//            // 1. 验证签名
//            boolean verifyResult = WXPaySignatureCertificateUtil.verifyNotify(request, requestBody);
//             if (!verifyResult) {
//                log.error("微信退款通知验签失败!");
//                responseMap.put("code", "FAIL");
//                responseMap.put("message", "验签失败");
//                return new ResponseEntity<>(responseMap, HttpStatus.BAD_REQUEST);
//            }
//            log.info("微信退款通知验签成功。");
//
//            // 2. 解析通知体
//            JsonNode notifyData = objectMapper.readTree(requestBody);
//            String eventType = notifyData.get("event_type").asText();
//
//             // 处理退款成功或异常的通知
//            if ("REFUND.SUCCESS".equals(eventType) || "REFUND.ABNORMAL".equals(eventType) || "REFUND.CLOSED".equals(eventType)) {
//                 log.info("处理退款通知,类型: {}", eventType);
//                JsonNode resourceNode = notifyData.get("resource");
//                String associatedData = resourceNode.get("associated_data").asText();
//                String nonce = resourceNode.get("nonce").asText();
//                String ciphertext = resourceNode.get("ciphertext").asText();
//
//                // 3. 解密关键信息
//                String decryptedDataJson = WxPayAesUtil.decrypt(WxV3PayConfig.apiV3Key, associatedData, nonce, ciphertext);
//                log.info("解密后的退款通知信息: {}", decryptedDataJson);
//                JsonNode decryptedData = objectMapper.readTree(decryptedDataJson);
//
//                // 4. 处理业务逻辑
//                String outTradeNo = decryptedData.get("out_trade_no").asText();
//                String outRefundNo = decryptedData.get("out_refund_no").asText();
//                String refundStatus = decryptedData.get("refund_status").asText(); // SUCCESS, CLOSED, ABNORMAL
//                // ... 获取其他需要的信息,例如退款金额、微信退款单号 refund_id 等
//
//                 // TODO: 在这里实现你的退款业务逻辑:
//                // 1. 根据 outRefundNo 查询你的数据库退款单状态。
//                // 2. 判断退款单是否已经处理过。
//                // 3. 根据 refundStatus 更新退款单状态。
//                // 4. 如果退款成功 (SUCCESS),可能需要执行一些操作,如返还库存、通知用户等。
//                // 5. 如果退款关闭或异常,也需要记录状态。
//
//                log.info("退款业务逻辑处理完成,商户订单号: {}, 商户退款单号: {}, 退款状态: {}", outTradeNo, outRefundNo, refundStatus);
//
//            } else {
//                log.warn("收到非退款类型的通知: {}", eventType);
//            }
//
//            // 5. 返回成功响应给微信平台
//            responseMap.put("code", "SUCCESS");
//            responseMap.put("message", "成功");
//            return new ResponseEntity<>(responseMap, HttpStatus.OK);
//
//        } catch (Exception e) {
//            log.error("处理微信退款通知异常", e);
//            responseMap.put("code", "FAIL");
//            responseMap.put("message", "处理失败");
//            return new ResponseEntity<>(responseMap, HttpStatus.INTERNAL_SERVER_ERROR);
//        }
//    }
//
//}