refactor(channel): 通道应用解析 appType 自动推导改为唯一性校验, >1 拒绝猜测

alipay/douyin/wechat 三通道 AppManager 新增 listByChannelMshNoAndAppType 返回全量列表;
CapabilityService 推导时若同类型应用存在多个, 抛 appNotUnique 要求显式配置能力绑定, 避免静默选错应用。
同步清理 resolveAppNotTenant 等冗余方法。
This commit is contained in:
DaxPay Dev
2026-07-27 23:26:46 +08:00
parent c905622477
commit 35ddd92c2b
13 changed files with 151 additions and 84 deletions

View File

@@ -56,6 +56,16 @@ public class AlipayDirectAppManager extends BaseManager<AlipayDirectAppMapper, A
.orderByAsc(AlipayDirectApp::getId));
}
/// 根据通道商户号与应用类型查询全部应用,供应用解析做唯一性判断
public List<AlipayDirectApp> listByChannelMchNoAndAppType(String channelMchNo, String appType) {
return lambdaQuery()
.eq(AlipayDirectApp::getChannelMchNo, channelMchNo)
.eq(AlipayDirectApp::getAppType, appType)
.orderByAsc(AlipayDirectApp::getCreateTime)
.orderByAsc(AlipayDirectApp::getId)
.list();
}
/// 根据通道商户号查询首个应用(支付/回调,租户内)
public Optional<AlipayDirectApp> findFirstByChannelMchNo(String channelMchNo) {
return firstOpt(q -> q

View File

@@ -112,11 +112,12 @@ public class AlipayDirectAppCapabilityService {
/// 支付/回调解析应用:显式配置 > appType 自动推导(须已装载 mchNo租户内
///
/// 认证无上下文请用 [#resolveAppNotTenant]。
/// appType 推导要求该通道商户下该类型应用唯一命中:
/// - 唯一命中:返回该应用
/// - 该类型存在多个应用:抛 appNotUnique要求显式配置能力绑
/// - 该类型无应用:返回 empty由调用方走最终报错
///
/// @param channelMchNo 通道商户号
/// @param capability 支付能力编码
/// @return 命中的应用;两者均未命中返回 empty由调用方兜底回退
/// 认证无上下文请用 [#resolveAppNotTenant]。
public Optional<AlipayDirectApp> resolveApp(String channelMchNo, String capability) {
if (StrUtil.hasBlank(channelMchNo, capability)) {
return Optional.empty();
@@ -126,20 +127,22 @@ public class AlipayDirectAppCapabilityService {
if (rel.isPresent()) {
return alipayDirectAppManager.findById(rel.get().getAlipayDirectAppId());
}
// 2. 未配置则按能力 → appType 自动推导
// 2. appType 推导:要求该通道商户下该类型应用唯一命中,>1 拒绝猜测
String appType = AlipayDirectAppTypeCode.resolveAppType(capability);
if (appType != null) {
return alipayDirectAppManager.findFirstByChannelMchNoAndAppType(channelMchNo, appType);
List<AlipayDirectApp> apps = alipayDirectAppManager.listByChannelMchNoAndAppType(channelMchNo, appType);
if (apps.size() > 1) {
// 存在多个同类型应用,请显式配置能力绑定以明确选择
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.alipay.appNotUnique", appType);
}
if (apps.size() == 1) {
return Optional.of(apps.getFirst());
}
}
return Optional.empty();
}
/// 认证等无租户上下文时解析应用(忽略租户)
@IgnoreTenant
public Optional<AlipayDirectApp> resolveAppNotTenant(String channelMchNo, String capability) {
return resolveApp(channelMchNo, capability);
}
/// 查询支付宝直连产品支持的支付能力候选列表(含国际化名称)
public List<AlipayDirectCapabilityOption> listSupportedCapabilities() {
AbsProductStrategy strategy = PaymentStrategyFactory.createByProduct(

View File

@@ -1,11 +1,12 @@
package cn.daxpay.open.channel.alipay.service.direct;
import cn.daxpay.open.channel.alipay.dao.direct.AlipayDirectAppManager;
import cn.daxpay.open.channel.alipay.dao.direct.AlipayDirectChannelMerchantManager;
import cn.daxpay.open.channel.alipay.client.credential.AlipaySdkCredential;
import cn.daxpay.open.channel.alipay.entity.direct.AlipayDirectApp;
import cn.daxpay.open.channel.alipay.entity.direct.AlipayDirectAppKeyConfig;
import cn.daxpay.open.channel.alipay.entity.direct.AlipayDirectChannelMerchant;
import cn.daxpay.open.platform.core.code.CommonErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -21,7 +22,7 @@ import java.util.Optional;
/// 沙箱标识直接读通道商户固化的 [AlipayDirectChannelMerchant#isSandbox]
/// (创建时按当时产品 activeEnv 写入, 不随产品切换改变), 据此选择对应环境的密钥与网关地址。
///
/// 应用解析优先级:能力关联(显式配置 > appType自动推导) > 通道商户首个应用 > 商户号首个应用(兜底)。
/// 应用解析优先级:能力关联(显式配置 > appType 唯一推导) > 未命中报错(拒绝首个兜底)。
///
/// 供支付策略([cn.daxpay.open.channel.alipay.strategy.direct.AlipayDirectPayStrategy])组装通道调用凭证。
@Slf4j
@@ -29,7 +30,6 @@ import java.util.Optional;
@RequiredArgsConstructor
public class AlipayDirectConfigAssembler {
private final AlipayDirectAppManager alipayDirectAppManager;
private final AlipayDirectChannelMerchantManager alipayDirectChannelMerchantManager;
private final AlipayDirectAppKeyConfigService alipayDirectAppKeyConfigService;
private final AlipayDirectAppCapabilityService alipayDirectAppCapabilityService;
@@ -64,21 +64,16 @@ public class AlipayDirectConfigAssembler {
}
/// 解析支付使用的应用(须已装载 mchNo租户内
/// 优先级:能力关联 > 通道商户首个 > 商户号首个兜底
/// 优先级:能力关联(显式配置 > appType 唯一推导)> 未命中报错(不再首个兜底
private AlipayDirectApp resolveApp(String mchNo, String channelMchNo, String capability) {
// 1. 能力关联解析(显式配置 > appType自动推导)
// 能力关联解析(显式配置 > appType 唯一推导)
Optional<AlipayDirectApp> resolved = alipayDirectAppCapabilityService.resolveApp(channelMchNo, capability);
if (resolved.isPresent()) {
return resolved.get();
}
// 2. 兜底:按通道商户号取首个应用
Optional<AlipayDirectApp> byChannel = alipayDirectAppManager.findFirstByChannelMchNo(channelMchNo);
if (byChannel.isPresent()) {
return byChannel.get();
}
// 3. 最终兜底:按商户号取首个应用(兼容单应用旧场景)
// 支付宝: 直连商户应用不存在
return alipayDirectAppManager.findFirstByMchNo(mchNo)
.orElseThrow(() -> new DataNotExistException("error.channel.alipay.mchAppNotFound"));
// 未命中:不再做通道商户首个/商户号首个兜底,直接报错
// 支付宝: 未配置该能力对应的应用
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.alipay.appNotConfigured", capability);
}
}

View File

@@ -54,6 +54,16 @@ public class DouyinDirectAppManager extends BaseManager<DouyinDirectAppMapper, D
.orderByAsc(DouyinDirectApp::getId));
}
/// 根据通道商户号与应用类型取全部应用,供应用解析做唯一性判断
public List<DouyinDirectApp> listByChannelMchNoAndAppType(String channelMchNo, String appType) {
return lambdaQuery()
.eq(DouyinDirectApp::getChannelMchNo, channelMchNo)
.eq(DouyinDirectApp::getAppType, appType)
.orderByAsc(DouyinDirectApp::getCreateTime)
.orderByAsc(DouyinDirectApp::getId)
.list();
}
/// 按通道商户号与 douyinAppId 查询应用(支付/回调,租户内)
public Optional<DouyinDirectApp> findByChannelMchNoAndDouyinAppId(String channelMchNo, String douyinAppId) {
return lambdaQuery()

View File

@@ -109,13 +109,15 @@ public class DouyinDirectAppCapabilityService {
capabilityManager.deleteByDouyinDirectAppId(douyinDirectAppId);
}
/// 支付/回调解析应用:显式配置 > appType 自动推导 > 通道商户首个兜底(须已装载 mchNo租户内
/// 支付/回调解析应用:显式配置 > appType 自动推导(须已装载 mchNo租户内
///
/// appType 推导要求该通道商户下该类型应用唯一命中:
/// - 唯一命中:返回该应用
/// - 该类型存在多个应用:抛 appNotUnique要求显式配置能力绑
/// - 该类型无应用:返回 empty由调用方走最终报错
/// 不再"通道商户首个"兜底。
///
/// 认证无上下文请用 [#resolveAppNotTenant]。
///
/// @param channelMchNo 通道商户号
/// @param capability 支付能力编码
/// @return 命中的应用;三者均未命中返回 empty由调用方兜底回退
public Optional<DouyinDirectApp> resolveApp(String channelMchNo, String capability) {
if (StrUtil.hasBlank(channelMchNo, capability)) {
return Optional.empty();
@@ -125,16 +127,21 @@ public class DouyinDirectAppCapabilityService {
if (rel.isPresent()) {
return douyinDirectAppManager.findById(rel.get().getDouyinDirectAppId());
}
// 2. 未配置则按能力 → appType 自动推导
// 2. appType 推导:要求该通道商户下该类型应用唯一命中,>1 拒绝猜测
String appType = DouyinAppTypeCode.resolveAppType(capability);
if (appType != null) {
Optional<DouyinDirectApp> byType = douyinDirectAppManager.findFirstByChannelMchNoAndAppType(channelMchNo, appType);
if (byType.isPresent()) {
return byType;
List<DouyinDirectApp> apps = douyinDirectAppManager.listByChannelMchNoAndAppType(channelMchNo, appType);
if (apps.size() > 1) {
// 存在多个同类型应用,请显式配置能力绑定以明确选择
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.douyin.appNotUnique", appType);
}
if (apps.size() == 1) {
return Optional.of(apps.getFirst());
}
}
// 3. 最终兜底:按通道商户号取首个应用
return douyinDirectAppManager.findFirstByChannelMchNo(channelMchNo);
// 3. 不再首个兜底,返回 empty 由调用方报错
return Optional.empty();
}
/// 认证等无租户上下文时解析应用(忽略租户)
@@ -145,8 +152,8 @@ public class DouyinDirectAppCapabilityService {
/// H5 silent_auth / JS-SDK 验签用网站应用解析(忽略租户)
///
/// 优先级: channelAppId 显式 > capability 命中且为 web_app > 通道商户首个 web_app。
/// 勿盲跟 DOUYIN_JSAPI→mini_program 推导
/// 优先级: channelAppId 显式 > capability 命中且为 web_app > 通道商户唯一 web_app。
/// 不再"首个 web_app"兜底web_app 不唯一或不存在均直接报错
@IgnoreTenant
public DouyinDirectApp resolveWebAppForH5Auth(String channelMchNo, String capability, String channelAppId) {
if (StrUtil.isNotBlank(channelAppId)) {
@@ -160,10 +167,19 @@ public class DouyinDirectAppCapabilityService {
return byCap.get();
}
}
return douyinDirectAppManager.findFirstByChannelMchNoAndAppTypeNotTenant(
channelMchNo, DouyinAppTypeCode.WEB_APP)
.orElseThrow(() -> new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.douyin.webAppNotFound"));
// H5 验签需 web_app要求该类型唯一命中>1 抛 notUnique==0 抛 notConfigured
List<DouyinDirectApp> webApps = douyinDirectAppManager.listByChannelMchNoAndAppType(
channelMchNo, DouyinAppTypeCode.WEB_APP);
if (webApps.size() > 1) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.douyin.appNotUnique", DouyinAppTypeCode.WEB_APP);
}
if (webApps.size() == 1) {
return webApps.getFirst();
}
// 抖音: 未配置 H5 验签所需的 web_app 类型应用
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.douyin.appNotConfigured", DouyinAppTypeCode.WEB_APP);
}
/// 查询抖音直连产品支持的支付能力候选列表(含国际化名称)

View File

@@ -5,6 +5,8 @@ import cn.daxpay.open.channel.douyin.dao.direct.DouyinDirectChannelMerchantManag
import cn.daxpay.open.channel.douyin.entity.direct.DouyinDirectApp;
import cn.daxpay.open.channel.douyin.entity.direct.DouyinDirectChannelMerchant;
import cn.daxpay.open.channel.douyin.entity.direct.DouyinDirectKeyConfig;
import cn.daxpay.open.platform.core.code.CommonErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -35,9 +37,10 @@ public class DouyinDirectConfigAssembler {
/// @param capability 支付能力编码(用于选择匹配的应用)
/// @return 抖音 SDK 凭证, 字段对齐子应用 DouyinSdkCredential
public DouyinSdkCredential buildConfig(String mchNo, String channelMchNo, String capability) {
// 1. 解析支付使用的应用(显式能力关联 > appType 推导 > 通道商户首个兜底)
// 1. 解析支付使用的应用(显式能力关联 > appType 唯一推导;未命中报错,不兜底)
DouyinDirectApp app = appCapabilityService.resolveApp(channelMchNo, capability)
.orElseThrow(() -> new DataNotExistException("error.channel.douyin.mchAppNotFound"));
.orElseThrow(() -> new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.douyin.appNotConfigured", capability));
// 2. 读取通道商户绑定(获取抖音商户号 dyMchId 作为 mchId)
DouyinDirectChannelMerchant merchant = channelMerchantManager.lambdaQuery()

View File

@@ -4,10 +4,9 @@ import cn.daxpay.open.channel.douyin.entity.direct.DouyinDirectApp;
import cn.daxpay.open.channel.douyin.entity.direct.DouyinDirectAppAuthConfig;
import cn.daxpay.open.channel.douyin.service.direct.DouyinDirectAppAuthConfigService;
import cn.daxpay.open.channel.douyin.service.direct.DouyinDirectAppCapabilityService;
import cn.daxpay.open.payment.auth.AuthRedirectUri;
import cn.daxpay.open.payment.auth.AuthSession;
import cn.daxpay.open.payment.strategy.auth.AbsChannelAuthStrategy;
import cn.daxpay.open.payment.strategy.auth.AuthContext;
import cn.daxpay.open.payment.auth.core.AuthRedirectUri;
import cn.daxpay.open.payment.auth.core.AuthSession;
import cn.daxpay.open.payment.auth.merchant.AbsProductAuthStrategy;
import cn.daxpay.open.payment.unipay.param.assist.AuthCodeParam;
import cn.daxpay.open.payment.unipay.param.assist.GenerateAuthUrlParam;
import cn.daxpay.open.payment.unipay.result.assist.AuthResult;
@@ -32,7 +31,7 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class DouyinDirectAuthStrategy extends AbsChannelAuthStrategy {
public class DouyinDirectAuthStrategy extends AbsProductAuthStrategy {
private final DouyinH5AuthService douyinH5AuthService;
private final DouyinDirectAppCapabilityService douyinDirectAppCapabilityService;
@@ -63,7 +62,7 @@ public class DouyinDirectAuthStrategy extends AbsChannelAuthStrategy {
/// 通过授权 code 换取 openId
@Override
public AuthResult doAuth(AuthCodeParam param, AuthSession session) {
AuthContext ctx = resolveContext(param, session);
var ctx = resolveContext(param, session);
DouyinDirectApp app = douyinDirectAppCapabilityService.resolveWebAppForH5Auth(
ctx.channelMchNo(), ctx.capability(), ctx.channelAppId());
DouyinDirectAppAuthConfig authConfig = douyinDirectAppAuthConfigService.findByDouyinDirectAppIdForAuth(app.getId());

View File

@@ -45,6 +45,16 @@ public class WechatDirectAppManager extends BaseManager<WechatDirectAppMapper, W
.orderByAsc(WechatDirectApp::getId));
}
/// 按通道商户号与应用类型查询全部应用,供应用解析做唯一性判断
public List<WechatDirectApp> listByChannelMchNoAndAppType(String channelMchNo, String appType) {
return lambdaQuery()
.eq(WechatDirectApp::getChannelMchNo, channelMchNo)
.eq(WechatDirectApp::getAppType, appType)
.orderByAsc(WechatDirectApp::getCreateTime)
.orderByAsc(WechatDirectApp::getId)
.list();
}
/// 按通道商户号与wxAppId查询应用支付/回调,租户内)
public Optional<WechatDirectApp> findByChannelMchNoAndWxAppId(String channelMchNo, String wxAppId) {
return firstOpt(q -> q

View File

@@ -39,6 +39,15 @@ public class WechatIsvAppManager extends BaseManager<WechatIsvAppMapper, WechatI
.orderByAsc(WechatIsvApp::getId));
}
/// 按应用类型查询全部应用,供应用解析做唯一性判断
public List<WechatIsvApp> listByAppType(String appType) {
return lambdaQuery()
.eq(WechatIsvApp::getAppType, appType)
.orderByAsc(WechatIsvApp::getCreateTime)
.orderByAsc(WechatIsvApp::getId)
.list();
}
/// 按 wxAppId 查询应用(channelAppId显式指定认证应用时使用, 校验该appId在系统中预配过)
public Optional<WechatIsvApp> findByWxAppId(String wxAppId) {
return firstOpt(q -> q.eq(WechatIsvApp::getWxAppId, wxAppId));

View File

@@ -102,14 +102,15 @@ public class WechatDirectAppCapabilityService {
capabilityManager.deleteByWechatDirectAppId(wechatDirectAppId);
}
/// 支付/回调解析应用:显式配置 > appType 自动推导 > 通道商户首个兜底
/// 支付/回调解析应用:显式配置 > appType 自动推导(须已装载 mchNo租户内
///
/// appType 推导要求该通道商户下该类型应用唯一命中:
/// - 唯一命中:返回该应用
/// - 该类型存在多个应用:抛 appNotUnique要求显式配置能力绑
/// - 该类型无应用:返回 empty由调用方走最终报错
/// 不再"通道商户首个"兜底。
///
/// **须已装载 mchNo**[PaymentVerify] 的 initMch / 回调 bindMchNoForCallback走租户过滤。
/// 认证无上下文请用 [#resolveAppNotTenant]。
///
/// @param channelMchNo 通道商户号
/// @param capability 支付能力编码
/// @return 命中的应用;三者均未命中返回 empty由调用方兜底回退
public Optional<WechatDirectApp> resolveApp(String channelMchNo, String capability) {
if (StrUtil.hasBlank(channelMchNo, capability)) {
return Optional.empty();
@@ -119,16 +120,21 @@ public class WechatDirectAppCapabilityService {
if (rel.isPresent()) {
return wechatDirectAppManager.findById(rel.get().getWechatDirectAppId());
}
// 2. 未配置则按能力 → appType 自动推导
// 2. appType 推导:要求该通道商户下该类型应用唯一命中,>1 拒绝猜测
String appType = WechatAppTypeCode.resolveAppType(capability);
if (appType != null) {
Optional<WechatDirectApp> byType = wechatDirectAppManager.findFirstByChannelMchNoAndAppType(channelMchNo, appType);
if (byType.isPresent()) {
return byType;
List<WechatDirectApp> apps = wechatDirectAppManager.listByChannelMchNoAndAppType(channelMchNo, appType);
if (apps.size() > 1) {
// 存在多个同类型应用,请显式配置能力绑定以明确选择
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.wechat.appNotUnique", appType);
}
if (apps.size() == 1) {
return Optional.of(apps.getFirst());
}
}
// 3. 最终兜底:按通道商户号取首个应用
return wechatDirectAppManager.findFirstByChannelMchNo(channelMchNo);
// 3. 不再首个兜底,返回 empty
return Optional.empty();
}
/// 认证等无租户上下文时解析应用(忽略租户,内部复用 [#resolveApp] 逻辑)

View File

@@ -95,10 +95,13 @@ public class WechatIsvAppCapabilityService {
capabilityManager.deleteByWechatIsvAppId(wechatIsvAppId);
}
/// 支付时解析当前支付能力对应的应用:显式配置 > appType自动推导 > 取首个兜底
/// 支付时解析当前支付能力对应的应用:显式配置 > appType自动推导(要求唯一命中)
///
/// @param capability 支付能力编码
/// @return 命中的应用;三者均未命中返回 empty由调用方兜底回退
/// appType 推导要求该类型服务商应用全局唯一命中:
/// - 唯一命中:返回该应用
/// - 该类型存在多个应用:抛 appNotUnique要求显式配置能力绑
/// - 该类型无应用:返回 empty由调用方走最终报错
/// 不再"首个服务商应用"兜底。
public Optional<WechatIsvApp> resolveApp(String capability) {
if (StrUtil.isBlank(capability)) {
return Optional.empty();
@@ -108,16 +111,21 @@ public class WechatIsvAppCapabilityService {
if (rel.isPresent()) {
return wechatIsvAppManager.findById(rel.get().getWechatIsvAppId());
}
// 2. 未配置则按能力 → appType 自动推导
// 2. appType 推导:要求该类型服务商应用唯一命中,>1 拒绝猜测
String appType = WechatAppTypeCode.resolveAppType(capability);
if (appType != null) {
Optional<WechatIsvApp> byType = wechatIsvAppManager.findFirstByAppType(appType);
if (byType.isPresent()) {
return byType;
List<WechatIsvApp> apps = wechatIsvAppManager.listByAppType(appType);
if (apps.size() > 1) {
// 存在多个同类型服务商应用,请显式配置能力绑定以明确选择
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.wechat.appNotUnique", appType);
}
if (apps.size() == 1) {
return Optional.of(apps.getFirst());
}
}
// 3. 最终兜底:取首个服务商应用
return wechatIsvAppManager.findFirst();
// 3. 不再首个兜底,返回 empty
return Optional.empty();
}
/// 查询微信服务商产品支持的支付能力候选列表(含国际化名称)

View File

@@ -1,11 +1,10 @@
package cn.daxpay.open.channel.wechat.strategy.direct.auth;
import cn.daxpay.open.payment.auth.AuthRedirectUri;
import cn.daxpay.open.payment.auth.AuthSession;
import cn.daxpay.open.payment.auth.core.AuthRedirectUri;
import cn.daxpay.open.payment.auth.core.AuthSession;
import cn.daxpay.open.payment.merchant.dao.channel.ChannelMerchantManager;
import cn.daxpay.open.payment.merchant.entity.channel.ChannelMerchant;
import cn.daxpay.open.payment.strategy.auth.AbsChannelAuthStrategy;
import cn.daxpay.open.payment.strategy.auth.AuthContext;
import cn.daxpay.open.payment.auth.merchant.AbsProductAuthStrategy;
import cn.daxpay.open.payment.unipay.param.assist.AuthCodeParam;
import cn.daxpay.open.payment.unipay.param.assist.GenerateAuthUrlParam;
import cn.daxpay.open.payment.unipay.result.assist.AuthResult;
@@ -34,7 +33,7 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatDirectAuthStrategy extends AbsChannelAuthStrategy {
public class WechatDirectAuthStrategy extends AbsProductAuthStrategy {
private final WechatMpAuthService wechatMpAuthService;
private final WxAppFacade wxAppFacade;
@@ -65,7 +64,7 @@ public class WechatDirectAuthStrategy extends AbsChannelAuthStrategy {
/// 应用上下文优先从 session(H5 会话码场景)恢复, 否则取 param(小程序直连场景)。
@Override
public AuthResult doAuth(AuthCodeParam param, AuthSession session) {
AuthContext ctx = resolveContext(param, session);
var ctx = resolveContext(param, session);
WxAppView app = resolveApp(param.getMchNo(), ctx.channelMchNo(), ctx.capability(), ctx.channelAppId());
WechatAuthResult data = wechatMpAuthService.getTokenAndOpenId(
param.getAuthCode(), app.wxAppId(), app.appSecret());

View File

@@ -3,12 +3,11 @@ package cn.daxpay.open.channel.wechat.strategy.isv.auth;
import cn.daxpay.open.channel.wechat.code.WechatAuthAppTypeEnum;
import cn.daxpay.open.channel.wechat.dao.isv.WechatIsvChannelMerchantManager;
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvChannelMerchant;
import cn.daxpay.open.payment.auth.AuthRedirectUri;
import cn.daxpay.open.payment.auth.AuthSession;
import cn.daxpay.open.payment.auth.core.AuthRedirectUri;
import cn.daxpay.open.payment.auth.core.AuthSession;
import cn.daxpay.open.payment.merchant.dao.channel.ChannelMerchantManager;
import cn.daxpay.open.payment.merchant.entity.channel.ChannelMerchant;
import cn.daxpay.open.payment.strategy.auth.AbsChannelAuthStrategy;
import cn.daxpay.open.payment.strategy.auth.AuthContext;
import cn.daxpay.open.payment.auth.merchant.AbsProductAuthStrategy;
import cn.daxpay.open.payment.unipay.param.assist.AuthCodeParam;
import cn.daxpay.open.payment.unipay.param.assist.GenerateAuthUrlParam;
import cn.daxpay.open.payment.unipay.result.assist.AuthResult;
@@ -41,7 +40,7 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatIsvAuthStrategy extends AbsChannelAuthStrategy {
public class WechatIsvAuthStrategy extends AbsProductAuthStrategy {
private final WechatMpAuthService wechatMpAuthService;
private final WxAppFacade wxAppFacade;
@@ -68,7 +67,7 @@ public class WechatIsvAuthStrategy extends AbsChannelAuthStrategy {
/// 通过授权 code 换取 openId
@Override
public AuthResult doAuth(AuthCodeParam param, AuthSession session) {
AuthContext ctx = resolveContext(param, session);
var ctx = resolveContext(param, session);
ResolvedAuthApp app = resolveAuthApp(param.getMchNo(), ctx.channelMchNo(), ctx.capability(), ctx.channelAppId());
WechatAuthResult data = wechatMpAuthService.getTokenAndOpenId(
param.getAuthCode(), app.wxAppId(), app.appSecret());