package com.ruoyi.payment.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.ruoyi.common.core.domain.R; import com.ruoyi.order.api.feignClient.ChargingOrderClient; import com.ruoyi.payment.api.vo.IcbcPaymentReq; import com.ruoyi.payment.api.vo.IcbcPaymentResp; import com.ruoyi.payment.api.vo.IcbcRefundReq; import com.ruoyi.payment.api.vo.IcbcRefundResp; import com.ruoyi.payment.api.vo.IcbcSubOrderQueryReq; import com.ruoyi.payment.api.vo.IcbcSubOrderQueryResp; import com.ruoyi.payment.api.vo.IcbcSubOrderRefundReq; import com.ruoyi.payment.api.vo.IcbcSubOrderRefundResp; import com.ruoyi.payment.api.vo.IcbcSubOrderSplitReq; import com.ruoyi.payment.api.vo.IcbcSubOrderSplitResp; import com.ruoyi.payment.icbc.config.IcbcPayProperties; import com.ruoyi.payment.icbc.service.IcbcPayService; import lombok.extern.slf4j.Slf4j; 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.math.BigDecimal; import java.io.PrintWriter; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.Map; /** * 工行统一支付入口。 */ @Slf4j @RestController @RequestMapping("/icbc") public class IcbcPayController { private static final String TEST_ORDER_PREFIX = "ICBCTEST"; private static final String TEST_SUB_MER_ID = "231011990055"; private static final String TEST_SUB_MER_PRTCL_NO = "2310119900550201"; @Resource private IcbcPayService icbcPayService; @Resource private IcbcPayProperties icbcPayProperties; @Resource private ChargingOrderClient chargingOrderClient; @PostMapping("/payment") public R payment(@RequestBody IcbcPaymentReq req) { log.info("工行支付请求入参:{}", JSON.toJSONString(req)); IcbcPaymentResp resp = icbcPayService.payment(req); log.info("工行支付返回前端:{}", JSON.toJSONString(resp)); return R.ok(resp); } @PostMapping("/test/payment") public R testPayment(@RequestBody(required = false) IcbcPaymentReq req) { log.info("工行测试支付请求入参:{}", JSON.toJSONString(req)); IcbcPaymentReq paymentReq = req == null ? new IcbcPaymentReq() : req; paymentReq.setOutTradeNo(TEST_ORDER_PREFIX + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))); if (paymentReq.getAmount() == null || paymentReq.getAmount().compareTo(BigDecimal.ZERO) <= 0) { paymentReq.setAmount(new BigDecimal("0.01")); } if (paymentReq.getSubject() == null || paymentReq.getSubject().trim().isEmpty()) { paymentReq.setSubject("工行测试支付"); } if (paymentReq.getBody() == null || paymentReq.getBody().trim().isEmpty()) { paymentReq.setBody("工行测试支付"); } if (paymentReq.getPaymentType() == null) { paymentReq.setPaymentType(1); } log.info("工行测试支付实际下单参数:{}", JSON.toJSONString(paymentReq)); IcbcPaymentResp resp = icbcPayService.payment(paymentReq); log.info("工行测试支付返回前端:{}", JSON.toJSONString(resp)); return R.ok(resp); } @PostMapping("/test/query") public R testQuery(@RequestBody Map req) { String outTradeNo = getString(req, "outTradeNo"); log.info("工行测试查询请求入参:{}", JSON.toJSONString(req)); IcbcPaymentResp resp = icbcPayService.query(outTradeNo); log.info("工行测试查询返回前端:{}", JSON.toJSONString(resp)); return R.ok(resp); } @PostMapping("/test/refund") public R testRefund(@RequestBody(required = false) IcbcRefundReq req) { req = req == null ? new IcbcRefundReq() : req; log.info("工行测试退款请求入参:{}", JSON.toJSONString(req)); if (req.getRefundAmount() == null || req.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) { req.setRefundAmount(new BigDecimal("0.01")); } if (req.getTotalAmount() == null || req.getTotalAmount().compareTo(BigDecimal.ZERO) <= 0) { req.setTotalAmount(req.getRefundAmount()); } if (req.getOutRefundNo() == null || req.getOutRefundNo().trim().isEmpty()) { req.setOutRefundNo("ICBCREFUND" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))); } if (req.getReason() == null || req.getReason().trim().isEmpty()) { req.setReason("工行测试退款"); } log.info("工行测试退款实际参数:{}", JSON.toJSONString(req)); IcbcRefundResp resp = icbcPayService.refund(req); log.info("工行测试退款返回前端:{}", JSON.toJSONString(resp)); return R.ok(resp); } @PostMapping("/test/sub-order/split") public R testSplitSubOrder(@RequestBody(required = false) IcbcSubOrderSplitReq req) { req = req == null ? new IcbcSubOrderSplitReq() : req; log.info("工行测试分账请求入参:{}", JSON.toJSONString(req)); if (req.getSubOrderId() == null || req.getSubOrderId().trim().isEmpty()) { req.setSubOrderId("ICBCSUB" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))); } if (req.getSeqNo() == null || req.getSeqNo().trim().isEmpty()) { req.setSeqNo(req.getChargingOrderCode()); } if (req.getBusiType() == null || req.getBusiType().trim().isEmpty()) { req.setBusiType("1"); } if (req.getOriTrxDate() == null || req.getOriTrxDate().trim().isEmpty()) { req.setOriTrxDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); } if (req.getClassifyAmt() == null || req.getClassifyAmt().compareTo(BigDecimal.ZERO) <= 0) { req.setClassifyAmt(new BigDecimal("0.01")); } if (req.getSubMerId() == null || req.getSubMerId().trim().isEmpty()) { req.setSubMerId(TEST_SUB_MER_ID); } if (req.getSubMerPrtclNo() == null || req.getSubMerPrtclNo().trim().isEmpty()) { req.setSubMerPrtclNo(TEST_SUB_MER_PRTCL_NO); } if (req.getOriMerId() == null || req.getOriMerId().trim().isEmpty()) { req.setOriMerId(icbcPayProperties.getMerId()); } log.info("工行测试分账实际参数:{}", JSON.toJSONString(req)); IcbcSubOrderSplitResp resp = icbcPayService.splitSubOrder(req); log.info("工行测试分账返回前端:{}", JSON.toJSONString(resp)); return R.ok(resp); } @PostMapping("/test/sub-order/refund") public R testRefundSubOrder(@RequestBody(required = false) IcbcSubOrderRefundReq req) { req = req == null ? new IcbcSubOrderRefundReq() : req; log.info("工行测试分账退款请求入参:{}", JSON.toJSONString(req)); if (req.getSeqNo() == null || req.getSeqNo().trim().isEmpty()) { req.setSeqNo(req.getChargingOrderCode()); } if (req.getBusiType() == null || req.getBusiType().trim().isEmpty()) { req.setBusiType("1"); } if (req.getOriTrxDate() == null || req.getOriTrxDate().trim().isEmpty()) { req.setOriTrxDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); } if (req.getRefundAmount() == null || req.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) { req.setRefundAmount(new BigDecimal("0.01")); } if (req.getRetSubOrderId() == null || req.getRetSubOrderId().trim().isEmpty()) { req.setRetSubOrderId("ICBCSUBREFUND" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))); } if (req.getOuttrxSerialNo() == null || req.getOuttrxSerialNo().trim().isEmpty()) { req.setOuttrxSerialNo("ICBCSUBREFUNDNO" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))); } if (req.getSubMerId() == null || req.getSubMerId().trim().isEmpty()) { req.setSubMerId(TEST_SUB_MER_ID); } if (req.getSubMerPrtclNo() == null || req.getSubMerPrtclNo().trim().isEmpty()) { req.setSubMerPrtclNo(TEST_SUB_MER_PRTCL_NO); } if (req.getOriMerId() == null || req.getOriMerId().trim().isEmpty()) { req.setOriMerId(icbcPayProperties.getMerId()); } log.info("工行测试分账退款实际参数:{}", JSON.toJSONString(req)); IcbcSubOrderRefundResp resp = icbcPayService.refundSubOrder(req); log.info("工行测试分账退款返回前端:{}", JSON.toJSONString(resp)); return R.ok(resp); } @PostMapping("/query") public R query(@RequestParam("outTradeNo") String outTradeNo) { return R.ok(icbcPayService.query(outTradeNo)); } @PostMapping("/close") public R close(@RequestParam("outTradeNo") String outTradeNo) { return R.ok(icbcPayService.close(outTradeNo)); } @PostMapping("/refund") public R refund(@RequestBody IcbcRefundReq req) { return R.ok(icbcPayService.refund(req)); } @PostMapping("/sub-order/split") public R splitSubOrder(@RequestBody IcbcSubOrderSplitReq req) { return R.ok(icbcPayService.splitSubOrder(req)); } @PostMapping("/sub-order/refund") public R refundSubOrder(@RequestBody IcbcSubOrderRefundReq req) { return R.ok(icbcPayService.refundSubOrder(req)); } @PostMapping("/sub-order/query") public R querySubOrder(@RequestBody IcbcSubOrderQueryReq req) { return R.ok(icbcPayService.querySubOrder(req)); } @RequestMapping(value = "/callBack", method = {RequestMethod.GET, RequestMethod.POST}) public void callBack(HttpServletRequest request, HttpServletResponse response) throws Exception { Map requestParams = new LinkedHashMap<>(request.getParameterMap()); String rawBody = requestParams.isEmpty() ? readRequestBody(request) : ""; mergeJsonBody(requestParams, rawBody); log.info("工行支付回调原始请求,method:{},uri:{},query:{},contentType:{},params:{},body:{},headers:{}", request.getMethod(), request.getRequestURI(), request.getQueryString(), request.getContentType(), JSON.toJSONString(requestParams), rawBody, summarizeHeaders(request)); try { Map callback = icbcPayService.parseNotify(requestParams); log.info("工行支付回调解析结果:{}", JSON.toJSONString(callback)); dispatchChargingOrderCallback(callback); } catch (Exception e) { log.error("工行支付回调处理失败,已记录原始请求并返回success避免重复通知", e); } finally { writeCallbackResponse(response); } } private void dispatchChargingOrderCallback(Map callback) { String outTradeNo = callback.get("out_trade_no"); String transactionId = callback.get("transaction_id"); String attach = callback.get("attach"); if (outTradeNo != null && outTradeNo.startsWith(TEST_ORDER_PREFIX)) { log.info("工行测试支付回调成功,订单号:{},流水号:{},支付类型:{}", outTradeNo, transactionId, attach); return; } Integer paymentType = Integer.valueOf(attach); if (Integer.valueOf(1).equals(paymentType)) { chargingOrderClient.chargingOrderWXCallback(outTradeNo, transactionId, attach); return; } if (Integer.valueOf(2).equals(paymentType)) { chargingOrderClient.chargingOrderALICallback(outTradeNo, transactionId, attach); return; } if (Integer.valueOf(3).equals(paymentType)) { chargingOrderClient.chargingOrderALIH5Callback(outTradeNo, transactionId, attach); return; } throw new IllegalArgumentException("工行回调支付类型不支持:" + attach); } private String getString(Map req, String key) { if (req == null || req.get(key) == null) { throw new IllegalArgumentException("缺少参数:" + key); } return String.valueOf(req.get(key)); } private String readRequestBody(HttpServletRequest request) { StringBuilder body = new StringBuilder(); try (BufferedReader reader = request.getReader()) { String line; while ((line = reader.readLine()) != null) { body.append(line); } } catch (Exception e) { log.warn("读取工行支付回调body失败", e); } return body.toString(); } private void mergeJsonBody(Map requestParams, String rawBody) { if (rawBody == null || rawBody.trim().isEmpty()) { return; } try { JSONObject jsonObject = JSON.parseObject(rawBody); if (jsonObject == null) { return; } for (Map.Entry entry : jsonObject.entrySet()) { if (entry.getValue() != null && !requestParams.containsKey(entry.getKey())) { requestParams.put(entry.getKey(), new String[]{String.valueOf(entry.getValue())}); } } } catch (Exception e) { log.warn("工行支付回调body不是JSON格式,body:{}", rawBody); } } private String summarizeHeaders(HttpServletRequest request) { Map headers = new LinkedHashMap<>(); Enumeration names = request.getHeaderNames(); while (names != null && names.hasMoreElements()) { String name = names.nextElement(); if ("user-agent".equalsIgnoreCase(name) || "host".equalsIgnoreCase(name) || "x-forwarded-for".equalsIgnoreCase(name) || "x-real-ip".equalsIgnoreCase(name)) { headers.put(name, request.getHeader(name)); } } return JSON.toJSONString(headers); } private void writeCallbackResponse(HttpServletResponse response) throws Exception { response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain;charset=UTF-8"); PrintWriter writer = response.getWriter(); writer.println("success"); writer.flush(); writer.close(); } }