From 7f34c9db57b81fffa7e24d53a4d048370944bf24 Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Tue, 23 Jun 2026 23:49:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(payment):=20=E6=96=B0=E5=A2=9E=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E8=B0=83=E8=AF=95=E6=8E=A5=E5=8F=A3=E4=B8=8E=E8=8F=9C?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 develop 签名调试接口 /admin/develop/sign/{gen,verify} - 新增 develop 支付调试接口 /admin/develop/trade/{sign,pay} - 新增 DevelopParam/DevelopSignParam/DevelopVerifyParam 参数类 - 新增 DevelopSignResult/DevelopPayResult 结果类 - 新增 DevelopSignService/DevelopTradeService 服务 - iam_perm_menu 新增开发调试目录及支付/签名调试菜单 --- _config/sql/iam_perm_menu.sql | 3 + .../develop/DevelopSignController.java | 41 +++++++++++++ .../develop/DevelopTradeController.java | 42 +++++++++++++ .../admin/param/develop/DevelopParam.java | 23 ++++++++ .../admin/param/develop/DevelopSignParam.java | 20 +++++++ .../param/develop/DevelopVerifyParam.java | 24 ++++++++ .../result/develop/DevelopPayResult.java | 25 ++++++++ .../result/develop/DevelopSignResult.java | 20 +++++++ .../service/develop/DevelopSignService.java | 40 +++++++++++++ .../service/develop/DevelopTradeService.java | 59 +++++++++++++++++++ 10 files changed, 297 insertions(+) create mode 100644 daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/develop/DevelopSignController.java create mode 100644 daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/develop/DevelopTradeController.java create mode 100644 daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopParam.java create mode 100644 daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopSignParam.java create mode 100644 daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopVerifyParam.java create mode 100644 daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/result/develop/DevelopPayResult.java create mode 100644 daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/result/develop/DevelopSignResult.java create mode 100644 daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/develop/DevelopSignService.java create mode 100644 daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/develop/DevelopTradeService.java diff --git a/_config/sql/iam_perm_menu.sql b/_config/sql/iam_perm_menu.sql index 2c6eff59e..8a4cee702 100644 --- a/_config/sql/iam_perm_menu.sql +++ b/_config/sql/iam_perm_menu.sql @@ -144,6 +144,9 @@ INSERT INTO "public"."iam_perm_menu" VALUES (4040116, 401, 'payment:platform:pro INSERT INTO "public"."iam_perm_menu" VALUES (204, 2, 'demos:artemis', 'admin', 'ArtemisDemo', '消息队列演示', 'Message Queue Demo', 'menu.demos.artemis', 'lucide:send', 'f', 'f', '/demos/artemis/ArtemisDemo', '/demos/artemis', NULL, 4, 'f', 't', 'f', 0, NULL, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-18 00:00:00+00', '2026-06-18 00:00:00+00'); INSERT INTO "public"."iam_perm_menu" VALUES (900001, 304, 'iam:social:config', 'admin', 'SocialConfig', '三方平台登录', 'Third-party Platform Login', 'menu.system.config.social', 'lucide:share-2', 'f', 'f', 'views/iam/social/social-config', '/system/config/social', NULL, 5, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO "public"."iam_perm_menu" VALUES (205, 2, 'demos:channel', 'admin', 'ChannelDemo', '通道连通性演示', 'Channel Connectivity Demo', 'menu.demos.channel', 'lucide:radio-tower', 'f', 'f', '/demos/channel/ChannelDemo', '/demos/channel', NULL, 5, 'f', 't', 'f', 0, NULL, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-23 00:00:00+00', '2026-06-23 00:00:00+00'); +INSERT INTO "public"."iam_perm_menu" VALUES (8, NULL, 'develop', 'admin', 'Develop', '开发调试', 'Develop', 'menu.develop', 'lucide:wrench', 'f', 'f', NULL, '/develop', NULL, 5, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-23 16:00:00+00', '2026-06-23 16:00:00+00'); +INSERT INTO "public"."iam_perm_menu" VALUES (801, 8, 'payment:develop:trade', 'admin', 'DevelopTrade', '支付调试', 'Payment Develop', 'menu.develop.trade', 'lucide:credit-card', 'f', 'f', '/payment/develop/trade/DevelopTrade', '/develop/trade', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-23 16:00:00+00', '2026-06-23 16:00:00+00'); +INSERT INTO "public"."iam_perm_menu" VALUES (802, 8, 'payment:develop:sign', 'admin', 'DevelopSign', '签名调试', 'Sign Develop', 'menu.develop.sign', 'lucide:file-signature', 'f', 'f', '/payment/develop/sign/DevelopSign', '/develop/sign', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-23 16:00:00+00', '2026-06-23 16:00:00+00'); -- ---------------------------- -- Primary Key structure for table iam_perm_menu diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/develop/DevelopSignController.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/develop/DevelopSignController.java new file mode 100644 index 000000000..c3cbf876e --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/develop/DevelopSignController.java @@ -0,0 +1,41 @@ +package cn.daxpay.open.payment.admin.controller.develop; + +import cn.daxpay.open.payment.admin.param.develop.DevelopSignParam; +import cn.daxpay.open.payment.admin.param.develop.DevelopVerifyParam; +import cn.daxpay.open.payment.admin.result.develop.DevelopSignResult; +import cn.daxpay.open.payment.admin.service.develop.DevelopSignService; +import cn.daxpay.open.platform.core.annotation.PermCode; +import cn.daxpay.open.platform.core.rest.Res; +import cn.daxpay.open.platform.core.rest.result.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/// 签名调试(管理) +@PermCode(menuCode = "payment:develop:sign") +@Tag(name = "签名调试服务") +@RestController +@RequestMapping("/admin/develop/sign") +@RequiredArgsConstructor +public class DevelopSignController { + + private final DevelopSignService developSignService; + + @PermCode(code = "view", nameCn = "查看", nameEn = "View") + @Operation(summary = "生成签名") + @PostMapping("/gen") + public Result sign(@RequestBody DevelopSignParam param) { + return Res.ok(developSignService.sign(param)); + } + + @PermCode(code = "view", nameCn = "查看", nameEn = "View") + @Operation(summary = "验签") + @PostMapping("/verify") + public Result verify(@RequestBody DevelopVerifyParam param) { + return Res.ok(developSignService.verify(param)); + } +} diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/develop/DevelopTradeController.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/develop/DevelopTradeController.java new file mode 100644 index 000000000..8dc522eb2 --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/controller/develop/DevelopTradeController.java @@ -0,0 +1,42 @@ +package cn.daxpay.open.payment.admin.controller.develop; + +import cn.daxpay.open.payment.admin.param.develop.DevelopParam; +import cn.daxpay.open.payment.admin.result.develop.DevelopPayResult; +import cn.daxpay.open.payment.admin.result.develop.DevelopSignResult; +import cn.daxpay.open.payment.admin.service.develop.DevelopTradeService; +import cn.daxpay.open.payment.unipay.param.trade.pay.PayParam; +import cn.daxpay.open.platform.core.annotation.PermCode; +import cn.daxpay.open.platform.core.rest.Res; +import cn.daxpay.open.platform.core.rest.result.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/// 交易开发调试(管理) +@PermCode(menuCode = "payment:develop:trade") +@Tag(name = "交易开发调试服务") +@RestController +@RequestMapping("/admin/develop/trade") +@RequiredArgsConstructor +public class DevelopTradeController { + + private final DevelopTradeService developTradeService; + + @PermCode(code = "sign", nameCn = "签名", nameEn = "Sign") + @Operation(summary = "支付参数签名") + @PostMapping("/sign") + public Result sign(@RequestBody DevelopParam param) { + return Res.ok(developTradeService.sign(param)); + } + + @PermCode(code = "pay", nameCn = "支付", nameEn = "Pay") + @Operation(summary = "支付调试(真实发起)") + @PostMapping("/pay") + public Result pay(@RequestBody DevelopParam param) { + return Res.ok(developTradeService.pay(param)); + } +} diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopParam.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopParam.java new file mode 100644 index 000000000..0ab39f858 --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopParam.java @@ -0,0 +1,23 @@ +package cn.daxpay.open.payment.admin.param.develop; + +import cn.daxpay.open.payment.unipay.param.MerchantPaymentCommonParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/// # 开发调试参数包装 +/// +/// 包装业务参数与调试用手填私钥, 私钥仅用于本次签名生成, 不入库 +@Data +@Accessors(chain = true) +@Schema(title = "开发调试参数包装") +public class DevelopParam { + + /// 业务参数 + @Schema(description = "业务参数") + private T param; + + /// 生成签名使用的私钥(PEM 格式, 调试页手填, 不入库) + @Schema(description = "生成签名使用的私钥(PEM格式)") + private String privateKey; +} diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopSignParam.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopSignParam.java new file mode 100644 index 000000000..d1da6395a --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopSignParam.java @@ -0,0 +1,20 @@ +package cn.daxpay.open.payment.admin.param.develop; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/// # 签名调试-生成签名参数 +@Data +@Accessors(chain = true) +@Schema(title = "签名调试-生成签名参数") +public class DevelopSignParam { + + /// 待签名 JSON 字符串 + @Schema(description = "待签名JSON字符串") + private String json; + + /// 私钥(PEM 格式) + @Schema(description = "私钥(PEM格式)") + private String privateKey; +} diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopVerifyParam.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopVerifyParam.java new file mode 100644 index 000000000..72e223442 --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/param/develop/DevelopVerifyParam.java @@ -0,0 +1,24 @@ +package cn.daxpay.open.payment.admin.param.develop; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/// # 签名调试-验签参数 +@Data +@Accessors(chain = true) +@Schema(title = "签名调试-验签参数") +public class DevelopVerifyParam { + + /// 待验签 JSON 字符串(不含 sign 字段) + @Schema(description = "待验签JSON字符串(不含sign字段)") + private String json; + + /// 签名值 + @Schema(description = "签名值") + private String sign; + + /// 公钥(PEM 格式) + @Schema(description = "公钥(PEM格式)") + private String publicKey; +} diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/result/develop/DevelopPayResult.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/result/develop/DevelopPayResult.java new file mode 100644 index 000000000..d21a6a83d --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/result/develop/DevelopPayResult.java @@ -0,0 +1,25 @@ +package cn.daxpay.open.payment.admin.result.develop; + +import cn.daxpay.open.payment.unipay.result.trade.pay.PayResult; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/// # 支付调试结果 +@Data +@Accessors(chain = true) +@Schema(title = "支付调试结果") +public class DevelopPayResult { + + /// 发送的请求体 JSON + @Schema(description = "请求体JSON") + private String requestBody; + + /// 签名信息(传入私钥时返回) + @Schema(description = "签名信息") + private DevelopSignResult signInfo; + + /// 支付结果 + @Schema(description = "支付结果") + private PayResult payResult; +} diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/result/develop/DevelopSignResult.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/result/develop/DevelopSignResult.java new file mode 100644 index 000000000..691733106 --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/result/develop/DevelopSignResult.java @@ -0,0 +1,20 @@ +package cn.daxpay.open.payment.admin.result.develop; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/// # 签名调试结果 +@Data +@Accessors(chain = true) +@Schema(title = "签名调试结果") +public class DevelopSignResult { + + /// 待签名原文(键值拼接串) + @Schema(description = "待签名原文") + private String signStr; + + /// 签名值 + @Schema(description = "签名值") + private String sign; +} diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/develop/DevelopSignService.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/develop/DevelopSignService.java new file mode 100644 index 000000000..27db22515 --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/develop/DevelopSignService.java @@ -0,0 +1,40 @@ +package cn.daxpay.open.payment.admin.service.develop; + +import cn.daxpay.open.payment.admin.param.develop.DevelopSignParam; +import cn.daxpay.open.payment.admin.param.develop.DevelopVerifyParam; +import cn.daxpay.open.payment.admin.result.develop.DevelopSignResult; +import cn.daxpay.open.payment.common.util.JsonSignStrUtil; +import cn.daxpay.open.platform.core.util.RsaSignUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/// # 签名调试服务 +/// +/// 提供参数签名生成与验签功能, 便于商户对接联调 +@Slf4j +@Service +@RequiredArgsConstructor +public class DevelopSignService { + + /// 生成签名 + public DevelopSignResult sign(DevelopSignParam param) { + // 待签名原文(扁平化排序后的键值拼接串) + String signStr = JsonSignStrUtil.buildSignStr(param.getJson()); + // 使用私钥生成签名 + String sign = RsaSignUtil.sign(signStr, param.getPrivateKey()); + return new DevelopSignResult().setSignStr(signStr).setSign(sign); + } + + /// 验签 + public boolean verify(DevelopVerifyParam param) { + String signStr = JsonSignStrUtil.buildSignStr(param.getJson()); + try { + return RsaSignUtil.verify(signStr, param.getSign(), param.getPublicKey()); + } catch (Exception e) { + // 验签失败不抛异常, 返回 false 便于前端展示 + log.warn("验签失败: {}", e.getMessage()); + return false; + } + } +} diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/develop/DevelopTradeService.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/develop/DevelopTradeService.java new file mode 100644 index 000000000..b6f86dc5a --- /dev/null +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/develop/DevelopTradeService.java @@ -0,0 +1,59 @@ +package cn.daxpay.open.payment.admin.service.develop; + +import cn.daxpay.open.payment.admin.param.develop.DevelopParam; +import cn.daxpay.open.payment.admin.result.develop.DevelopPayResult; +import cn.daxpay.open.payment.admin.result.develop.DevelopSignResult; +import cn.daxpay.open.payment.common.context.PaymentScopeManager; +import cn.daxpay.open.payment.common.util.ObjectSignStrUtil; +import cn.daxpay.open.payment.common.util.PaySignUtil; +import cn.daxpay.open.payment.pay.service.NormalPayService; +import cn.daxpay.open.payment.unipay.param.trade.pay.PayParam; +import cn.daxpay.open.payment.unipay.result.trade.pay.PayResult; +import cn.daxpay.open.platform.common.json.util.JsonUtil; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/// # 交易开发调试服务 +/// +/// 复用普通支付核心服务 NormalPayService, 在管理端以指定商户/应用身份真实发起一笔支付, +/// 便于联调验证通道配置与签名是否正确 +@Slf4j +@Service +@RequiredArgsConstructor +public class DevelopTradeService { + + private final NormalPayService normalPayService; + private final PaymentScopeManager paymentScopeManager; + + /// 生成支付参数签名(与正式签名逻辑一致) + public DevelopSignResult sign(DevelopParam param) { + // 签名串(与 PaySignUtil 内部一致) + String signStr = ObjectSignStrUtil.buildSignStr(param.getParam()); + // 签名值 + String sign = PaySignUtil.sign(param.getParam(), param.getPrivateKey()); + return new DevelopSignResult().setSignStr(signStr).setSign(sign); + } + + /// 支付调试(真实发起) + public DevelopPayResult pay(DevelopParam param) { + PayParam payParam = param.getParam(); + // 记录发送的请求体 + String requestBody = JsonUtil.toJsonStr(payParam); + // 签名信息(传入私钥时生成, 便于核对) + DevelopSignResult signInfo = null; + if (StrUtil.isNotBlank(param.getPrivateKey())) { + signInfo = this.sign(param); + } + // 在支付作用域内执行, 初始化商户上下文, 使 mchNo/appId 等字段自动填充 + PayResult payResult = paymentScopeManager.executeWithScope( + payParam.getMchNo(), payParam.getAppId(), + () -> normalPayService.pay(payParam) + ); + return new DevelopPayResult() + .setRequestBody(requestBody) + .setSignInfo(signInfo) + .setPayResult(payResult); + } +}