fix(transfer): 微信转账发起 AppId 回写落库, 确认收款查询凭证组装改用 buildTransferConfig 对齐发起路径

- WechatTransferStrategy 凭证组装后回写 wxAppId 到 TransferStrategyContext
- TransferAssistService.processing/mirrorProcessing 透传 wxAppId, 处理中镜像落库
- WechatTransferConfirmQueryService 改用 buildTransferConfig(查转账配置取发起应用), 与发起策略 buildCredential 一致
This commit is contained in:
daxpay
2026-08-08 14:49:10 +08:00
parent 09547d9018
commit 39637e9af6
5 changed files with 32 additions and 9 deletions

View File

@@ -1,12 +1,15 @@
package cn.daxpay.open.channel.wechat.service.payment.transfer;
import cn.daxpay.open.channel.wechat.client.credential.WechatSdkCredential;
import cn.daxpay.open.channel.wechat.dao.direct.WechatTransferConfigManager;
import cn.daxpay.open.channel.wechat.entity.direct.WechatTransferConfig;
import cn.daxpay.open.channel.wechat.result.direct.WechatTransferConfirmResult;
import cn.daxpay.open.channel.wechat.service.direct.WechatDirectConfigAssembler;
import cn.daxpay.open.payment.common.context.MerchantContextLoader;
import cn.daxpay.open.payment.trade.transfer.dao.WechatTransferOrderManager;
import cn.daxpay.open.payment.trade.transfer.entity.WechatTransferOrder;
import cn.daxpay.open.platform.core.code.CommonErrorCode;
import cn.daxpay.open.platform.core.code.DaxPayErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -20,6 +23,10 @@ import java.util.Set;
/// 跨租户引导读订单([WechatTransferOrderManager#findByTransferNoNotTenant]) →
/// 装载商户上下文 → 通道凭证组装获取 wxMchId/wxAppId → 返回 packageInfo 等拉起参数。
///
/// 凭证组装复用发起转账路径([WechatDirectConfigAssembler#buildTransferConfig]):
/// 查「微信转账配置」取发起应用引用(transferAppRefId) → 加载商户档应用 wxAppId + 密钥,
/// 与 [cn.daxpay.open.channel.wechat.strategy.direct.transfer.WechatTransferStrategy#buildCredential] 完全一致。
///
/// 安全模型: 凭 transferNo 高熵不可猜防枚举, 仅返回该订单的公开收款信息。
@Slf4j
@Service
@@ -32,6 +39,7 @@ public class WechatTransferConfirmQueryService {
private final WechatTransferOrderManager wechatTransferOrderManager;
private final MerchantContextLoader merchantContextLoader;
private final WechatDirectConfigAssembler wechatDirectConfigAssembler;
private final WechatTransferConfigManager wechatTransferConfigManager;
/// 查询确认收款信息
///
@@ -44,9 +52,13 @@ public class WechatTransferConfirmQueryService {
"pay.error.transfer.notFound"));
// 装载商户上下文, 后续通道配置查询走租户隔离
merchantContextLoader.initMch(order.getMchNo());
// 通道凭证组装(wxMchId + wxAppId)
WechatSdkCredential credential = wechatDirectConfigAssembler.buildConfig(
order.getMchNo(), order.getChannelMchNo(), null, null);
// 复用发起转账的凭证组装路径: 查转账配置 → buildTransferConfig
WechatTransferConfig transferConfig = wechatTransferConfigManager
.findByChannelMchNo(order.getChannelMchNo())
.orElseThrow(() -> new BizInfoException(DaxPayErrorCode.CONFIG_NOT_EXIST,
"error.channel.wechat.transferConfigNotConfigured"));
WechatSdkCredential credential = wechatDirectConfigAssembler.buildTransferConfig(
order.getChannelMchNo(), transferConfig.getTransferAppRefId());
boolean terminal = TERMINAL_STATES.contains(order.getStatus());
return new WechatTransferConfirmResult()
.setMchId(credential.getWxMchId())

View File

@@ -117,7 +117,10 @@ public class WechatTransferStrategy extends AbsTransferStrategy {
"error.channel.wechat.transferAppNotConfigured");
}
context.setTransferScene(transferConfig.getTransferScene());
return wechatDirectConfigAssembler.buildTransferConfig(
WechatSdkCredential credential = wechatDirectConfigAssembler.buildTransferConfig(
context.getChannelMchNo(), transferConfig.getTransferAppRefId());
// 回写发起应用 AppId, 编排层"处理中"镜像落库
context.setWxAppId(credential.getWxAppId());
return credential;
}
}

View File

@@ -81,6 +81,9 @@ public class TransferStrategyContext {
/// 收款人姓名(微信金额档位校验用)
private String userName;
/// 转账发起应用 AppId(微信, 策略层凭证组装后回写, 编排层"处理中"镜像落库)
private String wxAppId;
/// 收款人账号类型(支付宝/抖音)
private String payeeType;

View File

@@ -333,11 +333,11 @@ public class TransferAssistService {
/// 转账处理中回写(非终态): 补通道转账单号/特有字段(微信拉起确认参数/抖音转账场景), 不改变状态
@Transactional(rollbackFor = Exception.class)
public void processing(String channel, TransferTrade trade, String outTransferNo,
String transferBody, String transferScene) {
String transferBody, String transferScene, String wxAppId) {
trade.setOutTransferNo(outTransferNo);
transferTradeManager.updateById(trade);
// 容器回写: 通道单号 + 特有字段
mirrorProcessing(channel, trade.getContainerId(), outTransferNo, transferBody, transferScene);
mirrorProcessing(channel, trade.getContainerId(), outTransferNo, transferBody, transferScene, wxAppId);
}
/// 重试重置: 容器+凭证双表回 PROCESSING, 清空通道单号/完成时间/错误信息
@@ -412,13 +412,16 @@ public class TransferAssistService {
/// 处理中回写: 按通道装载容器补通道单号/特有字段(非 CAS, 不改变状态)
private void mirrorProcessing(String channel, Long containerId, String outTransferNo,
String transferBody, String transferScene) {
String transferBody, String transferScene, String wxAppId) {
switch (channel) {
case "wechat" -> wechatTransferOrderManager.findById(containerId).ifPresent(order -> {
order.setOutTransferNo(outTransferNo);
if (transferBody != null) {
order.setTransferBody(transferBody);
}
if (wxAppId != null) {
order.setWxAppId(wxAppId);
}
wechatTransferOrderManager.updateById(order);
});
case "alipay" -> alipayTransferOrderManager.findById(containerId).ifPresent(order -> {
@@ -454,7 +457,8 @@ public class TransferAssistService {
.setFinishTime(order.getFinishTime())
.setPayeeOpenid(order.getPayeeOpenid())
.setTransferScene(order.getTransferScene())
.setUserName(order.getUserName());
.setUserName(order.getUserName())
.setWxAppId(order.getWxAppId());
}
private TransferStrategyContext buildAlipayContext(AlipayTransferOrder order) {

View File

@@ -124,7 +124,8 @@ public class TransferStartService {
} else {
// 处理中: 回写通道单号(含特有字段: 微信拉起确认参数/抖音转账场景)并注册延迟同步
assistService.processing(channel, trade,
result.getOutTransferNo(), result.getTransferBody(), context.getTransferScene());
result.getOutTransferNo(), result.getTransferBody(),
context.getTransferScene(), context.getWxAppId());
this.registerDelaySync(trade.getTradeNo());
}
} catch (Exception e) {