mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-10 14:56:06 +08:00
fix(wx): 直连商户支付应用解析禁止回退到平台档应用
根因: WxAppResolveService.resolve() 的 product 参数未参与判断, 直连商户(WECHAT_PAY)未配置商户档应用时自动兜底到平台公众号, 导致 OAuth 拿到平台维度 openId, 与直连商户号不同主体, appid/mchid 不匹配 修复: 按 product 区分, 直连商户第2级(通道能力绑定)与第3级(appType 推导兜底)均不回退平台档, resolveByChannelAppId 直连时商户表优先且不查平台表; 未配置抛 directMchAppNotConfigured。服务商(ISV)平台档兜底保留 新增 i18n key directMchAppNotConfigured(zh-CN/en-US/zh-TW/zh-HK/ja-JP/ko-KR, 东盟回退 zh-CN)
This commit is contained in:
@@ -12,6 +12,7 @@ import cn.daxpay.open.payment.wx.facade.WxAppFacade;
|
||||
import cn.daxpay.open.payment.wx.facade.WxAppView;
|
||||
import cn.daxpay.open.payment.wx.facade.WxIsvAppPair;
|
||||
import cn.daxpay.open.platform.core.code.CommonErrorCode;
|
||||
import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
|
||||
import cn.daxpay.open.platform.core.exception.BizInfoException;
|
||||
import cn.daxpay.open.platform.core.exception.DataNotExistException;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -58,11 +59,16 @@ public class WxAppResolveService implements WxAppFacade {
|
||||
}
|
||||
|
||||
/// 解析单应用:显式 channelAppId → 通道能力绑 → appType 推导(平台应用唯一命中)
|
||||
///
|
||||
/// 直连产品(WECHAT_PAY)禁止回退到平台档应用: OAuth 拿到的 openId 必须与
|
||||
/// 直连商户号同主体, 使用平台应用会导致 appid 与 mchid 主体不一致。
|
||||
/// 仅服务商(WECHAT_ISV)及其他产品允许平台档兜底。
|
||||
@Override
|
||||
public WxAppView resolve(String mchNo, String channelMchNo, String capability, String channelAppId, String product) {
|
||||
boolean direct = ProductEnum.WECHAT_PAY.getCode().equals(product);
|
||||
// 1. 显式 channelAppId
|
||||
if (StrUtil.isNotBlank(channelAppId)) {
|
||||
return this.resolveByChannelAppId(mchNo, channelAppId);
|
||||
return this.resolveByChannelAppId(mchNo, channelAppId, direct);
|
||||
}
|
||||
// 2. 通道能力绑定(同能力优先 merchant,其次 platform)
|
||||
if (StrUtil.isNotBlank(channelMchNo) && StrUtil.isNotBlank(capability)) {
|
||||
@@ -71,18 +77,27 @@ public class WxAppResolveService implements WxAppFacade {
|
||||
if (merchantBind.isPresent()) {
|
||||
return this.getById(AppScopeEnum.MERCHANT, merchantBind.get().getWxAppRefId());
|
||||
}
|
||||
var platformBind = wxChannelAppCapabilityManager.findByChannelMchNoAndCapabilityAndScope(
|
||||
channelMchNo, capability, AppScopeEnum.PLATFORM.getCode());
|
||||
if (platformBind.isPresent()) {
|
||||
return this.getById(AppScopeEnum.PLATFORM, platformBind.get().getWxAppRefId());
|
||||
// 直连商户不使用平台档应用, 跳过平台能力绑定
|
||||
if (!direct) {
|
||||
var platformBind = wxChannelAppCapabilityManager.findByChannelMchNoAndCapabilityAndScope(
|
||||
channelMchNo, capability, AppScopeEnum.PLATFORM.getCode());
|
||||
if (platformBind.isPresent()) {
|
||||
return this.getById(AppScopeEnum.PLATFORM, platformBind.get().getWxAppRefId());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 3. appType 推导:要求该类型平台应用唯一命中
|
||||
WxAppView platformFallback = this.resolvePlatformFallback(capability);
|
||||
if (platformFallback != null) {
|
||||
return platformFallback;
|
||||
// 3. appType 推导:要求该类型平台应用唯一命中(仅非直连)
|
||||
if (!direct) {
|
||||
WxAppView platformFallback = this.resolvePlatformFallback(capability);
|
||||
if (platformFallback != null) {
|
||||
return platformFallback;
|
||||
}
|
||||
}
|
||||
// 微信: 直连商户未配置商户档应用 / 未配置该能力对应的应用
|
||||
if (direct) {
|
||||
throw new BizInfoException(CommonErrorCode.UN_SUPPORTED_OPERATE,
|
||||
"error.payment.wx.directMchAppNotConfigured", capability);
|
||||
}
|
||||
// 微信: 未配置该能力对应的应用
|
||||
throw new BizInfoException(CommonErrorCode.UN_SUPPORTED_OPERATE,
|
||||
"error.payment.wx.appNotConfigured", capability);
|
||||
}
|
||||
@@ -136,7 +151,19 @@ public class WxAppResolveService implements WxAppFacade {
|
||||
}
|
||||
|
||||
/// 按 channelAppId 解析单应用
|
||||
private WxAppView resolveByChannelAppId(String mchNo, String channelAppId) {
|
||||
///
|
||||
/// @param direct 是否直连产品(直连时商户表优先且不回退平台表)
|
||||
private WxAppView resolveByChannelAppId(String mchNo, String channelAppId, boolean direct) {
|
||||
// 直连商户优先查商户表, 且不回退到平台表
|
||||
if (direct) {
|
||||
var mchApp = wxMchAppManager.findByMchNoAndWxAppId(mchNo, channelAppId);
|
||||
if (mchApp.isPresent()) {
|
||||
return this.toMerchantView(mchApp.get());
|
||||
}
|
||||
// 直连商户未配置商户档应用
|
||||
throw new BizInfoException(CommonErrorCode.UN_SUPPORTED_OPERATE,
|
||||
"error.payment.wx.directMchAppNotConfigured", channelAppId);
|
||||
}
|
||||
var platform = wxPlatformAppManager.findByWxAppId(channelAppId);
|
||||
if (platform.isPresent()) {
|
||||
return this.toPlatformView(platform.get());
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"appNotBound": "No WeChat app is bound",
|
||||
"channelMerchantMismatch": "Channel merchant does not exist or does not match the merchant number",
|
||||
"appNotConfigured": "No application configured for {0}, please bind one on the channel merchant app configuration page",
|
||||
"directMchAppNotConfigured": "Direct merchant has no merchant-tier app configured for {0}. Please bind one (merchant scope) on the channel merchant app configuration page. Platform-tier apps are not allowed for direct merchants.",
|
||||
"appNotUnique": "Multiple applications of type {0} exist, please explicitly configure the capability binding to select one",
|
||||
"mpAuthNotConfigured": "WeChat Official Account auth config is missing, please configure AppId and AppSecret in Third-Party Platform → WeChat Official Account Auth Config"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"appNotBound": "利用可能な微信アプリがバインドされていません",
|
||||
"channelMerchantMismatch": "チャネル加盟店が存在しないか加盟店番号と一致しません",
|
||||
"appNotConfigured": "{0} に対応するアプリが未設定です。チャネル加盟店アプリ設定ページでバインドしてください",
|
||||
"directMchAppNotConfigured": "直連加盟店は {0} に対応する加盟店ティアアプリが未設定です。チャネル加盟店アプリ設定ページで加盟店ティアアプリをバインドしてください(直連加盟店はプラットフォームティアアプリを使用できません)",
|
||||
"appNotUnique": "タイプ {0} のアプリが複数存在します。能力バインディングを明示的に設定して選択してください",
|
||||
"mpAuthNotConfigured": "微信公式アカウント認証設定が未入力です。「サードパーティプラットフォーム管理 → 微信公式アカウント認証設定」で AppId と AppSecret を設定してください"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"appNotBound": "사용 가능한 위챗 앱이 바인딩되지 않았습니다",
|
||||
"channelMerchantMismatch": "채널 가맹점이 없거나 가맹점 번호와 일치하지 않습니다",
|
||||
"appNotConfigured": "{0}에 해당하는 앱이 구성되지 않았습니다. 채널 가맹점 앱 구성 페이지에서 바인딩하세요",
|
||||
"directMchAppNotConfigured": "직연 가맹점은 {0}에 해당하는 가맹점 티어 앱이 미구성되었습니다. 채널 가맹점 앱 구성 페이지에서 가맹점 티어 앱을 바인딩하세요(직연 가맹점은 플랫폼 티어 앱 사용 불가)",
|
||||
"appNotUnique": "{0} 유형의 앱이 여러 개 존재합니다. 명시적으로 역량 바인딩을 구성하여 선택하세요",
|
||||
"mpAuthNotConfigured": "위챗 공식계정 인증 설정이 누락되었습니다. 「서드파티 플랫폼 관리 → 위챗 공식계정 인증 설정」에서 AppId와 AppSecret을 설정해 주세요"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"appNotBound": "未绑定可用的微信应用",
|
||||
"channelMerchantMismatch": "通道商户不存在或与商户号不匹配",
|
||||
"appNotConfigured": "未配置 {0} 对应的应用,请前往通道商户应用配置页绑定",
|
||||
"directMchAppNotConfigured": "直连商户未配置 {0} 对应的商户档应用,请前往通道商户应用配置页绑定商户档应用(直连商户不可使用平台档应用)",
|
||||
"appNotUnique": "存在多个 {0} 类型的应用,请显式配置能力绑定以明确选择",
|
||||
"mpAuthNotConfigured": "微信公众号认证配置未填写,请在「三方平台管理 → 微信公众号认证配置」中配置 AppId 和 AppSecret"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"appNotBound": "未綁定可用的微信應用",
|
||||
"channelMerchantMismatch": "通道商戶不存在或與商戶號不匹配",
|
||||
"appNotConfigured": "未設定 {0} 對應的應用,請前往通道商戶應用設定頁綁定",
|
||||
"directMchAppNotConfigured": "直連商戶未設定 {0} 對應的商戶檔應用,請前往通道商戶應用設定頁綁定商戶檔應用(直連商戶不可使用平台檔應用)",
|
||||
"appNotUnique": "存在多個 {0} 類型的應用,請顯式設定能力綁定以明確選擇",
|
||||
"mpAuthNotConfigured": "微信公眾號認證配置未填寫,請在「三方平台管理 → 微信公眾號認證配置」中配置 AppId 和 AppSecret"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"appNotBound": "未綁定可用的微信應用",
|
||||
"channelMerchantMismatch": "通道商戶不存在或與商戶號不匹配",
|
||||
"appNotConfigured": "未設定 {0} 對應的應用,請前往通道商戶應用設定頁綁定",
|
||||
"directMchAppNotConfigured": "直連商戶未設定 {0} 對應的商戶檔應用,請前往通道商戶應用設定頁綁定商戶檔應用(直連商戶不可使用平台檔應用)",
|
||||
"appNotUnique": "存在多個 {0} 類型的應用,請顯式設定能力綁定以明確選擇",
|
||||
"mpAuthNotConfigured": "微信公眾號認證配置未填寫,請在「三方平台管理 → 微信公眾號認證配置」中配置 AppId 和 AppSecret"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user