From d10e18bc5b40cbc943736f1d1cb62aa859bb0116 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期四, 09 七月 2026 10:05:47 +0800
Subject: [PATCH] 支付对接
---
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/controller/IcbcPayController.java | 250 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 242 insertions(+), 8 deletions(-)
diff --git a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/controller/IcbcPayController.java b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/controller/IcbcPayController.java
index 0e4a833..52bdbb1 100644
--- a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/controller/IcbcPayController.java
+++ b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/controller/IcbcPayController.java
@@ -1,5 +1,7 @@
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;
@@ -8,32 +10,178 @@
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<IcbcPaymentResp> payment(@RequestBody IcbcPaymentReq req) {
- return R.ok(icbcPayService.payment(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<IcbcPaymentResp> 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<IcbcPaymentResp> testQuery(@RequestBody Map<String, Object> 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<IcbcRefundResp> 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<IcbcSubOrderSplitResp> 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<IcbcSubOrderRefundResp> 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")
@@ -56,25 +204,48 @@
return R.ok(icbcPayService.splitSubOrder(req));
}
+ @PostMapping("/sub-order/refund")
+ public R<IcbcSubOrderRefundResp> refundSubOrder(@RequestBody IcbcSubOrderRefundReq req) {
+ return R.ok(icbcPayService.refundSubOrder(req));
+ }
+
@PostMapping("/sub-order/query")
public R<IcbcSubOrderQueryResp> querySubOrder(@RequestBody IcbcSubOrderQueryReq req) {
return R.ok(icbcPayService.querySubOrder(req));
}
- @PostMapping("/callBack")
+ @RequestMapping(value = "/callBack", method = {RequestMethod.GET, RequestMethod.POST})
public void callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
- Map<String, String> callback = icbcPayService.parseNotify(request.getParameterMap());
- dispatchChargingOrderCallback(callback);
- PrintWriter writer = response.getWriter();
- writer.println("success");
- writer.flush();
- writer.close();
+ Map<String, String[]> 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<String, String> 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<String, String> 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);
@@ -90,4 +261,67 @@
}
throw new IllegalArgumentException("工行回调支付类型不支持:" + attach);
}
+
+ private String getString(Map<String, Object> 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<String, String[]> requestParams, String rawBody) {
+ if (rawBody == null || rawBody.trim().isEmpty()) {
+ return;
+ }
+ try {
+ JSONObject jsonObject = JSON.parseObject(rawBody);
+ if (jsonObject == null) {
+ return;
+ }
+ for (Map.Entry<String, Object> 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<String, String> headers = new LinkedHashMap<>();
+ Enumeration<String> 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();
+ }
}
--
Gitblit v1.7.1