feat 支付/退款/关闭结构调整

This commit is contained in:
xxm1995
2024-01-24 16:50:26 +08:00
parent a5c0de1ca1
commit dee7088b04
66 changed files with 579 additions and 476 deletions

View File

@@ -76,11 +76,12 @@
- [x] 支付对账收尾
- [x] 支付退款调整为订单+明细
- 2024-01-24:
- [ ] 支付策略优化通道支付单和可退款信息相关关系
- [x] 支付策略优化通道支付单和可退款信息相关关系
- [x] 支付退款处理退款中状态
- [ ] 支付修复策略优化存储记录信息
- [ ] 优化支付修复时的触发来源的获取
- [ ] 微信退款状态不一致补偿
- [ ] 去除各通道支付记录,统一为通道支付记录
- [x] 去除各通道支付记录,统一为通道支付记录
- **任务池**
- [ ] 支付SDK编写
- [ ] 接入支付网关的演示项目

View File

@@ -66,11 +66,16 @@ public class PayOrderController {
return Res.ok(payOrderExtraService.findById(id));
}
@Operation(summary = "查询支付订单关联支付通道")
@GetMapping("/getChannels")
public ResResult<List<PayChannelOrderDto>> getChannels(Long paymentId){
@Operation(summary = "查询支付订单关联支付通道订单")
@GetMapping("/listByChannel")
public ResResult<List<PayChannelOrderDto>> listByChannel(Long paymentId){
return Res.ok(payChannelOrderService.findAllByPaymentId(paymentId));
}
@Operation(summary = "查询支付通道订单详情")
@GetMapping("/getChannel")
public ResResult<PayChannelOrderDto> getChannel(Long id){
return Res.ok(payChannelOrderService.findById(id));
}
@Operation(summary = "同步支付状态")
@PostMapping("/sync")

View File

@@ -12,8 +12,12 @@ import lombok.Getter;
@AllArgsConstructor
public enum PayRefundStatusEnum {
/**
* 接口调用成功不代表成功
*/
PROGRESS("progress","退款中"),
SUCCESS("success","成功"),
FAIL("fail"," ");
FAIL("fail","失败");
/** 编码 */
private final String code;

View File

@@ -18,6 +18,7 @@ public enum PayStatusEnum {
PROGRESS("progress","支付中"),
SUCCESS("success","成功"),
CLOSE("close","支付关闭"),
REFUNDING("refunding","退款中"),
PARTIAL_REFUND("partial_refund","部分退款"),
REFUNDED("refunded","全部退款"),
FAIL("fail","失败");

View File

@@ -20,6 +20,7 @@ public enum PaySyncStatusEnum {
PAY_SUCCESS("pay_success", "支付成功"),
PAY_WAIT("pay_wait", "待支付"),
CLOSED("closed", "已关闭"),
REFUNDING("refunding", "退款中"),
/** 部分退款 */
PARTIAL_REFUND("partial_refund","部分退款"),
/** 全部退款 */

View File

@@ -1,10 +1,6 @@
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;
import cn.bootx.platform.daxpay.param.channel.WeChatPayParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -35,15 +31,4 @@ public class RefundChannelParam {
@Min(1)
private Integer amount;
/**
* 预留的扩展参数, 暂时未使用
* @see AliPayParam
* @see WeChatPayParam
* @see VoucherPayParam
* @see WalletPayParam
*/
@Schema(description = "附加退款参数")
private String channelExtra;
}

View File

@@ -30,7 +30,7 @@ public class RefundParam extends PayCommonParam {
private boolean refundAll;
/**
* 退款号, 可以为空, 但不可以重复, 部分退款时推荐传输
* 退款号可以为空, 但不可以重复, 如果退款号为空, 则系统会自动生成退款号, 与退款ID一致
*/
@Schema(description = "退款号")
private String refundNo;

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.result.order;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -12,15 +13,32 @@ import lombok.Data;
@Schema(title = "支付订单通道响应参数")
public class PayOrderChannelResult {
@Schema(description = "异步支付方式")
private boolean async;
@Schema(description = "通道")
private String channel;
@Schema(description = "支付方式")
private String payWay;
@Schema(description = "异步支付方式")
private boolean async;
/**
* 第三方支付网关生成的订单号, 用与将记录关联起来
*/
@Schema(description = "关联网关支付号")
private String gatewayOrderNo;
/**
* 支付状态
* @see PayStatusEnum
*/
@Schema(description = "支付状态")
private String status;
@Schema(description = "金额")
private Integer amount;
@Schema(description = "可退款金额")
private Integer refundableBalance;
}

View File

@@ -2,7 +2,6 @@ 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.entity.RefundableInfo;
import cn.bootx.platform.daxpay.result.CommonResult;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -73,9 +72,6 @@ public class PayOrderResult extends CommonResult {
@Schema(description = "过期时间")
private LocalDateTime expiredTime;
@Schema(description = "可退款信息列表")
private List<RefundableInfo> refundableInfos;
@Schema(description = "支付通道列表")
private List<PayOrderChannelResult> channels;
}

View File

@@ -27,7 +27,7 @@ public class RefundChannelOrderResult {
private boolean async;
@Schema(description = "订单金额")
private Integer totalAmount;
private Integer orderAmount;
@Schema(description = "退款金额")
private Integer amount;

View File

@@ -7,7 +7,7 @@ import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 异步支付信息,不只局限在支付流程,同步、退款、回调中都会用到
* 异步支付信息
* @author xxm
* @since 2021/2/28
*/
@@ -22,20 +22,21 @@ public class AsyncPayLocal {
private String payWay;
/**
* 第三方支付平台订单号
* 第三方支付网关生成的订单号, 用与将记录关联起来
* 1. 如付款码支付直接成功时会出现
* 2. 回调或者支付同步时也会有这个值
*/
private String gatewayOrderNo;
/** 是否支付完成 */
private boolean payComplete;
/** 支付参数体(通常用于发起支付的参数) */
private String payBody;
/** 订单失效时间, 优先用这个 */
/** 订单失效时间, */
private LocalDateTime expiredTime;
/** 支付完成时间(通常用于接收异步支付返回的时间) */
/** 支付完成时间 从支付网关中获取 */
private LocalDateTime payTime;
}

View File

@@ -0,0 +1,30 @@
package cn.bootx.platform.daxpay.service.common.context;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* 回调信息上下文
* @author xxm
* @since 2024/1/24
*/
@Data
@Accessors(chain = true)
public class CallbackLocal {
/** 回调参数内容 */
private final Map<String, String> callbackParam = new HashMap<>();
/**
* 第三方支付平台订单号
* 1. 如付款码支付直接成功时会出现
*/
private String gatewayOrderNo;
/** 支付完成时间 */
private LocalDateTime payTime;
}

View File

@@ -4,7 +4,7 @@ import lombok.Data;
import lombok.experimental.Accessors;
/**
* 支付通知
* 支付通知(主动发起, 用于通知客户系统)
* @author xxm
* @since 2023/12/24
*/

View File

@@ -0,0 +1,26 @@
package cn.bootx.platform.daxpay.service.common.context;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 支付同步信息
* @author xxm
* @since 2024/1/24
*/
@Data
@Accessors(chain = true)
public class PaySyncLocal {
/**
* 第三方支付网关生成的订单号, 用与将记录关联起来
* 1. 如付款码支付直接成功时会出现
*/
private String gatewayOrderNo;
/** 支付完成时间(通常用于接收异步支付返回的时间) */
private LocalDateTime payTime;
}

View File

@@ -3,9 +3,6 @@ package cn.bootx.platform.daxpay.service.common.context;
import lombok.Getter;
import lombok.experimental.Accessors;
import java.util.HashMap;
import java.util.Map;
/**
* 支付上下文
* @author xxm
@@ -19,24 +16,27 @@ public class PaymentContext {
private final ApiInfoLocal apiInfo = new ApiInfoLocal();;
/** 平台全局配置 */
private final PlatformLocal platform = new PlatformLocal();
private final PlatformLocal platformInfo = new PlatformLocal();
/** 异步支付相关信息, 不只局限在支付流程,同步、回调中都会用到 */
/** 异步支付相关信息 */
private final AsyncPayLocal asyncPayInfo = new AsyncPayLocal();
/** 异步退款相关信息 */
/** 退款相关信息 */
private final RefundLocal refundInfo = new RefundLocal();
/** 消息通知相关信息 */
/** 消息通知(主动发起)相关信息 */
private final NoticeLocal noticeInfo = new NoticeLocal();
/** 回调参数内容 */
private final Map<String, String> callbackParam = new HashMap<>();
/** 回调相关信息 */
private final CallbackLocal callbackInfo = new CallbackLocal();
/** 支付请求相关信息 */
private final RequestLocal request = new RequestLocal();
/** 请求相关信息 */
private final RequestLocal requestInfo = new RequestLocal();
/** 支付对账相关信息 */
private final ReconcileLocal reconcile = new ReconcileLocal();
/** 支付同步相关信息 */
private final PaySyncLocal paySyncInfo = new PaySyncLocal();
/** 对账相关信息 */
private final ReconcileLocal reconcileInfo = new ReconcileLocal();
}

View File

@@ -4,6 +4,7 @@ import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.PayReconcile
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -19,4 +20,7 @@ public class ReconcileLocal {
private List<PayReconcileDetail> reconcileDetails;
/** 支付完成时间 从支付网关中获取 */
private LocalDateTime payTime;
}

View File

@@ -1,5 +1,7 @@
package cn.bootx.platform.daxpay.service.common.context;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.hutool.core.util.IdUtil;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -13,9 +15,19 @@ import lombok.experimental.Accessors;
public class RefundLocal {
/**
* 异步通道退款时发给网关的退款号, 用与将记录关联起来
* 第三方支付网关生成的退款订单号, 用与将记录关联起来
*/
private String gatewayRequestNo;
private String gatewayOrderNo;
/**
* 支付退款ID, 用于异步支付时传入的退款号, 使用退款单ID
*/
private long refundId = IdUtil.getSnowflakeNextId();
/**
* 退款状态, 默认为成功, 通常含有异步支付时, 才会出现别的状态
*/
private PayRefundStatusEnum status = PayRefundStatusEnum.SUCCESS;
/** 错误码 */
private String errorCode;

View File

@@ -5,6 +5,7 @@ import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.redis.RedisClient;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.common.context.CallbackLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.service.core.payment.callback.service.PayCallbackService;
@@ -58,7 +59,7 @@ public class AliPayCallbackService extends AbsPayCallbackStrategy {
*/
@Override
public String getTradeStatus() {
Map<String, String> params = PaymentContextLocal.get().getCallbackParam();
Map<String, String> params = PaymentContextLocal.get().getCallbackInfo().getCallbackParam();
String tradeStatus = params.get(TRADE_STATUS);
if (Objects.equals(tradeStatus, NOTIFY_TRADE_SUCCESS)) {
return PayStatusEnum.SUCCESS.getCode();
@@ -72,7 +73,7 @@ public class AliPayCallbackService extends AbsPayCallbackStrategy {
@SneakyThrows
@Override
public boolean verifyNotify() {
Map<String, String> params =PaymentContextLocal.get().getCallbackParam();
Map<String, String> params =PaymentContextLocal.get().getCallbackInfo().getCallbackParam();
String callReq = JSONUtil.toJsonStr(params);
String appId = params.get(APP_ID);
if (StrUtil.isBlank(appId)) {
@@ -102,16 +103,19 @@ public class AliPayCallbackService extends AbsPayCallbackStrategy {
*/
@Override
public void initContext() {
Map<String, String> callbackParam = PaymentContextLocal.get().getCallbackParam();
CallbackLocal callback = PaymentContextLocal.get()
.getCallbackInfo();
Map<String, String> callbackParam = PaymentContextLocal.get().getCallbackInfo()
.getCallbackParam();
// 订单号
PaymentContextLocal.get().getAsyncPayInfo().setGatewayOrderNo(callbackParam.get(TRADE_NO));
callback.setGatewayOrderNo(callbackParam.get(TRADE_NO));
// 支付时间
String gmpTime = callbackParam.get(GMT_PAYMENT);
if (StrUtil.isNotBlank(gmpTime)) {
LocalDateTime time = LocalDateTimeUtil.parse(gmpTime, DatePattern.NORM_DATETIME_PATTERN);
PaymentContextLocal.get().getAsyncPayInfo().setPayTime(time);
callback.setPayTime(time);
} else {
PaymentContextLocal.get().getAsyncPayInfo().setPayTime(LocalDateTime.now());
callback.setPayTime(LocalDateTime.now());
}
}
@@ -120,7 +124,7 @@ public class AliPayCallbackService extends AbsPayCallbackStrategy {
*/
@Override
public Long getPaymentId() {
Map<String, String> params = PaymentContextLocal.get().getCallbackParam();
Map<String, String> params = PaymentContextLocal.get().getCallbackInfo().getCallbackParam();
return Long.valueOf(params.get(OUT_TRADE_NO));
}

View File

@@ -1,21 +1,16 @@
package cn.bootx.platform.daxpay.service.core.channel.alipay.service;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.entity.RefundableInfo;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.alipay.dao.AliPayOrderManager;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
@@ -36,27 +31,6 @@ public class AliPayOrderService {
private final PayChannelOrderService payChannelOrderService;
/**
* 支付调起成功 更新payment中异步支付类型信息, 如果支付完成, 创建支付宝支付单
*/
public void updatePaySuccess(PayOrder payOrder, PayChannelParam payChannelParam) {
// 更新支付宝异步支付类型信息
payOrder.setAsyncPay(true).setAsyncChannel(PayChannelEnum.ALI.getCode());
payChannelOrderService.updateAsyncChannelOrder(payOrder,payChannelParam);
// 更新支付宝可退款类型信息
List<RefundableInfo> refundableInfos = payOrder.getRefundableInfos();
refundableInfos.removeIf(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getChannel()));
refundableInfos.add(new RefundableInfo()
.setChannel(PayChannelEnum.ALI.getCode())
.setAmount(payChannelParam.getAmount())
);
payOrder.setRefundableInfos(refundableInfos);
// 如果支付完成(付款码情况) 调用 updateSyncSuccess 创建支付宝支付记录
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
this.updateAsyncSuccess(payOrder, payChannelParam.getAmount());
}
}
/**
* 更新异步支付记录成功状态, 并创建支付宝支付记录

View File

@@ -123,7 +123,7 @@ public class AliPayReconcileService {
.map(this::convert)
.collect(Collectors.toList());
// 写入到上下文中
PaymentContextLocal.get().getReconcile().setReconcileDetails(collect);
PaymentContextLocal.get().getReconcileInfo().setReconcileDetails(collect);
}
/**

View File

@@ -1,11 +1,11 @@
package cn.bootx.platform.daxpay.service.core.channel.alipay.service;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.AliPayCode;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.util.PayUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.response.AlipayTradeRefundResponse;
@@ -32,14 +32,13 @@ public class AliPayRefundService {
public void refund(PayOrder payOrder, int amount) {
AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel();
refundModel.setOutTradeNo(String.valueOf(payOrder.getId()));
// 金额转换
String refundAmount = String.valueOf(amount*0.01);
refundModel.setRefundAmount(refundAmount);
// 设置退款信息
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setGatewayRequestNo(PayUtil.getRefundNo());
refundModel.setOutRequestNo(refundInfo.getGatewayRequestNo());
refundModel.setOutRequestNo(String.valueOf(refundInfo.getRefundId()));
try {
AlipayTradeRefundResponse response = AliPayApi.tradeRefundToResponse(refundModel);
if (!Objects.equals(AliPayCode.SUCCESS, response.getCode())) {
@@ -48,6 +47,11 @@ 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(PayRefundStatusEnum.SUCCESS)
.setGatewayOrderNo(response.getTradeNo());
}
}
catch (AlipayApiException e) {
log.error("订单退款失败:", e);

View File

@@ -87,8 +87,7 @@ public class AliPayService {
}
// 付款码支付
else if (Objects.equals(payChannelParam.getWay(), PayWayEnum.BARCODE.getCode())) {
String tradeNo = this.barCode(amount, payOrder, aliPayParam, alipayConfig);
asyncPayInfo.setGatewayOrderNo(tradeNo);
this.barCode(amount, payOrder, aliPayParam, alipayConfig);
}
// 通常是发起支付的参数
asyncPayInfo.setPayBody(payBody);
@@ -99,7 +98,6 @@ public class AliPayService {
*/
public String wapPay(int amount, PayOrder payOrder, AliPayConfig alipayConfig) {
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setSubject(payOrder.getTitle());
model.setOutTradeNo(String.valueOf(payOrder.getId()));
@@ -210,9 +208,10 @@ public class AliPayService {
/**
* 付款码支付
*/
public String barCode(int amount, PayOrder payOrder, AliPayParam aliPayParam, AliPayConfig alipayConfig) {
AlipayTradePayModel model = new AlipayTradePayModel();
public void barCode(int amount, PayOrder payOrder, AliPayParam aliPayParam, AliPayConfig alipayConfig) {
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
AlipayTradePayModel model = new AlipayTradePayModel();
model.setSubject(payOrder.getTitle());
model.setOutTradeNo(String.valueOf(payOrder.getId()));
model.setScene(AliPayCode.BAR_CODE);
@@ -227,7 +226,8 @@ public class AliPayService {
// 支付成功处理 金额2000以下免密支付
if (Objects.equals(response.getCode(), AliPayCode.SUCCESS)) {
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode()).setPayTime(LocalDateTime.now());
return response.getTradeNo();
asyncPayInfo.setGatewayOrderNo(response.getTradeNo())
.setPayComplete(true);
}
// 非支付中响应码, 进行错误处理
if (!Objects.equals(response.getCode(), AliPayCode.INPROCESS)) {
@@ -238,7 +238,6 @@ public class AliPayService {
log.error("主动扫码支付失败", e);
throw new PayFailureException("主动扫码支付失败");
}
return null;
}
/**

View File

@@ -51,10 +51,10 @@ public class AliPaySyncService {
syncResult.setSyncPayInfo(JSONUtil.toJsonStr(response));
// 支付完成 TODO 部分退款也在这个地方, 但无法进行区分, 需要借助对账进行处理
if (Objects.equals(tradeStatus, AliPayCode.PAYMENT_TRADE_SUCCESS) || Objects.equals(tradeStatus, AliPayCode.PAYMENT_TRADE_FINISHED)) {
PaymentContextLocal.get().getAsyncPayInfo().setGatewayOrderNo(response.getTradeNo());
PaymentContextLocal.get().getPaySyncInfo().setGatewayOrderNo(response.getTradeNo());
// 支付完成时间
LocalDateTime payTime = LocalDateTimeUtil.of(response.getSendPayDate());
PaymentContextLocal.get().getAsyncPayInfo().setPayTime(payTime);
PaymentContextLocal.get().getPaySyncInfo().setPayTime(payTime);
return syncResult.setSyncStatus(PaySyncStatusEnum.PAY_SUCCESS);
}
// 待支付

View File

@@ -5,6 +5,7 @@ import cn.bootx.platform.common.redis.RedisClient;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
import cn.bootx.platform.daxpay.service.common.context.CallbackLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.record.callback.dao.PayCallbackRecordManager;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
@@ -57,7 +58,7 @@ public class WeChatPayCallbackService extends AbsPayCallbackStrategy {
*/
@Override
public Long getPaymentId() {
Map<String, String> params = PaymentContextLocal.get().getCallbackParam();
Map<String, String> params = PaymentContextLocal.get().getCallbackInfo().getCallbackParam();
String paymentId = params.get(WeChatPayCode.OUT_TRADE_NO);
return Long.valueOf(paymentId);
}
@@ -67,7 +68,7 @@ public class WeChatPayCallbackService extends AbsPayCallbackStrategy {
*/
@Override
public String getTradeStatus() {
Map<String, String> params = PaymentContextLocal.get().getCallbackParam();
Map<String, String> params = PaymentContextLocal.get().getCallbackInfo().getCallbackParam();
if (WxPayKit.codeIsOk(params.get(WeChatPayCode.RESULT_CODE))) {
return PayStatusEnum.SUCCESS.getCode();
}
@@ -81,7 +82,7 @@ public class WeChatPayCallbackService extends AbsPayCallbackStrategy {
*/
@Override
public boolean verifyNotify() {
Map<String, String> params = PaymentContextLocal.get().getCallbackParam();
Map<String, String> params = PaymentContextLocal.get().getCallbackInfo().getCallbackParam();
String callReq = JSONUtil.toJsonStr(params);
log.info("微信发起回调 报文: {}", callReq);
String appId = params.get(APPID);
@@ -104,16 +105,17 @@ public class WeChatPayCallbackService extends AbsPayCallbackStrategy {
*/
@Override
public void initContext() {
Map<String, String> callbackParam = PaymentContextLocal.get().getCallbackParam();
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
Map<String, String> callbackParam = callbackInfo.getCallbackParam();
// 订单号
PaymentContextLocal.get().getAsyncPayInfo().setGatewayOrderNo(callbackParam.get(WeChatPayCode.TRANSACTION_ID));
callbackInfo.setGatewayOrderNo(callbackParam.get(WeChatPayCode.TRANSACTION_ID));
// 支付时间
String timeEnd = callbackParam.get(TIME_END);
if (StrUtil.isNotBlank(timeEnd)) {
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
PaymentContextLocal.get().getAsyncPayInfo().setPayTime(time);
callbackInfo.setPayTime(time);
} else {
PaymentContextLocal.get().getAsyncPayInfo().setPayTime(LocalDateTime.now());
callbackInfo.setPayTime(LocalDateTime.now());
}
}

View File

@@ -1,19 +1,15 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.dao.WeChatPayOrderManager;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Optional;
/**
@@ -27,23 +23,8 @@ import java.util.Optional;
@RequiredArgsConstructor
public class WeChatPayOrderService {
private final PayChannelOrderService payChannelOrderService;
private final WeChatPayOrderManager weChatPayOrderManager;
/**
* 支付调起成功 更新payment中异步支付类型信息, 如果支付完成, 创建微信支付单
*/
public void updatePaySuccess(PayOrder payOrder, PayChannelParam payChannelParam) {
payOrder.setAsyncPay(true)
.setAsyncChannel(PayChannelEnum.WECHAT.getCode());
payChannelOrderService.updateAsyncChannelOrder(payOrder,payChannelParam);
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
this.createWeChatOrder(payOrder, payChannelParam.getAmount());
}
}
/**
* 异步支付成功, 更新支付记录成功状态, 并创建微信支付记录
*/

View File

@@ -104,8 +104,7 @@ public class WeChatPayService {
}
// 付款码支付
else if (payWayEnum == PayWayEnum.BARCODE) {
String tradeNo = this.barCode(totalFee, payOrder, weChatPayParam.getAuthCode(), weChatPayConfig);
asyncPayInfo.setGatewayOrderNo(tradeNo);
this.barCode(totalFee, payOrder, weChatPayParam.getAuthCode(), weChatPayConfig);
}
asyncPayInfo.setPayBody(payBody);
}
@@ -176,7 +175,9 @@ public class WeChatPayService {
/**
* 付款码支付
*/
private String barCode(String amount, PayOrder payment, String authCode, WeChatPayConfig weChatPayConfig) {
private void barCode(String amount, PayOrder payment, String authCode, WeChatPayConfig weChatPayConfig) {
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
Map<String, String> params = MicroPayModel.builder()
.appid(weChatPayConfig.getWxAppId())
.mch_id(weChatPayConfig.getWxMchId())
@@ -205,27 +206,27 @@ public class WeChatPayService {
if (Objects.equals(resultCode, WeChatPayCode.TRADE_SUCCESS)) {
payment.setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(LocalDateTime.now());
return result.get(WeChatPayCode.TRANSACTION_ID);
asyncPayInfo.setGatewayOrderNo(result.get(WeChatPayCode.TRANSACTION_ID))
.setPayComplete(true);
return;
}
// 支付中, 发起轮训同步
if (Objects.equals(resultCode, WeChatPayCode.TRADE_FAIL)
&& Objects.equals(errCode, WeChatPayCode.TRADE_USERPAYING)) {
SpringUtil.getBean(this.getClass()).rotationSync(payment);
return result.get(WeChatPayCode.TRANSACTION_ID);
asyncPayInfo.setGatewayOrderNo(result.get(WeChatPayCode.TRANSACTION_ID));
return;
}
// 支付撤销
if (Objects.equals(resultCode, WeChatPayCode.TRADE_REVOKED)) {
throw new PayFailureException("用户已撤销支付");
}
// 支付失败
if (Objects.equals(resultCode, WeChatPayCode.TRADE_PAYERROR)
|| Objects.equals(resultCode, WeChatPayCode.TRADE_FAIL)) {
String errorMsg = result.get(WeChatPayCode.ERR_CODE_DES);
throw new PayFailureException(errorMsg);
}
return null;
}
/**
@@ -265,7 +266,7 @@ public class WeChatPayService {
}
/**
* 重试同步支付状态, 最多10次, 30秒不操作微信会自动关闭
* 多次重试同步支付状态, 最多10次, 30秒不操作微信会自动关闭
*/
@Async("bigExecutor")
@Retryable(value = RetryableException.class, maxAttempts = 10, backoff = @Backoff(value = 5000L))

View File

@@ -108,7 +108,7 @@ public class WechatPayReconcileService{
.map(this::convert)
.collect(Collectors.toList());
// 写入到上下文中
PaymentContextLocal.get().getReconcile().setReconcileDetails(collect);
PaymentContextLocal.get().getReconcileInfo().setReconcileDetails(collect);
}
/**

View File

@@ -7,9 +7,8 @@ import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
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.util.PayUtil;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import com.ijpay.core.enums.SignType;
@@ -45,13 +44,12 @@ public class WechatRefundService {
String totalFee = String.valueOf(orderChannel.getAmount());
// 设置退款信息
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setGatewayRequestNo(PayUtil.getRefundNo());
Map<String, String> params = RefundModel.builder()
.appid(weChatPayConfig.getWxAppId())
.mch_id(weChatPayConfig.getWxMchId())
.notify_url(weChatPayConfig.getNotifyUrl())
.out_trade_no(String.valueOf(payOrder.getId()))
.out_refund_no(refundInfo.getGatewayRequestNo())
.out_refund_no(String.valueOf(refundInfo.getRefundId()))
.total_fee(totalFee)
.refund_fee(refundFee)
.nonce_str(WxPayKit.generateStr())
@@ -70,6 +68,9 @@ public class WechatRefundService {
String xmlResult = WxPayApi.orderRefund(false, params, inputStream, weChatPayConfig.getWxMchId());
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
// 微信退款是否成功需要查询状态或者回调, 所以设置为退款中状态
refundInfo.setStatus(PayRefundStatusEnum.PROGRESS)
.setGatewayOrderNo(result.get("refund_id"));
}
/**

View File

@@ -46,7 +46,7 @@ public class PayNoticeService {
try {
// 获取通知地址和内容相关的信息
ApiInfoLocal apiInfo = PaymentContextLocal.get().getApiInfo();
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo();
// 首先判断接口是开启了通知回调功能
if (apiInfo.isNotice()){
PayOrder payOrder = payOrderManager.findById(paymentId).orElseThrow(DataNotExistException::new);

View File

@@ -41,9 +41,6 @@ public class PayBuilder {
LocalDateTime expiredTime = PaymentContextLocal.get()
.getAsyncPayInfo()
.getExpiredTime();
// 可退款信息
@Deprecated
List<RefundableInfo> refundableInfos = buildRefundableInfo(payParam.getPayChannels());
// 计算总价
int sumAmount = payParam.getPayChannels().stream()
.map(PayChannelParam::getAmount)
@@ -59,7 +56,6 @@ public class PayBuilder {
return new PayOrder()
.setBusinessNo(payParam.getBusinessNo())
.setTitle(payParam.getTitle())
.setRefundableInfos(refundableInfos)
.setStatus(PayStatusEnum.PROGRESS.getCode())
.setAmount(sumAmount)
.setExpiredTime(expiredTime)
@@ -75,7 +71,7 @@ public class PayBuilder {
* @param paymentId 支付订单id
*/
public PayOrderExtra buildPayOrderExtra(PayParam payParam, Long paymentId) {
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo();
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
PayOrderExtra payOrderExtra = new PayOrderExtra()
.setClientIp(payParam.getClientIp())

View File

@@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.service.core.order.pay.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
import cn.bootx.platform.daxpay.param.channel.VoucherPayParam;
import cn.bootx.platform.daxpay.param.channel.WalletPayParam;
@@ -15,6 +16,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 关联支付订单支付时通道信息
* @author xxm
@@ -30,26 +33,37 @@ public class PayChannelOrder extends MpCreateEntity implements EntityBaseFunctio
@DbColumn(comment = "支付id")
private Long paymentId;
@DbColumn(comment = "异步支付方式")
private boolean async;
@DbColumn(comment = "通道")
private String channel;
@DbColumn(comment = "支付方式")
private String payWay;
@DbColumn(comment = "金额")
private Integer amount;
@DbColumn(comment = "可退款")
@DbColumn(comment = "可退款")
private Integer refundableBalance;
/**
* 支付状态
* @see PayStatusEnum
*/
@DbColumn(comment = "支付状态")
private String status;
/**
* 异步支付通道发给网关的退款号, 用与将记录关联起来
*/
@DbColumn(comment = "关联网关支付号")
private String gatewayOrderNo;
@DbColumn(comment = "支付方式")
private String payWay;
@DbColumn(comment = "支付时间")
private LocalDateTime payTime;
@DbColumn(comment = "异步支付方式")
private boolean async;
/**
* @see AliPayParam

View File

@@ -1,8 +1,11 @@
package cn.bootx.platform.daxpay.service.core.order.pay.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.util.ResultConvertUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
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;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
@@ -24,13 +27,20 @@ import java.util.Optional;
@Service
@RequiredArgsConstructor
public class PayChannelOrderService {
private final PayChannelOrderManager payChannelOrderManager;
private final PayChannelOrderManager channelOrderManager;
/**
* 根据支付ID查询列表
*/
public List<PayChannelOrderDto> findAllByPaymentId(Long paymentId){
return ResultConvertUtil.dtoListConvert(payChannelOrderManager.findAllByPaymentId(paymentId));
return ResultConvertUtil.dtoListConvert(channelOrderManager.findAllByPaymentId(paymentId));
}
/**
* 查询单条
*/
public PayChannelOrderDto findById(Long id){
return channelOrderManager.findById(id).map(PayChannelOrder::toDto).orElseThrow(() -> new DataNotExistException("通道支付订单未查到"));
}
/**
@@ -38,33 +48,37 @@ public class PayChannelOrderService {
*/
@Transactional(rollbackFor = Exception.class)
public void updateAsyncChannelOrder(PayOrder payOrder, PayChannelParam payChannelParam){
Optional<PayChannelOrder> payOrderChannelOpt = payChannelOrderManager.findByPaymentIdAndChannel(payOrder.getId(), PayChannelEnum.WECHAT.getCode());
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
// 是否支付完成
PayStatusEnum payStatus = asyncPayInfo.isPayComplete() ? PayStatusEnum.SUCCESS : PayStatusEnum.PROGRESS;
Optional<PayChannelOrder> payOrderChannelOpt =
channelOrderManager.findByPaymentIdAndChannel(payOrder.getId(), payChannelParam.getChannel());
if (!payOrderChannelOpt.isPresent()){
payChannelOrderManager.deleteByPaymentIdAndAsync(payOrder.getId());
payChannelOrderManager.save(new PayChannelOrder()
PayChannelOrder payChannelOrder = new PayChannelOrder();
// 替换原有的的支付通道信息
payChannelOrder.setPayWay(payChannelParam.getWay())
.setPaymentId(payOrder.getId())
.setChannel(PayChannelEnum.ALI.getCode())
.setAsync(true)
.setChannel(payChannelParam.getChannel())
.setPayWay(payChannelParam.getWay())
.setAmount(payChannelParam.getAmount())
.setRefundableBalance(payChannelParam.getAmount())
.setPayWay(payChannelParam.getWay())
.setGatewayOrderNo(asyncPayInfo.getGatewayOrderNo())
.setPayTime(asyncPayInfo.getPayTime())
.setChannelExtra(payChannelParam.getChannelExtra())
.setAsync(true)
);
.setStatus(payStatus.getCode());
channelOrderManager.deleteByPaymentIdAndAsync(payChannelOrder.getId());
channelOrderManager.save(payChannelOrder);
} else {
// 更新支付通道信息
payOrderChannelOpt.get()
.setPayWay(payChannelParam.getWay())
.setGatewayOrderNo(asyncPayInfo.getGatewayOrderNo())
.setPayTime(asyncPayInfo.getPayTime())
.setChannelExtra(payChannelParam.getChannelExtra())
.setPayWay(payChannelParam.getWay());
payChannelOrderManager.updateById(payOrderChannelOpt.get());
.setStatus(payStatus.getCode());
channelOrderManager.updateById(payOrderChannelOpt.get());
}
}
/**
* 支付调起成功 更新payment中异步支付类型信息, 如果支付完成, 创建支付宝支付单
*/
@Transactional(rollbackFor = Exception.class)
public void updatePaySuccess(PayOrder payOrder, PayChannelParam payChannelParam) {
// 更新支付宝异步支付类型信息
this.updateAsyncChannelOrder(payOrder, payChannelParam);
}
}

View File

@@ -63,7 +63,6 @@ public class PayOrderService {
refundableInfos.remove(refundableInfo);
refundableInfo.setAmount(refundableInfo.getAmount() - amount);
refundableInfos.add(refundableInfo);
payment.setRefundableInfos(refundableInfos);
}
}

View File

@@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.service.core.order.refund.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.service.core.order.refund.convert.RefundOrderChannelConvert;
import cn.bootx.platform.daxpay.service.dto.order.refund.RefundChannelOrderDto;
import cn.bootx.table.modify.annotation.DbColumn;
@@ -42,11 +43,19 @@ public class PayRefundChannelOrder extends MpBaseEntity implements EntityBaseFun
private boolean async;
@DbColumn(comment = "订单金额")
private Integer totalAmount;
private Integer orderAmount;
@DbColumn(comment = "退款金额")
private Integer amount;
/**
* 退款状态
* @see PayRefundStatusEnum
*/
@DbColumn(comment = "退款状态")
private String status;
/**
* 转换
*/

View File

@@ -3,22 +3,16 @@ package cn.bootx.platform.daxpay.service.core.order.refund.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.entity.RefundableInfo;
import cn.bootx.platform.daxpay.service.common.typehandler.RefundableInfoTypeHandler;
import cn.bootx.platform.daxpay.service.core.order.refund.convert.PayRefundOrderConvert;
import cn.bootx.platform.daxpay.service.dto.order.refund.PayRefundOrderDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import cn.bootx.table.modify.mysql.annotation.DbMySqlFieldType;
import cn.bootx.table.modify.mysql.constants.MySqlFieldTypeEnum;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
/**
* 退款记录
@@ -51,6 +45,10 @@ public class PayRefundOrder extends MpBaseEntity implements EntityBaseFunction<P
@DbColumn(comment = "标题")
private String title;
/** 订单金额 */
@DbColumn(comment = "订单金额")
private Integer orderAmount;
/** 退款金额 */
@DbColumn(comment = "退款金额")
private Integer amount;
@@ -75,14 +73,6 @@ public class PayRefundOrder extends MpBaseEntity implements EntityBaseFunction<P
@DbColumn(comment = "退款时间")
private LocalDateTime refundTime;
/**
* 退款信息列表
*/
@DbColumn(comment = "退款信息列表")
@TableField(typeHandler = RefundableInfoTypeHandler.class)
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
private List<RefundableInfo> refundableInfo;
/**
* 退款状态
* @see PayRefundStatusEnum

View File

@@ -1,18 +1,20 @@
package cn.bootx.platform.daxpay.service.core.payment.close.service;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.param.pay.PayCloseParam;
import cn.bootx.platform.daxpay.entity.RefundableInfo;
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;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.service.core.payment.close.factory.PayCloseStrategyFactory;
import cn.bootx.platform.daxpay.service.core.record.close.entity.PayCloseRecord;
import cn.bootx.platform.daxpay.service.core.record.close.service.PayCloseRecordService;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.service.func.AbsPayCloseStrategy;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.lock.LockInfo;
@@ -23,7 +25,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -35,6 +39,7 @@ import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class PayCloseService {
private final PayChannelOrderManager payChannelOrderManager;
private final PayOrderService payOrderService;
private final PayOrderQueryService payOrderQueryService;
private final PayCloseRecordService payCloseRecordService;
@@ -70,30 +75,39 @@ public class PayCloseService {
* 关闭支付记录
*/
private void close(PayOrder payOrder) {
// 状态检查, 只有支付中可以进行取消支付
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())){
throw new PayFailureException("订单不是支付中, 无法进行关闭订单");
}
// 1.获取支付方式(退款列表中提取),通过工厂生成对应的策略组
List<String> channels = payOrder.getRefundableInfos()
.stream()
.map(RefundableInfo::getChannel)
.collect(Collectors.toList());
List<AbsPayCloseStrategy> payCloseStrategies = PayCloseStrategyFactory.createAsyncLast(channels);
if (CollectionUtil.isEmpty(payCloseStrategies)) {
throw new PayUnsupportedMethodException();
}
// 2.初始化关闭支付的参数
payCloseStrategies.forEach(strategy -> strategy.initCloseParam(payOrder));
try {
// 状态检查, 只有支付中可以进行取消支付
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())){
throw new PayFailureException("订单不是支付中, 无法进行关闭订单");
}
// 0.基础数据准备
Map<String, PayChannelOrder> orderChannelMap = payChannelOrderManager.findAllByPaymentId(payOrder.getId())
.stream()
.collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
// 1.获取支付方式, 通过工厂生成对应的策略组
List<String> channels = orderChannelMap.values().stream()
.map(PayChannelOrder::getChannel)
.collect(Collectors.toList());
List<AbsPayCloseStrategy> payCloseStrategies = PayCloseStrategyFactory.createAsyncLast(channels);
if (CollectionUtil.isEmpty(payCloseStrategies)) {
throw new PayUnsupportedMethodException();
}
// 2.初始化关闭支付的参数
for (AbsPayCloseStrategy strategy : payCloseStrategies) {
strategy.initCloseParam(payOrder, orderChannelMap.get(strategy.getType().getCode()));
}
// 3.关闭前准备
payCloseStrategies.forEach(AbsPayCloseStrategy::doBeforeCloseHandler);
// 4.执行关闭策略
payCloseStrategies.forEach(AbsPayCloseStrategy::doCloseHandler);
// 5.关闭成功后处理
payCloseStrategies.forEach(AbsPayCloseStrategy::doSuccessHandler);
}
catch (PayFailureException e) {
// 记录关闭失败的记录
@@ -120,10 +134,10 @@ public class PayCloseService {
*/
private void saveRecord(PayOrder payOrder, boolean closed, String errMsg){
String clientIp = PaymentContextLocal.get()
.getRequest()
.getRequestInfo()
.getClientIp();
String reqId = PaymentContextLocal.get()
.getRequest()
.getRequestInfo()
.getReqId();
PayCloseRecord record = new PayCloseRecord()
.setPaymentId(payOrder.getId())

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.close.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
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;
@@ -29,6 +30,11 @@ public class AliPayCloseStrategy extends AbsPayCloseStrategy {
private final AliPayCloseService aliPayCloseService;
@Override
public PayChannelEnum getType() {
return PayChannelEnum.ALI;
}
/**
* 关闭前的处理方式
*/

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.close.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.core.channel.cash.service.CashService;
import cn.bootx.platform.daxpay.service.func.AbsPayCloseStrategy;
import lombok.RequiredArgsConstructor;
@@ -20,6 +21,12 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@RequiredArgsConstructor
public class CashPayCloseStrategy extends AbsPayCloseStrategy {
private final CashService cashService;
@Override
public PayChannelEnum getType() {
return PayChannelEnum.CASH;
}
/**
* 关闭操作
*/

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.close.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.func.AbsPayCloseStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -20,6 +21,11 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
public class UnionPayCloseStrategy extends AbsPayCloseStrategy {
@Override
public PayChannelEnum getType() {
return PayChannelEnum.UNION_PAY;
}
/**
* 关闭操作
*/

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.close.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.core.channel.voucher.service.VoucherPayOrderService;
import cn.bootx.platform.daxpay.service.core.channel.voucher.service.VoucherPayService;
import cn.bootx.platform.daxpay.service.func.AbsPayCloseStrategy;
@@ -24,6 +25,11 @@ public class VoucherPayCloseStrategy extends AbsPayCloseStrategy {
private final VoucherPayService voucherPayService;
private final VoucherPayOrderService voucherPayOrderService;
@Override
public PayChannelEnum getType() {
return PayChannelEnum.VOUCHER;
}
/**
* 关闭操作
*/

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.close.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletPayOrderService;
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletPayService;
import cn.bootx.platform.daxpay.service.func.AbsPayCloseStrategy;
@@ -23,6 +24,11 @@ public class WalletPayCloseStrategy extends AbsPayCloseStrategy {
private final WalletPayService walletPayService;
private final WalletPayOrderService walletPayOrderService;
@Override
public PayChannelEnum getType() {
return PayChannelEnum.WALLET;
}
/**
* 关闭操作
*/

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.close.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
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;
@@ -28,6 +29,12 @@ public class WeChatPayCloseStrategy extends AbsPayCloseStrategy {
private final WeChatPayCloseService weChatPayCloseService;
private WeChatPayConfig weChatPayConfig;
@Override
public PayChannelEnum getType() {
return PayChannelEnum.WECHAT;
}
/**
* 关闭前的处理方式
*/

View File

@@ -37,7 +37,7 @@ public class PaymentAssistService {
*/
private void initPlatform(){
PlatformConfig config = platformConfigService.getConfig();
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo();
platform.setSignType(config.getSignType());
platform.setSignSecret(config.getSignSecret());
platform.setNotifyUrl(config.getNotifyUrl());
@@ -50,7 +50,7 @@ public class PaymentAssistService {
* 初始化请求相关信息上下文
*/
private void initRequest(PayCommonParam payCommonParam){
RequestLocal request = PaymentContextLocal.get().getRequest();
RequestLocal request = PaymentContextLocal.get().getRequestInfo();
request.setClientIp(payCommonParam.getClientIp())
.setAttach(payCommonParam.getAttach())
.setSign(payCommonParam.getSign())

View File

@@ -39,7 +39,7 @@ public class PaymentSignService {
return;
}
// 参数转换为Map对象
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo();
String signType = platform.getSignType();
if (Objects.equals(PaySignTypeEnum.HMAC_SHA256.getCode(), signType)){
boolean verified = PaySignUtil.verifyHmacSha256Sign(param, platform.getSignSecret(), param.getSign());

View File

@@ -3,7 +3,6 @@ package cn.bootx.platform.daxpay.service.core.payment.pay.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.param.pay.PayParam;
@@ -31,6 +30,8 @@ import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import static cn.bootx.platform.daxpay.code.PayStatusEnum.*;
/**
* 支付支持服务
* @author xxm
@@ -73,7 +74,7 @@ public class PayAssistService {
return;
}
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo();
// 支付订单是非为空
if (Objects.nonNull(order)){
asyncPayInfo.setExpiredTime(order.getExpiredTime());
@@ -93,7 +94,7 @@ public class PayAssistService {
*/
private void initNotice(PayParam payParam){
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo();
// 异步回调
if (!payParam.isNotNotify()){
noticeInfo.setNotifyUrl(payParam.getReturnUrl());
@@ -171,7 +172,7 @@ public class PayAssistService {
PayOrder payOrder = payOrderQueryService.findByBusinessNo(businessNo).orElse(null);
if (Objects.nonNull(payOrder)) {
// 待支付
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())){
if (Objects.equals(payOrder.getStatus(), PROGRESS.getCode())){
// 如果支付超时, 触发订单同步操作, 同时抛出异常
if (Objects.nonNull(payOrder.getExpiredTime()) && LocalDateTimeUtil.ge(LocalDateTime.now(), payOrder.getExpiredTime())) {
paySyncService.syncPayOrder(payOrder);
@@ -180,16 +181,16 @@ public class PayAssistService {
return payOrder;
}
// 已经支付状态
if (PayStatusEnum.SUCCESS.getCode().equals(payOrder.getStatus())) {
if (SUCCESS.getCode().equals(payOrder.getStatus())) {
throw new PayFailureException("已经支付成功,请勿重新支付");
}
// 支付失败类型状态
List<String> tradesStatus = Arrays.asList(PayStatusEnum.FAIL.getCode(), PayStatusEnum.CLOSE.getCode());
List<String> tradesStatus = Arrays.asList(FAIL.getCode(), CLOSE.getCode());
if (tradesStatus.contains(payOrder.getStatus())) {
throw new PayFailureException("支付失败或已经被关闭");
}
// 退款类型状态
tradesStatus = Arrays.asList(PayStatusEnum.REFUNDED.getCode(), PayStatusEnum.PARTIAL_REFUND.getCode());
tradesStatus = Arrays.asList(REFUNDED.getCode(), PARTIAL_REFUND.getCode(), REFUNDING.getCode());
if (tradesStatus.contains(payOrder.getStatus())) {
throw new PayFailureException("退款中");
}

View File

@@ -1,20 +1,18 @@
package cn.bootx.platform.daxpay.service.core.payment.pay.service;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.SimplePayParam;
import cn.bootx.platform.daxpay.result.pay.PayResult;
import cn.bootx.platform.daxpay.service.core.order.pay.builder.PayBuilder;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.payment.pay.factory.PayStrategyFactory;
import cn.bootx.platform.daxpay.service.core.order.pay.builder.PayBuilder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.service.core.payment.pay.factory.PayStrategyFactory;
import cn.bootx.platform.daxpay.service.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.service.func.PayStrategyConsumer;
import cn.bootx.platform.daxpay.util.PayUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
@@ -27,10 +25,14 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Consumer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.bootx.platform.daxpay.code.PayStatusEnum.*;
/**
* 支付流程
@@ -146,23 +148,26 @@ public class PayService {
paymentStrategy.initPayParam(payOrder, payParam);
}
// 3.执行支付前处理动作
// 3.1 执行支付前处理动作
payStrategyList.forEach(AbsPayStrategy::doBeforePayHandler);
// 3.2 执行通道支付但的生成动作
payStrategyList.forEach(AbsPayStrategy::generateChannelOrder);
// 4.1 支付操作
payStrategyList.forEach(AbsPayStrategy::doPayHandler);
// 4.2 支付调用成功操作, 进行扣款、创建记录类类似的操作
payStrategyList.forEach(AbsPayStrategy::doSuccessHandler);
// 4.3 获取通道支付订单进行保存
// 4.3 获取通道支付订单进行保存, 异步支付通道的订单单独处理
List<PayChannelOrder> channelOrders = payStrategyList.stream()
.map(AbsPayStrategy::generateChannelOrder)
.map(AbsPayStrategy::getChannelOrder)
.filter(Objects::nonNull)
.collect(Collectors.toList());
payChannelOrderManager.saveAll(channelOrders);
// 5. 如果没有异步支付, 直接进行成功处理
if (PayUtil.isNotSync(payParam.getPayChannels())) {
// 修改支付订单状态为成功
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
payOrder.setStatus(SUCCESS.getCode());
payOrder.setPayTime(LocalDateTime.now());
payOrderService.updateById(payOrder);
}
@@ -173,9 +178,9 @@ public class PayService {
*/
private PayResult paySyncNotFirst(PayParam payParam, PayOrder payOrder) {
// 1. 处理支付完成情况(完成/退款)
List<String> trades = Arrays.asList(PayStatusEnum.SUCCESS.getCode(), PayStatusEnum.CLOSE.getCode(),
PayStatusEnum.PARTIAL_REFUND.getCode(), PayStatusEnum.REFUNDED.getCode());
// 1. 处理支付结束情况(完成/退款/关闭/错误)
List<String> trades = Arrays.asList(SUCCESS.getCode(), CLOSE.getCode(), PARTIAL_REFUND.getCode(),
REFUNDED.getCode(), REFUNDING.getCode(), FAIL.getCode());
if (trades.contains(payOrder.getStatus())) {
return PayBuilder.buildPayResultByPayOrder(payOrder);
}
@@ -203,22 +208,4 @@ public class PayService {
// 7. 组装返回参数
return PayBuilder.buildPayResultByPayOrder(payOrder);
}
/**
* 执行策略中不同的handler
* @param payment 主支付对象
* @param strategyList 策略列表
* @param payMethod 执行支付的函数或者支付前的函数
* @param successMethod 执行成功的函数
*/
private void doHandler(PayOrder payment, List<AbsPayStrategy> strategyList, Consumer<AbsPayStrategy> payMethod,
PayStrategyConsumer<List<AbsPayStrategy>, PayOrder> successMethod) {
// 执行策略操作,如支付前/支付时
// 等同strategyList.forEach(payMethod.accept(PaymentStrategy))
strategyList.forEach(payMethod);
// 执行操作成功的处理
Optional.ofNullable(successMethod).ifPresent(fun -> fun.accept(strategyList, payment));
}
}

View File

@@ -5,11 +5,9 @@ import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayService;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
import cn.bootx.platform.daxpay.service.func.AbsPayStrategy;
import cn.hutool.core.util.StrUtil;
@@ -84,7 +82,7 @@ public class AliPayStrategy extends AbsPayStrategy {
}
/**
* 支付调起成功
* 支付调起成功, 保存或更新通道支付订单
*/
@Override
public void doSuccessHandler() {
@@ -92,19 +90,12 @@ public class AliPayStrategy extends AbsPayStrategy {
}
/**
* 生成通道支付单
* 不使用默认的生成通道支付单方法, 异步支付通道的支付订单自己管理
*/
@Override
public PayChannelOrder generateChannelOrder() {
String gatewayOrderNo = PaymentContextLocal.get()
.getAsyncPayInfo()
.getGatewayOrderNo();
PayChannelOrder payChannelOrder = super.generateChannelOrder();
payChannelOrder.setGatewayOrderNo(gatewayOrderNo);
return payChannelOrder;
public void generateChannelOrder() {
}
/**
* 初始化支付宝配置信息
*/

View File

@@ -31,9 +31,5 @@ public class UnionPayStrategy extends AbsPayStrategy {
}
@Override
public void doCloseHandler() {
}
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.core.payment.pay.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.core.channel.voucher.entity.Voucher;
import cn.bootx.platform.daxpay.service.core.channel.voucher.entity.VoucherRecord;
import cn.bootx.platform.daxpay.service.core.channel.voucher.service.VoucherPayService;
@@ -47,17 +48,15 @@ public class VoucherPayStrategy extends AbsPayStrategy {
/**
* 支付操作
* 1. 异步支付: 发起支付时冻结, 支付完成后扣减, 支付失败和关闭支付后解冻
* 2. 同步支付: 直接扣减
*/
@Override
public void doPayHandler() {
VoucherRecord voucherRecord;
if (this.getOrder().isAsyncPay()){
voucherRecord = voucherPayService.freezeBalance(this.getPayChannelParam().getAmount(), this.getOrder(), this.voucher);
} else {
voucherRecord = voucherPayService.pay(this.getPayChannelParam().getAmount(), this.getOrder(), this.voucher);
}
// if (this.getOrder().isAsyncPay()){
// voucherRecord = voucherPayService.freezeBalance(this.getPayChannelParam().getAmount(), this.getOrder(), this.voucher);
// } else {
voucherRecord = voucherPayService.pay(this.getPayChannelParam().getAmount(), this.getOrder(), this.voucher);
// }
voucherPayOrderService.savePayment(this.getOrder(), getPayParam(), getPayChannelParam(), voucherRecord);
}
@@ -69,16 +68,8 @@ public class VoucherPayStrategy extends AbsPayStrategy {
if (this.getOrder().isAsyncPay()){
voucherPayService.paySuccess(this.getOrder().getId());
}
this.getChannelOrder().setStatus(PayStatusEnum.SUCCESS.getCode());
voucherPayOrderService.updateSuccess(this.getOrder().getId());
}
/**
* 关闭支付
*/
@Override
public void doCloseHandler() {
voucherPayService.close(this.getOrder().getId());
voucherPayOrderService.updateClose(this.getOrder().getId());
}
}

View File

@@ -101,13 +101,4 @@ public class WalletPayStrategy extends AbsPayStrategy {
walletPayOrderService.updateSuccess(this.getOrder().getId());
}
/**
* 取消支付并返还金额
*/
@Override
public void doCloseHandler() {
walletPayService.close(this.getOrder().getId());
walletPayOrderService.updateClose(this.getOrder().getId());
}
}

View File

@@ -4,11 +4,9 @@ import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayService;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
import cn.bootx.platform.daxpay.service.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.service.param.channel.wechat.WeChatPayParam;
@@ -89,26 +87,18 @@ public class WeChatPayStrategy extends AbsPayStrategy {
}
/**
* 支付调起成功
* 支付调起成功 , 保存或更新通道支付订单
*/
@Override
public void doSuccessHandler() {
channelOrderService.updateAsyncChannelOrder(this.getOrder(), this.getPayChannelParam());
}
/**
* 生成通道支付单
* 不使用默认的生成通道支付单方法, 异步支付通道的支付订单自己管理
*/
@Override
public PayChannelOrder generateChannelOrder() {
String gatewayOrderNo = PaymentContextLocal.get()
.getAsyncPayInfo()
.getGatewayOrderNo();
PayChannelOrder payChannelOrder = super.generateChannelOrder();
payChannelOrder.setGatewayOrderNo(gatewayOrderNo);
return payChannelOrder;
}
public void generateChannelOrder() {}
/**
* 初始化微信支付

View File

@@ -58,7 +58,7 @@ public class PayReconcileService {
}
// 保存转换后的通用结构对账单
List<PayReconcileDetail> reconcileDetails = PaymentContextLocal.get()
.getReconcile()
.getReconcileInfo()
.getReconcileDetails();
reconcileDetailManager.saveAll(reconcileDetails);
}

View File

@@ -2,22 +2,20 @@ package cn.bootx.platform.daxpay.service.core.payment.refund.service;
import cn.bootx.platform.common.core.exception.ValidationFailedException;
import cn.bootx.platform.common.core.util.CollUtil;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.service.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import cn.hutool.core.util.IdUtil;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -42,6 +40,7 @@ public class PayRefundAssistService {
private final PayOrderQueryService payOrderQueryService;
private final PayRefundOrderManager payRefundOrderManager;
private final PayRefundChannelOrderManager payRefundChannelOrderManager;
/**
@@ -57,7 +56,7 @@ public class PayRefundAssistService {
*/
private void initNotice(RefundParam param) {
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo();
// 异步回调
if (!param.isNotNotify()){
noticeInfo.setNotifyUrl(param.getReturnUrl());
@@ -67,25 +66,11 @@ public class PayRefundAssistService {
}
}
/**
* 根据退款参数获取支付订单, 并进行检查
*/
public PayOrder getPayOrderAndCheckByRefundParam(RefundParam param, boolean simple){
// 全额退款和部分退款校验
if (!param.isRefundAll()) {
if (CollUtil.isEmpty(param.getRefundChannels())) {
throw new ValidationFailedException("退款通道参数不能为空");
}
if (Objects.isNull(param.getRefundNo())) {
throw new ValidationFailedException("部分退款时退款单号必传");
}
}
// 退款号唯一校验
if (StrUtil.isNotBlank(param.getRefundNo())
&& payRefundOrderManager.existsByRefundNo(param.getRefundNo())){
throw new PayFailureException("退款单号已存在");
}
/**
* 根据退款参数获取支付订单
*/
public PayOrder getPayOrder(RefundParam param){
PayOrder payOrder = null;
if (Objects.nonNull(param.getPaymentId())){
payOrder = payOrderQueryService.findById(param.getPaymentId())
@@ -95,20 +80,27 @@ public class PayRefundAssistService {
payOrder = payOrderQueryService.findByBusinessNo(param.getBusinessNo())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
return payOrder;
}
// 简单退款处理
if (simple){
// 简单退款校验
if (payOrder.isCombinationPay()){
throw new PayFailureException("组合支付不可以使用简单退款方式");
/**
* 根据退款参数获取支付订单, 并进行检查
*/
public void checkByRefundParam(RefundParam param, PayOrder payOrder){
// 全额退款和部分退款校验
if (!param.isRefundAll()) {
if (CollUtil.isEmpty(param.getRefundChannels())) {
throw new ValidationFailedException("退款通道参数不能为空");
}
if (Objects.isNull(param.getRefundNo())) {
throw new ValidationFailedException("部分退款时退款单号必传");
}
// 设置退款参数的通道配置
String channel = payOrder.getRefundableInfos()
.get(0)
.getChannel();
param.getRefundChannels().get(0).setChannel(channel);
}
// 简单退款校验
if (payOrder.isCombinationPay()){
throw new PayFailureException("组合支付不可以使用简单退款方式");
}
// 状态判断, 支付中/失败/取消等不能进行退款
List<String> tradesStatus = Arrays.asList(
@@ -119,39 +111,46 @@ public class PayRefundAssistService {
PayStatusEnum statusEnum = PayStatusEnum.findByCode(payOrder.getStatus());
throw new PayFailureException("当前状态["+statusEnum.getName()+"]不允许状态非法, 无法退款");
}
return payOrder;
// 退款号唯一校验
if (StrUtil.isNotBlank(param.getRefundNo())
&& payRefundOrderManager.existsByRefundNo(param.getRefundNo())){
throw new PayFailureException("退款单号已存在");
}
}
/**
* 保存退款记录 成不成功都记录
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public PayRefundOrder generateRefundOrder(RefundParam refundParam, PayOrder payOrder){
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
// 总退款金额
// 此次的总退款金额
Integer amount = refundParam.getRefundChannels()
.stream()
.map(RefundChannelParam::getAmount)
.reduce(0, Integer::sum);
// 生成退款订单
PayRefundOrder refundOrder = new PayRefundOrder()
.setPaymentId(payOrder.getId())
.setBusinessNo(payOrder.getBusinessNo())
.setRefundNo(refundParam.getRefundNo())
.setOrderAmount(payOrder.getAmount())
.setAmount(amount)
.setRefundableBalance(payOrder.getRefundableBalance())
.setRefundTime(LocalDateTime.now())
.setTitle(payOrder.getTitle())
.setErrorCode(asyncRefundInfo.getErrorCode())
.setErrorMsg(asyncRefundInfo.getErrorMsg())
.setStatus(Objects.isNull(asyncRefundInfo.getErrorCode()) ? PayRefundStatusEnum.SUCCESS.getCode() : PayRefundStatusEnum.FAIL.getCode())
.setStatus(asyncRefundInfo.getStatus().getCode())
.setClientIp(refundParam.getClientIp())
.setReqId(PaymentContextLocal.get().getRequest().getReqId());
.setReqId(PaymentContextLocal.get().getRequestInfo().getReqId());
// 主键使用预先生成的ID, 如果有异步通道, 关联的退款号就是这个ID
long refundId = asyncRefundInfo.getRefundId();
refundOrder.setId(refundId);
// 退款号, 如不传输, 使用ID作为退款号
if(StrUtil.isBlank(refundOrder.getRefundNo())){
long id = IdUtil.getSnowflakeNextId();
refundOrder.setRefundNo(String.valueOf(id))
.setId(id);
refundOrder.setRefundNo(String.valueOf(refundId));
}
return refundOrder;
}
@@ -159,7 +158,7 @@ public class PayRefundAssistService {
/**
* 保存退款记录和对应的通道记录
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Transactional(rollbackFor = Exception.class)
public void saveOrderAndChannels(PayRefundOrder refundOrder,List<PayRefundChannelOrder> refundChannelOrders){
payRefundOrderManager.save(refundOrder);
for (PayRefundChannelOrder refundOrderChannel : refundChannelOrders) {

View File

@@ -3,6 +3,7 @@ package cn.bootx.platform.daxpay.service.core.payment.refund.service;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.common.core.util.ValidationUtil;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
@@ -10,6 +11,8 @@ import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import cn.bootx.platform.daxpay.param.pay.SimpleRefundParam;
import cn.bootx.platform.daxpay.result.pay.RefundResult;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
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;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
@@ -57,7 +60,7 @@ public class PayRefundService {
*/
@Transactional(rollbackFor = Exception.class )
public RefundResult refund(RefundParam param){
return this.refund(param,false);
return this.refundAdapter(param,false);
}
/**
@@ -69,46 +72,53 @@ public class PayRefundService {
// 构建退款参数
RefundParam refundParam = new RefundParam();
BeanUtil.copyProperties(param,refundParam);
RefundChannelParam channelParam = new RefundChannelParam()
.setAmount(param.getAmount())
.setChannelExtra(param.getChannelExtra());
RefundChannelParam channelParam = new RefundChannelParam().setAmount(param.getAmount());
refundParam.setRefundChannels(Collections.singletonList(channelParam));
return this.refund(refundParam,true);
return this.refundAdapter(refundParam,true);
}
/**
* 支付退款方法
* 支付退款适配方法, 处理简单退款与普通退款, 全部退款和部分退款的参数转换
* @param param 退款参数
* @param simple 是否简单退款
*/
private RefundResult refund(RefundParam param, boolean simple){
// 检查获取支付订单, 同时设置退款参数中对应的支付通道参数
PayOrder payOrder = payRefundAssistService.getPayOrderAndCheckByRefundParam(param, simple);
private RefundResult refundAdapter(RefundParam param, boolean simple){
// 获取支付订单
PayOrder payOrder = payRefundAssistService.getPayOrder(param);
// 第一次检查退款参数, 校验一些特殊情况
payRefundAssistService.checkByRefundParam(param, payOrder);
// 组装退款参数, 处理全部退款和简单退款情况
List<PayChannelOrder> payChannelOrders = payChannelOrderManager.findAllByPaymentId(payOrder.getId());
// 是否全部退款
if (param.isRefundAll()){
// 全部退款根据支付订单的退款信息构造退款参数
List<RefundChannelParam> channelParams = payChannelOrders
.stream()
.map(o -> new RefundChannelParam()
.setChannel(o.getChannel())
.setAmount(o.getAmount()))
.collect(Collectors.toList());
param.setRefundChannels(channelParams);
} else if (simple) {
// 如果不是全部退款且是简单退款的情况下, 设置要退款金额
String channel = payChannelOrders.get(0).getChannel();
param.getRefundChannels().get(0).setChannel(channel);
}
// 参数校验
ValidationUtil.validateParam(param);
// ----------------------------- 发起退款操作 --------------------------------------------
// 加锁
LockInfo lock = lockTemplate.lock("payment:refund:" + payOrder.getId());
if (Objects.isNull(lock)){
throw new RepetitiveOperationException("退款处理中,请勿重复操作");
}
try {
// 退款上下文初始化
payRefundAssistService.initRefundContext(param);
// 是否全部退款
if (param.isRefundAll()){
// 全部退款根据支付订单的退款信息构造退款参数
List<RefundChannelParam> channelParams = payOrder.getRefundableInfos()
.stream()
.map(o -> new RefundChannelParam()
.setChannel(o.getChannel())
.setAmount(o.getAmount()))
.collect(Collectors.toList());
param.setRefundChannels(channelParams);
}
// 分支付通道进行退款
return this.refundByChannel(param,payOrder);
return this.refundByChannel(param,payOrder,payChannelOrders);
} finally {
lockTemplate.releaseLock(lock);
}
@@ -117,75 +127,101 @@ public class PayRefundService {
/**
* 分支付通道进行退款
*/
public RefundResult refundByChannel(RefundParam refundParam, PayOrder payOrder){
try {
// 0.基础数据准备
List<RefundChannelParam> refundChannels = refundParam.getRefundChannels();
Map<String, PayChannelOrder> orderChannelMap = payChannelOrderManager.findAllByPaymentId(payOrder.getId())
.stream()
.collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
// 比对通道支付单是否与可退款记录数量一致
if (orderChannelMap.size() != refundChannels.size()){
throw new PayFailureException("通道支付单是否与可退款记录数量不一致");
}
public RefundResult refundByChannel(RefundParam refundParam, PayOrder payOrder, List<PayChannelOrder> payChannelOrders){
// 0.基础数据准备, 并比对通道支付单是否与可退款记录数量一致
Map<String, PayChannelOrder> orderChannelMap = payChannelOrders.stream()
.collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
List<RefundChannelParam> refundChannels = refundParam.getRefundChannels();
try {
// 1.获取退款参数方式,通过工厂生成对应的策略组
List<AbsPayRefundStrategy> payRefundStrategies = PayRefundStrategyFactory.createAsyncLast(refundChannels);
if (CollectionUtil.isEmpty(payRefundStrategies)) {
throw new PayUnsupportedMethodException();
}
// 2.初始化退款策略的参数
for (AbsPayRefundStrategy refundStrategy : payRefundStrategies) {
refundStrategy.initRefundParam(payOrder, refundParam, orderChannelMap.get(refundStrategy.getType().getCode()));
PayChannelOrder payChannelOrder = orderChannelMap.get(refundStrategy.getType().getCode());
if (Objects.isNull(payChannelOrder)){
throw new PayFailureException("[数据异常]进行退款的通道没有对应的支付单, 无法退款");
}
refundStrategy.initRefundParam(payOrder, refundParam, payChannelOrder);
}
// 3.退款前准备操作
// 3.1 退款前准备操作
payRefundStrategies.forEach(AbsPayRefundStrategy::doBeforeRefundHandler);
// 4.执行退款策略
// 3.2 生成各通道退款订单
payRefundStrategies.forEach(AbsPayRefundStrategy::generateChannelOrder);
// 3.3 执行退款策略
payRefundStrategies.forEach(AbsPayRefundStrategy::doRefundHandler);
// 5 生成退款相关订单并保存
List<PayRefundChannelOrder> refundChannelOrders = payRefundStrategies.stream()
.map(AbsPayRefundStrategy::generateChannelOrder)
// 3.4 执行退款发起成功后操作
payRefundStrategies.forEach(AbsPayRefundStrategy::doSuccessHandler);
// 4 更新各支付通道订单的信息
List<PayChannelOrder> channelOrders = payRefundStrategies.stream()
.map(AbsPayRefundStrategy::getPayChannelOrder)
.collect(Collectors.toList());
PayRefundOrder refundOrder = payRefundAssistService.generateRefundOrder(refundParam, payOrder);
payRefundAssistService.saveOrderAndChannels(refundOrder,refundChannelOrders);
// 6.退款成功后支付单处理
this.successHandler(refundParam, payOrder);
// 7. 返回结果
payChannelOrderManager.updateAllById(channelOrders);
// 5 获取退款通道订单, 进行保存
List<PayRefundChannelOrder> refundChannelOrders = payRefundStrategies.stream()
.map(AbsPayRefundStrategy::getRefundChannelOrder)
.collect(Collectors.toList());
// 6.进行成功处理, 分别处理退款订单, 通道退款订单, 支付订单
PayRefundOrder refundOrder = this.successHandler(refundParam, refundChannelOrders, payOrder);
return new RefundResult()
.setRefundId(refundOrder.getId())
.setRefundNo(refundParam.getRefundNo());
}
catch (Exception e) {
// 失败处理
this.errorHandler(refundParam, payOrder, e);
PaymentContextLocal.get().getRefundInfo().setStatus(PayRefundStatusEnum.FAIL);
this.errorHandler(refundParam, payOrder);
throw e;
}
}
/**
* 退款订单成功处理, 保存退订单, 通道退款订单, 更新支付订单
* 退款订单成功处理, 保存退订单, 通道退款订单, 更新支付订单
*/
private void successHandler(RefundParam refundParam, PayOrder payOrder) {
// 退款金额
private PayRefundOrder successHandler(RefundParam refundParam, List<PayRefundChannelOrder> refundChannelOrders, PayOrder payOrder) {
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
// ----------------------- 支付订单处理 ---------------------------------------
// 此次的总退款金额
Integer amount = refundParam.getRefundChannels().stream()
.map(RefundChannelParam::getAmount)
.reduce(0, Integer::sum);
// 剩余可退款余额
int refundableBalance = payOrder.getRefundableBalance() - amount;
// 退款完成
if (refundableBalance == 0) {
payOrder.setRefundableBalance(refundableBalance);
// 设置支付订单状态
if (asyncRefundInfo.getStatus() == PayRefundStatusEnum.PROGRESS) {
// 设置为退款中
payOrder.setStatus(PayStatusEnum.REFUNDING.getCode());
} else if (refundableBalance == 0) {
// 退款完成
payOrder.setStatus(PayStatusEnum.REFUNDED.getCode());
} else {
// 部分退款
payOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode());
}
payOrder.setRefundableBalance(refundableBalance);
// ----------------------- 退款订单处理 ---------------------------------------
// 生成退款订单
PayRefundOrder refundOrder = payRefundAssistService.generateRefundOrder(refundParam, payOrder);
// 更新或保存相关订单
payRefundAssistService.saveOrderAndChannels(refundOrder,refundChannelOrders);
payOrderService.updateById(payOrder);
return refundOrder;
}
/**
* 失败处理
*/
private void errorHandler(RefundParam refundParam, PayOrder payOrder, Exception e) {
private void errorHandler(RefundParam refundParam, PayOrder payOrder) {
// 记录退款失败的记录
PayRefundOrder refundOrder = payRefundAssistService.generateRefundOrder(refundParam, payOrder);
payRefundAssistService.saveOrder(refundOrder);

View File

@@ -1,13 +1,9 @@
package cn.bootx.platform.daxpay.service.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
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.channel.alipay.service.AliPayRefundService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import cn.bootx.platform.daxpay.service.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
@@ -26,7 +22,6 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
public class AliPayRefundStrategy extends AbsPayRefundStrategy {
private final AliPayConfigService alipayConfigService;
private final AliPayOrderService aliPayOrderService;
private final AliPayRefundService aliRefundService;
/**
* 策略标识
@@ -38,8 +33,6 @@ public class AliPayRefundStrategy extends AbsPayRefundStrategy {
return PayChannelEnum.ALI;
}
private final PayOrderService payOrderService;
/**
* 退款前前操作
@@ -56,20 +49,13 @@ public class AliPayRefundStrategy extends AbsPayRefundStrategy {
@Override
public void doRefundHandler() {
aliRefundService.refund(this.getPayOrder(), this.getRefundChannelParam().getAmount());
aliPayOrderService.updateRefund(this.getPayOrder().getId(), this.getRefundChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getPayOrder(), this.getRefundChannelParam().getAmount(), PayChannelEnum.ALI);
}
/**
* 生成通道退款订单对象
* 退款发起成功操作
*/
@Override
public PayRefundChannelOrder generateChannelOrder() {
PayRefundChannelOrder payRefundChannelOrder = super.generateChannelOrder();
// 追加关联对款请求号
String refundRequestNo = PaymentContextLocal.get()
.getRefundInfo()
.getGatewayRequestNo();
return payRefundChannelOrder.setGatewayOrderNo(refundRequestNo);
public void doSuccessHandler() {
// 查看
}
}

View File

@@ -1,13 +1,11 @@
package cn.bootx.platform.daxpay.service.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
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.channel.wechat.service.WechatRefundService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import cn.bootx.platform.daxpay.service.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
@@ -61,17 +59,4 @@ public class WeChatPayRefundStrategy extends AbsPayRefundStrategy {
payOrderService.updateRefundSuccess(this.getPayOrder(), this.getRefundChannelParam().getAmount(), PayChannelEnum.WECHAT);
}
/**
* 生成通道退款订单对象
*/
@Override
public PayRefundChannelOrder generateChannelOrder() {
PayRefundChannelOrder payRefundChannelOrder = super.generateChannelOrder();
// 追加关联对款请求号
String refundRequestNo = PaymentContextLocal.get()
.getRefundInfo()
.getGatewayRequestNo();
return payRefundChannelOrder.setGatewayOrderNo(refundRequestNo);
}
}

View File

@@ -99,7 +99,7 @@ public class PayRepairService {
*/
private void success(PayOrder order, List<AbsPayRepairStrategy> strategies) {
LocalDateTime payTime = PaymentContextLocal.get()
.getAsyncPayInfo()
.getReconcileInfo()
.getPayTime();
// 执行个通道的成功处理方法
strategies.forEach(AbsPayRepairStrategy::doSuccessHandler);

View File

@@ -243,8 +243,8 @@ public class PaySyncService {
.setRepairOrder(repair)
.setRepairOrderId(repairOrderId)
.setErrorMsg(errorMsg)
.setClientIp(PaymentContextLocal.get().getRequest().getClientIp())
.setReqId(PaymentContextLocal.get().getRequest().getReqId());
.setClientIp(PaymentContextLocal.get().getRequestInfo().getClientIp())
.setReqId(PaymentContextLocal.get().getRequestInfo().getReqId());
paySyncRecordService.saveRecord(paySyncRecord);
}
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.dto.order.pay;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -20,15 +21,31 @@ public class PayChannelOrderDto extends BaseDto {
@Schema(description = "支付id")
private Long paymentId;
@Schema(description = "异步支付方式")
private boolean async;
@Schema(description = "通道")
private String channel;
@Schema(description = "支付方式")
private String payWay;
@Schema(description = "异步支付方式")
private boolean async;
/**
* 异步支付通道发给网关的退款号, 用与将记录关联起来
*/
@Schema(description = "关联网关支付号")
private String gatewayOrderNo;
/**
* 支付状态
* @see PayStatusEnum
*/
@Schema(description = "支付状态")
private String status;
@Schema(description = "金额")
private Integer amount;
@Schema(description = "可退款金额")
private Integer refundableBalance;
}

View File

@@ -3,14 +3,12 @@ package cn.bootx.platform.daxpay.service.dto.order.pay;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.entity.RefundableInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author xxm
@@ -53,13 +51,6 @@ public class PayOrderDto extends BaseDto {
@Schema(description = "可退款余额")
private Integer refundableBalance;
/**
* 可退款信息列表
* @see RefundableInfo
*/
@Schema(description = "可退款信息列表")
private List<RefundableInfo> refundableInfos;
/**
* 支付状态
* @see PayStatusEnum

View File

@@ -48,9 +48,6 @@ public class PayRefundOrderDto extends BaseDto {
@Schema(description = "退款时间")
private LocalDateTime refundTime;
@Schema(description = "退款信息列表")
private List<RefundableInfo> refundableInfo;
/**
* @see PayRefundStatusEnum
*/

View File

@@ -35,7 +35,7 @@ public class RefundChannelOrderDto extends BaseDto {
private boolean async;
@Schema(description = "订单金额")
private Integer totalAmount;
private Integer orderAmount;
@Schema(description = "退款金额")
private Integer amount;

View File

@@ -34,7 +34,7 @@ public abstract class AbsPayCallbackStrategy implements PayStrategy {
* 支付回调
*/
public String payCallback(Map<String, String> params) {
PaymentContextLocal.get().getCallbackParam().putAll(params);
PaymentContextLocal.get().getCallbackInfo().getCallbackParam().putAll(params);
log.info("支付回调处理: {}", params);
// 验证消息
if (!this.verifyNotify()) {
@@ -99,7 +99,7 @@ public abstract class AbsPayCallbackStrategy implements PayStrategy {
* 保存回调记录
*/
public void saveNotifyRecord(PayCallbackResult result) {
Map<String, String> callbackParam = PaymentContextLocal.get().getCallbackParam();
Map<String, String> callbackParam = PaymentContextLocal.get().getCallbackInfo().getCallbackParam();
PayCallbackRecord payNotifyRecord = new PayCallbackRecord()
.setNotifyInfo(JSONUtil.toJsonStr(callbackParam))
.setNotifyTime(LocalDateTime.now())

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.func;
import cn.bootx.platform.daxpay.service.common.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import lombok.Getter;
import lombok.Setter;
@@ -14,11 +15,21 @@ import lombok.Setter;
@Setter
public abstract class AbsPayCloseStrategy implements PayStrategy{
/** 支付对象 */
/** 支付订单 */
private PayOrder order = null;
public void initCloseParam(PayOrder order){
/** 支付通道订单 */
private PayChannelOrder channelOrder = null;
/**
* 策略标识
* @see PayChannelEnum
*/
public abstract PayChannelEnum getType();
public void initCloseParam(PayOrder order, PayChannelOrder channelOrder){
this.order = order;
this.channelOrder = channelOrder;
}
/**
@@ -36,6 +47,6 @@ public abstract class AbsPayCloseStrategy implements PayStrategy{
/**
* 关闭失败的处理方式
*/
public void doErrorHandler(ExceptionInfo exceptionInfo) {
public void doSuccessHandler() {
}
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.func;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
@@ -19,18 +20,21 @@ import lombok.Setter;
@Setter
public abstract class AbsPayRefundStrategy implements PayStrategy{
/** 支付对象 */
/** 支付订单 */
private PayOrder payOrder = null;
/** 支付通道对象 */
/** 当前通道的订单 */
private PayChannelOrder payChannelOrder = null;
/** 退款参数 */
private RefundParam refundParam = null;
/** 当前支付通道退款参数 退款参数中的与这个不一致, 以这个为准 */
/** 当前通道退款参数 退款参数中的与这个不一致, 以这个为准 */
private RefundChannelParam refundChannelParam = null;
/** 当前通道的退款订单 */
private PayRefundChannelOrder refundChannelOrder;
/**
* 策略标识
* @see PayChannelEnum
@@ -49,24 +53,30 @@ public abstract class AbsPayRefundStrategy implements PayStrategy{
/**
* 退款前对处理 包含必要的校验以及对Payment对象的创建和保存操作
*/
public void doBeforeRefundHandler() {
}
public void doBeforeRefundHandler() {}
/**
* 退款操作
*/
public abstract void doRefundHandler();
/**
* 退款发起成功操作
*/
public void doSuccessHandler() {
this.refundChannelOrder.setStatus(PayRefundStatusEnum.SUCCESS.getCode());
}
/**
* 生成通道退款订单对象
*/
public PayRefundChannelOrder generateChannelOrder() {
return new PayRefundChannelOrder()
.setChannel(this.getPayChannelOrder().getChannel())
.setTotalAmount(this.getPayChannelOrder().getAmount())
.setAmount(this.getRefundChannelParam().getAmount())
public void generateChannelOrder() {
this.refundChannelOrder = new PayRefundChannelOrder()
.setPayChannelId(this.getPayChannelOrder().getId())
.setAsync(this.getPayChannelOrder().isAsync())
.setPayChannelId(this.getPayChannelOrder().getId());
.setChannel(this.getPayChannelOrder().getChannel())
.setOrderAmount(this.getPayChannelOrder().getAmount())
.setAmount(this.getRefundChannelParam().getAmount());
}
}

View File

@@ -20,9 +20,12 @@ import lombok.Setter;
public abstract class AbsPayStrategy implements PayStrategy{
/** 支付对象 */
/** 支付订单 */
private PayOrder order = null;
/** 通道支付订单, 异步支付通道单独处理通道支付订单 */
private PayChannelOrder channelOrder = null;
/** 支付参数 */
private PayParam payParam = null;
@@ -62,14 +65,11 @@ public abstract class AbsPayStrategy implements PayStrategy{
}
/**
* 生成通道支付单
* 生成通道支付单, 如果不需要可以进行覆盖
*/
public PayChannelOrder generateChannelOrder() {
public void generateChannelOrder() {
PayChannelOrder payChannelOrder = PayBuilder.buildPayChannelOrder(this.getPayChannelParam());
payChannelOrder.setId(this.getOrder().getId());
return payChannelOrder;
this.channelOrder = payChannelOrder;
}
}