diff --git a/OPEN_API.md b/OPEN_API.md new file mode 100644 index 0000000..5a9f43b --- /dev/null +++ b/OPEN_API.md @@ -0,0 +1,204 @@ +# 外部订单接口文档 + +## 接口概述 + +该接口用于外部系统向支付系统添加订单,支持MD5签名验证、时间戳校验、事务处理等功能。 + +## 接口信息 + +- **接口路径**: `POST /api/openapi/orders` +- **请求方法**: POST +- **Content-Type**: application/json + +## 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------------------------------------------| +| orderNo | String | 是 | 订单号,唯一标识 | +| amount | BigDecimal | 是 | 订单金额,必须大于0且小于等于100000 | +| payType | String | 是 | 支付方式(alipay/wechat/alipay_dmf/wechat_zs) | +| nickName | String | 否 | 用户昵称 | +| description | String | 否 | 订单描述 | +| email | String | 否 | 用户邮箱 | +| notifyUrl | String | 否 | 异步通知地址 | +| userId | String | 否 | 用户ID | +| productId | Long | 否 | 产品ID | +| timestamp | Long | 是 | 请求时间戳(毫秒),有效期5分钟 | +| sign | String | 是 | MD5签名 | + +## 签名算法 + +1. 将所有参数(除sign外)按字母顺序排序 +2. 拼接成 `key1=value1&key2=value2&...&key=secretKey` 格式 +3. 对拼接后的字符串进行MD5加密,并转为大写 + +### 签名示例 + +假设配置的密钥为:`your_openapi_secret_key_here` + +参数: +```json +{ + "orderNo": "EXT20250303001", + "amount": 10.00, + "payType": "alipay", + "timestamp": 1733232000000 +} +``` + +排序后的参数: +``` +amount=10.00&orderNo=EXT20250303001&payType=alipay×tamp=1733232000000&key=your_openapi_secret_key_here +``` + +MD5签名:`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` + +## 响应格式 + +### 成功响应 + +```json +{ + "code": 200, + "msg": "success", + "data": { + "orderId": "uuid-generated-id", + "orderNo": "EXT20250303001", + "amount": 10.00, + "payType": "alipay", + "payNum": "随机支付标识", + "state": 0, + "message": "订单创建成功", + "timestamp": 1733232000000 + } +} +``` + +### 失败响应 + +```json +{ + "code": 400, + "msg": "错误信息描述", + "data": null +} +``` + +## 错误码说明 + +| 错误码 | 说明 | +|--------|------| +| 200 | 成功 | +| 400 | 参数错误 | +| 401 | 签名验证失败 | +| 402 | 时间戳无效或已过期 | +| 403 | 订单号已存在 | +| 404 | 金额错误 | +| 500 | 系统内部异常 | + +## 请求示例 + +### cURL 示例 + +```bash +curl -X POST http://localhost:8889/api/openapi/orders \ + -H "Content-Type: application/json" \ + -d '{ + "orderNo": "EXT20250303001", + "amount": 10.00, + "payType": "alipay", + "nickName": "测试用户", + "description": "测试订单", + "email": "test@example.com", + "userId": "USER001", + "productId": 1, + "notifyUrl": "http://example.com/notify", + "timestamp": 1733232000000, + "sign": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + }' +``` + +### Java 示例 + +```java +import cn.hutool.http.HttpRequest; +import cn.hutool.json.JSONObject; +import cn.hutool.crypto.SecureUtil; + +public class ExternalOrderClient { + + private static final String API_URL = "http://localhost:8889/api/openapi/orders"; + private static final String SECRET_KEY = "your_openapi_secret_key_here"; + + public static void main(String[] args) { + JSONObject params = new JSONObject(); + params.put("orderNo", "EXT20250303001"); + params.put("amount", 10.00); + params.put("payType", "alipay"); + params.put("nickName", "测试用户"); + params.put("description", "测试订单"); + params.put("email", "test@example.com"); + params.put("userId", "USER001"); + params.put("productId", 1); + params.put("notifyUrl", "http://example.com/notify"); + params.put("timestamp", System.currentTimeMillis()); + + String sign = generateSign(params); + params.put("sign", sign); + + String response = HttpRequest.post(API_URL) + .header("Content-Type", "application/json") + .body(params.toString()) + .execute() + .body(); + + System.out.println(response); + } + + private static String generateSign(JSONObject params) { + List keys = new ArrayList<>(params.keySet()); + Collections.sort(keys); + + StringBuilder sb = new StringBuilder(); + for (String key : keys) { + Object value = params.get(key); + if (value != null && !"".equals(value) && !"sign".equals(key)) { + sb.append(key).append("=").append(value).append("&"); + } + } + sb.append("key=").append(SECRET_KEY); + + return SecureUtil.md5(sb.toString()).toUpperCase(); + } +} +``` + +## 配置说明 + +在 `application.yml` 中配置外部接口密钥: + +```yaml +paypro: + openapi: + secret: your_openapi_secret_key_here +``` + +## 安全特性 + +1. **MD5签名验证**:确保请求参数未被篡改 +2. **时间戳校验**:防止重放攻击,时间戳有效期5分钟 +3. **订单号唯一性**:防止重复订单 +4. **金额限制**:单笔订单金额不超过100000元 +5. **事务处理**:确保订单创建的原子性 + +## 注意事项 + +1. 请妥善保管密钥,不要泄露给第三方 +2. 时间戳必须使用毫秒级时间戳 +3. 订单号必须唯一,重复的订单号会返回错误 +4. 建议在生产环境中使用HTTPS协议 +5. 建议实现IP白名单等额外的安全措施 + +## 测试页面 + +访问 `http://localhost:8889/open-api-test.html` 可以使用测试页面进行接口测试。 diff --git a/src/main/java/com/wendao/controller/OrderController.java b/src/main/java/com/wendao/controller/OrderController.java index 1d3f066..20d1ddf 100644 --- a/src/main/java/com/wendao/controller/OrderController.java +++ b/src/main/java/com/wendao/controller/OrderController.java @@ -3,12 +3,15 @@ package com.wendao.controller; import cn.hutool.log.StaticLog; import com.wendao.config.PayProConfig; import com.wendao.entity.Order; +import com.wendao.exception.ApiException; import com.wendao.model.ResponseVO; import com.wendao.common.utils.*; import com.wendao.enums.OrderStatesEnum; import com.wendao.model.req.GetOrderListReq; +import com.wendao.model.req.OpenApiOrderReq; import com.wendao.model.req.OrderReq; import com.wendao.model.resp.AddOrderResp; +import com.wendao.model.resp.OpenApiOrderResp; import com.wendao.service.OrderService; import com.baomidou.mybatisplus.core.metadata.IPage; import io.swagger.annotations.Api; @@ -235,4 +238,26 @@ public class OrderController { public String getOrderId(String id){ return id; } + + @PostMapping("/api/openapi/orders") + @ApiOperation(value = "创建OpenApi订单", notes = "OpenApi通过此接口创建支付订单") + @ResponseBody + public ResponseVO createOpenApiOrder(@RequestBody @Validated OpenApiOrderReq req) { + try { + log.info("收到OpenApi订单创建请求 - 订单号: {}, 金额: {}, 支付方式: {}", + req.getOrderNo(), req.getAmount(), req.getPayType()); + + OpenApiOrderResp resp = orderService.createOpenApiOrder(req); + + log.info("OpenApi订单创建成功 - 订单ID: {}, 订单号: {}", resp.getOrderId(), resp.getOrderNo()); + + return new ResponseVO<>(ApiException.ErrorCode.SUCCESS, "success", resp); + } catch (ApiException e) { + log.warn("OpenApi订单创建失败 - 错误码: {}, 错误信息: {}", e.getCode(), e.getMessage()); + return new ResponseVO<>(e.getCode(), e.getMessage()); + } catch (Exception e) { + log.error("OpenApi订单创建系统异常", e); + return new ResponseVO<>(ApiException.ErrorCode.SYSTEM_ERROR, "系统内部异常"); + } + } } diff --git a/src/main/java/com/wendao/exception/ApiException.java b/src/main/java/com/wendao/exception/ApiException.java new file mode 100644 index 0000000..3ec04c4 --- /dev/null +++ b/src/main/java/com/wendao/exception/ApiException.java @@ -0,0 +1,30 @@ +package com.wendao.exception; + +import lombok.Getter; + +@Getter +public class ApiException extends RuntimeException { + + private final int code; + private final String message; + + public ApiException(int code, String message) { + super(message); + this.code = code; + this.message = message; + } + + public ApiException(String message) { + this(500, message); + } + + public static class ErrorCode { + public static final int SUCCESS = 200; + public static final int INVALID_PARAM = 400; + public static final int SIGN_ERROR = 401; + public static final int TIMESTAMP_ERROR = 402; + public static final int DUPLICATE_ORDER = 403; + public static final int AMOUNT_ERROR = 404; + public static final int SYSTEM_ERROR = 500; + } +} diff --git a/src/main/java/com/wendao/exception/GlobalExceptionHandler.java b/src/main/java/com/wendao/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..d6183c8 --- /dev/null +++ b/src/main/java/com/wendao/exception/GlobalExceptionHandler.java @@ -0,0 +1,40 @@ +package com.wendao.exception; + +import com.wendao.model.ResponseVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + @ExceptionHandler(ApiException.class) + public ResponseVO handleApiException(ApiException e) { + log.warn("API异常 - 错误码: {}, 错误信息: {}", e.getCode(), e.getMessage()); + return ResponseVO.builder() + .code(e.getCode()) + .msg(e.getMessage()) + .build(); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseVO handleIllegalArgumentException(IllegalArgumentException e) { + log.warn("参数异常: {}", e.getMessage()); + return ResponseVO.builder() + .code(ApiException.ErrorCode.INVALID_PARAM) + .msg(e.getMessage()) + .build(); + } + + @ExceptionHandler(Exception.class) + public ResponseVO handleException(Exception e) { + log.error("系统异常", e); + return ResponseVO.builder() + .code(ApiException.ErrorCode.SYSTEM_ERROR) + .msg("系统内部异常") + .build(); + } +} diff --git a/src/main/java/com/wendao/model/req/OpenApiOrderReq.java b/src/main/java/com/wendao/model/req/OpenApiOrderReq.java new file mode 100644 index 0000000..a9c4c34 --- /dev/null +++ b/src/main/java/com/wendao/model/req/OpenApiOrderReq.java @@ -0,0 +1,38 @@ +package com.wendao.model.req; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +public class OpenApiOrderReq implements Serializable { + + @NotBlank(message = "订单号不能为空") + private String orderNo; + + @NotNull(message = "金额不能为空") + private BigDecimal amount; + + @NotBlank(message = "支付方式不能为空") + private String payType; + + private String nickName; + + private String description; + + private String email; + + private String notifyUrl; + + private String userId; + + private Long productId; + + @NotBlank(message = "签名不能为空") + private String sign; + + private Long timestamp; +} diff --git a/src/main/java/com/wendao/model/resp/OpenApiOrderResp.java b/src/main/java/com/wendao/model/resp/OpenApiOrderResp.java new file mode 100644 index 0000000..c1e2f4a --- /dev/null +++ b/src/main/java/com/wendao/model/resp/OpenApiOrderResp.java @@ -0,0 +1,32 @@ +package com.wendao.model.resp; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OpenApiOrderResp implements Serializable { + + private String orderId; + + private String orderNo; + + private BigDecimal amount; + + private String payType; + + private String payNum; + + private Integer state; + + private String message; + + private Long timestamp; +} diff --git a/src/main/java/com/wendao/service/OrderService.java b/src/main/java/com/wendao/service/OrderService.java index 28e4fa1..f899595 100644 --- a/src/main/java/com/wendao/service/OrderService.java +++ b/src/main/java/com/wendao/service/OrderService.java @@ -2,6 +2,7 @@ package com.wendao.service; import com.wendao.entity.Order; import com.wendao.entity.PayChatMessage; +import com.wendao.model.req.OpenApiOrderReq; import com.wendao.model.resp.CountResp; import com.wendao.model.ResponseVO; import com.wendao.dto.WeChatMsgDTO; @@ -9,6 +10,8 @@ import com.wendao.model.req.GetOrderListReq; import com.wendao.model.req.OrderReq; import com.wendao.model.resp.AddOrderResp; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.wendao.model.resp.OpenApiOrderResp; + import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.List; @@ -71,7 +74,6 @@ public interface OrderService { Order getByPayNum(String desc, Date time); - void autoPass(WeChatMsgDTO dto); + OpenApiOrderResp createOpenApiOrder(OpenApiOrderReq req); - void autoPass(PayChatMessage dto); } diff --git a/src/main/java/com/wendao/service/impl/OrderServiceImpl.java b/src/main/java/com/wendao/service/impl/OrderServiceImpl.java index 7fec37f..914b83e 100644 --- a/src/main/java/com/wendao/service/impl/OrderServiceImpl.java +++ b/src/main/java/com/wendao/service/impl/OrderServiceImpl.java @@ -4,6 +4,7 @@ import com.wendao.config.PayProConfig; import com.wendao.entity.AutoPassPay; import com.wendao.entity.Order; import com.wendao.entity.PayChatMessage; +import com.wendao.exception.ApiException; import com.wendao.mapper.AutoPassPayMapper; import com.wendao.mapper.OrderMapper; import com.wendao.mapper.PayChatMessageMapper; @@ -12,8 +13,10 @@ import com.wendao.dto.MsgContentsDTO; import com.wendao.dto.WeChatMsgDTO; import com.wendao.enums.OrderStatesEnum; import com.wendao.model.req.GetOrderListReq; +import com.wendao.model.req.OpenApiOrderReq; import com.wendao.model.resp.AddOrderResp; import com.wendao.model.resp.CountResp; +import com.wendao.model.resp.OpenApiOrderResp; import com.wendao.service.OrderService; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Snowflake; @@ -27,6 +30,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.wendao.utils.OpenApiSignUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; @@ -36,6 +40,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; import java.util.*; import java.util.concurrent.TimeUnit; @@ -74,6 +79,9 @@ public class OrderServiceImpl implements OrderService { @Autowired PayChatMessageMapper payChatMessageMapper; + @Autowired + OpenApiSignUtil openApiSignUtil; + @Override public Order getOrderById(String id) { Order byId = orderMapper.selectById(id); @@ -291,64 +299,67 @@ public class OrderServiceImpl implements OrderService { } @Override - @Transactional - public void autoPass(WeChatMsgDTO dto) { - - QueryWrapper autoPassPayQueryWrapper = new QueryWrapper<>(); - autoPassPayQueryWrapper.lambda().eq(AutoPassPay::getMessageId, dto.getId()); - Long l = autoPassPayMapper.selectCount(autoPassPayQueryWrapper); - if (l > 0) { - return; + @Transactional(rollbackFor = Exception.class) + public OpenApiOrderResp createOpenApiOrder(OpenApiOrderReq req) { + if (!openApiSignUtil.verifyTimestamp(req.getTimestamp())) { + throw new ApiException(ApiException.ErrorCode.TIMESTAMP_ERROR, "请求时间戳无效或已过期"); } - String desc = dto.getContents().extractRemark(); - Order byPayNum = null; - if (StringUtils.isNotBlank(desc)) { - /** 找到匹配的订单*/ - byPayNum = thisService.getByPayNum(desc,dto.getTime()); - thisService.pass(byPayNum.getId()); + if (!openApiSignUtil.verifySign(req)) { + throw new ApiException(ApiException.ErrorCode.SIGN_ERROR, "签名验证失败"); } - AutoPassPay autoPassPay = new AutoPassPay(); - autoPassPay.setId(snowflake.nextId()); - if (byPayNum != null) { - autoPassPay.setOrderId(byPayNum.getId()); + if (req.getAmount() == null || req.getAmount().compareTo(new BigDecimal("0")) <= 0) { + throw new ApiException(ApiException.ErrorCode.AMOUNT_ERROR, "金额必须大于0"); } - autoPassPay.setMessageCreateTime(dto.getTime()); - autoPassPay.setMessageId(dto.getId()); - autoPassPay.setMessageDesc(desc); - autoPassPayMapper.insert(autoPassPay); + + if (req.getAmount().compareTo(new BigDecimal("100000")) > 0) { + throw new ApiException(ApiException.ErrorCode.AMOUNT_ERROR, "金额超出限制,单笔订单不能超过100000元"); + } + + Order existingOrder = orderMapper.selectById(req.getOrderNo()); + if (existingOrder != null) { + throw new ApiException(ApiException.ErrorCode.DUPLICATE_ORDER, "订单号已存在"); + } + + Order order = new Order(); + order.setId(req.getOrderNo()); + order.setMoney(req.getAmount()); + order.setPayType(req.getPayType()); + order.setNickName(req.getNickName()); + order.setEmail(req.getEmail()); + order.setNotifyUrl(req.getNotifyUrl()); + order.setUserId(req.getUserId()); + order.setProductId(req.getProductId()); + order.setOrderSource("OPENAPI"); + order.setState(OrderStatesEnum.WAIT_PAY.getState()); + order.setCreateTime(new Date()); + order.setPayNum(StringUtils.getRandomNum()); + + if (req.getProductId() != null) { + int i = new Random().nextInt(payProConfig.getQrCodeNum()) + 1; + order.setPayQrNum(i); + } + + try { + orderMapper.insert(order); + } catch (Exception e) { + log.error("创建OpenApi订单失败: {}", e.getMessage(), e); + throw new ApiException(ApiException.ErrorCode.SYSTEM_ERROR, "创建订单失败"); + } + + return OpenApiOrderResp.builder() + .orderId(order.getId()) + .orderNo(req.getOrderNo()) + .amount(req.getAmount()) + .payType(req.getPayType()) + .payNum(order.getPayNum()) + .state(order.getState()) + .message("订单创建成功") + .timestamp(System.currentTimeMillis()) + .build(); } - @Override - @Transactional - public void autoPass(PayChatMessage dto) { - - String desc = null; - if (dto.getPlatformType().equals("weixin")) { - MsgContentsDTO bean = JSONUtil.toBean(dto.getContents(), MsgContentsDTO.class); - desc = bean.extractRemark(); - if (StringUtils.isBlank(desc)) { - dto.setProcessStatus(2); - payChatMessageMapper.updateById(dto); - log.info("处理:{}.没有提取到备注" + JSONUtil.toJsonStr(dto)); - return; - } - } - - Order byPayNum = null; - if (StringUtils.isNotBlank(desc)) { - /** 找到匹配的订单*/ - byPayNum = thisService.getByPayNum(desc,dto.getTime()); - if (byPayNum != null){ - thisService.pass(byPayNum.getId()); - dto.setOrderId(byPayNum.getId()); - dto.setProcessStatus(1); - payChatMessageMapper.updateById(dto); - } - - } - } /** * 拼接管理员链接 diff --git a/src/main/java/com/wendao/utils/OpenApiSignUtil.java b/src/main/java/com/wendao/utils/OpenApiSignUtil.java new file mode 100644 index 0000000..5e8af2c --- /dev/null +++ b/src/main/java/com/wendao/utils/OpenApiSignUtil.java @@ -0,0 +1,112 @@ +package com.wendao.utils; + +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.digest.DigestUtil; +import com.wendao.model.req.OpenApiOrderReq; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.*; + +@Component +public class OpenApiSignUtil { + + private static final Logger log = LoggerFactory.getLogger(OpenApiSignUtil.class); + + @Value("${paypro.openapi.secret:default_secret_key}") + private String secretKey; + + public String generateSign(Map params) { + List keys = new ArrayList<>(params.keySet()); + Collections.sort(keys); + + log.info("开始生成签名,参数数量: {}", keys.size()); + + StringBuilder sb = new StringBuilder(); + for (String key : keys) { + Object value = params.get(key); + if (value != null && !"".equals(value) && !"sign".equals(key)) { + // 对BigDecimal进行特殊处理,保持与前端一致的格式 + String valueStr; + if (value instanceof BigDecimal) { + valueStr = ((BigDecimal) value).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString(); + } else { + valueStr = value.toString(); + } + // 检查 valueStr 是否包含空格 + log.info("处理参数: key={}, value类型={}, value值=[{}], 包含空格={}", + key, value.getClass().getSimpleName(), valueStr, valueStr.contains(" ")); + String paramPart = key + "=" + valueStr + "&"; + log.info("添加参数: key={}, value={}, paramPart={}", key, value, paramPart); + sb.append(paramPart); + } + } + sb.append("key=").append(secretKey); + + String signStr = sb.toString(); + log.info("服务端签名字符串: {}", signStr); + log.info("服务端使用的密钥: {}", secretKey); + + return SecureUtil.md5(signStr).toUpperCase(); + } + + public boolean verifySign(OpenApiOrderReq req) { + Map params = new TreeMap<>(); + params.put("orderNo", req.getOrderNo()); + params.put("amount", req.getAmount()); + params.put("payType", req.getPayType()); + params.put("nickName", req.getNickName()); + params.put("description", req.getDescription()); + params.put("email", req.getEmail()); + params.put("notifyUrl", req.getNotifyUrl()); + params.put("userId", req.getUserId()); + params.put("productId", req.getProductId()); + params.put("timestamp", req.getTimestamp()); + + log.info("服务端接收到的参数: orderNo={}, amount={}, payType={}, nickName={}, description={}, email={}, notifyUrl={}, userId={}, productId={}, timestamp={}", + req.getOrderNo(), req.getAmount(), req.getPayType(), req.getNickName(), + req.getDescription(), req.getEmail(), req.getNotifyUrl(), req.getUserId(), + req.getProductId(), req.getTimestamp()); + log.info("服务端接收到的签名: {}", req.getSign()); + + // 检查 notifyUrl 是否包含空格 + if (req.getNotifyUrl() != null) { + String notifyUrl = req.getNotifyUrl(); + log.info("notifyUrl 原始值: [{}]", notifyUrl); + log.info("notifyUrl 长度: {}, 包含空格: {}", notifyUrl.length(), notifyUrl.contains(" ")); + log.info("notifyUrl 字节数组: {}", notifyUrl.getBytes()); + } + + String calculatedSign = generateSign(params); + boolean isValid = calculatedSign.equals(req.getSign()); + + if (!isValid) { + log.warn("签名验证失败 - 期望: {}, 实际: {}", calculatedSign, req.getSign()); + } + + return isValid; + } + + public boolean verifyTimestamp(Long timestamp) { + if (timestamp == null) { + return false; + } + long currentTime = System.currentTimeMillis(); + long diff = Math.abs(currentTime - timestamp); + long maxDiff = 5 * 60 * 1000; + + if (diff > maxDiff) { + log.warn("时间戳验证失败 - 当前: {}, 请求: {}, 差值: {}ms", currentTime, timestamp, diff); + return false; + } + return true; + } + + public static void main(String[] args) { + + System.out.println(DigestUtil.md5Hex("amount=10.00&description=1&email=test@example.com&nickName=1")); + } +}