|
|
@@ -21,6 +21,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.fs.common.annotation.DataScope;
|
|
|
import com.fs.common.core.domain.R;
|
|
|
import com.fs.common.core.redis.RedisCache;
|
|
|
+import com.fs.common.core.text.Convert;
|
|
|
import com.fs.common.exception.CustomException;
|
|
|
import com.fs.common.utils.DateUtils;
|
|
|
import com.fs.common.utils.ServletUtils;
|
|
|
@@ -38,6 +39,7 @@ import com.fs.company.service.ICompanyUserService;
|
|
|
import com.fs.company.vo.FsStorePaymentStatisticsVO;
|
|
|
import com.fs.config.cloud.CloudHostProper;
|
|
|
import com.fs.core.config.WxMaConfiguration;
|
|
|
+import com.fs.core.config.WxOpenProperties;
|
|
|
import com.fs.core.config.WxPayProperties;
|
|
|
import com.fs.core.utils.OrderCodeUtils;
|
|
|
import com.fs.course.config.RedPacketConfig;
|
|
|
@@ -176,6 +178,8 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
@Autowired
|
|
|
private FsUserWxMapper userWxMapper;
|
|
|
@Autowired
|
|
|
+ private WxOpenProperties wxOpenProperties;
|
|
|
+ @Autowired
|
|
|
private ICompanyUserService companyUserService;
|
|
|
@Autowired
|
|
|
private ICompanyService companyService;
|
|
|
@@ -210,6 +214,9 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
@Autowired
|
|
|
private ICompanyConfigService companyConfigService;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private ISysRedpacketConfigMoreService sysRedpacketConfigMoreService;
|
|
|
+
|
|
|
@Autowired
|
|
|
private RedissonClient redissonClient;
|
|
|
|
|
|
@@ -634,36 +641,13 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
return R.error("[红包领取] 当前用户当前已经领取红包已经达到限制!");
|
|
|
}
|
|
|
|
|
|
- String json;
|
|
|
- RedPacketConfig config = new RedPacketConfig();
|
|
|
- // 根据红包模式获取配置
|
|
|
- switch (param.getRedPacketMode()){
|
|
|
- case 1:
|
|
|
- json = configService.selectConfigByKey("redPacket.config");
|
|
|
- config = JSONUtil.toBean(json, RedPacketConfig.class);
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- json = companyConfigService.selectRedPacketConfigByKey(param.getCompanyId());
|
|
|
- //如果分公司配置为空就走总后台的配置
|
|
|
- if (StringUtils.isEmpty(json)){
|
|
|
- json = configService.selectConfigByKey("redPacket.config");
|
|
|
- }
|
|
|
- config = JSONUtil.toBean(json, RedPacketConfig.class);
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw new UnsupportedOperationException("当前红包模式不支持!");
|
|
|
- }
|
|
|
- //H5的用公众号的appid发,小程序的用小程序的appid来发
|
|
|
- if (param.getSource()==2){
|
|
|
- // 传参appId为空时,仍然使用配置里面的
|
|
|
- String appId = StringUtils.isBlank(param.getAppId()) ? config.getMiniappId() : param.getAppId();
|
|
|
- config.setAppId(appId);
|
|
|
- }
|
|
|
+ RedPacketConfig config = resolveActiveRedPacketConfig(param.getRedPacketMode(), param.getCompanyId());
|
|
|
+ applyMerchantChannelIdentity(param, config);
|
|
|
logger.info("最终传参 {}",config);
|
|
|
//组合返回参数
|
|
|
R result = new R();
|
|
|
// 根据 isNew 判断使用哪种发红包方式
|
|
|
- if (config.getIsNew() != null && config.getIsNew() == 1) {
|
|
|
+ if (isNewMerchantChannel(config)) {
|
|
|
result = sendRedPacketV3Internal(param, config);
|
|
|
} else {
|
|
|
result= sendRedPacketLegacyInternal(param, config);
|
|
|
@@ -677,6 +661,15 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
return result;
|
|
|
}catch (Exception e){
|
|
|
logger.error("领取红包失败原因:{}", ExceptionUtils.getMessage(e),e);
|
|
|
+ if (e instanceof CustomException) {
|
|
|
+ return R.error(e.getMessage());
|
|
|
+ }
|
|
|
+ if (isMerchantBalanceInsufficient(e)) {
|
|
|
+ if (param.getRedPacketMode() != null && param.getRedPacketMode() == 1) {
|
|
|
+ redisCache.incr("sys_config:redPacket.config.newCount", 1L);
|
|
|
+ }
|
|
|
+ return R.error("[红包领取] 账户余额不足,请联系管理员!");
|
|
|
+ }
|
|
|
throw new RuntimeException(e);
|
|
|
}finally {
|
|
|
if (lock.isHeldByCurrentThread()) {
|
|
|
@@ -756,32 +749,13 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
redPacketLog.setAccBalanceBefore(companyMoney);
|
|
|
redPacketLog.setSource(param.getSource());
|
|
|
|
|
|
- String json;
|
|
|
- RedPacketConfig config = new RedPacketConfig();
|
|
|
- // 根据红包模式获取配置
|
|
|
- switch (param.getRedPacketMode()){
|
|
|
- case 1:
|
|
|
- json = configService.selectConfigByKey("redPacket.config");
|
|
|
- config = JSONUtil.toBean(json, RedPacketConfig.class);
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- json = companyConfigService.selectRedPacketConfigByKey(param.getCompanyId());
|
|
|
- config = JSONUtil.toBean(json, RedPacketConfig.class);
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw new UnsupportedOperationException("当前红包模式不支持!");
|
|
|
- }
|
|
|
- //H5的用公众号的appid发,小程序的用小程序的appid来发
|
|
|
- if (param.getSource()==2){
|
|
|
- // 传参appId为空时,仍然使用配置里面的
|
|
|
- String appId = StringUtils.isBlank(param.getAppId()) ? config.getMiniappId() : param.getAppId();
|
|
|
- config.setAppId(appId);
|
|
|
- }
|
|
|
+ RedPacketConfig config = resolveActiveRedPacketConfig(param.getRedPacketMode(), param.getCompanyId());
|
|
|
+ applyMerchantChannelIdentity(param, config);
|
|
|
logger.info("最终传参 {}",config);
|
|
|
//组合返回参数
|
|
|
R result = new R();
|
|
|
// 根据 isNew 判断使用哪种发红包方式
|
|
|
- if (config.getIsNew() != null && config.getIsNew() == 1) {
|
|
|
+ if (isNewMerchantChannel(config)) {
|
|
|
result = sendRedPacketV3Internal(param, config);
|
|
|
} else {
|
|
|
result= sendRedPacketLegacyInternal(param, config);
|
|
|
@@ -811,6 +785,15 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
redPacketLog.setStatus(2);
|
|
|
redPacketLog.setErrorMsg(ExceptionUtils.getMessage(e));
|
|
|
}
|
|
|
+ if (e instanceof CustomException) {
|
|
|
+ return R.error(e.getMessage());
|
|
|
+ }
|
|
|
+ if (isMerchantBalanceInsufficient(e)) {
|
|
|
+ if (param.getRedPacketMode() != null && param.getRedPacketMode() == 1) {
|
|
|
+ redisCache.incr("sys_config:redPacket.config.newCount", 1L);
|
|
|
+ }
|
|
|
+ return R.error("[红包领取] 账户余额不足,请联系管理员!");
|
|
|
+ }
|
|
|
throw new RuntimeException(e);
|
|
|
}finally {
|
|
|
if (lock.isHeldByCurrentThread()) {
|
|
|
@@ -822,6 +805,167 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 判断是否商户运营账户资金不足(含 RuntimeException 包装场景)
|
|
|
+ */
|
|
|
+ private boolean isMerchantBalanceInsufficient(Throwable e) {
|
|
|
+ Throwable cur = e;
|
|
|
+ while (cur != null) {
|
|
|
+ if (cur instanceof WxPayException) {
|
|
|
+ String customErrorMsg = ((WxPayException) cur).getCustomErrorMsg();
|
|
|
+ if (null != customErrorMsg && customErrorMsg.startsWith("商户运营账户资金不足")) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ String msg = cur.getMessage();
|
|
|
+ if (msg != null && (msg.contains("商户运营账户资金不足") || msg.contains("商家余额不足"))) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ cur = cur.getCause();
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public RedPacketConfig resolveActiveRedPacketConfig(Integer redPacketMode, Long companyId) {
|
|
|
+ // 公司绑定了具体多商户配置:不改红包模式,直接读该条配置
|
|
|
+ Long configId = companyService.getCompanyRedPacketConfigId(companyId);
|
|
|
+ if (configId != null) {
|
|
|
+ RedPacketConfig bindConfig = loadRedPacketConfigByMoreId(configId);
|
|
|
+ if (bindConfig == null) {
|
|
|
+ throw new CustomException("该公司绑定的红包商户配置不存在,请重新在公司管理中选择");
|
|
|
+ }
|
|
|
+ logger.info("公司{}走绑定商户发红包 configId={} mchId={} isNew={}",
|
|
|
+ companyId, configId, bindConfig.getMchId(), bindConfig.getIsNew());
|
|
|
+ return bindConfig;
|
|
|
+ }
|
|
|
+ String json;
|
|
|
+ int mode = redPacketMode == null ? 1 : redPacketMode;
|
|
|
+ switch (mode) {
|
|
|
+ case 1:
|
|
|
+ // 未绑定:走多商户页当前商户;Redis 为空再回落库表
|
|
|
+ json = Convert.toStr(redisCache.getCacheObject("sys_config:redPacket.config.new"));
|
|
|
+ if (StringUtils.isEmpty(json)) {
|
|
|
+ json = configService.selectConfigByKey("redPacket.config");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ json = companyConfigService.selectRedPacketConfigByKey(companyId);
|
|
|
+ if (StringUtils.isEmpty(json)) {
|
|
|
+ json = configService.selectConfigByKey("redPacket.config");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new UnsupportedOperationException("当前红包模式不支持!");
|
|
|
+ }
|
|
|
+ if (StringUtils.isEmpty(json)) {
|
|
|
+ throw new CustomException("红包商户配置为空,请检查多商户配置");
|
|
|
+ }
|
|
|
+ return JSONUtil.toBean(json, RedPacketConfig.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ private RedPacketConfig loadRedPacketConfigByMoreId(Long configId) {
|
|
|
+ if (configId == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ SysRedpacketConfigMore more = sysRedpacketConfigMoreService.selectSysRedpacketConfigMoreById(configId);
|
|
|
+ if (more == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ RedPacketConfig config = new RedPacketConfig();
|
|
|
+ BeanUtils.copyProperties(more, config);
|
|
|
+ return config;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean isNewMerchantChannel(RedPacketConfig config) {
|
|
|
+ return config != null && config.getIsNew() != null && config.getIsNew() == 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按商户类型设置发红包身份:
|
|
|
+ * 新商户(isNew=1):
|
|
|
+ * - App(source=3):开放平台移动应用 appId + fs_user.app_open_id
|
|
|
+ * - 其他:小程序 appId + 小程序 openId,禁止服务号改写
|
|
|
+ * 老商户:保持原逻辑(小程序来源可先切 miniappId,Legacy 再按需改回服务号)
|
|
|
+ */
|
|
|
+ private void applyMerchantChannelIdentity(WxSendRedPacketParam param, RedPacketConfig config) {
|
|
|
+ if (isNewMerchantChannel(config)) {
|
|
|
+ // App 新商户:必须用移动应用 appId + app_open_id
|
|
|
+ if (param.getSource() != null && param.getSource() == 3) {
|
|
|
+ String appAppId = wxOpenProperties != null ? wxOpenProperties.getAppId() : null;
|
|
|
+ if (StringUtils.isBlank(appAppId)) {
|
|
|
+ throw new CustomException("新商户App发红包缺少开放平台appId,请检查wx.open配置");
|
|
|
+ }
|
|
|
+ config.setAppId(appAppId);
|
|
|
+ param.setAppId(appAppId);
|
|
|
+ String appOpenId = resolveAppOpenId(param);
|
|
|
+ if (StringUtils.isBlank(appOpenId)) {
|
|
|
+ throw new CustomException("请先完成微信授权");
|
|
|
+ }
|
|
|
+ param.setOpenId(appOpenId);
|
|
|
+ param.setMpAppId(null);
|
|
|
+ logger.info("新商户App发红包身份 appId={}, openId={}", appAppId, appOpenId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String miniAppId = StringUtils.isNotBlank(param.getAppId()) ? param.getAppId() : config.getMiniappId();
|
|
|
+ if (StringUtils.isBlank(miniAppId)) {
|
|
|
+ throw new CustomException("新商户发红包缺少小程序appId,请检查商户配置或请求参数");
|
|
|
+ }
|
|
|
+ config.setAppId(miniAppId);
|
|
|
+ String miniOpenId = resolveMiniappOpenId(param, miniAppId);
|
|
|
+ if (StringUtils.isBlank(miniOpenId)) {
|
|
|
+ throw new CustomException("openId参数错误,请清理缓存重新授权小程序");
|
|
|
+ }
|
|
|
+ param.setOpenId(miniOpenId);
|
|
|
+ // 新商户禁止服务号改写
|
|
|
+ param.setMpAppId(null);
|
|
|
+ logger.info("新商户发红包身份 appId={}, openId={}", miniAppId, miniOpenId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 老商户:H5用公众号appId,小程序来源先切小程序appId(Legacy 有 mp 时可再改回服务号)
|
|
|
+ if (param.getSource() != null && param.getSource() == 2) {
|
|
|
+ String appId = StringUtils.isBlank(param.getAppId()) ? config.getMiniappId() : param.getAppId();
|
|
|
+ config.setAppId(appId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveAppOpenId(WxSendRedPacketParam param) {
|
|
|
+ FsUser user = param.getUser();
|
|
|
+ if (user != null && StringUtils.isNotBlank(user.getAppOpenId())) {
|
|
|
+ return user.getAppOpenId();
|
|
|
+ }
|
|
|
+ if (StringUtils.isNotBlank(param.getOpenId())) {
|
|
|
+ return param.getOpenId();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveMiniappOpenId(WxSendRedPacketParam param, String miniAppId) {
|
|
|
+ FsUser user = param.getUser();
|
|
|
+ if (user == null) {
|
|
|
+ return param.getOpenId();
|
|
|
+ }
|
|
|
+ if (StringUtils.isNotBlank(miniAppId)) {
|
|
|
+ Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
|
|
|
+ .eq(FsUserWx::getFsUserId, user.getUserId())
|
|
|
+ .eq(FsUserWx::getAppId, miniAppId)
|
|
|
+ .last("limit 1");
|
|
|
+ FsUserWx fsUserWx = userWxMapper.selectOne(queryWrapper);
|
|
|
+ if (fsUserWx != null && StringUtils.isNotBlank(fsUserWx.getOpenId())) {
|
|
|
+ return fsUserWx.getOpenId();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (StringUtils.isNotBlank(user.getCourseMaOpenId())) {
|
|
|
+ return user.getCourseMaOpenId();
|
|
|
+ }
|
|
|
+ if (StringUtils.isNotBlank(param.getOpenId())
|
|
|
+ && (user.getMpOpenId() == null || !param.getOpenId().equals(user.getMpOpenId()))) {
|
|
|
+ return param.getOpenId();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
private static long getExpireSeconds() {
|
|
|
Calendar calendar = Calendar.getInstance();
|
|
|
calendar.add(Calendar.DAY_OF_YEAR, 1);
|
|
|
@@ -880,8 +1024,11 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
// 内部方法:处理旧版本的发红包逻辑
|
|
|
private R sendRedPacketLegacyInternal(WxSendRedPacketParam param, RedPacketConfig config) {
|
|
|
//如果服务号的配置存在,小程序红包接口可以使用服务号来发红包,重新赋值
|
|
|
- //仅老商户支持
|
|
|
- if (param.getUser().getMpOpenId()!=null && StringUtils.isNotEmpty(param.getMpAppId())){
|
|
|
+ //仅老商户支持(新商户 isNew=1 禁止改写)
|
|
|
+ if (!isNewMerchantChannel(config)
|
|
|
+ && param.getUser() != null
|
|
|
+ && param.getUser().getMpOpenId()!=null
|
|
|
+ && StringUtils.isNotEmpty(param.getMpAppId())){
|
|
|
config.setAppId(param.getMpAppId());
|
|
|
param.setOpenId(param.getUser().getMpOpenId());
|
|
|
}
|
|
|
@@ -929,19 +1076,8 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
public String transferNotify(String notifyData, HttpServletRequest request) {
|
|
|
logger.info("zyp \n【收到转账回调】:{}",notifyData);
|
|
|
try {
|
|
|
- String json = configService.selectConfigByKey("redPacket.config");
|
|
|
- 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"));
|
|
|
- WxPayTransferBatchesNotifyV3Result result = wxPayService.parseTransferBatchesNotifyV3Result(notifyData,signatureHeader);
|
|
|
+ SignatureHeader signatureHeader = buildSignatureHeader(request);
|
|
|
+ WxPayTransferBatchesNotifyV3Result result = parseBatchesNotifyWithFallback(notifyData, signatureHeader);
|
|
|
logger.info("到零钱回调:{}",result.getResult());
|
|
|
if (result.getResult().getBatchStatus().equals("FINISHED") && result.getResult().getFailNum()==0) {
|
|
|
R r = redPacketLogService.syncRedPacket(result.getResult().getOutBatchNo(),result.getResult().getBatchId());
|
|
|
@@ -954,8 +1090,8 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
}else {
|
|
|
return WxPayNotifyResponse.fail("");
|
|
|
}
|
|
|
- } catch (WxPayException e) {
|
|
|
- logger.error("zyp \n【转账回调异常】:{}", e.getReturnMsg());
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("zyp \n【转账回调异常】msg={}", e.getMessage(), e);
|
|
|
return WxPayNotifyResponse.fail(e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
@@ -1093,40 +1229,140 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
|
|
|
|
|
|
@Override
|
|
|
public String v3TransferNotify(String notifyData, HttpServletRequest request) {
|
|
|
- logger.info("zyp \n【收到转账回调V3】:{}",notifyData);
|
|
|
+ logger.info("zyp \n【收到转账回调V3】:{}", notifyData);
|
|
|
try {
|
|
|
- String json = configService.selectConfigByKey("redPacket.config");
|
|
|
- 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("到零钱回调1:{}",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)){
|
|
|
+ SignatureHeader signatureHeader = buildSignatureHeader(request);
|
|
|
+ logger.info("V3回调请求头 timestamp={}, nonce={}, serial={}",
|
|
|
+ signatureHeader.getTimeStamp(), signatureHeader.getNonce(), signatureHeader.getSerial());
|
|
|
+ TransferBillsNotifyResult result = parseTransferBillsNotifyWithFallback(notifyData, signatureHeader);
|
|
|
+ logger.info("到零钱回调1:{}", result.getResult());
|
|
|
+ if (result.getResult() != null && "SUCCESS".equals(result.getResult().getState())) {
|
|
|
+ R r = redPacketLogService.syncRedPacket(result.getResult().getOutBillNo(), result.getResult().getTransferBillNo());
|
|
|
+ logger.info("result:{}", r);
|
|
|
+ if (r.get("code").equals(200)) {
|
|
|
return WxPayNotifyResponse.success("处理成功");
|
|
|
- }else {
|
|
|
+ } else {
|
|
|
return WxPayNotifyResponse.fail("");
|
|
|
}
|
|
|
- }else {
|
|
|
+ } else {
|
|
|
+ logger.warn("V3转账回调非SUCCESS,不更新红包状态 state={}",
|
|
|
+ result.getResult() == null ? null : result.getResult().getState());
|
|
|
return WxPayNotifyResponse.fail("");
|
|
|
}
|
|
|
- } catch (WxPayException e) {
|
|
|
- e.printStackTrace();
|
|
|
- logger.error("zyp \n【转账回调异常】:{}", e.getReturnMsg());
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 原先只捕 WxPayException,且 getReturnMsg() 常为 null,导致只看到「收到转账回调V3」后续全无
|
|
|
+ logger.error("zyp \n【转账回调异常V3】msg={}", e.getMessage(), e);
|
|
|
return WxPayNotifyResponse.fail(e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private SignatureHeader buildSignatureHeader(HttpServletRequest request) {
|
|
|
+ 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"));
|
|
|
+ return signatureHeader;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 灰度期间老/新商户回调并存:先 Redis 多商户,再 redPacket.config
|
|
|
+ */
|
|
|
+ private WxPayTransferBatchesNotifyV3Result parseBatchesNotifyWithFallback(String notifyData, SignatureHeader signatureHeader) throws WxPayException {
|
|
|
+ Exception last = null;
|
|
|
+ java.util.List<RedPacketConfig> candidates = listNotifyCandidateConfigs();
|
|
|
+ logger.info("批次回调验签候选商户数={} mchIds={}", candidates.size(),
|
|
|
+ candidates.stream().map(RedPacketConfig::getMchId).collect(java.util.stream.Collectors.toList()));
|
|
|
+ for (RedPacketConfig config : candidates) {
|
|
|
+ try {
|
|
|
+ WxPayConfig payConfig = new WxPayConfig();
|
|
|
+ BeanUtils.copyProperties(config, payConfig);
|
|
|
+ WxPayService wxPayService = new WxPayServiceImpl();
|
|
|
+ wxPayService.setConfig(payConfig);
|
|
|
+ return wxPayService.parseTransferBatchesNotifyV3Result(notifyData, signatureHeader);
|
|
|
+ } catch (Exception e) {
|
|
|
+ last = e;
|
|
|
+ logger.warn("转账回调验签失败,尝试下一商户配置 mchId={}, err={}", config.getMchId(), e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (last instanceof WxPayException) {
|
|
|
+ throw (WxPayException) last;
|
|
|
+ }
|
|
|
+ if (last != null) {
|
|
|
+ throw new WxPayException("批次回调验签全部失败: " + last.getMessage());
|
|
|
+ }
|
|
|
+ throw new WxPayException("无可用的红包商户配置用于回调验签");
|
|
|
+ }
|
|
|
+
|
|
|
+ private TransferBillsNotifyResult parseTransferBillsNotifyWithFallback(String notifyData, SignatureHeader signatureHeader) throws WxPayException {
|
|
|
+ Exception last = null;
|
|
|
+ // V3 单据回调优先用新商户配置验签,避免先拿老商户 apiV3Key 解密失败后日志不清晰
|
|
|
+ java.util.List<RedPacketConfig> candidates = listNotifyCandidateConfigs();
|
|
|
+ candidates.sort((a, b) -> {
|
|
|
+ int aNew = (a.getIsNew() != null && a.getIsNew() == 1) ? 0 : 1;
|
|
|
+ int bNew = (b.getIsNew() != null && b.getIsNew() == 1) ? 0 : 1;
|
|
|
+ return Integer.compare(aNew, bNew);
|
|
|
+ });
|
|
|
+ logger.info("V3单据回调验签候选商户数={} mchIds={}", candidates.size(),
|
|
|
+ candidates.stream().map(RedPacketConfig::getMchId).collect(java.util.stream.Collectors.toList()));
|
|
|
+ for (RedPacketConfig config : candidates) {
|
|
|
+ try {
|
|
|
+ WxPayConfig payConfig = new WxPayConfig();
|
|
|
+ BeanUtils.copyProperties(config, payConfig);
|
|
|
+ WxPayService wxPayService = new WxPayServiceImpl();
|
|
|
+ wxPayService.setConfig(payConfig);
|
|
|
+ TransferBillsNotifyResult parsed = wxPayService.parseTransferBillsNotifyV3Result(notifyData, signatureHeader);
|
|
|
+ logger.info("V3单据回调验签成功 mchId={}", config.getMchId());
|
|
|
+ return parsed;
|
|
|
+ } catch (Exception e) {
|
|
|
+ last = e;
|
|
|
+ logger.warn("V3转账回调验签失败,尝试下一商户配置 mchId={}, isNew={}, err={}",
|
|
|
+ config.getMchId(), config.getIsNew(), e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (last instanceof WxPayException) {
|
|
|
+ throw (WxPayException) last;
|
|
|
+ }
|
|
|
+ if (last != null) {
|
|
|
+ throw new WxPayException("V3单据回调验签全部失败: " + last.getMessage());
|
|
|
+ }
|
|
|
+ throw new WxPayException("无可用的红包商户配置用于回调验签");
|
|
|
+ }
|
|
|
+
|
|
|
+ private java.util.List<RedPacketConfig> listNotifyCandidateConfigs() {
|
|
|
+ java.util.List<RedPacketConfig> list = new java.util.ArrayList<>();
|
|
|
+ java.util.Set<String> mchIds = new java.util.HashSet<>();
|
|
|
+ // 多商户表优先(公司绑定的新/老商户都在这里),避免被 Redis 同 mchId 占坑跳过
|
|
|
+ List<SysRedpacketConfigMore> moreList = sysRedpacketConfigMoreService.selectSysRedpacketConfigMoreList(new SysRedpacketConfigMore());
|
|
|
+ if (moreList != null) {
|
|
|
+ for (SysRedpacketConfigMore more : moreList) {
|
|
|
+ if (more == null || StringUtils.isEmpty(more.getMchId())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (mchIds.add(more.getMchId())) {
|
|
|
+ RedPacketConfig config = new RedPacketConfig();
|
|
|
+ BeanUtils.copyProperties(more, config);
|
|
|
+ list.add(config);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ String redisJson = Convert.toStr(redisCache.getCacheObject("sys_config:redPacket.config.new"));
|
|
|
+ if (StringUtils.isNotEmpty(redisJson)) {
|
|
|
+ RedPacketConfig redisConfig = JSONUtil.toBean(redisJson, RedPacketConfig.class);
|
|
|
+ if (redisConfig != null && StringUtils.isNotEmpty(redisConfig.getMchId()) && mchIds.add(redisConfig.getMchId())) {
|
|
|
+ list.add(redisConfig);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ String oldJson = configService.selectConfigByKey("redPacket.config");
|
|
|
+ if (StringUtils.isNotEmpty(oldJson)) {
|
|
|
+ RedPacketConfig oldConfig = JSONUtil.toBean(oldJson, RedPacketConfig.class);
|
|
|
+ if (oldConfig != null && StringUtils.isNotEmpty(oldConfig.getMchId()) && mchIds.add(oldConfig.getMchId())) {
|
|
|
+ list.add(oldConfig);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
@Transactional
|
|
|
public R sendRedPacketTest(WxSendRedPacketParam param) {
|