mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-10-15 14:20:26 +00:00
feat 增加支付同步日志记录. 超时自动取消和定时同步支付状态功能. 退款功能优化
This commit is contained in:
15
_doc/Task.md
15
_doc/Task.md
@@ -27,11 +27,22 @@
|
||||
- [x] 同步记录/关闭记录/修复记录 增加记录请求ID
|
||||
- 2024-01-05:
|
||||
- [x] 支付同步日志记录, 无论同步成功还是失败, 以及修复成功还是失败, 都需要记录日志
|
||||
- [ ] 超时自动取消功能联调
|
||||
- [x] 超时自动取消功能联调, 先用spring定时任务实现, 通过支付同步实现
|
||||
- [x] 待支付支付单定时同步状态, 先用spring定时任务实现, 通过支付同步实现
|
||||
- [x] 退款功能联调
|
||||
- 2024-01-06:
|
||||
- [ ] 增加支付修复记录
|
||||
- [ ] 退款状态同步逻辑
|
||||
- [ ] 退款回调的处理
|
||||
- [ ] 支付状态同步处理考虑退款情况
|
||||
- [ ] 增加消息通知机制(通知客户端)
|
||||
- **任务池**
|
||||
- 支付同步时,有些状态无法区分处理, 导致无法修复
|
||||
- 下面内容同: 支付网关/本地
|
||||
- 支付宝: 订单未找到/支付关闭 支付网关/支付超时 支付网关/支付成功
|
||||
- 订单取消/修复/取消/同步添加分布式锁, 防止操作订单时出现重复操作
|
||||
- 支付状态同步处理退款情况
|
||||
- 支付配置支持数据库配置和配置文件配置
|
||||
- 超时任务处理支持轮训表
|
||||
- 增加回调机制(通知客户端)
|
||||
- 增加消息通知机制(通知客户端)
|
||||
- 新增支付单预警功能, 处理支付单与网关状态不一致且无法自动修复的情况
|
||||
|
@@ -15,13 +15,10 @@ import java.util.Objects;
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum PayStatusEnum {
|
||||
UNKNOWN("unknown","未知状态"),
|
||||
FAIL("fail","失败"),
|
||||
PROGRESS("progress","支付中"),
|
||||
SUCCESS("success","成功"),
|
||||
FAIL("fail","失败"),
|
||||
CANCEL("cancel","支付取消"),
|
||||
CLOSE("close","支付关闭"),
|
||||
TIMEOUT("timeout","超时取消"),
|
||||
PARTIAL_REFUND("partial_refund","部分退款"),
|
||||
REFUNDED("refunded","全部退款");
|
||||
|
||||
|
@@ -18,11 +18,17 @@ import java.util.Objects;
|
||||
public enum PaySyncStatusEnum {
|
||||
FAIL("fail", "查询失败"),
|
||||
PAY_SUCCESS("pay_success", "支付成功"),
|
||||
PAY_WAIT("pay_wait", "等待付款中"),
|
||||
PAY_WAIT("pay_wait", "待支付"),
|
||||
CLOSED("closed", "已关闭"),
|
||||
REFUND("refund", "已退款"),
|
||||
NOT_FOUND("not_found", "未查询到订单"),
|
||||
/** 本地订单到了超时时间, 但是网关和本地都未关闭, 需要触发关闭相关处理 */
|
||||
NOT_FOUND("not_found", "交易不存在"),
|
||||
/**
|
||||
* 未查询到订单(具体类型未知)
|
||||
* 区别于上面的未查询到订单,有些支付方式如支付宝,发起支付后并不能查询到订单,需要用户进行操作后才能查询到订单,
|
||||
* 所以查询为了区分,增加一个未知的状态, 用于处理这种特殊情况, 然后根据业务需要,关闭订单或者进行其他操作
|
||||
*/
|
||||
NOT_FOUND_UNKNOWN("not_found_unknown","交易不存在(特殊)"),
|
||||
/** 不属于网关同步过来的状态, 需要手动设置, 处理本地订单到了超时时间, 但是网关和本地都未关闭, 需要触发关闭相关处理 */
|
||||
TIMEOUT("timeout", "超时未关闭");
|
||||
|
||||
/** 编码 */
|
||||
|
@@ -1,7 +1,7 @@
|
||||
package cn.bootx.platform.daxpay.param.pay;
|
||||
|
||||
import cn.bootx.platform.daxpay.serializer.TimestampToLocalDateTimeDeserializer;
|
||||
import cn.bootx.platform.daxpay.service.util.PayUtil;
|
||||
import cn.bootx.platform.daxpay.util.PayUtil;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package cn.bootx.platform.daxpay.param.pay;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
|
||||
import cn.bootx.platform.daxpay.param.channel.VoucherPayParam;
|
||||
import cn.bootx.platform.daxpay.param.channel.WalletPayParam;
|
||||
@@ -9,7 +8,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
@@ -36,12 +34,6 @@ public class SimpleRefundParam extends PayCommonParam {
|
||||
*/
|
||||
@Schema(description = "退款订单号")
|
||||
private String refundNo;
|
||||
/**
|
||||
* @see PayChannelEnum#getCode()
|
||||
*/
|
||||
@Schema(description = "支付通道编码")
|
||||
@NotBlank(message = "支付通道编码不可为空")
|
||||
private String payChannel;
|
||||
|
||||
/**
|
||||
* 部分退款需要传输refundModes参数
|
||||
|
@@ -23,14 +23,12 @@ public class PaySyncResult extends PayCommonResult{
|
||||
* 支付网关同步状态
|
||||
* @see PaySyncStatusEnum
|
||||
*/
|
||||
private String syncStatus = FAIL.getCode();
|
||||
@Schema(description = "支付网关同步状态")
|
||||
private String gatewayStatus = FAIL.getCode();
|
||||
|
||||
@Schema(description = "是否同步成功")
|
||||
private boolean success;
|
||||
|
||||
@Schema(description = "失败原因")
|
||||
private String errorMsg;
|
||||
|
||||
@Schema(description = "是否进行了修复")
|
||||
private boolean repair;
|
||||
|
||||
@@ -40,4 +38,7 @@ public class PaySyncResult extends PayCommonResult{
|
||||
@Schema(description = "支付单修复后状态")
|
||||
private String repairStatus;
|
||||
|
||||
@Schema(description = "失败原因")
|
||||
private String errorMsg;
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package cn.bootx.platform.daxpay.service.util;
|
||||
package cn.bootx.platform.daxpay.util;
|
||||
|
||||
import cn.bootx.platform.daxpay.result.DaxResult;
|
||||
import lombok.experimental.UtilityClass;
|
@@ -1,4 +1,4 @@
|
||||
package cn.bootx.platform.daxpay.service.util;
|
||||
package cn.bootx.platform.daxpay.util;
|
||||
|
||||
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
@@ -0,0 +1,40 @@
|
||||
package cn.bootx.platform.daxpay.gateway.controller;
|
||||
|
||||
import cn.bootx.platform.common.core.rest.Res;
|
||||
import cn.bootx.platform.common.core.rest.ResResult;
|
||||
import cn.bootx.platform.daxpay.service.core.timeout.task.PayExpiredTimeTask;
|
||||
import cn.bootx.platform.daxpay.service.core.timeout.task.PayWaitOrderSyncTask;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 测试
|
||||
* @author xxm
|
||||
* @since 2024/1/5
|
||||
*/
|
||||
@Tag(name = "测试")
|
||||
@RestController
|
||||
@RequestMapping("/test")
|
||||
@RequiredArgsConstructor
|
||||
public class TestController {
|
||||
private final PayExpiredTimeTask expiredTimeTask;;
|
||||
private final PayWaitOrderSyncTask waitOrderSyncTask;
|
||||
|
||||
@Operation(summary = "同步")
|
||||
@GetMapping("/sync")
|
||||
public ResResult<Void> sync(){
|
||||
waitOrderSyncTask.task();
|
||||
return Res.ok();
|
||||
}
|
||||
@Operation(summary = "超时")
|
||||
@GetMapping("/expired")
|
||||
public ResResult<Void> expired(){
|
||||
expiredTimeTask.task();
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
}
|
@@ -2,17 +2,17 @@ package cn.bootx.platform.daxpay.gateway.controller;
|
||||
|
||||
import cn.bootx.platform.common.core.annotation.CountTime;
|
||||
import cn.bootx.platform.common.core.annotation.IgnoreAuth;
|
||||
import cn.bootx.platform.daxpay.service.annotation.PaymentApi;
|
||||
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;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PaySyncService;
|
||||
import cn.bootx.platform.daxpay.param.pay.*;
|
||||
import cn.bootx.platform.daxpay.result.DaxResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.PayResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.RefundResult;
|
||||
import cn.bootx.platform.daxpay.service.util.DaxRes;
|
||||
import cn.bootx.platform.daxpay.service.annotation.PaymentApi;
|
||||
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;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PaySyncService;
|
||||
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;
|
||||
|
@@ -49,11 +49,16 @@
|
||||
<groupId>cn.bootx.platform</groupId>
|
||||
<artifactId>common-spring</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 查询构造器 -->
|
||||
<dependency>
|
||||
<groupId>cn.bootx.platform</groupId>
|
||||
<artifactId>common-super-query</artifactId>
|
||||
<version>${bootx-platform.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 支付核心包-->
|
||||
|
@@ -13,8 +13,10 @@ import lombok.Getter;
|
||||
public enum PayRepairTypeEnum {
|
||||
|
||||
SUCCESS("success","成功"),
|
||||
CLOSE("close","关闭"),
|
||||
TIMEOUT("timeout","超时关闭"),
|
||||
CLOSE_LOCAL("close_local","关闭本地支付"),
|
||||
WAIT("wait","待支付"),
|
||||
/** 同时也会关闭本地支付 */
|
||||
CLOSE_GATEWAY("close_gateway","关闭网关支付"),
|
||||
REFUND("refund","退款");
|
||||
|
||||
private final String code;
|
||||
|
@@ -1,9 +1,11 @@
|
||||
package cn.bootx.platform.daxpay.service.common.entity;
|
||||
|
||||
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import cn.bootx.table.modify.annotation.DbColumn;
|
||||
import cn.bootx.table.modify.mysql.annotation.DbMySqlIndex;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -13,14 +15,23 @@ import java.time.LocalDateTime;
|
||||
* @author xxm
|
||||
* @since 2023/12/18
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class BasePayOrder {
|
||||
public class BasePayOrder extends MpIdEntity {
|
||||
|
||||
|
||||
|
||||
/** 交易记录ID */
|
||||
@DbColumn(comment = "交易记录ID")
|
||||
@DbMySqlIndex(comment = "交易记录ID")
|
||||
private Long paymentId;
|
||||
|
||||
/** 关联的业务号 */
|
||||
@DbMySqlIndex(comment = "业务号索引")
|
||||
@DbColumn(comment = "关联的业务号")
|
||||
private String businessNo;
|
||||
|
||||
/** 交易金额 */
|
||||
@DbColumn(comment = "交易金额")
|
||||
private Integer amount;
|
||||
@@ -29,11 +40,6 @@ public class BasePayOrder {
|
||||
@DbColumn(comment = "可退款金额")
|
||||
private Integer refundableBalance;
|
||||
|
||||
/** 关联的业务号 */
|
||||
@DbMySqlIndex(comment = "业务号索引")
|
||||
@DbColumn(comment = "关联的业务号")
|
||||
private String businessNo;
|
||||
|
||||
/**
|
||||
* 支付状态
|
||||
* @see PayStatusEnum
|
||||
|
@@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.function.EntityBaseFunction;
|
||||
import cn.bootx.platform.daxpay.service.common.entity.BasePayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.alipay.convert.AlipayConvert;
|
||||
import cn.bootx.platform.daxpay.service.dto.channel.alipay.AliPaymentDto;
|
||||
import cn.bootx.table.modify.annotation.DbColumn;
|
||||
import cn.bootx.table.modify.annotation.DbTable;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
@@ -24,9 +25,11 @@ import lombok.experimental.Accessors;
|
||||
public class AliPayOrder extends BasePayOrder implements EntityBaseFunction<AliPaymentDto> {
|
||||
|
||||
/** 支付宝交易号 */
|
||||
@DbColumn(comment = "交易号")
|
||||
private String tradeNo;
|
||||
|
||||
/** 支付方式 */
|
||||
/** 所使用的支付方式 */
|
||||
@DbColumn(comment = "支付方式")
|
||||
private String payWay;
|
||||
|
||||
@Override
|
||||
|
@@ -27,7 +27,7 @@ import java.util.Objects;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AliPayCloseService {
|
||||
private final AliPaySyncService paySyncService;
|
||||
private final AliPaySyncService aliPaySyncService;
|
||||
|
||||
/**
|
||||
* 关闭支付 此处使用交易关闭接口, 支付宝支持 交易关闭 和 交易撤销 两种关闭订单的方式, 区别如下
|
||||
@@ -70,7 +70,7 @@ public class AliPayCloseService {
|
||||
* 关闭失败后, 获取支付网关的状态, 如果是关闭返回true, 其他情况抛出异常
|
||||
*/
|
||||
private boolean syncStatus(PayOrder payOrder){
|
||||
GatewaySyncResult gatewaySyncResult = paySyncService.syncPayStatus(payOrder);
|
||||
GatewaySyncResult gatewaySyncResult = aliPaySyncService.syncPayStatus(payOrder);
|
||||
// 已经关闭
|
||||
if (Objects.equals(gatewaySyncResult.getSyncStatus(), PaySyncStatusEnum.CLOSED)){
|
||||
return true;
|
||||
|
@@ -70,7 +70,6 @@ public class AliPayConfigService {
|
||||
*/
|
||||
@SneakyThrows
|
||||
public void initConfig(AliPayConfig alipayConfig) {
|
||||
|
||||
AliPayApiConfig aliPayApiConfig;
|
||||
// 公钥
|
||||
if (Objects.equals(alipayConfig.getAuthType(), AliPayCode.AUTH_TYPE_KEY)) {
|
||||
|
@@ -76,6 +76,7 @@ public class AliPayOrderService {
|
||||
|
||||
AliPayOrder aliPayOrder = new AliPayOrder();
|
||||
aliPayOrder.setTradeNo(tradeNo)
|
||||
.setPayWay(PaymentContextLocal.get().getAsyncPayInfo().getPayWay().getCode())
|
||||
.setPaymentId(payOrder.getId())
|
||||
.setAmount(amount)
|
||||
.setRefundableBalance(amount)
|
||||
|
@@ -12,7 +12,7 @@ import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.service.util.PayUtil;
|
||||
import cn.bootx.platform.daxpay.util.PayUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.Method;
|
||||
|
@@ -68,9 +68,9 @@ public class AliPaySyncService {
|
||||
return syncResult.setSyncStatus(PaySyncStatusEnum.CLOSED);
|
||||
}
|
||||
}
|
||||
// 支付宝支付后, 客户未进行操作将不会创建出订单, 所以交易不存在等于未查询订单
|
||||
// 支付宝支付后, 客户未进行操作将不会创建出订单, 所以交易不存在约等于未查询订单
|
||||
if (Objects.equals(response.getSubCode(), AliPayCode.ACQ_TRADE_NOT_EXIST)) {
|
||||
return syncResult.setSyncStatus(PaySyncStatusEnum.PAY_WAIT);
|
||||
return syncResult.setSyncStatus(PaySyncStatusEnum.NOT_FOUND_UNKNOWN);
|
||||
}
|
||||
}
|
||||
catch (AlipayApiException e) {
|
||||
|
@@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.function.EntityBaseFunction;
|
||||
import cn.bootx.platform.daxpay.service.common.entity.BasePayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.convert.WeChatConvert;
|
||||
import cn.bootx.platform.daxpay.service.dto.channel.wechat.WeChatPayOrderDto;
|
||||
import cn.bootx.table.modify.annotation.DbColumn;
|
||||
import cn.bootx.table.modify.annotation.DbTable;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
@@ -21,11 +22,15 @@ import lombok.experimental.Accessors;
|
||||
@TableName("pay_wechat_pay_order")
|
||||
public class WeChatPayOrder extends BasePayOrder implements EntityBaseFunction<WeChatPayOrderDto> {
|
||||
|
||||
/**
|
||||
* 微信交易号
|
||||
*/
|
||||
/** 微信交易号 */
|
||||
@DbColumn(comment = "交易号")
|
||||
private String tradeNo;
|
||||
|
||||
/** 所使用的支付方式 */
|
||||
@DbColumn(comment = "支付方式")
|
||||
private String payWay;
|
||||
|
||||
|
||||
@Override
|
||||
public WeChatPayOrderDto toDto() {
|
||||
return WeChatConvert.CONVERT.convert(this);
|
||||
|
@@ -77,12 +77,13 @@ public class WeChatPayOrderService {
|
||||
// 创建微信支付记录
|
||||
WeChatPayOrder wechatPayOrder = new WeChatPayOrder();
|
||||
wechatPayOrder.setTradeNo(tradeNo)
|
||||
.setPaymentId(payOrder.getId())
|
||||
.setAmount(amount)
|
||||
.setRefundableBalance(amount)
|
||||
.setBusinessNo(payOrder.getBusinessNo())
|
||||
.setStatus(PayStatusEnum.SUCCESS.getCode())
|
||||
.setPayTime(LocalDateTime.now());
|
||||
.setPayWay(PaymentContextLocal.get().getAsyncPayInfo().getPayWay().getCode())
|
||||
.setPaymentId(payOrder.getId())
|
||||
.setAmount(amount)
|
||||
.setRefundableBalance(amount)
|
||||
.setBusinessNo(payOrder.getBusinessNo())
|
||||
.setStatus(PayStatusEnum.SUCCESS.getCode())
|
||||
.setPayTime(LocalDateTime.now());
|
||||
weChatPayOrderManager.save(wechatPayOrder);
|
||||
}
|
||||
|
||||
|
@@ -17,7 +17,7 @@ import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.service.param.channel.wechat.WeChatPayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.service.util.PayUtil;
|
||||
import cn.bootx.platform.daxpay.util.PayUtil;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -272,7 +272,7 @@ public class WeChatPayService {
|
||||
public void rotationSync(PayOrder payOrder) {
|
||||
PaySyncResult paySyncResult = paySyncService.syncPayOrder(payOrder);
|
||||
// 不为支付中状态后, 调用系统同步更新状态, 支付状态则继续重试
|
||||
if (Objects.equals(PAY_WAIT.getCode(), paySyncResult.getSyncStatus())) {
|
||||
if (Objects.equals(PAY_WAIT.getCode(), paySyncResult.getGatewayStatus())) {
|
||||
throw new RetryableException();
|
||||
}
|
||||
}
|
||||
|
@@ -105,7 +105,7 @@ public class PayCallbackService {
|
||||
// 执行支付关闭修复逻辑
|
||||
PayRepairParam payRepairParam = new PayRepairParam()
|
||||
.setRepairSource(PayRepairSourceEnum.CALLBACK)
|
||||
.setRepairType(PayRepairTypeEnum.CLOSE);
|
||||
.setRepairType(PayRepairTypeEnum.CLOSE_LOCAL);
|
||||
payRepairService.repair(payOrder, payRepairParam);
|
||||
return result;
|
||||
}
|
||||
|
@@ -112,6 +112,7 @@ public class PayCloseService {
|
||||
.getReqId();
|
||||
PayCloseRecord record = new PayCloseRecord()
|
||||
.setPaymentId(payOrder.getId())
|
||||
.setBusinessNo(payOrder.getBusinessNo())
|
||||
.setAsyncChannel(payOrder.getAsyncChannel())
|
||||
.setClosed(closed)
|
||||
.setErrorMsg(errMsg)
|
||||
|
@@ -19,7 +19,7 @@ import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrderChannel;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrderExtra;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.service.PayOrderService;
|
||||
import cn.bootx.platform.daxpay.service.util.PayUtil;
|
||||
import cn.bootx.platform.daxpay.util.PayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -184,8 +184,7 @@ public class PayAssistService {
|
||||
throw new PayFailureException("已经支付成功,请勿重新支付");
|
||||
}
|
||||
// 支付失败类型状态
|
||||
List<String> tradesStatus = Arrays.asList(PayStatusEnum.FAIL.getCode(), PayStatusEnum.CANCEL.getCode(),
|
||||
PayStatusEnum.CLOSE.getCode(), PayStatusEnum.TIMEOUT.getCode());
|
||||
List<String> tradesStatus = Arrays.asList(PayStatusEnum.FAIL.getCode(), PayStatusEnum.CLOSE.getCode());
|
||||
if (tradesStatus.contains(payOrder.getStatus())) {
|
||||
throw new PayFailureException("支付失败或已经被关闭");
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.SimplePayParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PayResult;
|
||||
import cn.bootx.platform.daxpay.service.util.PayUtil;
|
||||
import cn.bootx.platform.daxpay.util.PayUtil;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.bean.copier.CopyOptions;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
|
@@ -67,7 +67,6 @@ public class PayRefundAssistService {
|
||||
* 根据退款参数获取支付订单, 并进行检查
|
||||
*/
|
||||
public PayOrder getPayOrderAndCheckByRefundParam(RefundParam param, boolean simple){
|
||||
|
||||
if (!param.isRefundAll()) {
|
||||
if (CollUtil.isEmpty(param.getRefundChannels())) {
|
||||
throw new ValidationFailedException("退款通道参数不能为空");
|
||||
@@ -87,19 +86,28 @@ public class PayRefundAssistService {
|
||||
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
|
||||
}
|
||||
|
||||
// 简单退款校验
|
||||
if (payOrder.isCombinationPay() != simple){
|
||||
throw new PayFailureException("组合支付不可以使用简单退款方式");
|
||||
// 简单退款处理
|
||||
if (simple){
|
||||
// 简单退款校验
|
||||
if (payOrder.isCombinationPay()){
|
||||
throw new PayFailureException("组合支付不可以使用简单退款方式");
|
||||
}
|
||||
// 设置退款参数的通道配置
|
||||
String channel = payOrder.getRefundableInfos()
|
||||
.get(0)
|
||||
.getChannel();
|
||||
param.getRefundChannels().get(0).setChannel(channel);
|
||||
}
|
||||
|
||||
|
||||
// 状态判断, 支付中/失败/取消等不能进行退款
|
||||
List<String> tradesStatus = Arrays.asList(
|
||||
PayStatusEnum.PROGRESS.getCode(),
|
||||
PayStatusEnum.CLOSE.getCode(),
|
||||
PayStatusEnum.CANCEL.getCode(),
|
||||
PayStatusEnum.TIMEOUT.getCode(),
|
||||
PayStatusEnum.FAIL.getCode());
|
||||
if (tradesStatus.contains(payOrder.getStatus())) {
|
||||
throw new PayFailureException("状态非法, 无法退款");
|
||||
PayStatusEnum statusEnum = PayStatusEnum.findByCode(payOrder.getStatus());
|
||||
throw new PayFailureException("当前状态["+statusEnum.getName()+"]不允许状态非法, 无法退款");
|
||||
}
|
||||
return payOrder;
|
||||
}
|
||||
|
@@ -54,7 +54,6 @@ public class PayRefundService {
|
||||
BeanUtil.copyProperties(param,refundParam);
|
||||
RefundChannelParam channelParam = new RefundChannelParam()
|
||||
.setAmount(param.getAmount())
|
||||
.setChannel(param.getPayChannel())
|
||||
.setChannelExtra(param.getChannelExtra());
|
||||
refundParam.setRefundChannels(Collections.singletonList(channelParam));
|
||||
return this.refund(refundParam,true);
|
||||
@@ -66,9 +65,10 @@ public class PayRefundService {
|
||||
* @param simple 是否简单退款
|
||||
*/
|
||||
private RefundResult refund(RefundParam param, boolean simple){
|
||||
// 检查获取支付订单, 同时设置退款参数中对应的支付通道参数
|
||||
PayOrder payOrder = payRefundAssistService.getPayOrderAndCheckByRefundParam(param, simple);
|
||||
// 参数校验
|
||||
ValidationUtil.validateParam(param);
|
||||
PayOrder payOrder = payRefundAssistService.getPayOrderAndCheckByRefundParam(param, simple);
|
||||
// 退款上下文初始化
|
||||
payRefundAssistService.initRefundContext(param);
|
||||
// 是否全部退款
|
||||
|
@@ -33,30 +33,37 @@ public class PayRepairService {
|
||||
/**
|
||||
* 修复支付单
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class )
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public RepairResult repair(PayOrder order, PayRepairParam repairParam){
|
||||
// 从退款记录中获取支付通道 退款记录中的支付通道跟支付时关联的支付通道一致
|
||||
List<String> channels = order.getRefundableInfos()
|
||||
.stream()
|
||||
.map(OrderRefundableInfo::getChannel)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 初始化修复参数
|
||||
List<AbsPayRepairStrategy> repairStrategies = PayRepairStrategyFactory.createAsyncLast(channels);
|
||||
repairStrategies.forEach(repairStrategy -> repairStrategy.initRepairParam(order, repairParam.getRepairSource()));
|
||||
repairStrategies.forEach(AbsPayRepairStrategy::doBeforeHandler);
|
||||
RepairResult repairResult = new RepairResult().setOldStatus(PayStatusEnum.findByCode(order.getStatus()));
|
||||
|
||||
// 根据不同的类型执行对应的修复逻辑
|
||||
switch (repairParam.getRepairType()) {
|
||||
case SUCCESS:
|
||||
this.success(order, repairStrategies);
|
||||
repairResult.setRepairStatus(PayStatusEnum.SUCCESS);
|
||||
break;
|
||||
case CLOSE:
|
||||
this.close(order, repairStrategies);
|
||||
case CLOSE_LOCAL:
|
||||
this.closeLocal(order, repairStrategies);
|
||||
repairResult.setRepairStatus(PayStatusEnum.CLOSE);
|
||||
break;
|
||||
case TIMEOUT:
|
||||
this.timeout(order, repairStrategies);
|
||||
repairResult.setRepairStatus(PayStatusEnum.TIMEOUT);
|
||||
case WAIT:
|
||||
this.wait(order, repairStrategies);
|
||||
repairResult.setRepairStatus(PayStatusEnum.PROGRESS);
|
||||
break;
|
||||
case CLOSE_GATEWAY:
|
||||
this.closeGateway(order, repairStrategies);
|
||||
repairResult.setRepairStatus(PayStatusEnum.CLOSE);
|
||||
break;
|
||||
case REFUND:
|
||||
this.refund(order, repairStrategies);
|
||||
@@ -67,44 +74,53 @@ public class PayRepairService {
|
||||
return repairResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 变更未待支付
|
||||
*
|
||||
*/
|
||||
private void wait(PayOrder order, List<AbsPayRepairStrategy> repairStrategies) {
|
||||
// 修改订单支付状态为成功
|
||||
order.setStatus(PayStatusEnum.PROGRESS.getCode());
|
||||
payOrderService.updateById(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 变更为已支付
|
||||
* 同步: 将异步支付状态修改为成功
|
||||
* 回调: 将异步支付状态修改为成功
|
||||
*/
|
||||
private void success(PayOrder payment, List<AbsPayRepairStrategy> strategies) {
|
||||
private void success(PayOrder order, List<AbsPayRepairStrategy> strategies) {
|
||||
LocalDateTime payTime = PaymentContextLocal.get()
|
||||
.getAsyncPayInfo()
|
||||
.getPayTime();
|
||||
|
||||
// 执行个通道的成功处理方法
|
||||
strategies.forEach(AbsPayRepairStrategy::doSuccessHandler);
|
||||
|
||||
// 修改订单支付状态为成功
|
||||
payment.setStatus(PayStatusEnum.SUCCESS.getCode());
|
||||
// TODO 读取支付网关中的时间
|
||||
payment.setPayTime(payTime);
|
||||
payOrderService.updateById(payment);
|
||||
order.setStatus(PayStatusEnum.SUCCESS.getCode());
|
||||
// 读取支付网关中的时间
|
||||
order.setPayTime(payTime);
|
||||
payOrderService.updateById(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭支付
|
||||
* 同步: 执行支付单所有的支付通道关闭支付逻辑, 不再重复调用网关进行支付的关闭
|
||||
* 同步: 执行支付单所有的支付通道关闭支付逻辑, 如果来源是网关同步, 则不需要调用网关关闭
|
||||
* 回调: 执行所有的支付通道关闭支付逻辑
|
||||
*/
|
||||
private void close(PayOrder payOrder, List<AbsPayRepairStrategy> absPayStrategies) {
|
||||
private void closeLocal(PayOrder order, List<AbsPayRepairStrategy> absPayStrategies) {
|
||||
// 执行策略的关闭方法
|
||||
absPayStrategies.forEach(AbsPayRepairStrategy::doCloseHandler);
|
||||
payOrder.setStatus(PayStatusEnum.CLOSE.getCode());
|
||||
payOrderService.updateById(payOrder);
|
||||
absPayStrategies.forEach(AbsPayRepairStrategy::doCloseLocalHandler);
|
||||
order.setStatus(PayStatusEnum.CLOSE.getCode());
|
||||
payOrderService.updateById(order);
|
||||
}
|
||||
/**
|
||||
* 支付超时关闭订单
|
||||
* 关闭网关交易, 同时也会关闭本地支付
|
||||
*/
|
||||
private void timeout(PayOrder payOrder, List<AbsPayRepairStrategy> absPayStrategies) {
|
||||
private void closeGateway(PayOrder payOrder, List<AbsPayRepairStrategy> absPayStrategies) {
|
||||
// 执行策略的关闭方法
|
||||
absPayStrategies.forEach(AbsPayRepairStrategy::doTimeoutHandler);
|
||||
payOrder.setStatus(PayStatusEnum.TIMEOUT.getCode());
|
||||
absPayStrategies.forEach(AbsPayRepairStrategy::doCloseGatewayHandler);
|
||||
payOrder.setStatus(PayStatusEnum.CLOSE.getCode());
|
||||
payOrderService.updateById(payOrder);
|
||||
}
|
||||
|
||||
|
@@ -1,14 +1,13 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayCloseService;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayOrderService;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayConfigService;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayOrderService;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.dao.PayOrderChannelManager;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrderChannel;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -55,22 +54,18 @@ public class AliPayRepairStrategy extends AbsPayRepairStrategy {
|
||||
* 取消支付
|
||||
*/
|
||||
@Override
|
||||
public void doCloseHandler() {
|
||||
// 如果非同步出的订单取消状态, 则调用支付宝网关关闭订单
|
||||
if (this.getRepairSource() != PayRepairSourceEnum.SYNC){
|
||||
closeService.close(this.getOrder());
|
||||
}
|
||||
public void doCloseLocalHandler() {
|
||||
orderService.updateClose(this.getOrder().getId());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 订单支付超时取消支付
|
||||
* 关闭本地支付和网关支付
|
||||
*/
|
||||
@Override
|
||||
public void doTimeoutHandler() {
|
||||
public void doCloseGatewayHandler() {
|
||||
closeService.close(this.getOrder());
|
||||
orderService.updateClose(this.getOrder().getId());
|
||||
this.doCloseLocalHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -27,7 +27,7 @@ public class CashPayRepairStrategy extends AbsPayRepairStrategy {
|
||||
* 取消支付
|
||||
*/
|
||||
@Override
|
||||
public void doCloseHandler() {
|
||||
public void doCloseLocalHandler() {
|
||||
cashService.close(this.getOrder().getId());
|
||||
|
||||
|
||||
|
@@ -23,7 +23,7 @@ public class UnionPayRepairStrategy extends AbsPayRepairStrategy {
|
||||
* 取消支付
|
||||
*/
|
||||
@Override
|
||||
public void doCloseHandler() {
|
||||
public void doCloseLocalHandler() {
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@ public class VoucherPayRepairStrategy extends AbsPayRepairStrategy {
|
||||
* 取消支付
|
||||
*/
|
||||
@Override
|
||||
public void doCloseHandler() {
|
||||
public void doCloseLocalHandler() {
|
||||
voucherPayService.close(this.getOrder().getId());
|
||||
voucherPayOrderService.updateClose(this.getOrder().getId());
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ public class WalletPayRepairStrategy extends AbsPayRepairStrategy {
|
||||
* 取消支付
|
||||
*/
|
||||
@Override
|
||||
public void doCloseHandler() {
|
||||
public void doCloseLocalHandler() {
|
||||
walletPayService.close(this.getOrder().getId());
|
||||
walletPayOrderService.updateClose(this.getOrder().getId());
|
||||
}
|
||||
|
@@ -1,14 +1,13 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayCloseService;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayConfigService;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayOrderService;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.dao.PayOrderChannelManager;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrderChannel;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -61,23 +60,19 @@ public class WeChatPayRepairStrategy extends AbsPayRepairStrategy {
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消支付
|
||||
* 关闭本地支付
|
||||
*/
|
||||
@Override
|
||||
public void doCloseHandler() {
|
||||
// 如果非同步出的订单取消状态, 则调用支付网关关闭订单
|
||||
if (this.getRepairSource() != PayRepairSourceEnum.SYNC){
|
||||
closeService.close(this.getOrder(),this.weChatPayConfig);
|
||||
}
|
||||
public void doCloseLocalHandler() {
|
||||
orderService.updateClose(this.getOrder().getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单支付超时取消支付
|
||||
* 关闭本地支付和网关支付
|
||||
*/
|
||||
@Override
|
||||
public void doTimeoutHandler() {
|
||||
public void doCloseGatewayHandler() {
|
||||
closeService.close(this.getOrder(),this.weChatPayConfig);
|
||||
orderService.updateClose(this.getOrder().getId());
|
||||
this.doCloseLocalHandler();
|
||||
}
|
||||
}
|
||||
|
@@ -26,10 +26,13 @@ import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.bootx.platform.daxpay.code.PaySyncStatusEnum.*;
|
||||
|
||||
/**
|
||||
* 支付同步服务
|
||||
* @author xxm
|
||||
@@ -90,15 +93,15 @@ public class PaySyncService {
|
||||
}
|
||||
|
||||
// 判断网关状态是否和支付单一致, 同时更新网关同步状态
|
||||
boolean statusSync = this.checkStatusSync(syncResult,order);
|
||||
boolean statusSync = this.checkAndAdjustSyncStatus(syncResult,order);
|
||||
try {
|
||||
// 状态不一致,执行支付单修复逻辑
|
||||
if (!statusSync){
|
||||
this.resultHandler(syncResult, order);
|
||||
repairStatus = order.getStatus();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 同步失败, 返回失败响应, 同时记录失败的日志 TODO 后面异常范围能这么宽泛
|
||||
} catch (PayFailureException e) {
|
||||
// 同步失败, 返回失败响应, 同时记录失败的日志
|
||||
syncResult.setSyncStatus(PaySyncStatusEnum.FAIL);
|
||||
this.saveRecord(order, syncResult, false, oldStatus, null, e.getMessage());
|
||||
return new PaySyncResult().setErrorMsg(e.getMessage());
|
||||
@@ -107,39 +110,50 @@ public class PaySyncService {
|
||||
// 同步成功记录日志
|
||||
this.saveRecord( order, syncResult, !statusSync, oldStatus, repairStatus, null);
|
||||
return new PaySyncResult()
|
||||
.setGatewayStatus(syncResult.getSyncStatus().getCode())
|
||||
.setSuccess(true)
|
||||
.setRepair(!statusSync)
|
||||
.setRepairStatus(repairStatus)
|
||||
.setSyncStatus(syncResult.getSyncStatus().getCode());
|
||||
.setOldStatus(oldStatus)
|
||||
.setRepairStatus(repairStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付单和网关状态是否一致, 同时待支付状态下, 处理支付超时情况
|
||||
* 判断支付单和网关状态是否一致, 同时待支付状态下, 支付单支付超时进行状态的更改
|
||||
*/
|
||||
public boolean checkStatusSync(GatewaySyncResult syncResult, PayOrder order){
|
||||
private boolean checkAndAdjustSyncStatus(GatewaySyncResult syncResult, PayOrder order){
|
||||
PaySyncStatusEnum syncStatus = syncResult.getSyncStatus();
|
||||
String orderStatus = order.getStatus();
|
||||
// 支付成功比对
|
||||
if (orderStatus.equals(PayStatusEnum.SUCCESS.getCode()) && syncStatus.equals(PaySyncStatusEnum.PAY_SUCCESS)){
|
||||
// 本地支付成功/网关支付成功
|
||||
if (orderStatus.equals(PayStatusEnum.SUCCESS.getCode()) && syncStatus.equals(PAY_SUCCESS)){
|
||||
return true;
|
||||
}
|
||||
// 待支付比对 支付中都代表待支付, 需要处理订单超时的情况
|
||||
List<PaySyncStatusEnum> enums = Collections.singletonList(PaySyncStatusEnum.PAY_WAIT);
|
||||
if (orderStatus.equals(PayStatusEnum.PROGRESS.getCode()) && enums.contains(syncStatus)){
|
||||
|
||||
/*
|
||||
本地支付中/网关支付中或者订单未找到(未知) 支付宝特殊情况,未找到订单可能是发起支付用户未操作、支付已关闭、交易未找到三种情况
|
||||
所以需要根据本地订单不同的状态进行特殊处理
|
||||
*/
|
||||
List<PaySyncStatusEnum> syncWaitEnums = Arrays.asList(PAY_WAIT, NOT_FOUND_UNKNOWN);
|
||||
if (orderStatus.equals(PayStatusEnum.PROGRESS.getCode()) && syncWaitEnums.contains(syncStatus)){
|
||||
// 判断支付单是否支付超时, 如果待支付状态下触发超时
|
||||
if (LocalDateTimeUtil.le(order.getExpiredTime(), LocalDateTime.now())){
|
||||
// 将支付单同步状态状态调整为支付超时, 进行订单的关闭
|
||||
syncResult.setSyncStatus(PaySyncStatusEnum.TIMEOUT);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 支付关闭比对
|
||||
if (orderStatus.equals(PayStatusEnum.CLOSE.getCode()) && syncStatus.equals(PaySyncStatusEnum.CLOSED)){
|
||||
/*
|
||||
关闭 /网关支付关闭、订单未找到、订单未找到(特殊), 订单未找到(特殊)主要是支付宝特殊情况,
|
||||
未找到订单可能是发起支付用户未操作、支付已关闭、交易未找到三种情况
|
||||
所以需要根据本地订单不同的状态进行特殊处理, 此处视为支付已关闭、交易未找到这两种, 处理方式相同, 都作为支付关闭处理
|
||||
*/
|
||||
List<String> payCloseEnums = Collections.singletonList(PayStatusEnum.CLOSE.getCode());
|
||||
List<PaySyncStatusEnum> syncClose = Arrays.asList(CLOSED, NOT_FOUND, NOT_FOUND_UNKNOWN);
|
||||
if (payCloseEnums.contains(orderStatus) && syncClose.contains(syncStatus)){
|
||||
return true;
|
||||
}
|
||||
|
||||
// 退款比对
|
||||
// TODO 退款比对
|
||||
if (orderStatus.equals(PayStatusEnum.REFUNDED.getCode()) && syncStatus.equals(PaySyncStatusEnum.REFUND)){
|
||||
return true;
|
||||
}
|
||||
@@ -160,15 +174,23 @@ public class PaySyncService {
|
||||
repairService.repair(payOrder,repairParam);
|
||||
break;
|
||||
}
|
||||
// 待付款/ 支付中
|
||||
// 待支付, 将订单状态重新设置为待支付
|
||||
case PAY_WAIT: {
|
||||
log.info("依然是付款状态");
|
||||
repairParam.setRepairType(PayRepairTypeEnum.WAIT);
|
||||
repairService.repair(payOrder,repairParam);
|
||||
break;
|
||||
}
|
||||
// 订单已经超时关闭 和 网关没找到记录, 对订单进行关闭
|
||||
// 交易关闭和未找到, 都对本地支付订单进行关闭, 不需要再调用网关进行关闭
|
||||
case CLOSED:
|
||||
case NOT_FOUND: {
|
||||
repairParam.setRepairType(PayRepairTypeEnum.CLOSE);
|
||||
repairParam.setRepairType(PayRepairTypeEnum.CLOSE_LOCAL);
|
||||
repairService.repair(payOrder, repairParam);
|
||||
break;
|
||||
}
|
||||
// 超时关闭和交易不存在(特殊) 关闭本地支付订单, 同时调用网关进行关闭, 确保后续这个订单不能被支付
|
||||
case TIMEOUT:
|
||||
case NOT_FOUND_UNKNOWN:{
|
||||
repairParam.setRepairType(PayRepairTypeEnum.CLOSE_GATEWAY);
|
||||
repairService.repair(payOrder, repairParam);
|
||||
break;
|
||||
}
|
||||
@@ -179,10 +201,6 @@ public class PaySyncService {
|
||||
break;
|
||||
}
|
||||
// 调用出错
|
||||
case TIMEOUT:
|
||||
repairParam.setRepairType(PayRepairTypeEnum.TIMEOUT);
|
||||
repairService.repair(payOrder, repairParam);
|
||||
break;
|
||||
case FAIL: {
|
||||
// 不进行处理 TODO 添加重试
|
||||
log.warn("支付状态同步接口调用出错");
|
||||
@@ -207,9 +225,10 @@ public class PaySyncService {
|
||||
private void saveRecord(PayOrder payOrder,GatewaySyncResult syncResult, boolean repair, String oldStatus, String repairStatus, String errorMsg){
|
||||
PaySyncRecord paySyncRecord = new PaySyncRecord()
|
||||
.setPaymentId(payOrder.getId())
|
||||
.setBusinessNo(payOrder.getBusinessNo())
|
||||
.setChannel(payOrder.getAsyncChannel())
|
||||
.setSyncInfo(syncResult.getSyncInfo())
|
||||
.setSyncStatus(syncResult.getSyncStatus().getCode())
|
||||
.setGatewayStatus(syncResult.getSyncStatus().getCode())
|
||||
.setRepairOrder(repair)
|
||||
.setOldStatus(oldStatus)
|
||||
.setRepairStatus(repairStatus)
|
||||
|
@@ -25,7 +25,7 @@ public class PayOrderSyncTaskService {
|
||||
public void syncTask() {
|
||||
log.info("开始同步支付订单");
|
||||
// 1. 从超时订单列表中获取到未超时的订单号
|
||||
for (String s : PayExpiredTimeRepository.getNormalKeysBy7Day()) {
|
||||
for (String s : PayExpiredTimeRepository.getNormalKeysBy30Day()) {
|
||||
try {
|
||||
Long paymentId = Long.parseLong(s);
|
||||
PaySyncParam paySyncParam = new PaySyncParam();
|
||||
|
@@ -29,6 +29,10 @@ public class PayCloseRecord extends MpCreateEntity implements EntityBaseFunction
|
||||
@DbComment("支付记录id")
|
||||
private Long paymentId;
|
||||
|
||||
/** 业务号 */
|
||||
@DbComment("业务号")
|
||||
private String businessNo;
|
||||
|
||||
/**
|
||||
* 关闭的异步支付通道, 可以为空
|
||||
* @see PayChannelEnum#getCode()
|
||||
|
@@ -29,7 +29,8 @@ public class PayOrderService {
|
||||
|
||||
private final PayExpiredTimeService expiredTimeService;
|
||||
|
||||
private final List<String> ORDER_FINISH = Arrays.asList(PayStatusEnum.CLOSE.getCode(),PayStatusEnum.TIMEOUT.getCode(), PayStatusEnum.SUCCESS.getCode());
|
||||
//
|
||||
private final List<String> ORDER_FINISH = Arrays.asList(PayStatusEnum.CLOSE.getCode(), PayStatusEnum.SUCCESS.getCode());
|
||||
|
||||
/**
|
||||
* 根据id查询
|
||||
@@ -49,12 +50,11 @@ public class PayOrderService {
|
||||
* 新增
|
||||
*/
|
||||
public void save(PayOrder payOrder){
|
||||
payOrderManager.save(payOrder);
|
||||
// 异步支付需要添加订单超时任务记录
|
||||
if (payOrder.isAsyncPay()){
|
||||
expiredTimeService.registerExpiredTime(payOrder);
|
||||
}
|
||||
|
||||
payOrderManager.save(payOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -33,12 +33,12 @@ import java.util.List;
|
||||
@TableName(value = "pay_refund_order", autoResultMap = true)
|
||||
public class PayRefundOrder extends MpBaseEntity implements EntityBaseFunction<PayRefundOrderDto> {
|
||||
|
||||
/** 支付单号 */
|
||||
@DbColumn(comment = "关联的业务号")
|
||||
/** 支付id */
|
||||
@DbColumn(comment = "支付id")
|
||||
private Long paymentId;
|
||||
|
||||
/** 关联的业务号 */
|
||||
@DbColumn(comment = "关联的业务号")
|
||||
/** 业务号 */
|
||||
@DbColumn(comment = "业务号")
|
||||
private String businessNo;
|
||||
|
||||
/** 异步方式关联退款请求号(部分退款情况) */
|
||||
|
@@ -28,7 +28,7 @@ public class PaySyncRecordManager extends BaseManager<PaySyncRecordMapper, PaySy
|
||||
return lambdaQuery().orderByDesc(MpIdEntity::getId)
|
||||
.like(Objects.nonNull(param.getPaymentId()), PaySyncRecord::getPaymentId, param.getPaymentId())
|
||||
.eq(Objects.nonNull(param.getChannel()), PaySyncRecord::getChannel, param.getChannel())
|
||||
.eq(Objects.nonNull(param.getStatus()), PaySyncRecord::getSyncStatus, param.getStatus())
|
||||
.eq(Objects.nonNull(param.getStatus()), PaySyncRecord::getGatewayStatus, param.getStatus())
|
||||
.page(mpPage);
|
||||
}
|
||||
|
||||
|
@@ -32,6 +32,10 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<
|
||||
@DbComment("支付记录id")
|
||||
private Long paymentId;
|
||||
|
||||
/** 业务号 */
|
||||
@DbComment("业务号")
|
||||
private String businessNo;
|
||||
|
||||
/**
|
||||
* 支付通道
|
||||
* @see PayChannelEnum#getCode()
|
||||
@@ -45,14 +49,14 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<
|
||||
private String syncInfo;
|
||||
|
||||
/**
|
||||
* 同步状态
|
||||
* 网关返回状态
|
||||
* @see PaySyncStatusEnum
|
||||
*/
|
||||
@DbComment("同步状态")
|
||||
private String syncStatus;
|
||||
private String gatewayStatus;
|
||||
|
||||
/**
|
||||
* 支付单如果状态不一致, 是否修复成功
|
||||
* 支付单如果状态不一致, 是否进行修复
|
||||
*/
|
||||
@DbComment("是否进行修复")
|
||||
private boolean repairOrder;
|
||||
|
@@ -43,10 +43,10 @@ public class PayExpiredTimeRepository {
|
||||
/**
|
||||
* 获取所有未过期的订单ID. (7天内的订单)
|
||||
*/
|
||||
public Set<String> getNormalKeysBy7Day(){
|
||||
public Set<String> getNormalKeysBy30Day(){
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
long start = LocalDateTimeUtil.timestamp(now);
|
||||
long end = LocalDateTimeUtil.timestamp(now.plusDays(7));
|
||||
long end = LocalDateTimeUtil.timestamp(now.plusDays(30));
|
||||
return redisClient.zrangeByScore(KEY, start, end);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,7 @@
|
||||
package cn.bootx.platform.daxpay.service.core.timeout.task;
|
||||
|
||||
import cn.bootx.platform.common.core.exception.DataNotExistException;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.dao.PayOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.param.pay.PaySyncParam;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PaySyncService;
|
||||
import cn.bootx.platform.daxpay.service.core.timeout.dao.PayExpiredTimeRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -21,23 +20,23 @@ import java.util.Set;
|
||||
@RequiredArgsConstructor
|
||||
public class PayExpiredTimeTask {
|
||||
private final PayExpiredTimeRepository repository;
|
||||
private final PayOrderManager payOrderManager;
|
||||
private final PaySyncService paySyncService;
|
||||
|
||||
|
||||
// @Scheduled(cron = "*/5 * * * * ?")
|
||||
public void task(){
|
||||
log.info("执行....");
|
||||
log.info("执行超时取消任务....");
|
||||
Set<String> expiredKeys = repository.getExpiredKeys(LocalDateTime.now());
|
||||
for (String expiredKey : expiredKeys) {
|
||||
log.info("key:{}", expiredKey);
|
||||
try {
|
||||
// 查询对应的订单
|
||||
PayOrder payOrder = payOrderManager.findById(Long.parseLong(expiredKey))
|
||||
.orElseThrow(() -> new DataNotExistException("支付单未找到"));
|
||||
// 调用订单同步逻辑. 结果如果不是支付中, 进行补偿处理
|
||||
|
||||
// 如果状态是支付中, 则进行支付超时处理
|
||||
// 执行同步操作, 网关同步时会对支付的进行状态的处理
|
||||
Long paymentId = Long.parseLong(expiredKey);
|
||||
PaySyncParam paySyncParam = new PaySyncParam();
|
||||
paySyncParam.setPaymentId(paymentId);
|
||||
paySyncService.sync(paySyncParam);
|
||||
} catch (Exception e) {
|
||||
log.error("超时任务异常", e);
|
||||
log.error("超时取消任务 异常", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,43 @@
|
||||
package cn.bootx.platform.daxpay.service.core.timeout.task;
|
||||
|
||||
import cn.bootx.platform.daxpay.param.pay.PaySyncParam;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PaySyncService;
|
||||
import cn.bootx.platform.daxpay.service.core.timeout.dao.PayExpiredTimeRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2024/1/5
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayWaitOrderSyncTask {
|
||||
private final PayExpiredTimeRepository repository;
|
||||
|
||||
private final PaySyncService paySyncService;
|
||||
|
||||
public void task(){
|
||||
log.info("开始同步支付订单");
|
||||
// 从超时订单列表中获取到未超时的订单号
|
||||
Set<String> keys = repository.getNormalKeysBy30Day();
|
||||
for (String key : keys) {
|
||||
log.info("key:{}", key);
|
||||
try {
|
||||
Long paymentId = Long.parseLong(key);
|
||||
PaySyncParam paySyncParam = new PaySyncParam();
|
||||
paySyncParam.setPaymentId(paymentId);
|
||||
// 执行网关同步, 网关同步时会对支付的进行状态的处理
|
||||
paySyncService.sync(paySyncParam);
|
||||
} catch (Exception e) {
|
||||
log.error("同步支付订单异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -45,15 +45,15 @@ public abstract class AbsPayRepairStrategy {
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消支付
|
||||
* 关闭本地支付
|
||||
*/
|
||||
public abstract void doCloseHandler();
|
||||
public abstract void doCloseLocalHandler();
|
||||
|
||||
/**
|
||||
* 订单支付超时取消支付
|
||||
* 关闭本地支付和网关支付, 默认为关闭本地支付
|
||||
*/
|
||||
public void doTimeoutHandler() {
|
||||
|
||||
public void doCloseGatewayHandler() {
|
||||
this.doCloseLocalHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
|
1
pom.xml
1
pom.xml
@@ -32,6 +32,7 @@
|
||||
<dax.version>2.0.0</dax.version>
|
||||
<!-- 三方库 -->
|
||||
<slf4j.version>1.7.30</slf4j.version>
|
||||
<redisson.version>3.16.8</redisson.version>
|
||||
<xml-apis.version>1.4.01</xml-apis.version>
|
||||
<spring.checkstyle.version>0.0.38</spring.checkstyle.version>
|
||||
<plumelog.version>3.5.2</plumelog.version>
|
||||
|
Reference in New Issue
Block a user