- 添加支付常量类PaymentConstants,定义支付状态、微信、支付宝、银联等相关常量 - 创建微信支付类型常量类WechatPayType,支持JSAPI、NATIVE、H5、APP支付方式 - 新增支付控制器PaymentController,提供创建支付、查询状态、退款等统一接口 - 实现支付回调控制器PaymentNotifyController,处理微信、支付宝、银联异步通知 - 添加支付请求数据传输对象PaymentRequest,支持多种支付方式参数校验 - 定义支付响应、状态更新请求等相关DTO类- 集成Swagger注解,完善接口文档说明- 添加参数校验和异常处理机制,确保支付流程安全可靠
208 lines
6.5 KiB
Java
208 lines
6.5 KiB
Java
package com.gxwebsoft.payment.dto;
|
||
|
||
import com.gxwebsoft.payment.enums.PaymentChannel;
|
||
import com.gxwebsoft.payment.enums.PaymentType;
|
||
import io.swagger.v3.oas.annotations.media.Schema;
|
||
import lombok.Data;
|
||
|
||
import javax.validation.constraints.*;
|
||
import java.math.BigDecimal;
|
||
import java.util.Map;
|
||
|
||
/**
|
||
* 统一支付请求DTO
|
||
* 支持所有支付方式的统一请求格式
|
||
*
|
||
* @author 科技小王子
|
||
* @since 2025-01-26
|
||
*/
|
||
@Data
|
||
@Schema(name = "统一支付请求", description = "支持所有支付方式的统一支付请求参数")
|
||
public class PaymentRequest {
|
||
|
||
@Schema(description = "租户ID", required = true)
|
||
@NotNull(message = "租户ID不能为空")
|
||
@Positive(message = "租户ID必须为正数")
|
||
private Integer tenantId;
|
||
|
||
@Schema(description = "用户ID", required = true)
|
||
@NotNull(message = "用户ID不能为空")
|
||
@Positive(message = "用户ID必须为正数")
|
||
private Integer userId;
|
||
|
||
@Schema(description = "支付类型", required = true, example = "WECHAT")
|
||
@NotNull(message = "支付类型不能为空")
|
||
private PaymentType paymentType;
|
||
|
||
@Schema(description = "支付渠道", example = "wechat_native")
|
||
private PaymentChannel paymentChannel;
|
||
|
||
@Schema(description = "支付金额", required = true, example = "0.01")
|
||
@NotNull(message = "支付金额不能为空")
|
||
@DecimalMin(value = "0.01", message = "支付金额必须大于0.01元")
|
||
@DecimalMax(value = "999999.99", message = "支付金额不能超过999999.99元")
|
||
@Digits(integer = 6, fraction = 2, message = "支付金额格式不正确,最多6位整数2位小数")
|
||
private BigDecimal amount;
|
||
|
||
@Schema(description = "订单号(可选,不提供则自动生成)")
|
||
@Size(max = 32, message = "订单号不能超过32个字符")
|
||
@Pattern(regexp = "^[a-zA-Z0-9_-]*$", message = "订单号只能包含字母、数字、下划线和横线")
|
||
private String orderNo;
|
||
|
||
@Schema(description = "订单标题", required = true)
|
||
@NotBlank(message = "订单标题不能为空")
|
||
@Size(max = 127, message = "订单标题不能超过127个字符")
|
||
private String subject;
|
||
|
||
@Schema(description = "订单描述")
|
||
@Size(max = 500, message = "订单描述不能超过500个字符")
|
||
private String description;
|
||
|
||
@Schema(description = "商品ID")
|
||
@Positive(message = "商品ID必须为正数")
|
||
private Integer goodsId;
|
||
|
||
@Schema(description = "购买数量", example = "1")
|
||
@Min(value = 1, message = "购买数量必须大于0")
|
||
@Max(value = 9999, message = "购买数量不能超过9999")
|
||
private Integer quantity = 1;
|
||
|
||
@Schema(description = "订单类型", example = "0")
|
||
@Min(value = 0, message = "订单类型不能为负数")
|
||
private Integer orderType = 0;
|
||
|
||
@Schema(description = "客户端IP地址")
|
||
private String clientIp;
|
||
|
||
@Schema(description = "用户代理")
|
||
private String userAgent;
|
||
|
||
@Schema(description = "回调通知URL")
|
||
private String notifyUrl;
|
||
|
||
@Schema(description = "支付成功跳转URL")
|
||
private String returnUrl;
|
||
|
||
@Schema(description = "支付取消跳转URL")
|
||
private String cancelUrl;
|
||
|
||
@Schema(description = "订单超时时间(分钟)", example = "30")
|
||
@Min(value = 1, message = "订单超时时间必须大于0分钟")
|
||
@Max(value = 1440, message = "订单超时时间不能超过1440分钟(24小时)")
|
||
private Integer timeoutMinutes = 30;
|
||
|
||
@Schema(description = "买家备注")
|
||
@Size(max = 500, message = "买家备注不能超过500个字符")
|
||
private String buyerRemarks;
|
||
|
||
@Schema(description = "商户备注")
|
||
@Size(max = 500, message = "商户备注不能超过500个字符")
|
||
private String merchantRemarks;
|
||
|
||
@Schema(description = "收货地址ID")
|
||
@Positive(message = "收货地址ID必须为正数")
|
||
private Integer addressId;
|
||
|
||
@Schema(description = "扩展参数")
|
||
private Map<String, Object> extraParams;
|
||
|
||
// 微信支付特有参数
|
||
@Schema(description = "微信OpenID(JSAPI支付必填)")
|
||
private String openId;
|
||
|
||
@Schema(description = "微信UnionID")
|
||
private String unionId;
|
||
|
||
// 支付宝特有参数
|
||
@Schema(description = "支付宝用户ID")
|
||
private String alipayUserId;
|
||
|
||
@Schema(description = "花呗分期数")
|
||
private Integer hbFqNum;
|
||
|
||
// 银联支付特有参数
|
||
@Schema(description = "银行卡号")
|
||
private String cardNo;
|
||
|
||
@Schema(description = "银行代码")
|
||
private String bankCode;
|
||
|
||
/**
|
||
* 获取有效的支付渠道
|
||
*/
|
||
public PaymentChannel getEffectivePaymentChannel() {
|
||
if (paymentChannel != null) {
|
||
return paymentChannel;
|
||
}
|
||
return PaymentChannel.getDefaultByPaymentType(paymentType);
|
||
}
|
||
|
||
/**
|
||
* 获取有效的订单描述
|
||
*/
|
||
public String getEffectiveDescription() {
|
||
if (description != null && !description.trim().isEmpty()) {
|
||
return description.trim();
|
||
}
|
||
return subject;
|
||
}
|
||
|
||
/**
|
||
* 获取格式化的金额字符串
|
||
*/
|
||
public String getFormattedAmount() {
|
||
if (amount == null) {
|
||
return "0.00";
|
||
}
|
||
return String.format("%.2f", amount);
|
||
}
|
||
|
||
/**
|
||
* 转换为分(微信支付API需要)
|
||
*/
|
||
public Integer getAmountInCents() {
|
||
if (amount == null) {
|
||
return 0;
|
||
}
|
||
return amount.multiply(new BigDecimal(100)).intValue();
|
||
}
|
||
|
||
/**
|
||
* 验证必要参数是否完整
|
||
*/
|
||
public boolean isValid() {
|
||
return tenantId != null && tenantId > 0
|
||
&& userId != null && userId > 0
|
||
&& paymentType != null
|
||
&& amount != null && amount.compareTo(BigDecimal.ZERO) > 0
|
||
&& subject != null && !subject.trim().isEmpty();
|
||
}
|
||
|
||
/**
|
||
* 验证微信JSAPI支付参数
|
||
*/
|
||
public boolean isValidForWechatJsapi() {
|
||
return isValid() && paymentType.isWechatPay() && openId != null && !openId.trim().isEmpty();
|
||
}
|
||
|
||
/**
|
||
* 验证支付宝支付参数
|
||
*/
|
||
public boolean isValidForAlipay() {
|
||
return isValid() && paymentType == PaymentType.ALIPAY;
|
||
}
|
||
|
||
/**
|
||
* 获取订单超时时间(秒)
|
||
*/
|
||
public long getTimeoutSeconds() {
|
||
return timeoutMinutes * 60L;
|
||
}
|
||
|
||
@Override
|
||
public String toString() {
|
||
return String.format("PaymentRequest{tenantId=%d, userId=%d, paymentType=%s, amount=%s, orderNo='%s', subject='%s'}",
|
||
tenantId, userId, paymentType, getFormattedAmount(), orderNo, subject);
|
||
}
|
||
}
|