package com.ruoyi.system.service.impl;
|
|
|
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSONObject;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.icbc.api.IcbcApiException;
|
import com.icbc.api.IcbcConstants;
|
import com.icbc.api.utils.IcbcSignature;
|
import com.icbc.api.utils.WebUtils;
|
import com.ruoyi.system.config.ICBCConfig;
|
import com.ruoyi.system.model.TBigGiftPackageOrder;
|
import com.ruoyi.system.model.TPhysicalGoodsOrder;
|
import com.ruoyi.system.service.ICBCNotifyService;
|
import com.ruoyi.system.service.TBigGiftPackageOrderService;
|
import com.ruoyi.system.service.TCommissionTransactionFlowService;
|
import com.ruoyi.system.service.TPhysicalGoodsOrderService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import javax.annotation.Resource;
|
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletResponse;
|
import java.io.IOException;
|
import java.time.LocalDateTime;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* 工商银行支付回调服务实现
|
*
|
* @author mitao
|
* @since 2025-12-16
|
*/
|
@Service
|
@Slf4j
|
public class ICBCNotifyServiceImpl implements ICBCNotifyService {
|
|
@Resource
|
private ICBCConfig icbcConfig;
|
|
@Resource
|
private TBigGiftPackageOrderService bigGiftPackageOrderService;
|
|
@Resource
|
private TPhysicalGoodsOrderService physicalGoodsOrderService;
|
|
@Resource
|
private TCommissionTransactionFlowService commissionTransactionFlowService;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void bigGiftPayNotify(HttpServletRequest request, HttpServletResponse response) {
|
boolean signResult = false;
|
String msgId = null;
|
try {
|
JSONObject bizContent = parseRequest(request);
|
if (bizContent == null) {
|
log.error("【工商银行】大礼包支付回调参数解析失败");
|
sendResponse(response, false, null);
|
return;
|
}
|
log.info("【工商银行】大礼包支付回调参数:{}", bizContent.toJSONString());
|
|
String payOrderCode = bizContent.getString("out_trade_no");
|
String returnCode = bizContent.getString("return_code");
|
msgId = bizContent.getString("msg_id");
|
|
// 验签
|
if (validateSignature(request, icbcConfig.getNotifyUrlMethod() + "/big-gift/pay-notify")) {
|
signResult = true;
|
log.info("支付订单号{}【工商银行】大礼包支付回调签名验证成功", payOrderCode);
|
|
if ("0".equals(returnCode)) {
|
log.info("支付订单号{}【工商银行】大礼包支付成功", payOrderCode);
|
|
// 根据支付订单号查询所有订单
|
LambdaQueryWrapper<TBigGiftPackageOrder> wrapper = new LambdaQueryWrapper<>();
|
wrapper.eq(TBigGiftPackageOrder::getPayOrderCode, payOrderCode);
|
List<TBigGiftPackageOrder> orders = bigGiftPackageOrderService.list(wrapper);
|
|
if (orders == null || orders.isEmpty()) {
|
log.error("支付订单号{}未找到对应的大礼包订单", payOrderCode);
|
sendResponse(response, signResult, msgId);
|
return;
|
}
|
|
// 处理每个订单
|
for (TBigGiftPackageOrder order : orders) {
|
processBigGiftPayment(order);
|
}
|
} else {
|
log.error("支付订单号{}【工商银行】大礼包支付失败,错误信息:{}", payOrderCode, bizContent.getString("return_msg"));
|
}
|
} else {
|
log.error("支付订单号{}【工商银行】大礼包支付回调签名验证失败", payOrderCode);
|
}
|
|
sendResponse(response, signResult, msgId);
|
} catch (Exception e) {
|
log.error("【工商银行】大礼包支付回调处理失败: {}", e.getMessage(), e);
|
sendResponse(response, false, msgId);
|
}
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void bigGiftFinalPayNotify(HttpServletRequest request, HttpServletResponse response) {
|
boolean signResult = false;
|
String msgId = null;
|
try {
|
JSONObject bizContent = parseRequest(request);
|
if (bizContent == null) {
|
log.error("【工商银行】大礼包尾款支付回调参数解析失败");
|
sendResponse(response, false, null);
|
return;
|
}
|
log.info("【工商银行】大礼包尾款支付回调参数:{}", bizContent.toJSONString());
|
|
String finalPayOrderCode = bizContent.getString("out_trade_no");
|
String returnCode = bizContent.getString("return_code");
|
msgId = bizContent.getString("msg_id");
|
|
// 验签
|
if (validateSignature(request, icbcConfig.getNotifyUrlMethod() + "/big-gift/final-pay-notify")) {
|
signResult = true;
|
log.info("尾款支付订单号{}【工商银行】大礼包尾款支付回调签名验证成功", finalPayOrderCode);
|
|
if ("0".equals(returnCode)) {
|
log.info("尾款支付订单号{}【工商银行】大礼包尾款支付成功", finalPayOrderCode);
|
|
// 根据尾款支付订单号查询订单(尾款支付是单个订单)
|
// 注意:这里需要根据实际业务逻辑查询订单,可能需要在订单表中添加尾款支付订单号字段
|
// 暂时使用订单编码查询
|
LambdaQueryWrapper<TBigGiftPackageOrder> wrapper = new LambdaQueryWrapper<>();
|
wrapper.eq(TBigGiftPackageOrder::getFinalPaymentSerialNumber, finalPayOrderCode);
|
TBigGiftPackageOrder order = bigGiftPackageOrderService.getOne(wrapper);
|
|
if (order == null) {
|
log.error("尾款支付订单号{}未找到对应的大礼包订单", finalPayOrderCode);
|
sendResponse(response, signResult, msgId);
|
return;
|
}
|
|
// 更新订单状态为"完成支付"
|
order.setStatus(3);
|
order.setFinalPaymentTime(LocalDateTime.now());
|
order.setFinalPaymentSerialNumber(finalPayOrderCode);
|
bigGiftPackageOrderService.updateById(order);
|
log.info("订单{}状态更新为完成支付", order.getCode());
|
|
// 尾款支付完成后,必须计算分佣
|
commissionTransactionFlowService.calculateAndSaveCommission(
|
order.getId(),
|
1, // 订单类型:1=大礼包
|
order.getOrderPrice(),
|
order.getAppUserId()
|
);
|
log.info("订单{}分佣计算完成", order.getCode());
|
} else {
|
log.error("尾款支付订单号{}【工商银行】大礼包尾款支付失败,错误信息:{}", finalPayOrderCode, bizContent.getString("return_msg"));
|
}
|
} else {
|
log.error("尾款支付订单号{}【工商银行】大礼包尾款支付回调签名验证失败", finalPayOrderCode);
|
}
|
|
sendResponse(response, signResult, msgId);
|
} catch (Exception e) {
|
log.error("【工商银行】大礼包尾款支付回调处理失败: {}", e.getMessage(), e);
|
sendResponse(response, false, msgId);
|
}
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void physicalPayNotify(HttpServletRequest request, HttpServletResponse response) {
|
boolean signResult = false;
|
String msgId = null;
|
try {
|
JSONObject bizContent = parseRequest(request);
|
if (bizContent == null) {
|
log.error("【工商银行】实物商品支付回调参数解析失败");
|
sendResponse(response, false, null);
|
return;
|
}
|
log.info("【工商银行】实物商品支付回调参数:{}", bizContent.toJSONString());
|
|
String orderCode = bizContent.getString("out_trade_no");
|
String returnCode = bizContent.getString("return_code");
|
msgId = bizContent.getString("msg_id");
|
|
// 验签
|
if (validateSignature(request, icbcConfig.getNotifyUrlMethod() + "/physical/pay-notify")) {
|
signResult = true;
|
log.info("订单{}【工商银行】实物商品支付回调签名验证成功", orderCode);
|
|
if ("0".equals(returnCode)) {
|
log.info("订单{}【工商银行】实物商品支付成功", orderCode);
|
|
// 根据订单编码查询订单
|
LambdaQueryWrapper<TPhysicalGoodsOrder> wrapper = new LambdaQueryWrapper<>();
|
wrapper.eq(TPhysicalGoodsOrder::getCode, orderCode);
|
TPhysicalGoodsOrder order = physicalGoodsOrderService.getOne(wrapper);
|
|
if (order == null) {
|
log.error("订单{}未找到对应的实物商品订单", orderCode);
|
sendResponse(response, signResult, msgId);
|
return;
|
}
|
|
// 更新订单状态为"完成支付"
|
order.setStatus(2);
|
order.setPaymentTime(LocalDateTime.now());
|
order.setPaymentSerialNumber(orderCode);
|
physicalGoodsOrderService.updateById(order);
|
log.info("订单{}状态更新为完成支付", orderCode);
|
|
// 实物商品支付完成后,立即计算分佣
|
commissionTransactionFlowService.calculateAndSaveCommission(
|
order.getId(),
|
2, // 订单类型:2=实物商品
|
order.getOrderPrice(),
|
order.getAppUserId()
|
);
|
log.info("订单{}分佣计算完成", orderCode);
|
} else {
|
log.error("订单{}【工商银行】实物商品支付失败,错误信息:{}", orderCode, bizContent.getString("return_msg"));
|
}
|
} else {
|
log.error("订单{}【工商银行】实物商品支付回调签名验证失败", orderCode);
|
}
|
|
sendResponse(response, signResult, msgId);
|
} catch (Exception e) {
|
log.error("【工商银行】实物商品支付回调处理失败: {}", e.getMessage(), e);
|
sendResponse(response, false, msgId);
|
}
|
}
|
|
/**
|
* 处理大礼包支付(定金或全款)
|
*/
|
private void processBigGiftPayment(TBigGiftPackageOrder order) {
|
Integer paymentType = order.getPaymentType();
|
|
if (paymentType == 1) {
|
// 订金+尾款模式,更新状态为"已付订金"
|
order.setStatus(2);
|
order.setDepositTime(LocalDateTime.now());
|
order.setDepositPaymentSerialNumber(order.getPayOrderCode());
|
bigGiftPackageOrderService.updateById(order);
|
log.info("订单{}状态更新为已付订金,不计算分佣", order.getCode());
|
} else if (paymentType == 2) {
|
// 全款模式,更新状态为"完成支付"
|
order.setStatus(3);
|
order.setDepositTime(LocalDateTime.now());
|
order.setDepositPaymentSerialNumber(order.getPayOrderCode());
|
bigGiftPackageOrderService.updateById(order);
|
log.info("订单{}状态更新为完成支付", order.getCode());
|
|
// 判断是否启用生效日
|
Boolean effectDayFlag = order.getEffectDayFlag();
|
if (effectDayFlag == null || !effectDayFlag) {
|
// 未启用生效日,立即计算分佣
|
commissionTransactionFlowService.calculateAndSaveCommission(
|
order.getId(),
|
1, // 订单类型:1=大礼包
|
order.getOrderPrice(),
|
order.getAppUserId()
|
);
|
log.info("订单{}未启用生效日,立即计算分佣", order.getCode());
|
} else {
|
// 启用生效日,不计算分佣(由定时任务处理)
|
log.info("订单{}启用生效日,延迟计算分佣", order.getCode());
|
}
|
}
|
}
|
|
/**
|
* 解析请求中的回调参数
|
*
|
* @param request HTTP请求对象
|
* @return 解析后的JSON对象
|
*/
|
private JSONObject parseRequest(HttpServletRequest request) {
|
try {
|
String bizContentStr = request.getParameter("biz_content");
|
return JSON.parseObject(bizContentStr);
|
} catch (Exception e) {
|
log.error("【工商银行】解析回调参数失败: {}", e.getMessage());
|
return null;
|
}
|
}
|
|
/**
|
* 验证签名
|
*
|
* @param request 回调请求
|
* @param path 回调URL路径
|
* @return 签名验证结果
|
*/
|
private boolean validateSignature(HttpServletRequest request, String path) {
|
Map<String, String> params = new HashMap<>();
|
params.put("from", request.getParameter("from"));
|
params.put("api", request.getParameter("api"));
|
params.put("app_id", request.getParameter("app_id"));
|
params.put("charset", request.getParameter("charset"));
|
params.put("format", request.getParameter("format"));
|
params.put("timestamp", request.getParameter("timestamp"));
|
params.put("biz_content", request.getParameter("biz_content"));
|
params.put("sign_type", request.getParameter("sign_type"));
|
String sign = request.getParameter("sign");
|
String signStr = WebUtils.buildOrderedSignStr(path, params);
|
try {
|
return IcbcSignature.verify(signStr, IcbcConstants.SIGN_TYPE_RSA, icbcConfig.getApiGwPublicKey(), params.get("charset"), sign);
|
} catch (IcbcApiException e) {
|
log.error("【工商银行】签名验证异常: {}", e.getMessage(), e);
|
return false;
|
}
|
}
|
|
/**
|
* 发送回调响应
|
*
|
* @param response HTTP响应对象
|
* @param checkResult 签名验证结果
|
* @param msgId 消息ID
|
*/
|
private void sendResponse(HttpServletResponse response, boolean checkResult, String msgId) {
|
String responseContent = buildNotifyResponse(checkResult, msgId);
|
response.setContentType("application/json; charset=utf-8");
|
try {
|
response.getWriter().write(responseContent);
|
log.info("【工商银行】支付回调应答成功");
|
} catch (IOException e) {
|
log.error("【工商银行】支付回调应答失败: {}", e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 构建回调响应内容
|
* 工行要求保证返回参数的绝对顺序,故直接返回固定字符串不定义对象
|
* 接口文档地址 https://open.icbc.com.cn/icbc/apip/api_detail.html?apiId=10000000000000137000&baseUrl=%2Fcardbusiness%2Faggregatepay%2Fb2c%2Fonline&resUrl=%2Fconsumepurchase&version=V1&apiName=%E8%81%9A%E5%90%88%E6%94%AF%E4%BB%98B2C%E7%BA%BF%E4%B8%8A%E6%B6%88%E8%B4%B9%E4%B8%8B%E5%8D%95&serviceId=P0067&resourceId=10000000000000009800
|
*
|
* @param checkResult 签名验证结果
|
* @param msgId 消息ID
|
* @return 回调响应字符串
|
*/
|
private String buildNotifyResponse(boolean checkResult, String msgId) {
|
String responseBizContent = checkResult
|
? "{\"return_code\":0,\"return_msg\":\"success\",\"msg_id\":\"" + msgId + "\"}"
|
: "{\"return_code\":-1,\"return_msg\":\"icbc sign not pass.\"}";
|
String signStr = "\"response_biz_content\":" + responseBizContent + ",\"sign_type\":\"RSA2\"";
|
try {
|
String sign = IcbcSignature.sign(signStr, IcbcConstants.SIGN_TYPE_RSA2, icbcConfig.getMyPrivateKey(), "UTF-8", "");
|
return "{" + signStr + ",\"sign\":\"" + sign + "\"}";
|
} catch (IcbcApiException e) {
|
log.error("【工商银行】回调应答签名失败: {}", e.getMessage(), e);
|
return "{\"response_biz_content\":{\"return_code\":-1,\"return_msg\":\"response sign error\"},\"sign_type\":\"RSA2\",\"sign\":\"\"}";
|
}
|
}
|
|
}
|