ref 与多商户使用同架构实现

This commit is contained in:
bootx
2024-10-04 22:54:33 +08:00
parent 380757016c
commit a261732451
446 changed files with 26639 additions and 28 deletions

View File

@@ -11,10 +11,25 @@
<artifactId>daxpay-single-core</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- platform核心包 -->
<dependency>
<groupId>cn.bootx.platform</groupId>
<artifactId>bootx-platform-core</artifactId>
<version>${bootx-platform.version}</version>
</dependency>
<!-- javaEE web相关 -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.20</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,77 @@
package org.dromara.daxpay.core.code;
/**
* 公共错误码
* @author xxm
* @since 2024/6/17
*/
public interface DaxPayErrorCode {
/** 未归类的错误 */
int UNCLASSIFIED_ERROR = 20000;
/** 不存在的支付通道 */
int CHANNEL_NOT_EXIST = 20011;
/** 不存在的支付方式 */
int METHOD_NOT_EXIST = 20012;
/** 不存在的状态 */
int STATUS_NOT_EXIST = 20013;
/** 支付通道未启用 */
int CHANNEL_NOT_ENABLE = 20021;
/** 支付方式未启用 */
int METHOD_NOT_ENABLE = 20022;
/** 配置未启用 */
int CONFIG_NOT_ENABLE = 20023;
/** 配置错误 */
int CONFIG_ERROR = 20024;
/** 配置不存在 */
int CONFIG_NOT_EXIST = 20025;
/** 不支持该能力 */
int UNSUPPORTED_ABILITY = 20030;
/** 交易不存在 */
int TRADE_NOT_EXIST = 20041;
/** 交易已关闭 */
int TRADE_CLOSED = 20042;
/** 交易处理中, 请勿重复操作 */
int TRADE_PROCESSING = 20043;
/** 交易状态错误 */
int TRADE_STATUS_ERROR = 20044;
/** 交易失败 */
int TRADE_FAIL = 20045;
/** 验签失败 */
int VERIFY_SIGN_FAILED = 20052;
/** 金额超过限额 */
int AMOUNT_EXCEED_LIMIT = 20060;
/** 对账失败 */
int RECONCILE_FAIL = 20071;
/** 操作失败 */
int OPERATION_FAIL = 20080;
/** 操作处理中, 请勿重复操作 */
int OPERATION_PROCESSING = 20081;
/** 不支持的操作 */
int OPERATION_UNSUPPORTED = 20082;
/** 数据错误 */
int DATA_ERROR = 20091;
/** 未知异常,系统无法处理 */
int SYSTEM_UNKNOWN_ERROR = 30000;
}

View File

@@ -0,0 +1,24 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 分账明细处理结果
* @author xxm
* @since 2024/4/16
*/
@Getter
@AllArgsConstructor
public enum AllocDetailResultEnum {
PENDING("pending", "待分账"),
SUCCESS("success", "分账成功"),
FAIL("fail", "分账失败"),
/** 金额为0时不进行分账 */
IGNORE("ignore", "忽略分账"),
;
private final String code;
private final String name;
}

View File

@@ -0,0 +1,23 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 分账订单处理结果
* @author xxm
* @since 2024/4/16
*/
@Getter
@AllArgsConstructor
public enum AllocOrderResultEnum {
ALL_PENDING("all_pending", "处理中"),
ALL_SUCCESS("all_success", "全部成功"),
PART_SUCCESS("part_success", "部分成功"),
ALL_FAILED("all_failed", "全部失败"),
;
private final String code;
private final String name;
}

View File

@@ -0,0 +1,25 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 分账状态枚举
* @author xxm
* @since 2024/4/7
*/
@Getter
@AllArgsConstructor
public enum AllocOrderStatusEnum {
ALLOCATION_PROCESSING("allocation_processing", "分账处理中"),
ALLOCATION_END("allocation_end", "分账完成"),
ALLOCATION_FAILED("allocation_failed", "分账失败"),
FINISH("finish", "完结"),
FINISH_FAILED("finish_failed", "完结失败"),
;
final String code;
final String name;
}

View File

@@ -0,0 +1,49 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
/**
* 分账接收方类型
* @author xxm
* @since 2024/4/1
*/
@Getter
@AllArgsConstructor
public enum AllocReceiverTypeEnum {
/** 个人 */
WX_PERSONAL("wx_personal", "个人"),
/** 商户 */
WX_MERCHANT("wx_merchant", "商户"),
/** userId 以2088开头的纯16位数字 */
ALI_USER_ID("ali_user_id", "用户ID"),
/** openId */
ALI_OPEN_ID("ali_open_id", "openId"),
/** 账号 */
ALI_LOGIN_NAME("ali_login_name", "账号");
/** 编码 */
private final String code;
/** 名称 */
private final String name;
/**
* 根据编码查找
*/
public static AllocReceiverTypeEnum findByCode(String code) {
return Arrays.stream(AllocReceiverTypeEnum.values())
.filter(e -> e.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("未找到对应的分账接收方类型"));
}
/** 微信支持类型 */
public static final List<AllocReceiverTypeEnum> WECHAT_LIST = List.of(WX_PERSONAL, WX_MERCHANT);
/** 支付宝支持类型 */
public static final List<AllocReceiverTypeEnum> ALI_LIST = List.of(ALI_OPEN_ID, ALI_USER_ID, ALI_LOGIN_NAME);
}

View File

@@ -0,0 +1,28 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 分账关系类型
* @author xxm
* @since 2024/3/27
*/
@Getter
@AllArgsConstructor
public enum AllocRelationTypeEnum {
SERVICE_PROVIDER("SERVICE_PROVIDER","服务商"),
STORE("STORE","门店"),
STAFF("STAFF","员工"),
STORE_OWNER("STORE_OWNER","店主"),
PARTNER("PARTNER","合作伙伴"),
HEADQUARTER("HEADQUARTER","总部"),
BRAND("BRAND","品牌方"),
DISTRIBUTOR("DISTRIBUTOR","分销商"),
USER("USER","用户"),
SUPPLIER("SUPPLIER","供应商"),
CUSTOM("CUSTOM","自定义");
private final String code;
private final String name;
}

View File

@@ -0,0 +1,27 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 支付回调处理状态
* 字典: callback_status
* @author xxm
* @since 2023/12/20
*/
@Getter
@AllArgsConstructor
public enum CallbackStatusEnum {
/** 成功 */
SUCCESS("success"),
/** 失败 */
FAIL("fail"),
/** 忽略 */
IGNORE("ignore"),
/** 异常 */
EXCEPTION("exception"),
/** 未找到 */
NOT_FOUND("not_found");
private final String code;
}

View File

@@ -0,0 +1,22 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 收银台类型
* 字典: cashier_type
* @author xxm
* @since 2024/9/28
*/
@Getter
@AllArgsConstructor
public enum CashierTypeEnum {
WECHAT_PAY("wechat_pay", "微信收银台"),
ALIPAY("alipay", "支付宝收银台"),
;
private final String code;
private final String name;
}

View File

@@ -0,0 +1,24 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 通道认证状态
* @author xxm
* @since 2024/9/24
*/
@Getter
@AllArgsConstructor
public enum ChannelAuthStatusEnum {
/** 获取中 */
WAITING("waiting"),
/** 获取成功 */
SUCCESS("success"),
/** 数据不存在 */
NOT_EXIST("not_exist");
private final String code;
}

View File

@@ -0,0 +1,32 @@
package org.dromara.daxpay.core.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 支付通道枚举
* 字典值: channel
*
* @author xxm
* @since 2021/7/26
*/
@Getter
@RequiredArgsConstructor
public enum ChannelEnum {
/** 支付宝 - 直连商户 */
ALI("ali_pay"),
/** 支付宝 - 服务商商户 */
ALI_SERVICE("ali_service"),
/** 微信支付 */
WECHAT("wechat_pay"),
/** 微信支付服务商 */
WECHAT_SERVICE("wechat_service"),
/** 云闪付 */
UNION_PAY("union_pay"),
;
/** 支付通道编码 */
private final String code;
}

View File

@@ -0,0 +1,24 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 支付订单关闭类型
* 字典值 close_type
* @author xxm
* @since 2024/6/3
*/
@Getter
@AllArgsConstructor
public enum CloseTypeEnum {
/** 关闭 */
CLOSE("close", PayStatusEnum.CLOSE),
/** 撤销 */
CANCEL("cancel", PayStatusEnum.CANCEL),
;
private final String code;
private final PayStatusEnum payStatus;
}

View File

@@ -0,0 +1,25 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 商户消息通知类型, 业务系统使用什么样的方式接收通知消息
* 字典: merchant_notify_type
* @author xxm
* @since 2024/6/5
*/
@Getter
@AllArgsConstructor
public enum MerchantNotifyTypeEnum {
/** http */
HTTP("http"),
/** websocket */
WEBSOCKET("websocket"),
/** mq */
MQ("mq"),
/** 不启用 */
NONE("none");
private final String code;
}

View File

@@ -0,0 +1,21 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 支付订单的分账状态
* 字典表pay_alloc_status
* @author xxm
* @since 2024/4/16
*/
@Getter
@AllArgsConstructor
public enum PayAllocStatusEnum {
WAITING("waiting", "待分账"),
ALLOCATION("allocation", "已分账"),
;
private final String code;
private final String name;
}

View File

@@ -0,0 +1,45 @@
package org.dromara.daxpay.core.enums;
import org.dromara.daxpay.core.exception.ConfigNotExistException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Objects;
/**
* 支付方式
* 字典: pay_method
* @author xxm
* @since 2021/7/26
*/
@Getter
@AllArgsConstructor
public enum PayMethodEnum {
/** wap支付 */
WAP("wap"),
/** 应用支付 */
APP("app"),
/** web支付 */
WEB("web"),
/** 扫码支付 */
QRCODE("qrcode"),
/** 付款码 */
BARCODE("barcode"),
/** 小程序支付 */
JSAPI("jsapi"),
;
/** 编码 */
private final String code;
/**
* 根据编码获取枚举
*/
public static PayMethodEnum findByCode(String code){
return Arrays.stream(values())
.filter(o -> Objects.equals(o.getCode(), code))
.findFirst()
.orElseThrow(() -> new ConfigNotExistException("该支付方式不存在"));
}
}

View File

@@ -0,0 +1,37 @@
package org.dromara.daxpay.core.enums;
import org.dromara.daxpay.core.exception.StatusNotExistException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Objects;
/**
* 支付订单的退款状态
* 字典: pay_refund_status
* @author xxm
* @since 2024/6/7
*/
@Getter
@AllArgsConstructor
public enum PayRefundStatusEnum {
NO_REFUND("no_refund","未退款"),
REFUNDING("refunding","退款中"),
PARTIAL_REFUND("partial_refund","部分退款"),
REFUNDED("refunded","全部退款"),
;
private final String code;
private final String name;
/**
* 根据编码获取枚举
*/
public static PayRefundStatusEnum findByCode(String code){
return Arrays.stream(values())
.filter(payStatusEnum -> Objects.equals(payStatusEnum.getCode(), code))
.findFirst()
.orElseThrow(() -> new StatusNotExistException("该退款状态不存在"));
}
}

View File

@@ -0,0 +1,44 @@
package org.dromara.daxpay.core.enums;
import cn.bootx.platform.core.exception.DataNotExistException;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.Objects;
/**
* 支付状态
* 字典: pay_status
* @author xxm
* @since 2023/12/17
*/
@Getter
@RequiredArgsConstructor
public enum PayStatusEnum {
PROGRESS("progress","支付中"),
SUCCESS("success","成功"),
CLOSE("close","支付关闭"),
CANCEL("cancel","支付撤销"),
FAIL("fail","失败"),
/** 订单到了超时时间, 被手动设置订单为这个状态 */
TIMEOUT("timeout", "支付超时"),
;
/** 编码 */
private final String code;
/** 名称 */
private final String name;
/**
* 根据编码获取枚举
*/
public static PayStatusEnum findByCode(String code){
return Arrays.stream(values())
.filter(payStatusEnum -> Objects.equals(payStatusEnum.getCode(), code))
.findFirst()
.orElseThrow(() -> new DataNotExistException("该支付状态不存在"));
}
}

View File

@@ -0,0 +1,40 @@
package org.dromara.daxpay.core.enums;
import org.dromara.daxpay.core.exception.ConfigNotExistException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Objects;
/**
* 退款状态枚举
* 字典: refund_status
* @author xxm
* @since 2023/12/18
*/
@Getter
@AllArgsConstructor
public enum RefundStatusEnum {
/**
* 接口调用成功不代表成功
*/
PROGRESS("progress","退款中"),
SUCCESS("success","退款成功"),
CLOSE("close","退款关闭"),
FAIL("fail","退款失败");
/** 编码 */
private final String code;
/** 名称 */
private final String name;
public static RefundStatusEnum findByCode(String code) {
return Arrays.stream(values())
.filter(item -> Objects.equals(item.code, code))
.findFirst()
.orElseThrow(() -> new ConfigNotExistException("退款状态不存在"));
}
}

View File

@@ -0,0 +1,24 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 支付签名类型
* 字典 sign_type
* @author xxm
* @since 2023/12/24
*/
@Getter
@AllArgsConstructor
public enum SignTypeEnum {
HMAC_SHA256("hmac_sha256", "HMAC_SHA256"),
MD5("md5", "MD5"),
SM3("sm3", "SM3"),;
/** 支付方式 */
private final String code;
private final String name;
}

View File

@@ -0,0 +1,26 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 交易流水记录类型
* 字典: trade_flow_type
* @author xxm
* @since 2024/2/20
*/
@Getter
@AllArgsConstructor
public enum TradeFlowTypeEnum {
/** 支付 */
PAY("pay", "支付"),
/** 退款 */
REFUND("refund", "退款"),
/** 转账 */
TRANSFER("transfer", "转账"),
;
private final String code;
private final String name;
}

View File

@@ -0,0 +1,42 @@
package org.dromara.daxpay.core.enums;
import org.dromara.daxpay.core.exception.ConfigNotExistException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 交易状态
* 字典 trade_status
* @author xxm
* @since 2024/8/20
*/
@Getter
@AllArgsConstructor
public enum TradeStatusEnum {
/** 执行中 */
PROGRESS("progress","执行中"),
/** 成功 */
SUCCESS("success","成功"),
/** 失败 */
FAIL("fail","失败"),
/** 关闭 */
CLOSED("closed","关闭"),
/** 撤销 */
REVOKED("revoked","撤销"),
/** 异常 */
EXCEPTION("exception","异常"),
;
private final String code;
private final String name;
public static TradeStatusEnum findByCode(String code) {
return Arrays.stream(values())
.filter(e -> e.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new ConfigNotExistException("交易状态不存在"));
}
}

View File

@@ -0,0 +1,32 @@
package org.dromara.daxpay.core.enums;
import org.dromara.daxpay.core.exception.ConfigNotExistException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 交易类型, 如支付/退款/转账等
* 字典: trade_type
* @author xxm
* @since 2024/1/28
*/
@Getter
@AllArgsConstructor
public enum TradeTypeEnum {
PAY("pay","支付"),
REFUND("refund","退款"),
TRANSFER("transfer","转账"),;
private final String code;
private final String name;
public static TradeTypeEnum findByCode(String code) {
return Arrays.stream(values())
.filter(tradeTypeEnum -> tradeTypeEnum.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new ConfigNotExistException("交易类型不存在"));
}
}

View File

@@ -0,0 +1,43 @@
package org.dromara.daxpay.core.enums;
import org.dromara.daxpay.core.exception.UnsupportedAbilityException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 转账接收方类型
* 字典: transfer_payee_type
* @author xxm
* @since 2024/4/1
*/
@Getter
@AllArgsConstructor
public enum TransferPayeeTypeEnum {
/** 微信 个人 */
WX_PERSONAL("wx_personal","openid", "OpenId(微信)"),
/** 支付宝 userId 以2088开头的纯16位数字 */
ALI_USER_ID("ali_user_id","ALIPAY_USER_ID", "用户ID(支付宝)"),
/** 支付宝 openId */
ALI_OPEN_ID("ali_open_id","ALIPAY_OPEN_ID", "OpenId(支付宝)"),
/** 支付宝 账号 支持邮箱和手机号格式 */
ALI_LOGIN_NAME("ali_login_name","ALIPAY_LOGON_ID", "账号(支付宝)");
/** 编码 */
private final String code;
/** 外部编码, 三方支付系统使用的编码 */
private final String outCode;
/** 名称 */
private final String name;
/**
* 根据编码查找
*/
public static TransferPayeeTypeEnum findByCode(String code) {
return Arrays.stream(TransferPayeeTypeEnum.values())
.filter(e -> e.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new UnsupportedAbilityException("未找到对应的转账接收方类型"));
}
}

View File

@@ -0,0 +1,28 @@
package org.dromara.daxpay.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 转账状态
* 字典: transfer_status
* @author xxm
* @since 2024/6/11
*/
@Getter
@AllArgsConstructor
public enum TransferStatusEnum {
/** 转账中 */
PROGRESS("progress"),
/** 转账成功 */
SUCCESS("success"),
/** 转账关闭 */
CLOSE("close"),
/** 转账失败 */
FAIL("fail"),
;
private final String code;
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 金额超过限额
* @author xxm
* @since 2024/6/17
*/
public class AmountExceedLimitException extends PayFailureException{
public AmountExceedLimitException(String message) {
super(DaxPayErrorCode.AMOUNT_EXCEED_LIMIT,message);
}
public AmountExceedLimitException() {
super(DaxPayErrorCode.AMOUNT_EXCEED_LIMIT,"金额超过限额");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 支付通道未启用
* @author xxm
* @since 2024/6/17
*/
public class ChannelNotEnableException extends PayFailureException{
public ChannelNotEnableException(String message) {
super(DaxPayErrorCode.CHANNEL_NOT_ENABLE,message);
}
public ChannelNotEnableException() {
super(DaxPayErrorCode.CHANNEL_NOT_ENABLE,"支付通道未启用");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 不存在的支付通道
* @author xxm
* @since 2024/6/17
*/
public class ChannelNotExistException extends PayFailureException{
public ChannelNotExistException(String message) {
super(DaxPayErrorCode.CHANNEL_NOT_EXIST,message);
}
public ChannelNotExistException() {
super(DaxPayErrorCode.CHANNEL_NOT_EXIST,"不存在的支付通道");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 配置错误
* @author xxm
* @since 2024/6/18
*/
public class ConfigErrorException extends PayFailureException{
public ConfigErrorException(String message) {
super(DaxPayErrorCode.CONFIG_ERROR,message);
}
public ConfigErrorException() {
super(DaxPayErrorCode.CONFIG_ERROR,"配置错误");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 配置未启用
* @author xxm
* @since 2024/6/17
*/
public class ConfigNotEnableException extends PayFailureException{
public ConfigNotEnableException(String message) {
super(DaxPayErrorCode.CONFIG_NOT_ENABLE,message);
}
public ConfigNotEnableException() {
super(DaxPayErrorCode.CONFIG_NOT_ENABLE,"配置未启用");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 配置不存在
* @author xxm
* @since 2024/6/27
*/
public class ConfigNotExistException extends PayFailureException{
public ConfigNotExistException(String message) {
super(DaxPayErrorCode.CONFIG_NOT_EXIST,message);
}
public ConfigNotExistException() {
super(DaxPayErrorCode.CONFIG_NOT_EXIST,"配置不存在");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 数据错误
* @author xxm
* @since 2024/6/17
*/
public class DataErrorException extends PayFailureException{
public DataErrorException(String message) {
super(DaxPayErrorCode.DATA_ERROR,message);
}
public DataErrorException() {
super(DaxPayErrorCode.DATA_ERROR,"数据错误");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 支付方式未启用
* @author xxm
* @since 2024/6/17
*/
public class MethodNotEnableException extends PayFailureException{
public MethodNotEnableException(String message) {
super(DaxPayErrorCode.METHOD_NOT_ENABLE,message);
}
public MethodNotEnableException() {
super(DaxPayErrorCode.METHOD_NOT_ENABLE,"支付方式未启用");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 不存在的支付方式
* @author xxm
* @since 2024/6/17
*/
public class MethodNotExistException extends PayFailureException{
public MethodNotExistException(String message) {
super(DaxPayErrorCode.METHOD_NOT_EXIST,message);
}
public MethodNotExistException() {
super(DaxPayErrorCode.METHOD_NOT_EXIST,"不存在的支付方式");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 操作失败
* @author xxm
* @since 2024/6/17
*/
public class OperationFailException extends PayFailureException{
public OperationFailException(String message) {
super(DaxPayErrorCode.OPERATION_FAIL,message);
}
public OperationFailException() {
super(DaxPayErrorCode.OPERATION_FAIL,"操作失败");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 操作处理中, 请勿重复操作
* @author xxm
* @since 2024/6/17
*/
public class OperationProcessingException extends PayFailureException{
public OperationProcessingException(String message) {
super(DaxPayErrorCode.OPERATION_PROCESSING,message);
}
public OperationProcessingException() {
super(DaxPayErrorCode.OPERATION_PROCESSING,"操作处理中, 请勿重复操作");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 不支持的操作
* @author xxm
* @since 2024/6/17
*/
public class OperationUnsupportedException extends PayFailureException{
public OperationUnsupportedException(String message) {
super(DaxPayErrorCode.OPERATION_UNSUPPORTED,message);
}
public OperationUnsupportedException() {
super(DaxPayErrorCode.OPERATION_UNSUPPORTED,"不支持的操作");
}
}

View File

@@ -0,0 +1,27 @@
package org.dromara.daxpay.core.exception;
import cn.bootx.platform.core.exception.BizException;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 支付错误
*
* @author xxm
* @since 2020/12/8
*/
public class PayFailureException extends BizException {
public PayFailureException(int code, String message) {
super(code, message);
}
public PayFailureException(String message) {
super(DaxPayErrorCode.UNCLASSIFIED_ERROR, message);
}
public PayFailureException() {
super(DaxPayErrorCode.UNCLASSIFIED_ERROR, "支付失败");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 对账失败
* @author xxm
* @since 2024/6/17
*/
public class ReconciliationFailException extends PayFailureException{
public ReconciliationFailException(String message) {
super(DaxPayErrorCode.RECONCILE_FAIL,message);
}
public ReconciliationFailException() {
super(DaxPayErrorCode.RECONCILE_FAIL,"对账失败");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 不存在的状态
* @author xxm
* @since 2024/6/17
*/
public class StatusNotExistException extends PayFailureException{
public StatusNotExistException(String message) {
super(DaxPayErrorCode.STATUS_NOT_EXIST,message);
}
public StatusNotExistException() {
super(DaxPayErrorCode.STATUS_NOT_EXIST,"不存在的状态");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 未知异常,系统无法处理
* @author xxm
* @since 2024/6/17
*/
public class SystemUnknownErrorException extends PayFailureException{
public SystemUnknownErrorException(String message) {
super(DaxPayErrorCode.SYSTEM_UNKNOWN_ERROR,message);
}
public SystemUnknownErrorException() {
super(DaxPayErrorCode.SYSTEM_UNKNOWN_ERROR,"未知异常,系统无法处理");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 交易已关闭
* @author xxm
* @since 2024/6/17
*/
public class TradeClosedException extends PayFailureException{
public TradeClosedException(String message) {
super(DaxPayErrorCode.TRADE_CLOSED,message);
}
public TradeClosedException() {
super(DaxPayErrorCode.TRADE_CLOSED,"交易已关闭");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 交易失败
* @author xxm
* @since 2024/6/17
*/
public class TradeFailException extends PayFailureException{
public TradeFailException(String message) {
super(DaxPayErrorCode.TRADE_FAIL,message);
}
public TradeFailException() {
super(DaxPayErrorCode.TRADE_FAIL,"交易失败");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 交易不存在
* @author xxm
* @since 2024/6/17
*/
public class TradeNotExistException extends PayFailureException{
public TradeNotExistException(String message) {
super(DaxPayErrorCode.TRADE_NOT_EXIST,message);
}
public TradeNotExistException() {
super(DaxPayErrorCode.TRADE_NOT_EXIST,"交易不存在");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 交易处理中, 请勿重复操作
* @author xxm
* @since 2024/6/17
*/
public class TradeProcessingException extends PayFailureException{
public TradeProcessingException(String message) {
super(DaxPayErrorCode.TRADE_PROCESSING,message);
}
public TradeProcessingException() {
super(DaxPayErrorCode.TRADE_PROCESSING,"交易处理中,请勿重复操作");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 交易状态错误
* @author xxm
* @since 2024/6/17
*/
public class TradeStatusErrorException extends PayFailureException{
public TradeStatusErrorException(String message) {
super(DaxPayErrorCode.TRADE_STATUS_ERROR,message);
}
public TradeStatusErrorException() {
super(DaxPayErrorCode.TRADE_STATUS_ERROR,"交易状态错误");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 不支持该能力
* @author xxm
* @since 2024/6/17
*/
public class UnsupportedAbilityException extends PayFailureException{
public UnsupportedAbilityException(String message) {
super(DaxPayErrorCode.UNSUPPORTED_ABILITY,message);
}
public UnsupportedAbilityException() {
super(DaxPayErrorCode.UNSUPPORTED_ABILITY,"不支持该能力");
}
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.exception;
import org.dromara.daxpay.core.code.DaxPayErrorCode;
/**
* 验签失败
* @author xxm
* @since 2024/6/17
*/
public class VerifySignFailedException extends PayFailureException{
public VerifySignFailedException(String message) {
super(DaxPayErrorCode.VERIFY_SIGN_FAILED,message);
}
public VerifySignFailedException() {
super(DaxPayErrorCode.VERIFY_SIGN_FAILED,"验签失败");
}
}

View File

@@ -0,0 +1,51 @@
package org.dromara.daxpay.core.param;
import cn.bootx.platform.core.validation.IpAddress;
import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 支付公共参数
* @author xxm
* @since 2023/12/17
*/
@Data
@Schema(title = "支付公共参数")
public abstract class PaymentCommonParam {
/** 应用号 */
@Schema(description = "应用号")
@NotBlank(message = "应用号不可为空")
@Size(max = 32, message = "应用号不可超过32位")
private String appId;
/** 客户端ip */
@Schema(description = "客户端ip")
@IpAddress
@Size(max=64, message = "客户端ip不可超过64位")
private String clientIp;
/** 随机数 */
@Schema(description = "随机数")
@Size(max = 32, message = "随机数不可超过32位")
private String nonceStr;
/** 签名 */
@Schema(description = "签名")
@Size(max = 64, message = "签名不可超过64位")
private String sign;
/** 请求时间 格式yyyy-MM-dd HH:mm:ss */
@Schema(description = "请求时间, 格式yyyy-MM-dd HH:mm:ss")
@NotNull(message = "请求时间必填")
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
private LocalDateTime reqTime;
}

View File

@@ -0,0 +1,33 @@
package org.dromara.daxpay.core.param.assist;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 通道认证参数
* @author xxm
* @since 2024/9/19
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "通道认证参数")
public class AuthCodeParam extends PaymentCommonParam {
@Schema(description = "通道")
@NotBlank(message = "通道不可为空")
private String channel;
@Schema(description = "标识码")
@NotBlank(message = "标识码不可为空")
private String authCode;
/** 用于查询Code值, 可以为空 */
@Schema(description = "查询Code")
private String queryCode;
}

View File

@@ -0,0 +1,32 @@
package org.dromara.daxpay.core.param.assist;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 生成授权链接参数
* @author xxm
* @since 2024/9/19
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "生成授权链接参数")
public class GenerateAuthUrlParam extends PaymentCommonParam {
/**
* 通道
*/
@Schema(description = "通道")
private String channel;
/**
* 自定义授权重定向地址, 如果不传, 使用系统提供的默认地址
*/
@Schema(description = "自定义授权重定向地址")
private String authRedirectUrl;
}

View File

@@ -0,0 +1,23 @@
package org.dromara.daxpay.core.param.assist;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 查询OpenId参数
* @author xxm
* @since 2024/9/19
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "查询OpenId参数")
public class QueryAuthParam extends PaymentCommonParam {
@Schema(description = "标识码")
private String queryCode;
}

View File

@@ -0,0 +1,25 @@
package org.dromara.daxpay.core.param.cashier;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 获取通道收银认证结果参数
* @author xxm
* @since 2024/9/28
*/
@Data
@Accessors(chain = true)
@Schema(title = "通道收银认证参数")
public class CashierAuthCodeParam {
@Schema(description = "应用号")
private String appId;
@Schema(description = "收银台类型")
private String cashierType;
@Schema(description = "认证Code")
private String authCode;
}

View File

@@ -0,0 +1,22 @@
package org.dromara.daxpay.core.param.cashier;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 获取通道收银认证url参数
* @author xxm
* @since 2024/9/28
*/
@Data
@Accessors(chain = true)
@Schema(title = "通道收银认证参数")
public class CashierAuthUrlParam {
@Schema(description = "应用号")
private String appId;
@Schema(description = "收银台类型")
private String cashierType;
}

View File

@@ -0,0 +1,33 @@
package org.dromara.daxpay.core.param.cashier;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
/**
* 通道收银支付参数
* @author xxm
* @since 2024/9/28
*/
@Data
@Accessors(chain = true)
@Schema(title = "通道收银支付参数")
public class CashierPayParam {
@Schema(description = "应用号")
private String appId;
@Schema(description = "支付金额")
private BigDecimal amount;
@Schema(description = "收银台类型")
private String cashierType;
@Schema(description = "唯一标识")
private String openId;
@Schema(description = "描述")
private String description;
}

View File

@@ -0,0 +1,33 @@
package org.dromara.daxpay.core.param.reconcile;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
/**
* 对账文件下载参数
* @author xxm
* @since 2024/8/21
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title = "对账下载参数")
public class ReconcileDownParam extends PaymentCommonParam {
@Schema(description = "通道")
@Size(max = 32, message = "通道不可超过32位")
@NotBlank(message = "通道不可为空")
private String channel;
@Schema(description = "日期")
@NotNull(message = "日期不可为空")
@JsonFormat(pattern = DatePattern.NORM_DATE_PATTERN)
private LocalDate date;
}

View File

@@ -0,0 +1,36 @@
package org.dromara.daxpay.core.param.trade.pay;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 支付关闭参数
* @author xxm
* @since 2023/12/17
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title = "支付关闭参数")
public class PayCloseParam extends PaymentCommonParam {
/** 订单号 */
@Schema(description = "订单号")
@Size(max = 32, message = "支付订单号不可超过32位")
private String orderNo;
/** 商户订单号 */
@Schema(description = "商户订单号")
@Size(max = 100, message = "商户支付订单号不可超过100位")
private String bizOrderNo;
/**
* 是否使用撤销方式进行订单关闭, 只有部分支付通道的支付方式才可以使用,
* 如果支付订单不支持撤销, 这个参数将不会生效
*/
@Schema(description = "是否使用撤销方式进行订单关闭")
private boolean useCancel;
}

View File

@@ -0,0 +1,107 @@
package org.dromara.daxpay.core.param.trade.pay;
import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.daxpay.core.enums.ChannelEnum;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 统一下单参数
*
* @author xxm
* @since 2020/12/9
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title = "支付参数")
public class PayParam extends PaymentCommonParam {
/** 商户订单号 */
@Schema(description = "商户订单号")
@NotBlank(message = "商户订单号不可为空")
@Size(max = 100, message = "商户订单号不可超过100位")
private String bizOrderNo;
/** 支付标题 */
@Schema(description = "支付标题")
@NotBlank(message = "支付标题不可为空")
@Size(max = 100, message = "支付标题不可超过100位")
private String title;
/** 支付描述 */
@Schema(description = "支付描述")
@Size(max = 500, message = "支付描述不可超过500位")
private String description;
/** 是否开启分账 */
@Schema(description = "是否开启分账")
private Boolean allocation;
/** 自动分账 */
@Schema(description = "自动分账")
private Boolean autoAllocation;
/** 过期时间 */
@Schema(description = "过期时间")
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
private LocalDateTime expiredTime;
/**
* 支付通道编码
* @see ChannelEnum#getCode()
*/
@Schema(description = "支付通道编码")
@NotBlank(message = "支付通道编码不可为空")
@Size(max = 20, message = "支付通道编码不可超过20位")
private String channel;
/**
* 支付方式编码
*/
@Schema(description = "支付方式编码")
@NotBlank(message = "支付方式不可为空")
@Size(max = 20, message = "支付方式编码不可超过20位")
private String method;
/** 支付金额 */
@Schema(description = "支付金额")
@NotNull(message = "支付金额不可为空")
@DecimalMin(value = "0.01", message = "支付金额不可小于0.01元")
@Digits(integer = 8, fraction = 2, message = "支付金额精度到分, 且要小于一亿元")
private BigDecimal amount;
/**
* 支付扩展参数
*/
@Schema(description = "支付扩展参数")
@Size(max = 2048, message = "支付扩展参数不可超过2048位")
private String extraParam;
/** 商户扩展参数,回调时会原样返回 */
@Schema(description = "商户扩展参数")
@Size(max = 500, message = "商户扩展参数不可超过500位")
private String attach;
/** 同步跳转地址, 支付完毕后用户浏览器返回到该地址, 不传输跳转到默认地址 */
@Schema(description = "同步通知URL")
@Size(max = 200, message = "同步通知URL不可超过200位")
private String returnUrl;
/** 用户付款中途退出返回商户网站的地址(部分支付场景中可用) */
@Schema(description = "退出地址")
@Size(max = 200, message = "退出地址不可超过200位")
private String quitUrl;
/** 异步通知地址 */
@Schema(description = "异步通知地址")
@Size(max = 200, message = "异步通知地址不可超过200位")
private String notifyUrl;
}

View File

@@ -0,0 +1,29 @@
package org.dromara.daxpay.core.param.trade.pay;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 支付状态同步参数
* @author xxm
* @since 2023/12/17
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title = "支付订单信息同步参数")
public class PaySyncParam extends PaymentCommonParam {
/** 订单号 */
@Schema(description = "订单号")
@Size(max = 32, message = "支付订单号不可超过32位")
private String orderNo;
/** 商户订单号 */
@Schema(description = "商户订单号")
@Size(max = 100, message = "商户支付订单号不可超过100位")
private String bizOrderNo;
}

View File

@@ -0,0 +1,27 @@
package org.dromara.daxpay.core.param.trade.pay;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 支付单查询参数
* @author xxm
* @since 2024/1/16
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title = "支付单查询参数")
public class QueryPayParam extends PaymentCommonParam {
@Schema(description = "订单号")
@Size(max = 32, message = "支付订单号不可超过32位")
private String orderNo;
@Schema(description = "商户订单号")
@Size(max = 100, message = "商户支付订单号不可超过100位")
private String bizOrderNoeNo;
}

View File

@@ -0,0 +1,29 @@
package org.dromara.daxpay.core.param.trade.refund;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 查询退款订单参数类
* @author xxm
* @since 2024/1/16
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title = "查询退款订单参数类")
public class QueryRefundParam extends PaymentCommonParam {
/** 退款号 */
@Schema(description = "退款号")
@Size(max = 100, message = "退款号不可超过100位")
private String refundNo;
/** 商户退款号 */
@Schema(description = "商户退款号")
@Size(max = 100, message = "商户退款号不可超过100位")
private String bizRefundNo;
}

View File

@@ -0,0 +1,73 @@
package org.dromara.daxpay.core.param.trade.refund;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import java.math.BigDecimal;
/**
* 退款参数,适用于组合支付的订单退款操作中,
* @author xxm
* @since 2023/12/18
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title = "退款参数")
public class RefundParam extends PaymentCommonParam {
/**
* 商户退款号,不可以为空,同样的商户退款号多次请求,同一退款单号多次请求只退一笔
*/
@Schema(description = "商户退款号")
@NotBlank(message = "商户退款号不可为空")
@Size(max = 100, message = "商户退款号不可超过100位")
private String bizRefundNo;
/**
* 支付订单号,与商户订单号至少要传输一个,同时传输以订单号为准
*/
@Schema(description = "订单号")
@Size(max = 32, message = "支付订单号不可超过32位")
private String orderNo;
/**
* 商户支付订单号,与订单号至少要传输一个,同时传输以订单号为准
*/
@Schema(description = "商户订单号")
@Size(max = 100, message = "商户订单号不可超过100位")
private String bizOrderNo;
/** 退款金额 */
@Schema(description = "退款金额")
@NotNull(message = "退款金额不可为空")
@DecimalMin(value = "0.01", message = "支付金额不可小于0.01元")
@Digits(integer = 8, fraction = 2, message = "支付金额精度到分, 且要小于一亿元")
private BigDecimal amount;
/**
* 预留的退款扩展参数
*/
@Schema(description = "退款扩展参数")
@Size(max = 2048, message = "退款扩展参数不可超过2048位")
private String extraParam;
/** 退款原因 */
@Schema(description = "退款原因")
@Size(max = 150, message = "退款原因不可超过150位")
private String reason;
/** 商户扩展参数,回调时会原样返回 */
@Schema(description = "商户扩展参数")
@Size(max = 500, message = "商户扩展参数不可超过500位")
private String attach;
/** 异步通知地址 */
@Schema(description = "异步通知地址")
@Size(max = 200, message = "异步通知地址不可超过200位")
private String notifyUrl;
}

View File

@@ -0,0 +1,30 @@
package org.dromara.daxpay.core.param.trade.refund;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 退款状态同步参数
* @author xxm
* @since 2023/12/17
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title = "退款状态同步参数")
public class RefundSyncParam extends PaymentCommonParam {
/** 退款号 */
@Schema(description = "退款号")
@Size(max = 100, message = "退款号不可超过100位")
private String refundNo;
/** 商户退款号 */
@Schema(description = "商户退款号")
@Size(max = 100, message = "商户退款号不可超过100位")
private String bizRefundNo;
}

View File

@@ -0,0 +1,31 @@
package org.dromara.daxpay.core.param.trade.transfer;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 转账订单查询参数
* @author xxm
* @since 2024/5/27
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "转账订单查询参数")
public class QueryTransferParam extends PaymentCommonParam {
/** 商户转账号 */
@Size(max = 100, message = "商户转账号不可超过100位")
@Schema(description = "商户转账号")
private String bizTransferNo;
/** 转账号 */
@Size(max = 32, message = "转账号不可超过100位")
@Schema(description = "转账号")
private String transferNo;
}

View File

@@ -0,0 +1,95 @@
package org.dromara.daxpay.core.param.trade.transfer;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.dromara.daxpay.core.enums.ChannelEnum;
import org.dromara.daxpay.core.enums.TransferPayeeTypeEnum;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import java.math.BigDecimal;
/**
* 转账参数
* @author xxm
* @since 2024/5/26
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "转账参数")
public class TransferParam extends PaymentCommonParam {
/** 商户转账号 */
@NotNull(message = "商户转账号必填")
@Size(max = 100, message = "商户转账号不可超过100位")
@Schema(description = "商户转账号")
private String bizTransferNo;
/**
* 支付通道
* @see ChannelEnum
*/
@NotNull(message = "支付通道必填")
@Size(max = 20, message = "支付通道不可超过20位")
@Schema(description = "支付通道")
private String channel;
/** 转账金额 */
@Schema(description = "转账金额")
@NotNull(message = "转账金额必填")
@DecimalMin(value = "0.01", message = "支付金额不可小于0.01元")
@Digits(integer = 8, fraction = 2, message = "支付金额精度到分, 且要小于一亿元")
private BigDecimal amount;
/** 标题 */
@Size(max = 100, message = "标题不可超过100位")
@Schema(description = "标题")
private String title;
/** 转账原因/备注 */
@Size(max = 150, message = "转账原因/备注不可超过150位")
@Schema(description = "转账原因/备注")
private String reason;
/**
* 收款人账号类型
* @see TransferPayeeTypeEnum
*/
@NotBlank(message = "收款人账号类型必填")
@Size(max = 20, message = "收款人账号类型不可超过20位")
@Schema(description = "收款人账号类型")
private String payeeType;
/** 收款人账号 */
@NotBlank(message = "收款人账号必填")
@Size(max = 100, message = "收款人账号不可超过100位")
@Schema(description = "收款人账号")
private String payeeAccount;
/** 收款人姓名 */
@Size(max = 50, message = "收款人姓名不可超过50位")
@Schema(description = "收款人姓名")
private String payeeName;
/**
* 预留的转账扩展参数
*/
@Schema(description = "转账扩展参数")
@Size(max = 2048, message = "退转账扩展参数不可超过2048位")
private String extraParam;
/** 商户扩展参数,回调时会原样返回 */
@Size(max = 500, message = "商户扩展参数,回调时会原样返回不可超过500位")
@Schema(description = "商户扩展参数,回调时会原样返回")
private String attach;
/** 回调通知地址 */
@Size(max = 200, message = "回调通知地址不可超过200位")
@Schema(description = "回调通知地址")
private String notifyUrl;
}

View File

@@ -0,0 +1,29 @@
package org.dromara.daxpay.core.param.trade.transfer;
import org.dromara.daxpay.core.param.PaymentCommonParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 转账状态同步参数
* @author xxm
* @since 2024/6/17
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title = "转账状态同步参数")
public class TransferSyncParam extends PaymentCommonParam {
/** 商户转账号 */
@Size(max = 100, message = "商户转账号不可超过100位")
@Schema(description = "商户转账号")
private String bizTransferNo;
/** 转账号 */
@Size(max = 32, message = "转账号不可超过100位")
@Schema(description = "转账号")
private String transferNo;
}

View File

@@ -0,0 +1,56 @@
package org.dromara.daxpay.core.result;
import cn.bootx.platform.core.code.CommonCode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.slf4j.MDC;
import java.time.LocalDateTime;
/**
* 支付通用响应参数
* @author xxm
* @since 2023/12/17
*/
@Getter
@Setter
@ToString(callSuper = true)
@Schema(title = "支付通用响应参数")
public class DaxResult<T>{
/** 状态码 */
@Schema(description = "状态码")
private int code;
/** 提示信息 */
@Schema(description = "提示信息")
private String msg;
/** 业务内容 */
@Schema(description = "业务内容")
private T data;
/** 签名 */
@Schema(description = "签名")
private String sign;
@Schema(description = "响应时间")
private LocalDateTime resTime = LocalDateTime.now();
/** 追踪ID */
@Schema(description = "追踪ID")
private String traceId = MDC.get(CommonCode.TRACE_ID);
public DaxResult(int successCode, T data, String msg) {
this.code = successCode;
this.data = data;
this.msg = msg;
}
public DaxResult(int successCode, String msg) {
this.code = successCode;
this.msg = msg;
}
}

View File

@@ -0,0 +1,23 @@
package org.dromara.daxpay.core.result;
import cn.bootx.platform.core.result.BaseResult;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 商户应用基础返回结果
* @author xxm
* @since 2024/7/20
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "商户应用基础返回结果")
public class MchAppResult extends BaseResult {
@Schema(description = "应用AppId")
private String appId;
}

View File

@@ -0,0 +1,38 @@
package org.dromara.daxpay.core.result.assist;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 认证结果
* @author xxm
* @since 2024/9/24
*/
@Data
@Accessors(chain = true)
@Schema(title = "认证结果")
public class AuthResult {
@Schema(description = "OpenId")
private String openId;
/**
* 支付宝存量商户部分返回的是用户ID
*/
@Schema(description = "用户ID")
private String userId;
/**
* 微信会返回accessToken用于获取用户信息
*/
@Schema(description = "AccessToken")
private String accessToken;
/**
* 状态
* @see org.dromara.daxpay.core.enums.ChannelAuthStatusEnum
*/
@Schema(description = "状态")
private String status;
}

View File

@@ -0,0 +1,25 @@
package org.dromara.daxpay.core.result.assist;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 获取OpenId授权链接和查询标识返回类
* @author xxm
* @since 2024/6/15
*/
@Data
@Accessors(chain = true)
@Schema(title = "授权链接和查询标识返回类")
public class AuthUrlResult {
/** 授权访问链接 */
@Schema(description = "授权访问链接")
private String authUrl;
/** 查询标识码, 用于查询是否获取到了OpenId */
@Schema(description = "查询标识码")
private String queryCode;
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.result.assist;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 微信OpenId查询结果
* @author xxm
* @since 2024/6/15
*/
@Data
@Accessors(chain = true)
@Schema(title = "微信OpenId查询结果")
public class OpenIdResult {
@Schema(description = "OpenId")
private String openId;
}

View File

@@ -0,0 +1,19 @@
package org.dromara.daxpay.core.result.reconcile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 对账单文件下载链接
* @author xxm
* @since 2024/8/21
*/
@Data
@Accessors(chain = true)
@Schema(title = "对账单文件下载链接")
public class ReconcileDownResult {
@Schema(description = "文件下载链接")
private String fileUrl;
}

View File

@@ -0,0 +1,114 @@
package org.dromara.daxpay.core.result.trade.pay;
import org.dromara.daxpay.core.enums.ChannelEnum;
import org.dromara.daxpay.core.enums.PayAllocStatusEnum;
import org.dromara.daxpay.core.enums.PayRefundStatusEnum;
import org.dromara.daxpay.core.enums.PayStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 支付订单
* @author xxm
* @since 2021/2/25
*/
@Data
@Accessors(chain = true)
@Schema(title = "支付订单")
public class PayOrderResult {
/** 商户订单号 */
@Schema(description = "商户订单号")
private String bizOrderNo;
@Schema(description = "支付订单号")
private String orderNo;
/**
* 通道系统交易号
*/
@Schema(description = "通道支付订单号")
private String outOrderNo;
/** 标题 */
@Schema(description = "标题")
private String title;
/** 描述 */
@Schema(description = "描述")
private String description;
/** 是否支持分账 */
@Schema(description = "是否需要分账")
private Boolean allocation;
/** 是否开启自动分账, 不传输为不开启 */
@Schema(description = "是否开启自动分账")
private Boolean autoAllocation;
/**
* 支付通道
* @see ChannelEnum
*/
@Schema(description = "异步支付通道")
private String channel;
/**
* 支付方式
*/
@Schema(description = "支付方式")
private String method;
/** 金额 */
@Schema(description = "金额")
private BigDecimal amount;
/** 可退款余额 */
@Schema(description = "可退款余额")
private BigDecimal refundableBalance;
/**
* 支付状态
* @see PayStatusEnum
*/
@Schema(description = "支付状态")
private String status;
/**
* 退款状态
* @see PayRefundStatusEnum
*/
@Schema(description = "退款状态")
private String refundStatus;
/**
* 分账状态
* @see PayAllocStatusEnum
*/
@Schema(description = "分账状态")
private String allocStatus;
/** 支付时间 */
@Schema(description = "支付时间")
private LocalDateTime payTime;
/** 关闭时间 */
@Schema(description = "关闭时间")
private LocalDateTime closeTime;
/** 过期时间 */
@Schema(description = "过期时间")
private LocalDateTime expiredTime;
/** 商户扩展参数,回调时会原样返回 */
@Schema(description = "商户扩展参数")
private String attach;
/** 错误信息 */
@Schema(description = "错误信息")
private String errorMsg;
}

View File

@@ -0,0 +1,32 @@
package org.dromara.daxpay.core.result.trade.pay;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 统一支付响应参数
* @author xxm
* @since 2024/6/27
*/
@Data
@Accessors(chain = true)
@Schema(title = "统一支付响应参数")
public class PayResult {
/** 商户订单号 */
@Schema(description = "商户订单号")
private String bizOrderNo;
/** 订单号 */
@Schema(description = "订单号")
private String orderNo;
/** 支付状态 */
@Schema(description = "支付状态")
private String status;
/** 支付参数体(通常用于发起支付的参数) */
@Schema(description = "支付参数体")
private String payBody;
}

View File

@@ -0,0 +1,31 @@
package org.dromara.daxpay.core.result.trade.pay;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 支付同步结果
* @author xxm
* @since 2023/12/27
*/
@Data
@Accessors(chain = true)
@Schema(title = "支付同步结果")
public class PaySyncResult {
/**
* 退款订单同步后的状态状态
* @see org.dromara.daxpay.core.enums.PayStatusEnum
*/
@Schema(description = "同步状态")
private String orderStatus;
/**
* 是否触发了调整
*/
@Schema(description = "是否触发了调整")
private boolean adjust;
}

View File

@@ -0,0 +1,99 @@
package org.dromara.daxpay.core.result.trade.refund;
import org.dromara.daxpay.core.enums.ChannelEnum;
import org.dromara.daxpay.core.enums.RefundStatusEnum;
import org.dromara.daxpay.core.result.MchAppResult;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 退款订单数据
* @author xxm
* @since 2024/1/16
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "退款订单数据")
public class RefundOrderResult extends MchAppResult {
/** 支付订单ID */
@Schema(description = "支付订单ID")
private Long orderId;
/** 支付订单号 */
@Schema(description = "支付订单号")
private String orderNo;
/** 通道支付订单号 */
@Schema(description = "通道支付订单号")
private String outOrderNo;
/** 商户支付订单号 */
@Schema(description = "商户支付订单号")
private String bizOrderNo;
/** 支付标题 */
@Schema(description = "支付标题")
private String title;
/** 退款号 */
@Schema(description = "退款号")
private String refundNo;
/** 商户退款号 */
@Schema(description = "商户退款号")
private String bizRefundNo;
/** 通道退款交易号 */
@Schema(description = "通道退款交易号")
private String outRefundNo;
/**
* 退款通道
* @see ChannelEnum
*/
@Schema(description = "支付通道")
private String channel;
/** 订单金额 */
@Schema(description = "订单金额")
private Integer orderAmount;
/** 退款金额 */
@Schema(description = "退款金额")
private BigDecimal amount;
/** 退款原因 */
@Schema(description = "退款原因")
private String reason;
/** 退款结束时间 */
@Schema(description = "退款结束时间")
private LocalDateTime finishTime;
/**
* 退款状态
* @see RefundStatusEnum
*/
@Schema(description = "退款状态")
private String status;
/** 商户扩展参数,回调时会原样返回, 以最后一次为准 */
@Schema(description = "商户扩展参数,回调时会原样返回, 以最后一次为准")
private String attach;
/** 终端ip */
@Schema(description = "终端ip")
private String clientIp;
/** 错误信息 */
@Schema(description = "错误信息")
private String errorMsg;
}

View File

@@ -0,0 +1,31 @@
package org.dromara.daxpay.core.result.trade.refund;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 退款响应参数
* @author xxm
* @since 2023/12/18
*/
@Data
@Accessors(chain = true)
@Schema(title = "退款响应参数")
public class RefundResult {
/** 退款号 */
@Schema(description = "退款号")
private String refundNo;
/** 商户退款号 */
@Schema(description = "商户退款号")
private String bizRefundNo;
/** 退款状态 */
@Schema(description = "退款状态")
private String status;
@Schema(description = "错误提示")
private String errorMsg;
}

View File

@@ -0,0 +1,31 @@
package org.dromara.daxpay.core.result.trade.refund;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 退款同步结果
* @author xxm
* @since 2023/12/27
*/
@Data
@Accessors(chain = true)
@Schema(title = "退款同步结果")
public class RefundSyncResult{
/**
* 退款订单同步后的状态状态
* @see org.dromara.daxpay.core.enums.RefundStatusEnum
*/
@Schema(description = "同步状态")
private String orderStatus;
/**
* 是否触发了调整
*/
@Schema(description = "是否触发了调整")
private boolean adjust;
}

View File

@@ -0,0 +1,98 @@
package org.dromara.daxpay.core.result.trade.transfer;
import org.dromara.daxpay.core.enums.ChannelEnum;
import org.dromara.daxpay.core.enums.TransferPayeeTypeEnum;
import org.dromara.daxpay.core.enums.TransferStatusEnum;
import org.dromara.daxpay.core.result.MchAppResult;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 转账订单
* @author xxm
* @since 2024/7/20
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "转账订单")
public class TransferOrderResult extends MchAppResult {
/** 商户转账号 */
@Schema(description = "商户转账号")
private String bizTransferNo;
/** 转账号 */
@Schema(description = "转账号")
private String transferNo;
/** 通道转账号 */
@Schema(description = "通道转账号")
private String outTransferNo;
/**
* 支付通道
* @see ChannelEnum
*/
@Schema(description = "支付通道")
private String channel;
/** 转账金额 */
@Schema(description = "转账金额")
private BigDecimal amount;
/** 标题 */
@Schema(description = "标题")
private String title;
/** 转账原因/备注 */
@Schema(description = "转账原因/备注")
private String reason;
/**
* 收款人类型
* @see TransferPayeeTypeEnum
*/
@Schema(description = "收款人类型")
private String payeeType;
/** 收款人账号 */
@Schema(description = "收款人账号")
private String payeeAccount;
/** 收款人姓名 */
@Schema(description = "收款人姓名")
private String payeeName;
/**
* 状态
* @see TransferStatusEnum
*/
@Schema(description = "状态")
private String status;
/** 完成时间 */
@Schema(description = "完成时间")
private LocalDateTime finishTime;
/** 商户扩展参数,回调时会原样返回, 以最后一次为准 */
@Schema(description = "商户扩展参数")
private String attach;
/** 终端ip */
@Schema(description = "终端ip")
private String clientIp;
/**
* 错误原因
*/
@Schema(description = "错误原因")
private String errorMsg;
}

View File

@@ -0,0 +1,38 @@
package org.dromara.daxpay.core.result.trade.transfer;
import org.dromara.daxpay.core.enums.TransferStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 转账结果
* @author xxm
* @since 2024/6/6
*/
@Data
@Accessors(chain = true)
@Schema(title = "转账结果")
public class TransferResult {
/** 商户转账号 */
@Schema(description = "商户转账号")
private String bizTransferNo;
/** 转账号 */
@Schema(description = "转账号")
private String transferNo;
/**
* 状态
* @see TransferStatusEnum
*/
@Schema(description = "状态")
private String status;
/**
* 提示信息
*/
@Schema(description = "提示信息")
private String errorMsg;
}

View File

@@ -0,0 +1,29 @@
package org.dromara.daxpay.core.result.trade.transfer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 转账同步结果
* @author xxm
* @since 2024/6/17
*/
@Data
@Accessors(chain = true)
@Schema(title = "转账同步结果")
public class TransferSyncResult {
/**
* 转账状态
* @see org.dromara.daxpay.core.enums.TransferStatusEnum
*/
@Schema(description = "转账状态")
private String orderStatus;
/**
* 是否触发了调整
*/
@Schema(description = "是否触发了调整")
private boolean adjust;
}

View File

@@ -0,0 +1,24 @@
package org.dromara.daxpay.core.util;
import org.dromara.daxpay.core.result.DaxResult;
import lombok.experimental.UtilityClass;
import static cn.bootx.platform.core.code.CommonCode.SUCCESS_CODE;
/**
* 支付相应参数构造工具类
* @author xxm
* @since 2023/12/17
*/
@UtilityClass
public class DaxRes {
private final static String SUCCESS = "success";
public <T> DaxResult<T> ok(T data) {
return new DaxResult<>(SUCCESS_CODE, data, SUCCESS);
}
public static <T> DaxResult<T> ok() {
return new DaxResult<>(SUCCESS_CODE, SUCCESS);
}
}

View File

@@ -0,0 +1,245 @@
package org.dromara.daxpay.core.util;
import cn.bootx.platform.core.util.JsonUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.digest.HmacAlgorithm;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* 如果需要进行签名,
* 1. 参数名ASCII码从小到大排序字典序
* 2. 如果参数的值为空不参与签名
* 3. 参数名不区分大小写
* 4. 嵌套对象转换成先转换成MAP再序列化为字符串
* 5. 支持两层嵌套, 更多层级嵌套未测试, 可能会导致不可预知的问题
*/
@UtilityClass
public class PaySignUtil {
private final String FIELD_SIGN = "sign";
/**
* 将参数转换为map对象. 使用ChatGPT生成
* 1. 参数名ASCII码从小到大排序字典序
* 2. 如果参数的值为空不参与签名;
* 3. 参数名不区分大小写;
*/
public Map<String, String> toMap(Object object) {
Map<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
toMap(object, map);
return map;
}
/**
* 将参数转换为map对象. 使用ChatGPT生成, 仅局限于对支付相关参数和返回值进行签名
*/
@SuppressWarnings({"unchecked", "rawtypes"})
@SneakyThrows
private void toMap(Object object, Map<String, String> map) {
Class<?> clazz = object.getClass();
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String fieldName = field.getName();
Object fieldValue = field.get(object);
if (fieldValue != null) {
// 基础类型及包装类 和 字符串类型
if (ClassUtil.isBasicType(field.getType())|| field.getType().equals(String.class)) {
String fieldValueString = String.valueOf(fieldValue);
map.put(fieldName, fieldValueString);
}
// java8时间类型 转为 yyyy-MM-dd HH:mm:ss 格式
else if (field.getType().equals(LocalDateTime.class)) {
LocalDateTime localDateTime = (LocalDateTime) fieldValue;
String datetime = LocalDateTimeUtil.format(localDateTime, DatePattern.NORM_DATETIME_PATTERN);
map.put(fieldName, datetime);
}
// map类型
else if (Map.class.isAssignableFrom(field.getType())) {
Map<String, String> m = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
m.putAll((Map) fieldValue);
map.put(fieldName, JsonUtil.toJsonStr(m));
}
// BigDecimal类型
else if (field.getType().equals(BigDecimal.class)) {
BigDecimal bigDecimal = (BigDecimal) fieldValue;
String decimalString = bigDecimal.toString();
map.put(fieldName, decimalString);
}
// 集合类型
else if (Collection.class.isAssignableFrom(field.getType())) {
Collection<?> collection = (Collection<?>) fieldValue;
if (!collection.isEmpty()) {
List<Map<String, String>> maps = collection.stream()
.filter(Objects::nonNull)
.map(item -> {
Map<String, String> nestedMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
toMap(item, nestedMap);
return nestedMap;
})
.collect(Collectors.toList());
map.put(fieldName, JsonUtil.toJsonStr(maps));
}
// 其他类型直接转换为json
} else {
Map<String, String> nestedMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
toMap(fieldValue, nestedMap);
String nestedJson = JsonUtil.toJsonStr(fieldValue);
map.put(fieldName, nestedJson);
}
}
}
clazz = clazz.getSuperclass();
}
}
/**
* 把所有元素排序, 并拼接成字符, 用于签名, 同时会过滤掉 " 和 \ 字符
*/
public static String createLinkString(Map<String, String> params) {
String connStr = "&";
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
StringBuilder content = new StringBuilder();
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
// 拼接时,不包括最后一个&字符
if (i == keys.size() - 1) {
content.append(key)
.append("=")
.append(value);
} else {
content.append(key)
.append("=")
.append(value)
.append(connStr);
}
}
String s = content.toString();
s = StrUtil.replace(s,"\\","");
s = StrUtil.replace(s,"\"","");
return s;
}
/**
* 生成16进制 MD5 字符串
*
* @param data 数据
* @return MD5 字符串
*/
public String md5(String data) {
return SecureUtil.md5(data);
}
/**
* 生成16进制的 sha256 字符串
*
* @param data 数据
* @param signKey 密钥
* @return sha256 字符串
*/
public String hmacSha256(String data, String signKey) {
return SecureUtil.hmac(HmacAlgorithm.HmacSHA256, signKey).digestHex(data);
}
/**
* 生成16进制 sm3 字符串
*
* @param data 数据
* @return SM3方式进行签名 字符串
*/
public String sm3(String data) {
return SmUtil.sm3(data);
}
/**
* 生成待签名字符串
* @param object 待签名对象
* @param signKey 签名Key
* @return 待签名字符串
*/
public String signString(Object object, String signKey){
// 签名
Map<String, String> map = toMap(object);
// 生成签名前先去除sign参数
map.remove(FIELD_SIGN);
// 创建待签名字符串
String data = createLinkString(map);
// 将签名key追加到字符串最后
return data + "&key=" + signKey;
}
/**
* md5方式进行签名
*
* @return 签名值
*/
public String md5Sign(Object object, String signKey){
String data = signString(object, signKey);
return md5(data);
}
/**
* hmacSha256方式进行签名
*
* @return 签名值
*/
public String hmacSha256Sign(Object object, String signKey){
String data = signString(object, signKey);
return hmacSha256(data, signKey);
}
/**
* sm3方式进行签名
*
* @return 签名值
*/
public String sm3Sign(Object object, String signKey){
String data = signString(object, signKey);
return sm3(data);
}
/**
* MD5签名验证
*/
public boolean verifyMd5Sign(Object object, String signKey, String sign){
String md5Sign = md5Sign(object, signKey);
return md5Sign.equals(sign);
}
/**
* hmacSha256签名验证
*/
public boolean verifyHmacSha256Sign(Object object, String signKey, String sign){
String hmacSha256Sign = hmacSha256Sign(object, signKey);
return hmacSha256Sign.equals(sign);
}
/**
* SM3签名验证
*/
public boolean verifySm3Sign(Object object, String signKey, String sign){
String sm3Sign = sm3Sign(object, signKey);
return sm3Sign.equals(sign);
}
}

View File

@@ -0,0 +1,83 @@
package org.dromara.daxpay.core.util;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.experimental.UtilityClass;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
/**
* 支付工具类
* @author xxm
* @since 2023/12/24
*/
@UtilityClass
public class PayUtil {
private static final BigDecimal HUNDRED = new BigDecimal(100);
/**
* 获取云闪付的过期时间 yyyyMMddHHmmss
*/
@Deprecated
public String getUnionExpiredTime(LocalDateTime dateTime) {
return LocalDateTimeUtil.format(dateTime, DatePattern.PURE_DATETIME_PATTERN);
}
/**
* 获取支付单的超时时间
*/
public LocalDateTime getPaymentExpiredTime(Integer minute) {
return LocalDateTimeUtil.offset(LocalDateTime.now(), minute, ChronoUnit.MINUTES);
}
/**
* 元转分
* @param amount 元的金额
* @return 分的金额
*/
public int convertCentAmount(BigDecimal amount) {
return amount.multiply(HUNDRED).setScale(0, RoundingMode.HALF_UP).intValue();
}
/**
* 分转元,保留两位小数
*
* @param amount 元的金额
* @return 元的金额 两位小数
*/
public BigDecimal conversionAmount(int amount) {
return BigDecimal.valueOf(amount).divide(HUNDRED,2, RoundingMode.HALF_UP);
}
/**
* 保留两位小数
*/
public BigDecimal toDecimal(BigDecimal amount) {
return amount.setScale(2, RoundingMode.HALF_UP);
}
/**
* 获取请求参数
*/
public Map<String, String> toMap(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; ++i) {
valueStr = i == values.length - 1 ? valueStr + values[i] : valueStr + values[i] + ",";
}
params.put(name, valueStr);
}
return params;
}
}

View File

@@ -0,0 +1,95 @@
package org.dromara.daxpay.core.util;
import cn.hutool.core.date.DatePattern;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicLong;
/**
* 各类型订单号生成工具类
*
* 前缀(5)+业务类型(1)+机器码(2)+日期(14)+流水号(6)
*
* @author yxc
* @since 2024/4/15
*/
@Slf4j
public class TradeNoGenerateUtil {
private static final AtomicLong ATOMIC_LONG = new AtomicLong();
private final static long ORDER_MAX_LIMIT = 999999L;
/** 机器号 两位 */
@Setter
private static String machineNo;
/** 环境前缀 最长五位 */
@Setter
private static String env;
/**
* 生成支付订单号
*/
public static String pay() {
StringBuilder orderNo = new StringBuilder();
String dateStr = LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER);
long id = ATOMIC_LONG.incrementAndGet();
orderNo.append(env).append("P").append(dateStr).append(machineNo).append(String.format("%06d", Math.abs(id) % ORDER_MAX_LIMIT));
return orderNo.toString();
}
/**
* 生成退款订单号
*/
public static String refund() {
StringBuilder orderNo = new StringBuilder();
String dateStr = LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER);
long id = ATOMIC_LONG.incrementAndGet();
orderNo.append(env).append("R").append(dateStr).append(machineNo).append(String.format("%06d", Math.abs(id) % ORDER_MAX_LIMIT));
return orderNo.toString();
}
/**
* 生成转账订单号
*/
public static String transfer() {
StringBuilder orderNo = new StringBuilder();
String dateStr = LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER);
long id = ATOMIC_LONG.incrementAndGet();
orderNo.append(env).append("T").append(dateStr).append(machineNo).append(String.format("%06d", Math.abs(id) % ORDER_MAX_LIMIT));
return orderNo.toString();
}
/**
* 生成分账订单号
*/
public static String allocation() {
StringBuilder orderNo = new StringBuilder();
String dateStr = LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER);
long id = ATOMIC_LONG.incrementAndGet();
orderNo.append(env).append("A").append(dateStr).append(machineNo).append(String.format("%06d", Math.abs(id) % ORDER_MAX_LIMIT));
return orderNo.toString();
}
/**
* 生成对账订单号
*/
public static String reconciliation() {
StringBuilder orderNo = new StringBuilder();
String dateStr = LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER);
long id = ATOMIC_LONG.incrementAndGet();
orderNo.append(env).append("C").append(dateStr).append(machineNo).append(String.format("%06d", Math.abs(id) % ORDER_MAX_LIMIT));
return orderNo.toString();
}
/**
* 生成修复单号
*/
public static String repair() {
StringBuilder orderNo = new StringBuilder();
String dateStr = LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER);
long id = ATOMIC_LONG.incrementAndGet();
orderNo.append(env).append("X").append(dateStr).append(machineNo).append(String.format("%06d", Math.abs(id) % ORDER_MAX_LIMIT));
return orderNo.toString();
}
}