From fb205727e29095cff64f6f22075b225a9e2f5bd3 Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Tue, 28 Jul 2026 09:23:29 +0800 Subject: [PATCH] =?UTF-8?q?docs(exception):=20=E7=BB=99=20OperationFailExc?= =?UTF-8?q?eption=20=E6=8A=9B=E5=87=BA=E7=82=B9=E8=A1=A5=E5=85=85=E4=B8=AD?= =?UTF-8?q?=E6=96=87=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/service/close/PayCloseService.java | 1 + .../pay/gateway/GatewayPayAssistService.java | 1 + .../device/MchTerminalDeviceService.java | 4 ++++ ...roller.java => QrCodeClientController.java} | 0 .../client/service/CodePayAssistService.java | 18 +++++++++++++++--- .../social/SocialAutoLoginConfigService.java | 7 ++++--- .../social/SocialLoginConfigService.java | 4 ++++ .../iam/service/social/SocialLoginService.java | 9 ++++++++- .../wechat/WechatMessageRecordService.java | 7 ++++++- 9 files changed, 43 insertions(+), 8 deletions(-) rename daxpay-payment/daxpay-payment-unipay/src/main/java/cn/daxpay/open/payment/unipay/client/controller/{DeviceQrCodeClientController.java => QrCodeClientController.java} (100%) diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/close/PayCloseService.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/close/PayCloseService.java index c6a82f125..73ca55766 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/close/PayCloseService.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/close/PayCloseService.java @@ -128,6 +128,7 @@ public class PayCloseService { throw re; } } + // 支付: 关闭订单失败 throw new OperationFailException(CommonCode.FAIL_CODE, "pay.error.pay.closeFailed"); } }, diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/gateway/GatewayPayAssistService.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/gateway/GatewayPayAssistService.java index 0128f1e98..6ccbab51b 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/gateway/GatewayPayAssistService.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/gateway/GatewayPayAssistService.java @@ -177,6 +177,7 @@ public class GatewayPayAssistService { PlatformUrlConfig urlConfig = platformUrlConfigService.getUrlConfig(); String gatewayBase = urlConfig.getPaymentGatewayBaseUrl(); if (StrUtil.isBlank(gatewayBase)) { + // 网关: 支付网关前端地址未配置 throw new OperationFailException(CommonCode.FAIL_CODE, "error.common.gatewayUrlNotConfigured"); } gatewayBase = StrUtil.removeSuffix(gatewayBase, "/"); diff --git a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/service/device/MchTerminalDeviceService.java b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/service/device/MchTerminalDeviceService.java index 2a121b85e..7752799fb 100644 --- a/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/service/device/MchTerminalDeviceService.java +++ b/daxpay-payment/daxpay-payment-merchant/src/main/java/cn/daxpay/open/payment/merchant/service/device/MchTerminalDeviceService.java @@ -142,9 +142,11 @@ public class MchTerminalDeviceService { .orElseThrow(() -> new DataNotExistException("error.device.terminal.channelNotFound")); if (!Objects.equals(system.getMchNo(), channel.getMchNo()) || !Objects.equals(channel.getMchNo(), requireMchNo())) { + // 终端: 系统终端与通道终端须属于同一商户 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.terminal.mchMismatch"); } if (terminalChannelBindManager.existsBind(param.getSystemTerminalNo(), param.getChannelTerminalId())) { + // 终端: 绑定关系已存在 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.terminal.bindExists"); } TerminalChannelBind bind = new TerminalChannelBind(); @@ -161,6 +163,7 @@ public class MchTerminalDeviceService { .orElseThrow(() -> new DataNotExistException("error.device.terminal.systemNotFound")); this.checkTerminal(system); if (!terminalChannelBindManager.existsBind(param.getSystemTerminalNo(), param.getChannelTerminalId())) { + // 终端: 绑定关系不存在 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.terminal.bindNotFound"); } terminalChannelBindManager.deleteBind(param.getSystemTerminalNo(), param.getChannelTerminalId()); @@ -212,6 +215,7 @@ public class MchTerminalDeviceService { MchStoreInfo store = mchStoreInfoManager.findByStoreNo(storeNo) .orElseThrow(() -> new DataNotExistException("error.payment.merchant.storeNotFound")); if (!Objects.equals(store.getMchNo(), mchNo)) { + // 商户: 门店不属于当前商户 throw new OperationFailException(CommonCode.FAIL_CODE, "error.payment.merchant.storeNoMatch"); } return storeNo; diff --git a/daxpay-payment/daxpay-payment-unipay/src/main/java/cn/daxpay/open/payment/unipay/client/controller/DeviceQrCodeClientController.java b/daxpay-payment/daxpay-payment-unipay/src/main/java/cn/daxpay/open/payment/unipay/client/controller/QrCodeClientController.java similarity index 100% rename from daxpay-payment/daxpay-payment-unipay/src/main/java/cn/daxpay/open/payment/unipay/client/controller/DeviceQrCodeClientController.java rename to daxpay-payment/daxpay-payment-unipay/src/main/java/cn/daxpay/open/payment/unipay/client/controller/QrCodeClientController.java diff --git a/daxpay-payment/daxpay-payment-unipay/src/main/java/cn/daxpay/open/payment/unipay/client/service/CodePayAssistService.java b/daxpay-payment/daxpay-payment-unipay/src/main/java/cn/daxpay/open/payment/unipay/client/service/CodePayAssistService.java index 1af7701d9..c7b24d948 100644 --- a/daxpay-payment/daxpay-payment-unipay/src/main/java/cn/daxpay/open/payment/unipay/client/service/CodePayAssistService.java +++ b/daxpay-payment/daxpay-payment-unipay/src/main/java/cn/daxpay/open/payment/unipay/client/service/CodePayAssistService.java @@ -102,13 +102,14 @@ public class CodePayAssistService { /// 码牌发起支付: 普通订单 + source=cashier_code; 策略仅读码牌配置 public NormalPayResult pay(CodePayParam param) { - DeviceQrCode entity = this.loadEnabledAssigned(param.getCode()); + var entity = this.loadEnabledAssigned(param.getCode()); merchantContextLoader.initMch(entity.getMchNo()); var mchApp = merchantContextLoader.resolveApp(entity.getMchNo(), entity.getAppId()); - + // 获取金额 long amount = this.resolveAmount(entity, param.getAmount()); ClientEnvEnum clientEnv = ClientEnvEnum.findByCode(param.getClientEnv()); if (clientEnv == ClientEnvEnum.BROWSER) { + // 码牌: 当前打开环境不支持码牌支付, 请使用微信/支付宝等扫码 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.clientEnvNotSupport"); } @@ -119,6 +120,7 @@ public class CodePayAssistService { var resolved = codePayResolveService.resolveRequired(mchApp.getAppId(), clientEnv, payForm); // JSAPI/MINI 必须已在授权回跳页换好 openId,支付只带 openId、禁止支付时再换 code if (PayMethodOpenIdSupport.needsOpenId(resolved.method()) && StrUtil.isBlank(param.getOpenId())) { + // 码牌: 当前支付方式需要 openId, 请先完成授权 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.openIdRequired"); } @@ -158,12 +160,14 @@ public class CodePayAssistService { ClientEnvEnum clientEnv = ClientEnvEnum.findByCode(param.getClientEnv()); if (clientEnv == ClientEnvEnum.BROWSER) { + // 码牌: 当前打开环境不支持码牌支付, 请使用微信/支付宝等扫码 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.clientEnvNotSupport"); } CodePayFormEnum payForm = CodePayFormEnum.fromProgramType(entity.getProgramType()); var resolved = codePayResolveService.resolveRequired(mchApp.getAppId(), clientEnv, payForm); if (!PayMethodOpenIdSupport.canAcquireOpenId(resolved.method(), clientEnv)) { // 当前支付方式/环境无法走 OAuth(付款码/APP/PC/外部浏览器/union_pay 一期) + // 码牌: 当前支付方式/环境不需要授权 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.authNotRequired"); } @@ -230,9 +234,11 @@ public class CodePayAssistService { DeviceQrCode entity = deviceQrCodeManager.findByCode(code) .orElseThrow(() -> new DataNotExistException("error.device.qrcode.notFound")); if (!QrCodeStatusEnum.ENABLED.getCode().equals(entity.getStatus())) { + // 码牌: 码牌未启用 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.disabled"); } if (StrUtil.isBlank(entity.getMchNo())) { + // 码牌: 码牌未分配商户 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.notAssigned"); } return entity; @@ -242,11 +248,13 @@ public class CodePayAssistService { QrCodeAmountTypeEnum amountType = QrCodeAmountTypeEnum.findByCode(entity.getAmountType()); if (amountType == QrCodeAmountTypeEnum.FIXED) { if (entity.getFixedAmount() == null || entity.getFixedAmount() <= 0) { + // 码牌: 固定金额必须大于0 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.fixedAmountInvalid"); } return entity.getFixedAmount(); } if (requestAmount == null || requestAmount <= 0) { + // 码牌: 金额必须大于0 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.amountRequired"); } return requestAmount; @@ -256,6 +264,7 @@ public class CodePayAssistService { private String resolveMethod(DeviceQrCode entity, String clientEnvCode) { ClientEnvEnum clientEnv = ClientEnvEnum.findByCode(clientEnvCode); if (clientEnv == ClientEnvEnum.BROWSER) { + // 码牌: 当前打开环境不支持码牌支付, 请使用微信/支付宝等扫码 throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.clientEnvNotSupport"); } merchantContextLoader.initMch(entity.getMchNo()); @@ -294,7 +303,10 @@ public class CodePayAssistService { case ALIPAY -> "alipay"; case UNION_PAY -> "union-pay"; case DOUYIN -> "douyin"; - default -> throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.clientEnvNotSupport"); + default -> { + // 码牌: 不支持的客户端环境, 无法确定分端页 + throw new OperationFailException(CommonCode.FAIL_CODE, "error.device.qrcode.clientEnvNotSupport"); + } }; return "/h/" + segment + "/" + code + "?authed=1"; } diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialAutoLoginConfigService.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialAutoLoginConfigService.java index acf587a43..b975f6879 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialAutoLoginConfigService.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialAutoLoginConfigService.java @@ -85,21 +85,22 @@ public class SocialAutoLoginConfigService { } List sources = this.resolveParamSources(item); if (CollUtil.isEmpty(sources)) { - // 开启自动登录时必须至少选择一个平台 + // 社交登录: 开启自动登录时必须至少选择一个平台 throw new OperationFailException("error.social.autoLogin.sourceRequired", clientCode); } for (String code : sources) { if (!AUTO_LOGIN_ALLOWED_SOURCES.contains(code)) { - // 所选平台不支持应用内自动登录(如微信开放平台扫码) + // 社交登录: 所选平台不支持应用内自动登录(如微信开放平台扫码) throw new OperationFailException("error.social.autoLogin.sourceNotAllowed", code); } SocialSourceEnum source = SocialSourceEnum.of(code); if (source == null) { + // 社交登录: 不支持的平台 throw new OperationFailException("error.social.unsupportedSource"); } SocialLoginConfig enabled = socialLoginConfigService.findEnabledBySource(code); if (enabled == null) { - // 所选平台未配置或未启用 + // 社交登录: 所选平台未配置或未启用 throw new OperationFailException("error.social.autoLogin.sourceNotEnabled", code); } } diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginConfigService.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginConfigService.java index 1c43fae8f..d5adf382a 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginConfigService.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginConfigService.java @@ -91,6 +91,7 @@ public class SocialLoginConfigService { public void update(SocialLoginConfigParam param) { SocialSourceEnum socialSource = SocialSourceEnum.of(param.getSource()); if (socialSource == null) { + // 社交登录: 不支持的平台 throw new OperationFailException("error.social.unsupportedSource"); } // 跳转型(SocialSourceEnum.isPlatformRedirect, 如 ALIPAY): 不校验/不写入 clientId, 只启停 @@ -100,6 +101,7 @@ public class SocialLoginConfigService { } // 标准 OAuth 平台: clientId 必填 if (StrUtil.isBlank(param.getClientId())) { + // 社交登录: 平台未配置, 请先填写 clientId throw new OperationFailException("error.social.notConfigured"); } SocialLoginConfig entity = socialLoginConfigManager.findBySource(param.getSource()) @@ -116,6 +118,7 @@ public class SocialLoginConfigService { public void updateEnabled(String source, Boolean enabled) { SocialSourceEnum socialSource = SocialSourceEnum.of(source); if (socialSource == null) { + // 社交登录: 不支持的平台 throw new OperationFailException("error.social.unsupportedSource"); } if (this.isPlatformRedirectSource(socialSource)) { @@ -130,6 +133,7 @@ public class SocialLoginConfigService { .orElseThrow(() -> new OperationFailException("error.social.configNotExist")); // 社交登录: 未配置平台不允许启停 if (!entity.isConfigured()) { + // 社交登录: 平台未配置, 请先填写 clientId throw new OperationFailException("error.social.notConfigured"); } entity.setEnabled(enabled); diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginService.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginService.java index ff1eaec40..8c0e2dc39 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginService.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginService.java @@ -141,6 +141,7 @@ public class SocialLoginService { // 按 client 解析前端 baseUrl String baseUrl = this.resolveBaseUrl(socialClient); if (StrUtil.isBlank(baseUrl)) { + // 社交登录: 运营/商户端前端地址未配置 throw new OperationFailException(this.endpointMissingKey(socialClient)); } SocialAuthMode authMode = this.resolveMode(mode); @@ -231,6 +232,7 @@ public class SocialLoginService { // 当前用户必须属于该身份域, 防止跨端绑定 UserInfoResult userInfo = userQueryService.findById(userId); if (!Objects.equals(clientCode, userInfo.getClientCode())) { + // 社交登录: 绑定用户不属于当前终端 throw new OperationFailException("error.social.bind.clientMismatch"); } String baseUrl = this.requireBaseUrl(socialClient); @@ -270,6 +272,7 @@ public class SocialLoginService { SocialAuthConfig authConfig = socialLoginConfigService.buildAuthConfig(config, redirectUri); SocialSourceEnum socialSource = SocialSourceEnum.of(source); if (socialSource == null) { + // 社交登录: 不支持的平台 throw new OperationFailException("error.social.unsupportedSource"); } // 公众号: 凭据来自平台级配置 @@ -285,6 +288,7 @@ public class SocialLoginService { } PlatformWechatMpAuthConfig mp = platformWechatMpAuthConfigService.getWechatMpAuthConfig(); if (mp == null || StrUtil.isBlank(mp.getAppId()) || StrUtil.isBlank(mp.getAppSecret())) { + // 社交登录: 微信公众号配置不完整 throw new OperationFailException("error.social.wechatMpNotConfigured"); } authConfig.setClientId(mp.getAppId()); @@ -315,7 +319,10 @@ public class SocialLoginService { /// 校验并解析社交登录终端(仅 admin/merchant) private SocialClientEnum requireSocialClient(String clientCode) { return SocialClientEnum.findByCode(clientCode) - .orElseThrow(() -> new OperationFailException("error.social.unsupportedClient")); + .orElseThrow(() -> { + // 社交登录: 不支持的终端编码(仅 admin/merchant) + throw new OperationFailException("error.social.unsupportedClient"); + }); } /// 解析并校验 baseUrl 非空 diff --git a/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/service/wechat/WechatMessageRecordService.java b/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/service/wechat/WechatMessageRecordService.java index 4aa7e4f98..8432956c8 100644 --- a/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/service/wechat/WechatMessageRecordService.java +++ b/daxpay-platform/daxpay-platform-service/service-notify/src/main/java/cn/daxpay/open/platform/notify/service/wechat/WechatMessageRecordService.java @@ -81,13 +81,18 @@ public class WechatMessageRecordService { /// 重发(仅公众号模板消息, 只允许重发失败的记录) public void resend(Long recordId) { WechatMessageRecord record = recordManager.findById(recordId) - .orElseThrow(() -> new OperationFailException("error.channel.wechat.messageRecordNotExist")); + .orElseThrow(() -> { + // 微信: 消息记录不存在 + throw new OperationFailException("error.channel.wechat.messageRecordNotExist"); + }); if (!STATUS_FAILED.equals(record.getStatus())) { + // 微信: 只允许重发失败的记录 throw new OperationFailException("error.channel.wechat.onlyFailedCanResend"); } // 从三方平台取 AppId/AppSecret PlatformWechatMpAuthConfig auth = mpAuthConfigService.getWechatMpAuthConfig(); if (StrUtil.isBlank(auth.getAppId()) || StrUtil.isBlank(auth.getAppSecret())) { + // 微信: 公众号授权配置不完整 throw new OperationFailException("error.channel.wechat.mpAuthConfigIncomplete"); } // 构造发送参数