mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-10 14:56:06 +08:00
feat(payment): 新增开发调试接口与菜单
- 新增 develop 签名调试接口 /admin/develop/sign/{gen,verify}
- 新增 develop 支付调试接口 /admin/develop/trade/{sign,pay}
- 新增 DevelopParam/DevelopSignParam/DevelopVerifyParam 参数类
- 新增 DevelopSignResult/DevelopPayResult 结果类
- 新增 DevelopSignService/DevelopTradeService 服务
- iam_perm_menu 新增开发调试目录及支付/签名调试菜单
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<DevelopSignResult> sign(@RequestBody DevelopSignParam param) {
|
||||
return Res.ok(developSignService.sign(param));
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "查看", nameEn = "View")
|
||||
@Operation(summary = "验签")
|
||||
@PostMapping("/verify")
|
||||
public Result<Boolean> verify(@RequestBody DevelopVerifyParam param) {
|
||||
return Res.ok(developSignService.verify(param));
|
||||
}
|
||||
}
|
||||
@@ -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<DevelopSignResult> sign(@RequestBody DevelopParam<PayParam> param) {
|
||||
return Res.ok(developTradeService.sign(param));
|
||||
}
|
||||
|
||||
@PermCode(code = "pay", nameCn = "支付", nameEn = "Pay")
|
||||
@Operation(summary = "支付调试(真实发起)")
|
||||
@PostMapping("/pay")
|
||||
public Result<DevelopPayResult> pay(@RequestBody DevelopParam<PayParam> param) {
|
||||
return Res.ok(developTradeService.pay(param));
|
||||
}
|
||||
}
|
||||
@@ -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<T extends MerchantPaymentCommonParam> {
|
||||
|
||||
/// 业务参数
|
||||
@Schema(description = "业务参数")
|
||||
private T param;
|
||||
|
||||
/// 生成签名使用的私钥(PEM 格式, 调试页手填, 不入库)
|
||||
@Schema(description = "生成签名使用的私钥(PEM格式)")
|
||||
private String privateKey;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PayParam> 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<PayParam> 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user