mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-10 06:46:04 +08:00
feat(alipay): 对接关闭订单(直连关闭策略+子应用HTTP调用)
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package cn.daxpay.open.channel.alipay.client;
|
||||
|
||||
import cn.daxpay.open.channel.alipay.client.req.AlipayCloseReq;
|
||||
import cn.daxpay.open.channel.alipay.client.req.AlipayPayReq;
|
||||
import cn.daxpay.open.channel.alipay.client.req.AlipaySyncReq;
|
||||
import cn.daxpay.open.channel.alipay.client.resp.AlipayCloseResp;
|
||||
import cn.daxpay.open.channel.alipay.client.resp.AlipayPayResp;
|
||||
import cn.daxpay.open.channel.alipay.client.resp.AlipaySyncResp;
|
||||
import cn.daxpay.open.payment.common.result.DaxResult;
|
||||
@@ -23,4 +25,8 @@ public interface AlipayChannelClient {
|
||||
/// 支付同步(查询支付宝订单状态)
|
||||
@PostExchange("/channel/alipay/sync")
|
||||
DaxResult<AlipaySyncResp> sync(@RequestBody AlipaySyncReq req);
|
||||
|
||||
/// 关闭/撤销支付宝订单
|
||||
@PostExchange("/channel/alipay/close")
|
||||
DaxResult<AlipayCloseResp> close(@RequestBody AlipayCloseReq req);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.daxpay.open.channel.alipay.client.req;
|
||||
|
||||
import cn.daxpay.open.channel.alipay.client.credential.AlipaySdkCredential;
|
||||
import lombok.Data;
|
||||
|
||||
/// # 支付宝通道关闭请求
|
||||
///
|
||||
/// 与子应用 dax-pay-channel-one 的 `AlipayCloseReq` 镜像, 经声明式 HTTP 客户端序列化传输, 字段对齐。
|
||||
@Data
|
||||
public class AlipayCloseReq {
|
||||
|
||||
/// 商户订单号(主应用支付交易号, 作为支付宝 out_trade_no)
|
||||
private String outTradeNo;
|
||||
|
||||
/// 支付宝交易号(trade_no, 下单成功后由支付宝返回, 可选)
|
||||
private String tradeNo;
|
||||
|
||||
/// 是否使用撤销方式关闭订单(false=交易关闭, true=交易撤销)
|
||||
private boolean useCancel;
|
||||
|
||||
/// 通道调用凭证
|
||||
private AlipaySdkCredential credential;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.daxpay.open.channel.alipay.client.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/// # 支付宝通道关闭响应
|
||||
///
|
||||
/// 与子应用 dax-pay-channel-one 的 `AlipayCloseResp` 镜像, 字段对齐。
|
||||
/// 关闭失败且可兜底(交易已关闭/交易不存在)时由子应用内部消化为成功;
|
||||
/// 真正失败时子应用抛 [cn.daxpay.open.platform.core.exception.ChannelServiceException](经 DaxResult 透传)。
|
||||
@Data
|
||||
public class AlipayCloseResp {
|
||||
|
||||
/// 商户订单号(透传 AlipayCloseReq.outTradeNo)
|
||||
private String outTradeNo;
|
||||
|
||||
/// 支付宝交易号(trade_no)
|
||||
private String tradeNo;
|
||||
|
||||
/// 网关返回码(code, 10000=成功)
|
||||
private String code;
|
||||
|
||||
/// 业务返回码(sub_code, 如 ACQ.TRADE_NOT_EXIST)
|
||||
private String subCode;
|
||||
|
||||
/// 业务返回消息(sub_msg)
|
||||
private String subMsg;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package cn.daxpay.open.channel.alipay.service.payment.close;
|
||||
|
||||
import cn.daxpay.open.channel.alipay.client.AlipayChannelClient;
|
||||
import cn.daxpay.open.channel.alipay.client.credential.AlipaySdkCredential;
|
||||
import cn.daxpay.open.channel.alipay.client.req.AlipayCloseReq;
|
||||
import cn.daxpay.open.channel.alipay.client.resp.AlipayCloseResp;
|
||||
import cn.daxpay.open.payment.common.result.DaxResult;
|
||||
import cn.daxpay.open.payment.pay.order.entity.PayTrade;
|
||||
import cn.daxpay.open.platform.core.enums.pay.pay.CloseTypeEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/// # 支付宝支付关闭业务服务
|
||||
///
|
||||
/// 通过 [AlipayChannelClient] 调用子应用 dax-pay-channel-one 关闭/撤销支付宝订单。
|
||||
/// 请求构建、结果判定在本类中完成; 关闭失败的网关状态兜底由子应用内部处理。
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AlipayCloseService {
|
||||
|
||||
private final AlipayChannelClient alipayChannelClient;
|
||||
|
||||
/// 执行支付宝订单关闭/撤销
|
||||
///
|
||||
/// @param trade 支付订单(tradeNo 作为 out_trade_no, outOrderNo 作为 trade_no)
|
||||
/// @param credential 通道调用凭证
|
||||
/// @param useCancel 是否使用撤销方式关闭
|
||||
/// @return 实际使用的关闭方式([CloseTypeEnum.CLOSE] / [CloseTypeEnum.CANCEL])
|
||||
public CloseTypeEnum close(PayTrade trade, AlipaySdkCredential credential, boolean useCancel) {
|
||||
// 构建请求
|
||||
AlipayCloseReq req = new AlipayCloseReq();
|
||||
req.setOutTradeNo(trade.getTradeNo());
|
||||
req.setTradeNo(trade.getOutOrderNo());
|
||||
req.setUseCancel(useCancel);
|
||||
req.setCredential(credential);
|
||||
|
||||
// 调用子应用
|
||||
DaxResult<AlipayCloseResp> result = alipayChannelClient.close(req);
|
||||
if (result.getCode() != 0) {
|
||||
throw new IllegalStateException("支付宝通道关闭失败: " + result.getMsg());
|
||||
}
|
||||
|
||||
return useCancel ? CloseTypeEnum.CANCEL : CloseTypeEnum.CLOSE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package cn.daxpay.open.channel.alipay.strategy.direct;
|
||||
|
||||
import cn.daxpay.open.channel.alipay.client.credential.AlipaySdkCredential;
|
||||
import cn.daxpay.open.channel.alipay.service.direct.AlipayDirectConfigAssembler;
|
||||
import cn.daxpay.open.channel.alipay.service.payment.close.AlipayCloseService;
|
||||
import cn.daxpay.open.payment.pay.order.dao.NormalPayOrderManager;
|
||||
import cn.daxpay.open.payment.pay.order.entity.NormalPayOrder;
|
||||
import cn.daxpay.open.payment.pay.order.entity.PayTrade;
|
||||
import cn.daxpay.open.payment.strategy.pay.AbsPayCloseStrategy;
|
||||
import cn.daxpay.open.platform.core.enums.pay.pay.CloseTypeEnum;
|
||||
import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/// # 支付宝直连支付关闭策略
|
||||
///
|
||||
/// 支付宝直连模式(ProductEnum.ALIPAY)下的支付关闭策略。
|
||||
/// 从容器层 NormalPayOrder 读取通道路由参数(channelMchNo / capability),
|
||||
/// 组装通道凭证(委托 [AlipayDirectConfigAssembler]), 关闭执行委托给 [AlipayCloseService]。
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AlipayDirectCloseStrategy extends AbsPayCloseStrategy {
|
||||
|
||||
private final AlipayCloseService alipayCloseService;
|
||||
private final AlipayDirectConfigAssembler alipayDirectConfigAssembler;
|
||||
private final NormalPayOrderManager normalPayOrderManager;
|
||||
|
||||
@Override
|
||||
public ProductEnum getProduct() {
|
||||
return ProductEnum.ALIPAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloseTypeEnum doClose(PayTrade trade, boolean useCancel) {
|
||||
// 从容器层读取通道路由参数, 用于凭证解析
|
||||
NormalPayOrder normalOrder = normalPayOrderManager.findById(trade.getContainerId()).orElse(null);
|
||||
String channelMchNo = normalOrder != null ? normalOrder.getChannelMchNo() : null;
|
||||
String capability = normalOrder != null ? normalOrder.getCapability() : null;
|
||||
|
||||
// 组装通道调用凭证
|
||||
AlipaySdkCredential credential = alipayDirectConfigAssembler.buildConfig(
|
||||
trade.getMchNo(), channelMchNo, capability);
|
||||
|
||||
return alipayCloseService.close(trade, credential, useCancel);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user