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);
|
}
|
}
|
|
}
|