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 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); } }