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