diff --git a/_config/sql/update-tables.sql b/_config/sql/update-tables.sql index e69de29bb..478f29418 100644 --- a/_config/sql/update-tables.sql +++ b/_config/sql/update-tables.sql @@ -0,0 +1,2 @@ +-- 订单商品明细列表 +ALTER TABLE pay_normal_order ADD COLUMN IF NOT EXISTS goods_detail jsonb; diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/pay/order/entity/PayNormalOrder.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/pay/order/entity/PayNormalOrder.java index ed2b0db69..c804fbea7 100644 --- a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/pay/order/entity/PayNormalOrder.java +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/pay/order/entity/PayNormalOrder.java @@ -5,15 +5,18 @@ import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.FieldStrategy; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import org.dromara.daxpay.payment.common.enums.NormalOrderStatusEnum; +import org.dromara.daxpay.payment.unipay.param.trade.pay.GoodsDetail; import org.dromara.daxpay.platform.core.enums.pay.channel.CurrencyEnum; import org.dromara.daxpay.platform.core.enums.pay.channel.PayMethodEnum; import org.dromara.daxpay.platform.core.enums.pay.channel.ProductEnum; import java.time.OffsetDateTime; +import java.util.List; /// # 普通支付业务单容器 /// @@ -23,7 +26,7 @@ import java.time.OffsetDateTime; @Data @EqualsAndHashCode(callSuper = true) @Accessors(chain = true) -@TableName("pay_normal_order") +@TableName(value = "pay_normal_order", autoResultMap = true) public class PayNormalOrder extends MchBaseEntity { /// 应用号 @@ -90,6 +93,10 @@ public class PayNormalOrder extends MchBaseEntity { /// 通道附加参数 private String extraParam; + /// 订单商品明细列表(jsonb 存储) + @TableField(typeHandler = JacksonTypeHandler.class) + private List goodsDetail; + /// 客户端 IP private String clientIp; diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/pay/service/PayAssistService.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/pay/service/PayAssistService.java index 6d567902e..99b75cf38 100644 --- a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/pay/service/PayAssistService.java +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/pay/service/PayAssistService.java @@ -81,6 +81,7 @@ public class PayAssistService { normalOrder.setMethod(payParam.getMethod()); normalOrder.setProduct(payParam.getProduct()); normalOrder.setExtraParam(payParam.getExtraParam()); + normalOrder.setGoodsDetail(payParam.getGoodsDetail()); normalOrder.setTerminalNo(terminalNo); normalOrder.setAppId(appId); payNormalOrderManager.save(normalOrder); diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/unipay/param/trade/pay/GoodsDetail.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/unipay/param/trade/pay/GoodsDetail.java new file mode 100644 index 000000000..0e28dea69 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/unipay/param/trade/pay/GoodsDetail.java @@ -0,0 +1,63 @@ +package org.dromara.daxpay.payment.unipay.param.trade.pay; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.Digits; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; + +/// # 订单商品明细 +/// +/// 对应支付宝 goods_detail 和微信支付 detail.goods_detail, +/// 用于单品营销、电子发票等场景。金额单位为分(Long),与系统内部全局一致。 +@Data +@Schema(title = "订单商品明细") +public class GoodsDetail { + + /// 商户侧商品编码 + /// Alipay → goods_id, WeChat → merchant_goods_id + @NotBlank(message = "{validation.field.goodsId.notBlank}") + @Size(max = 64, message = "{validation.field.goodsId.size}") + @Schema(description = "商户侧商品编码") + private String goodsId; + + /// 商品名称 + @NotBlank(message = "{validation.field.goodsName.notBlank}") + @Size(max = 256, message = "{validation.field.goodsName.size}") + @Schema(description = "商品名称") + private String goodsName; + + /// 商品数量 + @NotNull(message = "{validation.field.quantity.notNull}") + @Min(value = 1, message = "{validation.field.quantity.min}") + @Schema(description = "商品数量") + private Integer quantity; + + /// 商品单价(分) + /// Alipay 需转为元,WeChat 直接使用分 + @NotNull(message = "{validation.field.unitPrice.notNull}") + @DecimalMin(value = "0.01", message = "{validation.field.unitPrice.decimalMin}") + @Digits(integer = 10, fraction = 2, message = "{validation.field.unitPrice.digits}") + @Schema(description = "商品单价(分)") + private BigDecimal unitPrice; + + /// 商品分类(Alipay 独有,选填) + @Size(max = 32, message = "{validation.field.goodsCategory.size}") + @Schema(description = "商品分类") + private String category; + + /// 商品描述(选填) + @Size(max = 1000, message = "{validation.field.goodsDescription.size}") + @Schema(description = "商品描述") + private String description; + + /// 商品展示链接(选填) + @Size(max = 400, message = "{validation.field.showUrl.size}") + @Schema(description = "商品展示链接") + private String showUrl; +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/unipay/param/trade/pay/PayParam.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/unipay/param/trade/pay/PayParam.java index fead88792..c810d2710 100644 --- a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/unipay/param/trade/pay/PayParam.java +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/unipay/param/trade/pay/PayParam.java @@ -3,6 +3,7 @@ package org.dromara.daxpay.payment.unipay.param.trade.pay; import org.dromara.daxpay.platform.core.enums.pay.channel.ProductEnum; import org.dromara.daxpay.platform.core.enums.pay.channel.PayMethodEnum; import org.dromara.daxpay.payment.unipay.param.MerchantPaymentCommonParam; +import jakarta.validation.Valid; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; @@ -85,6 +86,14 @@ public class PayParam extends MerchantPaymentCommonParam { @Size(max = 2048, message = "{validation.field.extraParam.size}") private String extraParam; + /// 订单商品明细列表 + /// 传给支付渠道(支付宝 goods_detail / 微信 detail.goods_detail), + /// 用于单品营销、电子发票等场景 + @Valid + @Size(max = 50, message = "{validation.field.goodsDetail.size}") + @Schema(description = "订单商品明细列表") + private List goodsDetail; + // ===== 通知回调 ===== /// 异步通知地址 diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json index a330adbe5..4d7b0e038 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json @@ -188,6 +188,35 @@ "extraParam": { "size": "Extra param cannot exceed 2048 characters" }, + "goodsDetail": { + "size": "Goods detail list cannot exceed 50 items" + }, + "goodsId": { + "notBlank": "Goods ID cannot be empty", + "size": "Goods ID cannot exceed 64 characters" + }, + "goodsName": { + "notBlank": "Goods name cannot be empty", + "size": "Goods name cannot exceed 256 characters" + }, + "quantity": { + "notNull": "Quantity cannot be null", + "min": "Quantity cannot be less than 1" + }, + "unitPrice": { + "notNull": "Unit price cannot be null", + "decimalMin": "Unit price cannot be less than 0.01", + "digits": "Unit price precision up to cents" + }, + "goodsCategory": { + "size": "Goods category cannot exceed 32 characters" + }, + "goodsDescription": { + "size": "Goods description cannot exceed 1000 characters" + }, + "showUrl": { + "size": "Show URL cannot exceed 400 characters" + }, "attach": { "size": "Attach cannot exceed 500 characters" }, diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json index b43b07f23..2ca55fa32 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json @@ -188,6 +188,35 @@ "extraParam": { "size": "支付扩展参数不可超过2048位" }, + "goodsDetail": { + "size": "商品明细列表不可超过50条" + }, + "goodsId": { + "notBlank": "商品编码不可为空", + "size": "商品编码不可超过64位" + }, + "goodsName": { + "notBlank": "商品名称不可为空", + "size": "商品名称不可超过256位" + }, + "quantity": { + "notNull": "商品数量不可为空", + "min": "商品数量不能小于1" + }, + "unitPrice": { + "notNull": "商品单价不可为空", + "decimalMin": "商品单价不可小于0.01元", + "digits": "商品单价精度到分" + }, + "goodsCategory": { + "size": "商品分类不可超过32位" + }, + "goodsDescription": { + "size": "商品描述不可超过1000位" + }, + "showUrl": { + "size": "商品展示链接不可超过400位" + }, "attach": { "size": "商户扩展参数不可超过500位" },