|
|
@@ -8,6 +8,7 @@ import com.alibaba.fastjson.JSONArray;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.alibaba.fastjson.TypeReference;
|
|
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.fs.common.annotation.DataScope;
|
|
|
import com.fs.common.core.domain.R;
|
|
|
@@ -89,7 +90,6 @@ import com.google.gson.Gson;
|
|
|
import com.hc.openapi.tool.fastjson.JSON;
|
|
|
import io.netty.util.internal.StringUtil;
|
|
|
import me.chanjar.weixin.common.error.WxErrorException;
|
|
|
-import org.apache.commons.collections4.CollectionUtils;
|
|
|
import org.redisson.api.RedissonClient;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
@@ -106,7 +106,7 @@ import java.lang.reflect.Field;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.text.SimpleDateFormat;
|
|
|
import java.util.*;
|
|
|
-import java.util.function.Function;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
@@ -983,42 +983,112 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
@Override
|
|
|
public String v3TransferNotify(String notifyData, HttpServletRequest request) {
|
|
|
logger.info("zyp \n【收到转账回调】:{}", notifyData);
|
|
|
- try {
|
|
|
-// String json = configService.selectConfigByKey("redPacket.config");
|
|
|
|
|
|
- String json = redisCache.getCacheObject("sys_config:redPacket.config.new");
|
|
|
- if (StringUtil.isNullOrEmpty(json) || json.isEmpty()) {
|
|
|
- json = configService.selectConfigByKey("redPacket.config");
|
|
|
- }
|
|
|
+ // 获取所有商户号列表
|
|
|
+ List<String> mchIdList = getAvailableMchIds();
|
|
|
|
|
|
- RedPacketConfig config = JSONUtil.toBean(json, RedPacketConfig.class);
|
|
|
- //创建微信订单
|
|
|
- WxPayConfig payConfig = new WxPayConfig();
|
|
|
- BeanUtils.copyProperties(config, payConfig);
|
|
|
- WxPayService wxPayService = new WxPayServiceImpl();
|
|
|
- wxPayService.setConfig(payConfig);
|
|
|
- SignatureHeader signatureHeader = new SignatureHeader();
|
|
|
- signatureHeader.setTimeStamp(request.getHeader("Wechatpay-Timestamp"));
|
|
|
- signatureHeader.setNonce(request.getHeader("Wechatpay-Nonce"));
|
|
|
- signatureHeader.setSerial(request.getHeader("Wechatpay-Serial"));
|
|
|
- signatureHeader.setSignature(request.getHeader("Wechatpay-Signature"));
|
|
|
- TransferBillsNotifyResult result = wxPayService.parseTransferBillsNotifyV3Result(notifyData, signatureHeader);
|
|
|
- logger.info("到零钱回调:{}", result.getResult());
|
|
|
- if (result.getResult().getState().equals("SUCCESS")) {
|
|
|
- R r = redPacketLogService.syncRedPacket(result.getResult().getOutBillNo(), result.getResult().getTransferBillNo());
|
|
|
- logger.info("result:{}", r);
|
|
|
- if (r.get("code").equals(200)) {
|
|
|
- return WxPayNotifyResponse.success("处理成功");
|
|
|
+ // 记录最后一次异常,用于日志
|
|
|
+ WxPayException lastException = null;
|
|
|
+
|
|
|
+ for (String mchId : mchIdList) {
|
|
|
+ try {
|
|
|
+ // 1. 获取该商户号的配置
|
|
|
+ RedPacketConfig config = getMchConfig(mchId);
|
|
|
+ logger.info("尝试商户号:{}", mchId);
|
|
|
+
|
|
|
+ // 2. 构建 WxPayService
|
|
|
+ WxPayConfig payConfig = new WxPayConfig();
|
|
|
+ BeanUtils.copyProperties(config, payConfig);
|
|
|
+ WxPayService wxPayService = new WxPayServiceImpl();
|
|
|
+ wxPayService.setConfig(payConfig);
|
|
|
+
|
|
|
+ // 3. 准备签名头
|
|
|
+ SignatureHeader signatureHeader = new SignatureHeader();
|
|
|
+ signatureHeader.setTimeStamp(request.getHeader("Wechatpay-Timestamp"));
|
|
|
+ signatureHeader.setNonce(request.getHeader("Wechatpay-Nonce"));
|
|
|
+ signatureHeader.setSerial(request.getHeader("Wechatpay-Serial"));
|
|
|
+ signatureHeader.setSignature(request.getHeader("Wechatpay-Signature"));
|
|
|
+
|
|
|
+ // 4. 尝试验签并解密
|
|
|
+ TransferBillsNotifyResult result = wxPayService.parseTransferBillsNotifyV3Result(notifyData, signatureHeader);
|
|
|
+
|
|
|
+ // 5. 验签成功,处理业务
|
|
|
+ logger.info("匹配到商户号:{},回调结果:{}", mchId, result.getResult());
|
|
|
+ if ("SUCCESS".equals(result.getResult().getState())) {
|
|
|
+ R r = redPacketLogService.syncRedPacket(
|
|
|
+ result.getResult().getOutBillNo(),
|
|
|
+ result.getResult().getTransferBillNo()
|
|
|
+ );
|
|
|
+ logger.info("同步结果:{}", r);
|
|
|
+ if (r.get("code").equals(200)) {
|
|
|
+ return WxPayNotifyResponse.success("处理成功");
|
|
|
+ } else {
|
|
|
+ return WxPayNotifyResponse.fail("业务处理失败");
|
|
|
+ }
|
|
|
} else {
|
|
|
return WxPayNotifyResponse.fail("");
|
|
|
}
|
|
|
- } else {
|
|
|
- return WxPayNotifyResponse.fail("");
|
|
|
+
|
|
|
+ } catch (WxPayException e) {
|
|
|
+ // 验签失败或解密失败,记录日志,继续尝试下一个商户号
|
|
|
+ logger.warn("商户号 {} 验签/解密失败:{}", mchId, e.getMessage());
|
|
|
+ lastException = e;
|
|
|
+ // 继续循环
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("商户号 {} 处理异常: {} - {}",
|
|
|
+ mchId, e.getClass().getSimpleName(), e.getMessage());
|
|
|
+ lastException = new WxPayException(e.getMessage());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 所有商户号均失败
|
|
|
+ logger.error("所有商户号配置均无法处理该回调,最后异常:", lastException);
|
|
|
+ return WxPayNotifyResponse.fail("无法匹配商户号配置");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取所有可用商户号列表(从数据库查询,缓存到 Redis)
|
|
|
+ * @return 商户号列表,不会返回 null
|
|
|
+ */
|
|
|
+ private List<String> getAvailableMchIds() {
|
|
|
+ String cacheKey = "sys_config:redPacket.allMchIds";
|
|
|
+
|
|
|
+ // 1. 从 Redis 获取
|
|
|
+ String cachedJson = redisCache.getCacheObject(cacheKey);
|
|
|
+ if (StringUtils.isNotBlank(cachedJson)) {
|
|
|
+ try {
|
|
|
+ // 解析 JSON 数组字符串 -> List<String>
|
|
|
+ return JSONUtil.parseArray(cachedJson).toList(String.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.warn("解析缓存的商户号列表失败,将重新查库,错误:{}", e.getMessage());
|
|
|
+ // 缓存数据异常,删除缓存并重新查库
|
|
|
+ redisCache.deleteObject(cacheKey);
|
|
|
}
|
|
|
- } catch (WxPayException e) {
|
|
|
- logger.error("zyp \n【转账回调异常】:{}", e.getReturnMsg());
|
|
|
- return WxPayNotifyResponse.fail(e.getMessage());
|
|
|
}
|
|
|
+
|
|
|
+ // 2. 缓存未命中或解析失败,从数据库查询
|
|
|
+ List<SysRedpacketConfigMore> configMoreList =
|
|
|
+ sysRedpacketConfigMoreMapper.selectSysRedpacketConfigMoreList(new SysRedpacketConfigMore());
|
|
|
+
|
|
|
+ if (CollectionUtils.isEmpty(configMoreList)) {
|
|
|
+ logger.warn("数据库中没有商户号配置,请检查");
|
|
|
+ // 缓存空列表,防穿透
|
|
|
+ redisCache.setCacheObject(cacheKey, "[]", 300, TimeUnit.SECONDS);
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取商户号列表
|
|
|
+ List<String> mchIds = configMoreList.stream()
|
|
|
+ .map(SysRedpacketConfigMore::getMchId)
|
|
|
+ .distinct()
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 3. 存入 Redis(存字符串数组的 JSON)
|
|
|
+ String jsonArray = JSONUtil.toJsonStr(mchIds); // 如 ["1733024912","1701206124","1747857442"]
|
|
|
+ redisCache.setCacheObject(cacheKey, jsonArray, 30, TimeUnit.DAYS);
|
|
|
+
|
|
|
+ return mchIds;
|
|
|
}
|
|
|
|
|
|
@Override
|