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.enums.BigGiftPackageOrderStatusEnum;
|
import com.ruoyi.system.enums.PhysicalGoodsOrderStatusEnum;
|
import com.ruoyi.system.model.TAppUser;
|
import com.ruoyi.system.model.TBigGiftPackage;
|
import com.ruoyi.system.model.TBigGiftPackageOrder;
|
import com.ruoyi.system.model.TPhysicalGoodsOrder;
|
import com.ruoyi.system.service.*;
|
import com.ruoyi.common.core.domain.entity.SysUser;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.context.annotation.Lazy;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
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
|
@RequiredArgsConstructor(onConstructor_ = @Lazy)
|
public class ICBCNotifyServiceImpl implements ICBCNotifyService {
|
|
private final ICBCConfig icbcConfig;
|
|
private final TBigGiftPackageService bigGiftPackageService;
|
|
private final TBigGiftPackageOrderService bigGiftPackageOrderService;
|
|
private final TPhysicalGoodsOrderService physicalGoodsOrderService;
|
|
private final TCommissionTransactionFlowService commissionTransactionFlowService;
|
|
private final TAppUserService appUserService;
|
|
private final ISysUserService sysUserService;
|
|
@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");
|
String orderId = bizContent.getString("order_id");
|
// 验签
|
if (validateSignature(request, icbcConfig.getNotifyUrlMethod() + "/big-gift/pay-notify")) {
|
signResult = true;
|
log.info("支付订单号{}【工商银行】大礼包支付回调签名验证成功", payOrderCode);
|
|
// 根据支付订单号查询所有订单
|
LambdaQueryWrapper<TBigGiftPackageOrder> wrapper = new LambdaQueryWrapper<>();
|
wrapper.eq(TBigGiftPackageOrder::getPayOrderCode, payOrderCode);
|
List<TBigGiftPackageOrder> orders = bigGiftPackageOrderService.list(wrapper);
|
if ("0".equals(returnCode)) {
|
log.info("支付订单号{}【工商银行】大礼包支付成功", payOrderCode);
|
|
if (orders == null || orders.isEmpty()) {
|
log.error("支付订单号{}未找到对应的大礼包订单", payOrderCode);
|
sendResponse(response, signResult, msgId);
|
return;
|
}
|
|
// 处理每个订单
|
for (TBigGiftPackageOrder order : orders) {
|
processBigGiftPayment(order, orderId);
|
}
|
} else {
|
log.error("支付订单号{}【工商银行】大礼包支付失败,错误信息:{}", payOrderCode, bizContent.getString("return_msg"));
|
//更新订单状态为已取消
|
orders.forEach(item->item.setStatus(BigGiftPackageOrderStatusEnum.CANCELED.getCode()));
|
bigGiftPackageOrderService.updateBatchById(orders);
|
}
|
} 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");
|
String orderId = bizContent.getString("order_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(BigGiftPackageOrderStatusEnum.PAID.getCode());
|
order.setFinalPaymentTime(LocalDateTime.now());
|
order.setFinalPaymentThirdSerialNumber(orderId);
|
order.setPaymentPrice(order.getPaymentPrice().add(order.getFinalPayment()));
|
bigGiftPackageOrderService.updateById(order);
|
log.info("订单{}状态更新为完成支付", order.getCode());
|
|
// 查询最新大礼包商品配置,获取积分结算价用于佣金计算
|
TBigGiftPackage bigGiftPackage = bigGiftPackageService.getById(order.getBigGiftPackageId());
|
if (bigGiftPackage == null || bigGiftPackage.getPointsSettlementPrice() == null) {
|
log.error("订单{}对应的大礼包商品配置不存在或积分结算价为空,无法计算佣金", order.getCode());
|
sendResponse(response, signResult, msgId);
|
return;
|
}
|
|
// 尾款支付完成后,使用大礼包商品配置的积分结算价计算分佣
|
commissionTransactionFlowService.calculateAndSaveCommission(
|
order.getId(),
|
1, // 订单类型:1=大礼包
|
bigGiftPackage.getPointsSettlementPrice(),
|
order.getAppUserId()
|
);
|
log.info("订单{}分佣计算完成,使用积分结算价:{}", order.getCode(), bigGiftPackage.getPointsSettlementPrice());
|
|
// 处理用户推荐状态和公司关联
|
processUserReferrerAndCompany(order.getAppUserId());
|
} 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");
|
String orderId = bizContent.getString("order_id");
|
|
// 验签
|
if (validateSignature(request, icbcConfig.getNotifyUrlMethod() + "/physical/pay-notify")) {
|
signResult = true;
|
log.info("订单{}【工商银行】实物商品支付回调签名验证成功", orderCode);
|
// 根据订单编码查询订单
|
LambdaQueryWrapper<TPhysicalGoodsOrder> wrapper = new LambdaQueryWrapper<>();
|
wrapper.eq(TPhysicalGoodsOrder::getCode, orderCode);
|
TPhysicalGoodsOrder order = physicalGoodsOrderService.getOne(wrapper);
|
if ("0".equals(returnCode)) {
|
log.info("订单{}【工商银行】实物商品支付成功", orderCode);
|
|
if (order == null) {
|
log.error("订单{}未找到对应的实物商品订单", orderCode);
|
sendResponse(response, signResult, msgId);
|
return;
|
}
|
|
// 更新订单状态为"完成支付"
|
order.setStatus(PhysicalGoodsOrderStatusEnum.PAID.getCode());
|
order.setPaymentTime(LocalDateTime.now());
|
order.setPaymentSerialNumber(orderId);
|
physicalGoodsOrderService.updateById(order);
|
log.info("订单{}状态更新为完成支付", orderCode);
|
|
// 处理用户推荐状态和公司关联
|
processUserReferrerAndCompany(order.getAppUserId());
|
|
} else {
|
log.error("订单{}【工商银行】实物商品支付失败,错误信息:{}", orderCode, bizContent.getString("return_msg"));
|
order.setStatus(PhysicalGoodsOrderStatusEnum.CANCELED.getCode());
|
physicalGoodsOrderService.updateById(order);
|
}
|
} 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, String orderId) {
|
Integer paymentType = order.getPaymentType();
|
|
if (paymentType == 1) {
|
// 订金+尾款模式,更新状态为"已付订金"
|
order.setStatus(BigGiftPackageOrderStatusEnum.DEPOSIT_PAID.getCode());
|
order.setDepositTime(LocalDateTime.now());
|
order.setDepositPaymentThirdSerialNumber(orderId);
|
bigGiftPackageOrderService.updateById(order);
|
log.info("订单{}状态更新为已付订金,不计算分佣", order.getCode());
|
} else if (paymentType == 2) {
|
// 全款模式,更新状态为"完成支付"
|
order.setStatus(BigGiftPackageOrderStatusEnum.PAID.getCode());
|
order.setDepositTime(LocalDateTime.now());
|
order.setDepositPaymentThirdSerialNumber(orderId);
|
bigGiftPackageOrderService.updateById(order);
|
log.info("订单{}状态更新为完成支付", order.getCode());
|
|
// 判断是否启用生效日
|
Boolean effectDayFlag = order.getEffectDayFlag();
|
if (effectDayFlag == null || !effectDayFlag) {
|
// 查询最新大礼包商品配置,获取积分结算价用于佣金计算
|
TBigGiftPackage bigGiftPackage = bigGiftPackageService.getById(order.getBigGiftPackageId());
|
if (bigGiftPackage == null || bigGiftPackage.getPointsSettlementPrice() == null) {
|
log.error("订单{}对应的大礼包商品配置不存在或积分结算价为空,无法计算佣金", order.getCode());
|
return;
|
}
|
// 未启用生效日,使用大礼包商品配置的积分结算价立即计算分佣
|
commissionTransactionFlowService.calculateAndSaveCommission(
|
order.getId(),
|
1, // 订单类型:1=大礼包
|
bigGiftPackage.getPointsSettlementPrice(),
|
order.getAppUserId()
|
);
|
log.info("订单{}未启用生效日,立即计算分佣,使用积分结算价:{}", order.getCode(), bigGiftPackage.getPointsSettlementPrice());
|
} else {
|
// 启用生效日,不计算分佣(由定时任务处理)
|
log.info("订单{}启用生效日,延迟计算分佣", order.getCode());
|
}
|
|
// 处理用户推荐状态和公司关联
|
processUserReferrerAndCompany(order.getAppUserId());
|
}
|
}
|
|
/**
|
* 解析请求中的回调参数
|
*
|
* @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\":\"\"}";
|
}
|
}
|
|
/**
|
* 处理用户推荐状态和公司关联
|
* 支付成功后调用此方法更新用户信息
|
*
|
* @param appUserId 下单用户ID
|
*/
|
private void processUserReferrerAndCompany(Integer appUserId) {
|
log.info("开始处理用户{}推荐状态和公司关联", appUserId);
|
if (appUserId == null) {
|
return;
|
}
|
|
TAppUser appUser = appUserService.getById(appUserId);
|
if (appUser == null) {
|
log.warn("用户{}不存在,跳过推荐状态和公司关联处理", appUserId);
|
return;
|
}
|
|
boolean needUpdate = false;
|
|
// 1. 更新推荐状态:如果 referrerUserId 不为空且 referrerStatus=1,则更新为 referrerStatus=2
|
if (appUser.getReferrerUserId() != null && Integer.valueOf(1).equals(appUser.getReferrerStatus())) {
|
appUser.setReferrerStatus(2);
|
needUpdate = true;
|
log.info("用户{}推荐状态从临时(1)更新为正式(2)", appUserId);
|
}
|
|
// 2. 关联公司:如果用户 companyId 为空
|
if (appUser.getCompanyId() == null) {
|
Integer companyId = null;
|
|
// 判断是否同时存在两种推荐来源且推荐状态为临时
|
boolean hasBothReferrer = appUser.getReferrerUserId() != null
|
&& Integer.valueOf(1).equals(appUser.getReferrerStatus())
|
&& appUser.getReferrerSysUserId() != null;
|
|
if (hasBothReferrer) {
|
// 两种推荐来源都存在,比较推荐时间,取后推荐的用户的 companyId
|
LocalDateTime referrerTime = appUser.getReferrerTime();
|
LocalDateTime referrerSysUserTime = appUser.getReferrerSysUserTime();
|
|
// 判断哪个推荐时间更晚
|
boolean useSysUserCompany = false;
|
if (referrerSysUserTime != null && referrerTime != null) {
|
useSysUserCompany = referrerSysUserTime.isAfter(referrerTime);
|
} else if (referrerSysUserTime != null) {
|
useSysUserCompany = true;
|
}
|
// 如果 referrerTime != null 且 referrerSysUserTime == null,则 useSysUserCompany 保持 false
|
|
if (useSysUserCompany) {
|
// 使用股东(SysUser)的 companyId
|
SysUser sysUser = sysUserService.selectUserById(Long.valueOf(appUser.getReferrerSysUserId()));
|
if (sysUser != null && sysUser.getCompanyId() != null) {
|
companyId = sysUser.getCompanyId();
|
log.info("用户{}两种推荐来源都存在,股东推荐时间({})晚于普通推荐时间({}),通过股东(SysUser:{})关联公司{}",
|
appUserId, referrerSysUserTime, referrerTime, appUser.getReferrerSysUserId(), companyId);
|
}
|
} else {
|
// 使用推荐人(TAppUser)的 companyId
|
TAppUser referrerAppUser = appUserService.getById(appUser.getReferrerUserId());
|
if (referrerAppUser != null && referrerAppUser.getCompanyId() != null) {
|
companyId = referrerAppUser.getCompanyId();
|
log.info("用户{}两种推荐来源都存在,普通推荐时间({})晚于或等于股东推荐时间({}),通过推荐人(TAppUser:{})关联公司{}",
|
appUserId, referrerTime, referrerSysUserTime, appUser.getReferrerUserId(), companyId);
|
}
|
}
|
} else {
|
// 只有一种推荐来源,按原逻辑处理
|
// 优先使用 referrerSysUserId 对应的 SysUser 的 companyId
|
if (appUser.getReferrerSysUserId() != null) {
|
SysUser sysUser = sysUserService.selectUserById(Long.valueOf(appUser.getReferrerSysUserId()));
|
if (sysUser != null && sysUser.getCompanyId() != null) {
|
companyId = sysUser.getCompanyId();
|
log.info("用户{}通过股东(SysUser:{})关联公司{}", appUserId, appUser.getReferrerSysUserId(), companyId);
|
}
|
}
|
|
// 如果 referrerSysUserId 为空或对应的 SysUser 没有 companyId,则使用 referrerUserId 对应的 TAppUser 的 companyId
|
if (companyId == null && appUser.getReferrerUserId() != null) {
|
TAppUser referrerAppUser = appUserService.getById(appUser.getReferrerUserId());
|
if (referrerAppUser != null && referrerAppUser.getCompanyId() != null) {
|
companyId = referrerAppUser.getCompanyId();
|
log.info("用户{}通过推荐人(TAppUser:{})关联公司{}", appUserId, appUser.getReferrerUserId(), companyId);
|
}
|
}
|
}
|
|
if (companyId != null) {
|
appUser.setCompanyId(companyId);
|
needUpdate = true;
|
}
|
}
|
|
// 3. 更新用户信息
|
if (needUpdate) {
|
appUserService.updateById(appUser);
|
log.info("用户{}信息更新完成,referrerStatus={},companyId={}", appUserId, appUser.getReferrerStatus(), appUser.getCompanyId());
|
}
|
}
|
|
}
|