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 wrapper = new LambdaQueryWrapper<>(); wrapper.eq(TBigGiftPackageOrder::getPayOrderCode, payOrderCode); List 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 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 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 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()); } } }