fix 退款修复时完成时间为空, 通道退款订单可退余额不正确问题修复

This commit is contained in:
bootx
2024-02-08 00:55:25 +08:00
parent 28b96cf822
commit 5ea91081eb
53 changed files with 924 additions and 129 deletions

View File

@@ -26,9 +26,12 @@
## 🍒文档和演示地址
### 文档地址
在 [Bootx开源文档站](https://bootx.gitee.io/) 下的`支付网关(DaxPay)`模块下可以进行差异相关文档,具体链接地址如下
[快速指南](https://bootx.gitee.io/daxpay/guides/overview/项目介绍.html)、
[支付对接](https://bootx.gitee.io/daxpay/gateway/overview/接口清单.html)
在 [Bootx开源文档站](https://bootx.gitee.io/) 下的**支付网关(DaxPay)**模块下可以进行查阅相关文档,具体链接地址如下
[快速指南](https://bootx.gitee.io/daxpay/guides/overview/项目介绍.html)
[支付对接](https://bootx.gitee.io/daxpay/gateway/overview/接口清单.html)
[平台配置](https://bootx.gitee.io/daxpay/admin/config/平台配置.html)

View File

@@ -9,9 +9,40 @@
<version>2.0.0</version>
<packaging>jar</packaging>
<!-- 项目信息 -->
<name>bootx-platform</name>
<description>bootx-platform 基于Spring Boot框架打造针对单体式应用进行专门设计提供整套服务模块努力为打造全方位企业级开发解决方案</description>
<url>https://gitee.com/bootx/bootx-platform</url>
<!-- 许可证 -->
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-- 开发者信息 -->
<developers>
<developer>
<name>bootx</name>
<email>xxm1995@outlook.com</email>
<organization>bootx</organization>
<url>https://gitee.com/bootx/bootx-platform</url>
</developer>
</developers>
<scm>
<connection>scm:git:git@gitee.com:bootx/bootx-platform.git</connection>
<developerConnection>scm:git:git@gitee.com:bootx/bootx-platform.git</developerConnection>
<url>https://gitee.com/bootx/bootx-platform</url>
</scm>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<logback-classic.version>1.2.7</logback-classic.version>
<hutool.version>5.8.25</hutool.version>
@@ -29,6 +60,7 @@
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback-classic.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
@@ -44,4 +76,111 @@
</dependency>
</dependencies>
<build>
<plugins>
<!-- 编译插件设置源码以及编译的jdk版本 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<!--打包源码的插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Javadoc 文档生成插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<failOnError>false</failOnError>
<doclint>none</doclint>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
<!--公钥私钥插件,也就是上传需要进行验证用户名和密码过程中需要用到的插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<!--部署插件-->
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.7</version>
<extensions>true</extensions>
<configuration>
<!-- setting.xml中配置仓库平台id -->
<serverId>sonatype</serverId>
<!-- 仓库平台地址 -->
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
<!-- 自动完成在平台上close后的release的操作 -->
<!-- <autoReleaseAfterClose>false</autoReleaseAfterClose>-->
</configuration>
</plugin>
</plugins>
<resources>
<!-- 指定打包资源路径 -->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
<filtering>true</filtering>
</resource>
<!-- java类路径中会被打包的软件 -->
<resource>
<directory>src/main/java</directory>
</resource>
</resources>
</build>
<distributionManagement>
<snapshotRepository>
<id>sonatype</id>
<name>OSS Snapshots Repository</name>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<repository>
<id>sonatype</id>
<name>OSS Staging Repository</name>
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
</project>

View File

@@ -0,0 +1,22 @@
package cn.bootx.platform.daxpay.sdk.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 退款同步状态枚举
* @author xxm
* @since 2024/1/29
*/
@Getter
@AllArgsConstructor
public enum RefundSyncStatusEnum {
SUCCESS("refund_success","退款成功"),
FAIL("refund_fail","退款失败"),
PROGRESS("refund_progress","退款中");
/** 编码 */
private final String code;
/** 名称 */
private final String name;
}

View File

@@ -0,0 +1,20 @@
package cn.bootx.platform.daxpay.sdk.model.divide;
import cn.bootx.platform.daxpay.sdk.net.DaxPayResponseModel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* 分账结果(目前未支持)
* @author xxm
* @since 2024/2/7
*/
@Getter
@Setter
@ToString
public class DivideOrderModel extends DaxPayResponseModel {
/** 分账状态 */
private String status;
}

View File

@@ -0,0 +1,43 @@
package cn.bootx.platform.daxpay.sdk.model.pay;
import cn.bootx.platform.daxpay.sdk.code.PayStatusEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* 支付订单通道响应参数
* @author xxm
* @since 2024/1/16
*/
@Getter
@Setter
@ToString
public class QueryPayChannelOrder {
/** 是否为异步支付通道 */
private boolean async;
/** 支付通道 */
private String channel;
/** 支付方式 */
private String payWay;
/**
* 网关订单号, 用与将记录关联起来
*/
private String gatewayOrderNo;
/**
* 支付状态
* @see PayStatusEnum
*/
private String status;
/** 金额 */
private Integer amount;
/** 可退款金额 */
private Integer refundableBalance;
}

View File

@@ -0,0 +1,66 @@
package cn.bootx.platform.daxpay.sdk.model.pay;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import cn.bootx.platform.daxpay.sdk.code.PayStatusEnum;
import cn.bootx.platform.daxpay.sdk.net.DaxPayResponseModel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
/**
* 支付订单查询响应参数
* @author xxm
* @since 2024/1/16
*/
@Getter
@Setter
@ToString
public class QueryPayOrderModel extends DaxPayResponseModel {
/** 支付ID */
private Long paymentId;
/** 业务号 */
private String businessNo;
/** 标题 */
private String title;
/** 描述 */
private String description;
/** 是否是异步支付 */
private boolean asyncPay;
/** 是否是组合支付 */
private boolean combinationPay;
/**
* 异步支付通道
* @see PayChannelEnum
*/
private String asyncChannel;
/** 金额 */
private Integer amount;
/** 可退款余额 */
private Integer refundableBalance;
/**
* 支付状态
* @see PayStatusEnum
*/
private String status;
/** 支付时间(秒级时间戳) */
private Integer payTime;
/** 过期时间(秒级时间戳) */
private Integer expiredTime;
/** 支付通道列表 */
private List<QueryPayChannelOrder> channels;
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.sdk.model.refund;
import cn.bootx.platform.daxpay.sdk.net.DaxPayResponseModel;
import cn.bootx.platform.daxpay.sdk.code.RefundStatusEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@@ -13,7 +13,7 @@ import lombok.ToString;
@Getter
@Setter
@ToString
public class RefundChannelOrderModel extends DaxPayResponseModel {
public class QueryRefundChannelOrderModel {
/** 通道 */
private String channel;
@@ -24,9 +24,21 @@ public class RefundChannelOrderModel extends DaxPayResponseModel {
/** 异步支付方式 */
private boolean async;
/** 订单金额" */
/** 订单金额 */
private Integer orderAmount;
/** 退款金额 */
private Integer amount;
/** 剩余可退余额 */
private Integer refundableAmount;
/**
* 退款状态
* @see RefundStatusEnum
*/
private String status;
/** 退款完成时间(秒级时间戳) */
private Long refundTime;
}

View File

@@ -18,17 +18,20 @@ import java.util.List;
@Getter
@Setter
@ToString
public class RefundOrderResult extends DaxPayResponseModel {
public class QueryRefundOrderModel extends DaxPayResponseModel {
/** 支付号 */
private Long paymentId;
/** 关联的业务id */
private String businessNo;
/** 退款id */
private Long refundId;
/** 退款号 */
private String refundNo;
/** 关联支付号 */
private Long paymentId;
/** 关联业务号 */
private String businessNo;
/** 标题 */
private String title;
@@ -43,12 +46,6 @@ public class RefundOrderResult extends DaxPayResponseModel {
*/
private String gatewayOrderNo;
/** 通道退款订单 */
private List<RefundChannelOrderModel> channels;
/** 退款终端ip */
private String clientIp;
/** 退款时间 */
private LocalDateTime refundTime;
@@ -57,4 +54,7 @@ public class RefundOrderResult extends DaxPayResponseModel {
* @see RefundStatusEnum
*/
private String status;
/** 通道退款订单 */
private List<QueryRefundChannelOrderModel> channels;
}

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.sdk.model.sync;
import cn.bootx.platform.daxpay.sdk.code.PaySyncStatusEnum;
import cn.bootx.platform.daxpay.sdk.net.DaxPayResponseModel;
import lombok.Getter;
import lombok.Setter;
@@ -16,21 +17,15 @@ import lombok.ToString;
public class PaySyncModel extends DaxPayResponseModel {
/**
* 支付网关同步状态
* 支付网关同步结果
* @see PaySyncStatusEnum
*/
private String gatewayStatus;
/** 是否同步成功 */
private boolean success;
/** 是否进行了修复 */
private boolean repair;
/** 修复号 */
private String repairOrderNo;
/** 失败原因 */
private String errorMsg;
}

View File

@@ -0,0 +1,31 @@
package cn.bootx.platform.daxpay.sdk.model.sync;
import cn.bootx.platform.daxpay.sdk.code.RefundSyncStatusEnum;
import cn.bootx.platform.daxpay.sdk.net.DaxPayResponseModel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* 退款信息同步结果
* @author xxm
* @since 2024/2/7
*/
@Getter
@Setter
@ToString
public class RefundSyncModel extends DaxPayResponseModel {
/**
* 支付网关同步结果
* @see RefundSyncStatusEnum
*/
private String gatewayStatus;
/** 是否进行了修复 */
private boolean repair;
/** 修复号 */
private String repairOrderNo;
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.sdk.net;
import cn.bootx.platform.daxpay.sdk.response.DaxPayResult;
import cn.hutool.core.date.DateUtil;
import lombok.Getter;
import lombok.Setter;
@@ -47,6 +48,6 @@ public abstract class DaxPayRequest<T extends DaxPayResponseModel> {
private String version = "1.0";
/** 请求时间,传输时间戳 */
private Long reqTime;
private Long reqTime = DateUtil.currentSeconds();
}

View File

@@ -0,0 +1,41 @@
package cn.bootx.platform.daxpay.sdk.param.divide;
import cn.bootx.platform.daxpay.sdk.model.divide.DivideOrderModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayRequest;
import cn.bootx.platform.daxpay.sdk.response.DaxPayResult;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
import lombok.Getter;
import lombok.Setter;
/**
* 分账请求接口(目前未支持)
* @author xxm
* @since 2024/2/7
*/
@Getter
@Setter
public class DivideOrderParam extends DaxPayRequest<DivideOrderModel> {
/** 支付ID */
private Long paymentId;
/** 业务号 */
private String businessNo;
/**
* 方法请求路径
*/
@Override
public String path() {
return "/unipay/divide";
}
/**
* 将请求返回结果反序列化为实体类
*/
@Override
public DaxPayResult<DivideOrderModel> toModel(String json) {
return JSONUtil.toBean(json, new TypeReference<DaxPayResult<DivideOrderModel>>() {}, false);
}
}

View File

@@ -0,0 +1,41 @@
package cn.bootx.platform.daxpay.sdk.param.pay;
import cn.bootx.platform.daxpay.sdk.model.pay.QueryPayOrderModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayRequest;
import cn.bootx.platform.daxpay.sdk.response.DaxPayResult;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 支付单查询参数
* @author xxm
* @since 2024/1/16
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class QueryPayParam extends DaxPayRequest<QueryPayOrderModel> {
/** 支付ID */
private Long paymentId;
/** 业务号 */
private String businessNo;
/**
* 方法请求路径
*/
@Override
public String path() {
return "/uni/query/payOrder";
}
/**
* 将请求返回结果反序列化为实体类
*/
@Override
public DaxPayResult<QueryPayOrderModel> toModel(String json) {
return JSONUtil.toBean(json, new TypeReference<DaxPayResult<QueryPayOrderModel>>() {}, false);
}
}

View File

@@ -0,0 +1,41 @@
package cn.bootx.platform.daxpay.sdk.param.refund;
import cn.bootx.platform.daxpay.sdk.model.refund.QueryRefundOrderModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayRequest;
import cn.bootx.platform.daxpay.sdk.response.DaxPayResult;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 查询退款订单参数类
* @author xxm
* @since 2024/1/16
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class QueryRefundParam extends DaxPayRequest<QueryRefundOrderModel> {
/** 退款ID */
private Long refundId;
/** 退款号 */
private String refundNo;
/**
* 方法请求路径
*/
@Override
public String path() {
return "/uni/query/refundOrder";
}
/**
* 将请求返回结果反序列化为实体类
*/
@Override
public DaxPayResult<QueryRefundOrderModel> toModel(String json) {
return JSONUtil.toBean(json, new TypeReference<DaxPayResult<QueryRefundOrderModel>>() {}, false);
}
}

View File

@@ -0,0 +1,44 @@
package cn.bootx.platform.daxpay.sdk.param.sync;
import cn.bootx.platform.daxpay.sdk.model.sync.RefundSyncModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayRequest;
import cn.bootx.platform.daxpay.sdk.response.DaxPayResult;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
import lombok.Getter;
import lombok.Setter;
/**
* 退款同步参数
* @author xxm
* @since 2024/2/7
*/
@Getter
@Setter
public class RefundSyncParam extends DaxPayRequest<RefundSyncModel> {
/**
* 退款IDrefundId和refundNo 必传一个, 同时传输时,以 refundId 为准
*/
private Long refundId;
/**
* 退款号refundId和refundNo 必传一个,同时传输时,以 refundId 为准
*/
private String refundNo;
/**
* 方法请求路径
*/
@Override
public String path() {
return "/unipay/syncRefund";
}
/**
* 将请求返回结果反序列化为实体类
*/
@Override
public DaxPayResult<RefundSyncModel> toModel(String json) {
return JSONUtil.toBean(json, new TypeReference<DaxPayResult<RefundSyncModel>>() {}, false);
}
}

View File

@@ -15,10 +15,10 @@ import java.util.stream.Collectors;
/**
* 如果需要进行签名,
* 1. 参数名ASCII码从小到大排序字典序
* 2. 如果参数的值为空不参与签名
* 3. 参数名不区分大小写
* 4. 嵌套对象转换成先转换成MAP再序列化为字符串
* 5. 支持两层嵌套, 更多层级嵌套未测试, 可能会导致不可预知的问题
* * 2. 如果参数的值为空不参与签名
* * 3. 参数名不区分大小写
* * 4. 嵌套对象转换成先转换成MAP再序列化为字符串
* * 5. 支持两层嵌套, 更多层级嵌套未测试, 可能会导致不可预知的问题
*/
@UtilityClass
public class PaySignUtil {

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.sdk;
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.model.pay.PayCloseModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.sdk;
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.model.sync.PaySyncModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.sdk;
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import cn.bootx.platform.daxpay.sdk.code.PayWayEnum;

View File

@@ -0,0 +1,40 @@
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.model.sync.RefundSyncModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
import cn.bootx.platform.daxpay.sdk.param.sync.RefundSyncParam;
import cn.bootx.platform.daxpay.sdk.response.DaxPayResult;
import org.junit.Before;
import org.junit.Test;
/**
* 退款同步接口
* @author xxm
* @since 2024/2/5
*/
public class RefundOrderSyncTest {
@Before
public void init() {
// 初始化支付配置
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.build();
DaxPayKit.initConfig(config);
}
@Test
public void testPay() {
RefundSyncParam param = new RefundSyncParam();
param.setRefundNo("R0001");
DaxPayResult<RefundSyncModel> execute = DaxPayKit.execute(param);
System.out.println(execute);
System.out.println(execute.getData());
}
}

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.sdk;
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.model.refund.RefundModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.sdk;
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import cn.bootx.platform.daxpay.sdk.code.PayWayEnum;
@@ -17,8 +17,6 @@ import org.junit.Test;
*/
public class SimplePayOrderTest {
@Before
public void init() {
// 初始化支付配置

View File

@@ -0,0 +1,38 @@
package cn.bootx.platform.daxpay.sdk.query;
import cn.bootx.platform.daxpay.sdk.model.pay.QueryPayOrderModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
import cn.bootx.platform.daxpay.sdk.param.pay.QueryPayParam;
import cn.bootx.platform.daxpay.sdk.response.DaxPayResult;
import org.junit.Before;
import org.junit.Test;
/**
* 查询支付订单测试类
* @author xxm
* @since 2024/2/7
*/
public class QueryPayOrderTest {
@Before
public void init() {
// 初始化支付配置
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.build();
DaxPayKit.initConfig(config);
}
@Test
public void testPay() {
QueryPayParam param = new QueryPayParam();
param.setBusinessNo("2");
DaxPayResult<QueryPayOrderModel> execute = DaxPayKit.execute(param);
System.out.println(execute);
System.out.println(execute.getData());
}
}

View File

@@ -0,0 +1,38 @@
package cn.bootx.platform.daxpay.sdk.query;
import cn.bootx.platform.daxpay.sdk.model.refund.QueryRefundOrderModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
import cn.bootx.platform.daxpay.sdk.param.refund.QueryRefundParam;
import cn.bootx.platform.daxpay.sdk.response.DaxPayResult;
import org.junit.Before;
import org.junit.Test;
/**
* 退款订单查询接口
* @author xxm
* @since 2024/2/7
*/
public class QueryRefundOrderTest {
@Before
public void init() {
// 初始化支付配置
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.build();
DaxPayKit.initConfig(config);
}
@Test
public void testPay() {
QueryRefundParam param = new QueryRefundParam();
param.setRefundId(1755263825769361408L);
DaxPayResult<QueryRefundOrderModel> execute = DaxPayKit.execute(param);
System.out.println(execute);
System.out.println(execute.getData());
}
}

View File

@@ -0,0 +1,55 @@
package cn.bootx.platform.daxpay.sdk.util;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import cn.bootx.platform.daxpay.sdk.code.PayWayEnum;
import cn.bootx.platform.daxpay.sdk.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.sdk.param.pay.PayParam;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* 参数签名测试类
* @author xxm
* @since 2024/2/7
*/
@Slf4j
public class PayParamSignTest {
/**
* 签名测试
*/
@Test
public void sign(){
PayParam param = new PayParam();
param.setClientIp("127.0.0.1");
param.setNotNotify(true);
param.setNotReturn(true);
param.setBusinessNo("P0001");
param.setTitle("测试接口支付");
PayChannelParam payChannelParam = new PayChannelParam();
payChannelParam.setChannel(PayChannelEnum.WECHAT.getCode());
payChannelParam.setWay(PayWayEnum.QRCODE.getCode());
payChannelParam.setAmount(1);
List<PayChannelParam> payChannels = Collections.singletonList(payChannelParam);
param.setPayChannels(payChannels);
Map<String, String> map = PaySignUtil.toMap(param);
log.info("转换为有序MAP后的内容: {}",map);
String data = PaySignUtil.createLinkString(map);
log.info("将MAP拼接字符串: {}",data);
String sign = "123456";
data += "&sign="+sign;
data = data.toUpperCase();
log.info("添加秘钥并转换为大写的字符串: {}",data);
log.info("MD5: {}",PaySignUtil.md5(data));
log.info("HmacSHA256: {}",PaySignUtil.hmacSha256(data,sign));
}
}

View File

@@ -15,7 +15,6 @@ import cn.bootx.platform.daxpay.service.dto.order.refund.PayRefundOrderDto;
import cn.bootx.platform.daxpay.service.dto.order.refund.RefundChannelOrderDto;
import cn.bootx.platform.daxpay.service.param.order.PayOrderRefundParam;
import cn.bootx.platform.daxpay.service.param.order.PayRefundOrderQuery;
import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.servlet.ServletUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -74,7 +73,6 @@ public class PayRefundOrderController {
.orElse("未知");
RefundParam refundParam = new RefundParam();
refundParam.setRefundNo(IdUtil.getSnowflakeNextIdStr());
refundParam.setPaymentId(param.getPaymentId());
refundParam.setRefundChannels(param.getRefundChannels());
refundParam.setReqTime(LocalDateTime.now());

View File

@@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
@@ -53,7 +52,7 @@ public abstract class PayCommonParam {
/** 请求时间,时间戳转时间 */
@Schema(description = "请求时间,传输时间戳")
// @NotNull(message = "请求时间必填")
@NotNull(message = "请求时间必填")
@JsonDeserialize(using = TimestampToLocalDateTimeDeserializer.class)
private LocalDateTime reqTime;

View File

@@ -11,7 +11,7 @@ import lombok.Data;
*/
@Data
@Schema(title = "支付订单通道响应参数")
public class PayOrderChannelResult {
public class PayChannelOrderResult {
@Schema(description = "异步支付方式")

View File

@@ -3,6 +3,8 @@ package cn.bootx.platform.daxpay.result.order;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.result.CommonResult;
import cn.bootx.platform.daxpay.serializer.LocalDateTimeToTimestampSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -22,8 +24,12 @@ import java.util.List;
@Schema(title = "支付单响应参数")
public class PayOrderResult extends CommonResult {
/** 关联的业务号 */
@Schema(description = "关联的业务号")
/** 支付ID */
@Schema(description = "支付ID")
private Long paymentId;
/** 业务号 */
@Schema(description = "业务号")
private String businessNo;
/** 标题 */
@@ -66,12 +72,14 @@ public class PayOrderResult extends CommonResult {
/** 支付时间 */
@Schema(description = "支付时间")
@JsonSerialize(using = LocalDateTimeToTimestampSerializer.class)
private LocalDateTime payTime;
/** 过期时间 */
@Schema(description = "过期时间")
@JsonSerialize(using = LocalDateTimeToTimestampSerializer.class)
private LocalDateTime expiredTime;
@Schema(description = "支付通道列表")
private List<PayOrderChannelResult> channels;
private List<PayChannelOrderResult> channels;
}

View File

@@ -1,9 +1,14 @@
package cn.bootx.platform.daxpay.result.order;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.serializer.LocalDateTimeToTimestampSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 支付退款通道明细数据
* @author xxm
@@ -28,4 +33,18 @@ public class RefundChannelOrderResult {
@Schema(description = "退款金额")
private Integer amount;
@Schema(description = "剩余可退余额")
private Integer refundableAmount;
/**
* @see RefundStatusEnum
*/
@Schema(description = "退款状态")
private String status;
@Schema(description = "退款完成时间")
@JsonSerialize(using = LocalDateTimeToTimestampSerializer.class)
private LocalDateTime refundTime;
}

View File

@@ -1,6 +1,8 @@
package cn.bootx.platform.daxpay.result.order;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.serializer.LocalDateTimeToTimestampSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -18,15 +20,19 @@ import java.util.List;
@Accessors(chain = true)
@Schema(title = "退款订单数据")
public class RefundOrderResult {
@Schema(description = "支付号")
private Long paymentId;
@Schema(description = "关联的业务id")
private String businessNo;
@Schema(description = "退款ID")
private Long refundId;
@Schema(description = "退款号")
private String refundNo;
@Schema(description = "关联支付ID")
private Long paymentId;
@Schema(description = "关联支付业务号")
private String businessNo;
@Schema(description = "标题")
private String title;
@@ -42,13 +48,8 @@ public class RefundOrderResult {
@Schema(description = "支付网关订单号")
private String gatewayOrderNo;
@Schema(description = "通道退款订单")
private List<RefundChannelOrderResult> channels;
@Schema(description = "退款终端ip")
private String clientIp;
@Schema(description = "退款时间")
@Schema(description = "退款完成时间")
@JsonSerialize(using = LocalDateTimeToTimestampSerializer.class)
private LocalDateTime refundTime;
/**
@@ -56,4 +57,7 @@ public class RefundOrderResult {
*/
@Schema(description = "退款状态")
private String status;
@Schema(description = "通道退款订单")
private List<RefundChannelOrderResult> channels;
}

View File

@@ -29,16 +29,10 @@ public class SyncResult extends CommonResult {
@Schema(description = "支付网关同步状态")
private String gatewayStatus = FAIL.getCode();
@Schema(description = "是否同步成功")
private boolean success;
@Schema(description = "是否进行了修复")
private boolean repair;
@Schema(description = "修复号")
private String repairOrderNo;
@Schema(description = "失败原因")
private String errorMsg;
}

View File

@@ -0,0 +1,27 @@
package cn.bootx.platform.daxpay.serializer;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* Java8时间类型序列化为时间戳 (10位秒级时间戳)
* @author xxm
* @since 2024/2/7
*/
public class LocalDateTimeToTimestampSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (Objects.nonNull(value)){
long timestamp = LocalDateTimeUtil.timestamp(value)/1000;
gen.writeNumber(timestamp);
} else {
gen.writeNull();
}
}
}

View File

@@ -10,7 +10,7 @@ import java.io.IOException;
import java.time.LocalDateTime;
/**
* 时间戳转LocalDateTime
* 时间戳转LocalDateTime(10位秒级时间戳)
* @author xxm
* @since 2023/12/25
*/
@@ -22,7 +22,7 @@ public class TimestampToLocalDateTimeDeserializer extends JsonDeserializer<Local
if (StrUtil.isBlank(value)){
return null;
}
long timestamp = Long.parseLong(value);
long timestamp = Long.parseLong(value) * 1000;
return LocalDateTimeUtil.of(timestamp);
}
}

View File

@@ -4,14 +4,10 @@ import cn.bootx.platform.common.core.annotation.CountTime;
import cn.bootx.platform.common.core.annotation.IgnoreAuth;
import cn.bootx.platform.daxpay.param.pay.*;
import cn.bootx.platform.daxpay.result.DaxResult;
import cn.bootx.platform.daxpay.result.order.PayOrderResult;
import cn.bootx.platform.daxpay.result.order.RefundOrderResult;
import cn.bootx.platform.daxpay.result.pay.PayResult;
import cn.bootx.platform.daxpay.result.pay.SyncResult;
import cn.bootx.platform.daxpay.result.pay.RefundResult;
import cn.bootx.platform.daxpay.result.pay.SyncResult;
import cn.bootx.platform.daxpay.service.annotation.PaymentApi;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService;
import cn.bootx.platform.daxpay.service.core.order.refund.service.PayRefundOrderQueryService;
import cn.bootx.platform.daxpay.service.core.payment.close.service.PayCloseService;
import cn.bootx.platform.daxpay.service.core.payment.pay.service.PayService;
import cn.bootx.platform.daxpay.service.core.payment.refund.service.PayRefundService;
@@ -21,7 +17,10 @@ import cn.bootx.platform.daxpay.util.DaxRes;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 统一支付接口
@@ -39,8 +38,6 @@ public class UniPayController {
private final PaySyncService paySyncService;
private final PayCloseService payCloseService;
private final PayRefundSyncService payRefundSyncService;
private final PayOrderQueryService PayOrderQueryService;
private final PayRefundOrderQueryService payRefundQueryService;
@CountTime
@@ -99,20 +96,4 @@ public class UniPayController {
public DaxResult<SyncResult> syncRefund(@RequestBody RefundSyncParam param){
return DaxRes.ok(payRefundSyncService.sync(param));
}
@CountTime
@PaymentApi("queryPayOrder")
@Operation(summary = "查询支付订单")
@PostMapping("/queryPayOrder")
public DaxResult<PayOrderResult> queryPayOrder(@RequestBody QueryPayParam param){
return DaxRes.ok(PayOrderQueryService.queryPayOrder(param));
}
@CountTime
@PaymentApi("queryRefundOrder")
@Operation(summary = "查询退款订单")
@PostMapping("/queryRefundOrder")
public DaxResult<RefundOrderResult> queryRefundOrder(@RequestBody QueryRefundParam param){
return DaxRes.ok(payRefundQueryService.queryRefundOrder(param));
}
}

View File

@@ -0,0 +1,49 @@
package cn.bootx.platform.daxpay.gateway.controller;
import cn.bootx.platform.common.core.annotation.CountTime;
import cn.bootx.platform.daxpay.param.pay.QueryPayParam;
import cn.bootx.platform.daxpay.param.pay.QueryRefundParam;
import cn.bootx.platform.daxpay.result.DaxResult;
import cn.bootx.platform.daxpay.result.order.PayOrderResult;
import cn.bootx.platform.daxpay.result.order.RefundOrderResult;
import cn.bootx.platform.daxpay.service.annotation.PaymentApi;
import cn.bootx.platform.daxpay.service.core.order.refund.service.PayRefundOrderQueryService;
import cn.bootx.platform.daxpay.util.DaxRes;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 统一查询接口
* @author xxm
* @since 2024/2/7
*/
@Tag(name = "统一查询接口")
@RestController
@RequestMapping("/uni/query")
@RequiredArgsConstructor
public class UniQueryController {
private final cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService PayOrderQueryService;
private final PayRefundOrderQueryService payRefundQueryService;
@CountTime
@PaymentApi("queryPayOrder")
@Operation(summary = "查询支付订单")
@PostMapping("/payOrder")
public DaxResult<PayOrderResult> queryPayOrder(@RequestBody QueryPayParam param){
return DaxRes.ok(PayOrderQueryService.queryPayOrder(param));
}
@CountTime
@PaymentApi("queryRefundOrder")
@Operation(summary = "查询退款订单")
@PostMapping("/refundOrder")
public DaxResult<RefundOrderResult> queryRefundOrder(@RequestBody QueryRefundParam param){
return DaxRes.ok(payRefundQueryService.queryRefundOrder(param));
}
}

View File

@@ -14,6 +14,8 @@ import java.time.LocalDateTime;
@Accessors(chain = true)
public class RequestLocal {
/** 接口信息 */
/** 客户端ip */
private String clientIp;

View File

@@ -47,13 +47,14 @@ public class AliPayRefundService {
log.error("网关返回退款失败: {}", response.getSubMsg());
throw new PayFailureException(response.getSubMsg());
}
// 接口返回fund_change=Y为退款成功fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态
if (response.getFundChange().equals("Y")){
// refundInfo.setStatus(RefundStatusEnum.SUCCESS)
// .setGatewayOrderNo(response.getTradeNo());
}
// 默认为退款中状态
refundInfo.setStatus(RefundStatusEnum.PROGRESS)
.setGatewayOrderNo(response.getTradeNo());
// 接口返回fund_change=Y为退款成功fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态
if (response.getFundChange().equals("Y")){
refundInfo.setStatus(RefundStatusEnum.SUCCESS);
}
}
catch (AlipayApiException e) {
log.error("订单退款失败:", e);

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.service.core.order.pay.convert;
import cn.bootx.platform.daxpay.result.order.PayOrderChannelResult;
import cn.bootx.platform.daxpay.result.order.PayChannelOrderResult;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
@@ -25,5 +25,5 @@ public interface PayOrderConvert {
PayChannelOrderDto convert(PayChannelOrder in);
PayOrderChannelResult convertResult(PayChannelOrder in);
PayChannelOrderResult convertResult(PayChannelOrder in);
}

View File

@@ -87,12 +87,10 @@ public class PayChannelOrderService {
* 更新异步支付通道退款余额和状态
*/
public void updateAsyncPayRefund(PayChannelOrder payChannelOrder, PayRefundChannelOrder refundChannelOrder){
// 支付通道订单可退余额
int refundableBalance = payChannelOrder.getRefundableBalance() - refundChannelOrder.getAmount();
payChannelOrder.setRefundableBalance(refundableBalance);
// 支付通道订单状态
if (Objects.equals(refundChannelOrder.getStatus(), RefundStatusEnum.SUCCESS.getCode())){
PayStatusEnum status = refundableBalance == 0 ? PayStatusEnum.REFUNDED : PayStatusEnum.PARTIAL_REFUND;
// 如果可退金额为0说明已经全部退款
PayStatusEnum status = payChannelOrder.getRefundableBalance() == 0 ? PayStatusEnum.REFUNDED : PayStatusEnum.PARTIAL_REFUND;
payChannelOrder.setStatus(status.getCode());
refundChannelOrder.setRefundTime(LocalDateTime.now());
} else {

View File

@@ -7,7 +7,7 @@ import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.QueryPayParam;
import cn.bootx.platform.daxpay.result.order.PayOrderChannelResult;
import cn.bootx.platform.daxpay.result.order.PayChannelOrderResult;
import cn.bootx.platform.daxpay.result.order.PayOrderResult;
import cn.bootx.platform.daxpay.service.core.order.pay.convert.PayOrderConvert;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
@@ -92,12 +92,13 @@ public class PayOrderQueryService {
// 查询通道数据
List<PayChannelOrder> orderChannelList = payChannelOrderManager.findAllByPaymentId(payOrder.getId());
List<PayOrderChannelResult> channels = orderChannelList.stream()
List<PayChannelOrderResult> channels = orderChannelList.stream()
.map(PayOrderConvert.CONVERT::convertResult)
.collect(Collectors.toList());
PayOrderResult payOrderResult = new PayOrderResult();
BeanUtil.copyProperties(payOrder, payOrderResult);
payOrderResult.setPaymentId(payOrder.getId());
payOrderResult.setDescription(payOrderExtra.getDescription())
.setChannels(channels);

View File

@@ -102,6 +102,7 @@ public class PayRefundOrderQueryService {
.collect(Collectors.toList());
RefundOrderResult refundOrderResult = PayRefundOrderConvert.CONVERT.convertResult(refundOrder);
refundOrderResult.setRefundId(refundOrder.getId());
refundOrderResult.setChannels(channels);
return refundOrderResult;
}

View File

@@ -1,10 +1,10 @@
package cn.bootx.platform.daxpay.service.core.payment.common.aop;
import cn.bootx.platform.common.core.util.ValidationUtil;
import cn.bootx.platform.daxpay.service.annotation.PaymentApi;
import cn.bootx.platform.daxpay.service.core.payment.common.service.PaymentSignService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.PayCommonParam;
import cn.bootx.platform.daxpay.service.annotation.PaymentApi;
import cn.bootx.platform.daxpay.service.core.payment.common.service.PaymentSignService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;

View File

@@ -23,7 +23,6 @@ import java.util.Objects;
@RequiredArgsConstructor
public class PaymentSignService {
private final PaymentAssistService paymentAssistService;;
/**

View File

@@ -98,9 +98,9 @@ public class PayRefundAssistService {
if (CollUtil.isEmpty(param.getRefundChannels())) {
throw new ValidationFailedException("退款通道参数不能为空");
}
if (Objects.isNull(param.getRefundNo())) {
throw new ValidationFailedException("部分退款时退款单号必传");
}
// if (Objects.isNull(param.getRefundNo())) {
// throw new ValidationFailedException("部分退款时退款单号必传");
// }
}
// 简单退款校验

View File

@@ -152,8 +152,8 @@ public class PayRefundService {
refundStrategy.initRefundParam(payOrder, refundParam, payChannelOrder);
}
// 对通道支付订单进行预扣款
payRefundStrategies.forEach(AbsRefundStrategy::doPreDeductOrderHandler);
// 生成通道退款订单对象
payRefundStrategies.forEach(AbsRefundStrategy::generateChannelOrder);
// 退款操作的预处理, 使用独立的新事物进行发起, 返回创建成功的退款订单, 成功后才可以进行下一阶段的操作
PayRefundOrder refundOrder = SpringUtil.getBean(this.getClass()).preRefundMethod(refundParam, payOrder, payRefundStrategies);
@@ -193,7 +193,7 @@ public class PayRefundService {
public PayRefundOrder preRefundMethod(RefundParam refundParam, PayOrder payOrder, List<AbsRefundStrategy> payRefundStrategies) {
// --------------------------- 支付订单 -------------------------------------
// 预扣支付相关订单要退款的金额并进行更新
payRefundStrategies.forEach(AbsRefundStrategy::generateChannelOrder);
payRefundStrategies.forEach(AbsRefundStrategy::doPreDeductOrderHandler);
List<PayChannelOrder> channelOrders = payRefundStrategies.stream()
.map(AbsRefundStrategy::getPayChannelOrder)
.collect(Collectors.toList());

View File

@@ -1,10 +1,11 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.service;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
import cn.bootx.platform.daxpay.service.code.RefundRepairWayEnum;
import cn.bootx.platform.daxpay.service.common.context.RepairLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
@@ -102,7 +103,7 @@ public class RefundRepairService {
* 退款成功, 更新退款单和支付单
*/
private RefundRepairResult success(PayRefundOrder refundOrder, PayOrder payOrder, List<AbsRefundRepairStrategy> repairStrategies) {
RepairLocal repairInfo = PaymentContextLocal.get().getRepairInfo();
// 订单相关状态
PayStatusEnum beforePayStatus = PayStatusEnum.findByCode(refundOrder.getStatus());
PayStatusEnum afterPayRefundStatus;
@@ -114,8 +115,9 @@ public class RefundRepairService {
} else {
afterPayRefundStatus = PayStatusEnum.PARTIAL_REFUND;
}
// 设置退款为完成状态
refundOrder.setStatus(RefundStatusEnum.SUCCESS.getCode());
// 设置退款为完成状态和完成时间
refundOrder.setStatus(RefundStatusEnum.SUCCESS.getCode())
.setRefundTime(repairInfo.getFinishTime());
payOrder.setStatus(afterPayRefundStatus.getCode());
// 执行退款成功逻辑

View File

@@ -1,12 +1,15 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -27,4 +30,18 @@ public class AliRefundRepairStrategy extends AbsRefundRepairStrategy {
public PayChannelEnum getChannel() {
return PayChannelEnum.ALI;
}
/**
* 退款成功修复
*/
@Override
public void doSuccessHandler() {
LocalDateTime finishTime = PaymentContextLocal.get()
.getRepairInfo()
.getFinishTime();
// 首先执行父类的修复逻辑
super.doSuccessHandler();
// 异步支付需要追加完成时间
this.getRefundChannelOrder().setRefundTime(finishTime);
}
}

View File

@@ -1,12 +1,15 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -26,4 +29,18 @@ public class WeChatRefundRepairStrategy extends AbsRefundRepairStrategy {
public PayChannelEnum getChannel() {
return PayChannelEnum.WECHAT;
}
/**
* 退款成功修复
*/
@Override
public void doSuccessHandler() {
LocalDateTime finishTime = PaymentContextLocal.get()
.getRepairInfo()
.getFinishTime();
// 首先执行父类的修复逻辑
super.doSuccessHandler();
// 异步支付需要追加完成时间
this.getRefundChannelOrder().setRefundTime(finishTime);
}
}

View File

@@ -64,11 +64,13 @@ public class PayRefundSyncService {
}
// 如果不是异步支付, 直接返回返回
if (!refundOrder.isAsyncPay()){
return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步通道的退款,不需要同步");
// return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步通道的退款,不需要同步");
throw new PayFailureException("订单没有异步通道的退款,不需要同步");
}
// 如果订单已经关闭, 直接返回失败
if (Objects.equals(refundOrder.getStatus(), RefundStatusEnum.CLOSE.getCode())){
return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单已经关闭,不需要同步");
// return new SyncResult().setSuccess(false).setRepair(false). setErrorMsg("订单已经关闭,不需要同步");
throw new PayFailureException("订单已经关闭,不需要同步");
}
return this.syncRefundOrder(refundOrder);
}
@@ -95,7 +97,9 @@ public class PayRefundSyncService {
// 判断是否同步成功
if (Objects.equals(syncResult.getSyncStatus(), RefundSyncStatusEnum.FAIL)) {
// 同步失败, 返回失败响应, 同时记录失败的日志
return new SyncResult().setErrorMsg(syncResult.getErrorMsg());
// return new SyncResult().setErrorMsg(syncResult.getErrorMsg());
this.saveRecord(refundOrder, syncResult, false, null, syncResult.getErrorMsg());
throw new PayFailureException(syncResult.getErrorMsg());
}
// 支付订单的网关订单号是否一致, 不一致进行更新
if (Objects.nonNull(syncResult.getGatewayOrderNo()) && !Objects.equals(syncResult.getGatewayOrderNo(), refundOrder.getGatewayOrderNo())){
@@ -120,13 +124,12 @@ public class PayRefundSyncService {
// 同步失败, 返回失败响应, 同时记录失败的日志
syncResult.setSyncStatus(RefundSyncStatusEnum.FAIL);
this.saveRecord(refundOrder, syncResult, false, null, e.getMessage());
return new SyncResult().setErrorMsg(e.getMessage());
throw e;
}
// 同步成功记录日志
this.saveRecord(refundOrder, syncResult, !statusSync, repairResult.getRepairNo(), null);
return new SyncResult()
.setGatewayStatus(syncResult.getSyncStatus().getCode())
.setSuccess(true)
.setRepair(!statusSync)
.setRepairOrderNo(repairResult.getRepairNo());
} finally {
@@ -175,7 +178,7 @@ public class PayRefundSyncService {
repair = repairService.repair(order, RefundRepairWayEnum.SUCCESS);
break;
case PROGRESS:
// 不进行处理 TODO 添加重试
// 不进行处理
log.warn("退款状态同步接口调用出错");
break;
case FAIL: {

View File

@@ -74,7 +74,8 @@ public class PaySyncService {
}
// 如果不是异步支付, 直接返回返回
if (!payOrder.isAsyncPay()){
return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步支付方式,不需要同步");
// return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步支付方式,不需要同步");
throw new PayFailureException("订单没有异步支付方式,不需要同步");
}
// 执行订单同步逻辑
return this.syncPayOrder(payOrder);
@@ -103,7 +104,8 @@ public class PaySyncService {
if (Objects.equals(syncResult.getSyncStatus(), PaySyncStatusEnum.FAIL)){
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(payOrder, syncResult, false, null, syncResult.getErrorMsg());
return new SyncResult().setErrorMsg(syncResult.getErrorMsg());
// return new SyncResult().setErrorMsg(syncResult.getErrorMsg());
throw new PayFailureException(syncResult.getErrorMsg());
}
// 支付订单的网关订单号是否一致, 不一致进行更新
if (Objects.nonNull(syncResult.getGatewayOrderNo()) && !Objects.equals(syncResult.getGatewayOrderNo(), payOrder.getGatewayOrderNo())){
@@ -129,14 +131,14 @@ public class PaySyncService {
// 同步失败, 返回失败响应, 同时记录失败的日志
syncResult.setSyncStatus(PaySyncStatusEnum.FAIL);
this.saveRecord(payOrder, syncResult, false, null, e.getMessage());
return new SyncResult().setErrorMsg(e.getMessage());
// return new SyncResult().setErrorMsg(e.getMessage());
throw e;
}
// 同步成功记录日志
this.saveRecord( payOrder, syncResult, !statusSync, repairResult.getRepairNo(), null);
return new SyncResult()
.setGatewayStatus(syncResult.getSyncStatus().getCode())
.setSuccess(true)
.setRepair(!statusSync)
.setRepairOrderNo(repairResult.getRepairNo());
} finally {
@@ -180,7 +182,7 @@ public class PaySyncService {
return true;
}
// TODO 退款比对
// 退款比对状态不做额外处理, 需要通过退款接口进行处理
if (orderStatus.equals(PayStatusEnum.REFUNDED.getCode()) && syncStatus.equals(PaySyncStatusEnum.REFUND)){
return true;
}
@@ -221,7 +223,7 @@ public class PaySyncService {
}
// 调用出错
case FAIL: {
// 不进行处理 TODO 添加重试
// 不进行处理
log.warn("支付状态同步接口调用出错");
break;
}

View File

@@ -75,12 +75,14 @@ public abstract class AbsRefundStrategy implements PayStrategy{
* 退款发起成功操作, 异步支付通道需要进行重写
*/
public void doSuccessHandler() {
// 更新退款订单数据状态
this.refundChannelOrder.setStatus(RefundStatusEnum.SUCCESS.getCode()).setRefundTime(LocalDateTime.now());
// 更新退款订单数据状态和完成时间
this.refundChannelOrder
.setStatus(RefundStatusEnum.SUCCESS.getCode()).
setRefundTime(LocalDateTime.now());
// 支付通道订单可退余额
int refundableBalance = this.getPayChannelOrder().getRefundableBalance() - this.refundChannelOrder.getAmount();
// 支付通道订单状态
// 如果可退金额为0说明已经全部退款
PayStatusEnum status = refundableBalance == 0 ? PayStatusEnum.REFUNDED : PayStatusEnum.PARTIAL_REFUND;
this.payChannelOrder.setRefundableBalance(refundableBalance)
.setStatus(status.getCode());

View File

@@ -34,6 +34,9 @@
<groupId>cn.bootx.platform</groupId>
<artifactId>common-starter-code-gen</artifactId>
</dependency>
<!-- 支付演示模块 -->
</dependencies>
</project>