mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-23 23:45:08 +08:00
mall + pay:
1. 重构支付回调的逻辑,将回调解析改成 PayOrderRespDTO,为后续轮询做铺垫 2. 调整退款单的表结构 3. 调整退款调用的实现
This commit is contained in:
@ -1,12 +1,12 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 支付客户端,用于对接各支付渠道的 SDK,实现发起支付、退款等功能
|
||||
@ -32,20 +32,22 @@ public interface PayClient {
|
||||
|
||||
/**
|
||||
* 调用支付渠道,进行退款
|
||||
*
|
||||
* @param reqDTO 统一退款请求信息
|
||||
* @return 各支付渠道的统一返回结果
|
||||
* @return 退款信息
|
||||
*/
|
||||
PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO);
|
||||
PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 解析回调数据
|
||||
*
|
||||
* @param rawNotify 通知内容
|
||||
* @param params HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
|
||||
* @param body HTTP 回调接口的 request body
|
||||
* @return 回调对象
|
||||
* 1. {@link PayRefundNotifyRespDTO} 退款通知
|
||||
* 2. {@link PayOrderNotifyRespDTO} 支付通知
|
||||
* 1. {@link PayRefundRespDTO} 退款通知
|
||||
* 2. {@link PayOrderRespDTO} 支付通知
|
||||
*/
|
||||
default Object parseNotify(PayNotifyReqDTO rawNotify) {
|
||||
default Object parseNotify(Map<String, String> params, String body) {
|
||||
throw new UnsupportedOperationException("未实现 parseNotify 方法!");
|
||||
}
|
||||
|
||||
|
@ -1,29 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto.notify;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 支付订单,退款订单回调,渠道的统一通知请求数据
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@Builder
|
||||
public class PayNotifyReqDTO {
|
||||
|
||||
|
||||
/**
|
||||
* HTTP 回调接口的 request body
|
||||
*/
|
||||
private String body;
|
||||
|
||||
/**
|
||||
* HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
|
||||
*/
|
||||
private Map<String,String> params;
|
||||
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto.notify;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 支付通知 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PayOrderNotifyRespDTO {
|
||||
|
||||
/**
|
||||
* 支付订单号(支付模块的)
|
||||
*/
|
||||
private String orderExtensionNo;
|
||||
/**
|
||||
* 支付渠道编号
|
||||
*/
|
||||
private String channelOrderNo;
|
||||
/**
|
||||
* 支付渠道用户编号
|
||||
*/
|
||||
private String channelUserId;
|
||||
/**
|
||||
* 支付成功时间
|
||||
*/
|
||||
private LocalDateTime successTime;
|
||||
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto.notify;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 从渠道返回数据中解析得到的支付退款通知的Notify DTO
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@Builder
|
||||
public class PayRefundNotifyRespDTO {
|
||||
|
||||
/**
|
||||
* 支付渠道编号
|
||||
*/
|
||||
private String channelOrderNo;
|
||||
|
||||
/**
|
||||
* 交易订单号,根据规则生成
|
||||
* 调用支付渠道时,使用该字段作为对接的订单号。
|
||||
* 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no
|
||||
* 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no
|
||||
* 这里对应 pay_extension 里面的 no
|
||||
* 例如说,P202110132239124200055
|
||||
*/
|
||||
private String tradeNo;
|
||||
|
||||
/**
|
||||
* https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no
|
||||
* https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no
|
||||
* 退款请求号。
|
||||
* 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。
|
||||
* 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更,
|
||||
* 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。
|
||||
* 退款单请求号,根据规则生成
|
||||
*
|
||||
* 例如说,RR202109181134287570000
|
||||
*/
|
||||
private String reqNo;
|
||||
|
||||
/**
|
||||
* 退款是否成功
|
||||
*/
|
||||
private PayNotifyRefundStatusEnum status;
|
||||
|
||||
/**
|
||||
* 退款成功时间
|
||||
*/
|
||||
private LocalDateTime refundSuccessTime;
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 渠道支付订单 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PayOrderRespDTO {
|
||||
|
||||
/**
|
||||
* 支付状态
|
||||
*
|
||||
* 枚举:{@link PayOrderStatusRespEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 外部订单号
|
||||
*
|
||||
* 对应 PayOrderExtensionDO 的 no 字段
|
||||
*/
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* 支付渠道编号
|
||||
*/
|
||||
private String channelOrderNo;
|
||||
/**
|
||||
* 支付渠道用户编号
|
||||
*/
|
||||
private String channelUserId;
|
||||
|
||||
/**
|
||||
* 支付成功时间
|
||||
*/
|
||||
private LocalDateTime successTime;
|
||||
|
||||
/**
|
||||
* 原始的异步通知结果
|
||||
*/
|
||||
private Object rawData;
|
||||
|
||||
}
|
@ -28,10 +28,12 @@ public class PayOrderUnifiedReqDTO {
|
||||
// ========== 商户相关字段 ==========
|
||||
|
||||
/**
|
||||
* 商户订单编号
|
||||
* 外部订单号
|
||||
*
|
||||
* 对应 PayOrderExtensionDO 的 no 字段
|
||||
*/
|
||||
@NotEmpty(message = "商户订单编号不能为空")
|
||||
private String merchantOrderId;
|
||||
@NotEmpty(message = "外部订单编号不能为空")
|
||||
private String outTradeNo;
|
||||
/**
|
||||
* 商品标题
|
||||
*/
|
||||
@ -63,7 +65,7 @@ public class PayOrderUnifiedReqDTO {
|
||||
*/
|
||||
@NotNull(message = "支付金额不能为空")
|
||||
@DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
|
||||
private Integer amount;
|
||||
private Integer price;
|
||||
|
||||
/**
|
||||
* 支付过期时间
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
@ -24,11 +23,12 @@ public class PayOrderUnifiedRespDTO {
|
||||
private String displayContent;
|
||||
|
||||
/**
|
||||
* 同步的通知信息
|
||||
* 渠道支付订单
|
||||
*
|
||||
* 只有在订单直接支付成功时,才会进行返回。
|
||||
* 目前只有 bar 条码支付才会出现,它是支付发起时,直接返回是否支付成功的,而其它支付还是异步通知
|
||||
*/
|
||||
private PayOrderNotifyRespDTO notify;
|
||||
private PayOrderRespDTO order;
|
||||
|
||||
public PayOrderUnifiedRespDTO(String displayMode, String displayContent) {
|
||||
this.displayMode = displayMode;
|
||||
|
@ -0,0 +1,40 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto.refund;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 渠道退款订单 Response DTO
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Data
|
||||
public class PayRefundRespDTO {
|
||||
|
||||
/**
|
||||
* 退款状态
|
||||
*
|
||||
* 枚举 {@link PayRefundStatusRespEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 渠道退款单号
|
||||
*
|
||||
* 对应 PayRefundDO.channelRefundNo 字段
|
||||
*/
|
||||
private String channelRefundNo;
|
||||
|
||||
/**
|
||||
* 退款成功时间
|
||||
*/
|
||||
private LocalDateTime successTime;
|
||||
|
||||
/**
|
||||
* 原始的异步通知结果
|
||||
*/
|
||||
private Object rawData;
|
||||
|
||||
}
|
@ -24,33 +24,20 @@ import javax.validation.constraints.NotNull;
|
||||
public class PayRefundUnifiedReqDTO {
|
||||
|
||||
/**
|
||||
* 用户 IP
|
||||
* 外部订单号
|
||||
*
|
||||
* 对应 PayOrderExtensionDO 的 no 字段
|
||||
*/
|
||||
private String userIp;
|
||||
|
||||
// TODO @jason:这个是否为非必传字段呀,只需要传递 payTradeNo 字段即可。尽可能精简
|
||||
/**
|
||||
* https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 transaction_id
|
||||
* https://opendocs.alipay.com/apis alipay.trade.refund 中的 trade_no
|
||||
* 渠道订单号
|
||||
*/
|
||||
private String channelOrderNo;
|
||||
@NotEmpty(message = "外部订单编号不能为空")
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no
|
||||
* https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no
|
||||
* 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空
|
||||
* 外部退款号
|
||||
*
|
||||
* 对应 PayRefundDO 的 no 字段
|
||||
*/
|
||||
private String payTradeNo;
|
||||
|
||||
/**
|
||||
* https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no
|
||||
* https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no
|
||||
* 退款请求单号 同一退款请求单号多次请求只退一笔。
|
||||
* 使用 商户的退款单号。{PayRefundDO 字段 merchantRefundNo}
|
||||
*/
|
||||
@NotEmpty(message = "退款请求单号")
|
||||
private String merchantRefundId;
|
||||
@NotEmpty(message = "退款请求单号不能为空")
|
||||
private String outRefundNo;
|
||||
|
||||
/**
|
||||
* 退款原因
|
||||
@ -63,11 +50,12 @@ public class PayRefundUnifiedReqDTO {
|
||||
*/
|
||||
@NotNull(message = "退款金额不能为空")
|
||||
@DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
|
||||
private Integer amount;
|
||||
private Integer price;
|
||||
|
||||
/**
|
||||
* 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要
|
||||
* 退款结果的 notify 回调地址
|
||||
*/
|
||||
@NotEmpty(message = "支付结果的回调地址不能为空")
|
||||
@URL(message = "支付结果的 notify 回调地址必须是 URL 格式")
|
||||
private String notifyUrl;
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto.refund;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 统一退款 Response DTO
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Accessors(chain = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
public class PayRefundUnifiedRespDTO {
|
||||
|
||||
/**
|
||||
* 渠道退款单编号
|
||||
*/
|
||||
private String channelRefundId;
|
||||
}
|
@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -92,10 +92,10 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
|
||||
throws Throwable;
|
||||
|
||||
@Override
|
||||
public PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
||||
public PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
||||
Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO);
|
||||
// 执行统一退款
|
||||
PayRefundUnifiedRespDTO resp;
|
||||
PayRefundRespDTO resp;
|
||||
try {
|
||||
resp = doUnifiedRefund(reqDTO);
|
||||
} catch (ServiceException ex) {
|
||||
@ -109,7 +109,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
|
||||
return resp;
|
||||
}
|
||||
|
||||
protected abstract PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
|
||||
protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
|
||||
|
||||
// ========== 各种工具方法 ==========
|
||||
|
||||
|
@ -2,16 +2,19 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum;
|
||||
import com.alipay.api.*;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.AlipayConfig;
|
||||
import com.alipay.api.AlipayResponse;
|
||||
import com.alipay.api.DefaultAlipayClient;
|
||||
import com.alipay.api.domain.AlipayTradeRefundModel;
|
||||
import com.alipay.api.internal.util.AlipaySignature;
|
||||
import com.alipay.api.request.AlipayTradeRefundRequest;
|
||||
@ -22,6 +25,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
||||
@ -52,69 +57,72 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
||||
|
||||
/**
|
||||
* 支付宝统一的退款接口 alipay.trade.refund
|
||||
*
|
||||
* @param reqDTO 退款请求 request DTO
|
||||
* @return 退款请求 Response
|
||||
*/
|
||||
@Override
|
||||
protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
||||
AlipayTradeRefundModel model=new AlipayTradeRefundModel();
|
||||
model.setTradeNo(reqDTO.getChannelOrderNo());
|
||||
model.setOutTradeNo(reqDTO.getPayTradeNo());
|
||||
|
||||
model.setOutRequestNo(reqDTO.getMerchantRefundId());
|
||||
model.setRefundAmount(formatAmount(reqDTO.getAmount()));
|
||||
protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
||||
// 1.1 构建 AlipayTradeRefundModel 请求
|
||||
AlipayTradeRefundModel model = new AlipayTradeRefundModel();
|
||||
model.setOutTradeNo(reqDTO.getOutTradeNo());
|
||||
model.setOutRequestNo(reqDTO.getOutRefundNo());
|
||||
model.setRefundAmount(formatAmount(reqDTO.getPrice()));
|
||||
// model.setRefundAmount(formatAmount(reqDTO.getPrice() / 2));
|
||||
model.setRefundReason(reqDTO.getReason());
|
||||
|
||||
AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
|
||||
refundRequest.setBizModel(model);
|
||||
refundRequest.setNotifyUrl(reqDTO.getNotifyUrl());
|
||||
refundRequest.setReturnUrl(reqDTO.getNotifyUrl());
|
||||
// 1.2 构建 AlipayTradePayRequest 请求
|
||||
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
|
||||
request.setBizModel(model);
|
||||
try {
|
||||
AlipayTradeRefundResponse response = client.execute(refundRequest);
|
||||
log.info("[doUnifiedRefund][response({}) 发起退款 渠道返回", toJsonString(response));
|
||||
// 2.1 执行请求
|
||||
AlipayTradeRefundResponse response = client.execute(request);
|
||||
PayRefundRespDTO refund = new PayRefundRespDTO()
|
||||
.setRawData(response);
|
||||
// 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。
|
||||
// 另外,支付宝没有退款单号,所以不用设置
|
||||
if (response.isSuccess()) {
|
||||
//退款导致触发的异步通知是发送到支付接口中设置的notify_url
|
||||
//支付宝不返回退款单号,设置为空
|
||||
PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO();
|
||||
respDTO.setChannelRefundId("");
|
||||
// return PayCommonResult.build(response.getCode(), response.getMsg(), respDTO, codeMapping); TODO
|
||||
return null;
|
||||
refund.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())
|
||||
.setSuccessTime(LocalDateTimeUtil.of(response.getGmtRefundPay()));
|
||||
Assert.notNull(refund.getSuccessTime(), "退款成功时间不能为空");
|
||||
} else {
|
||||
refund.setStatus(PayOrderStatusRespEnum.CLOSED.getStatus());
|
||||
}
|
||||
// 失败。需要抛出异常
|
||||
// return PayCommonResult.build(response.getCode(), response.getMsg(), null, codeMapping); TODO
|
||||
return null;
|
||||
return refund;
|
||||
} catch (AlipayApiException e) {
|
||||
// TODO 记录异常日志
|
||||
log.error("[doUnifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), e);
|
||||
// return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping); TODO
|
||||
log.error("[doUnifiedRefund][request({}) 发起退款异常]", toJsonString(reqDTO), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public Object parseNotify(PayNotifyReqDTO rawNotify) {
|
||||
public Object parseNotify(Map<String, String> params, String body) {
|
||||
// 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
|
||||
// ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调
|
||||
// ② 全部退款:Wap 支付有订单状态的同步回调,但是 PC/扫码又没有
|
||||
// 所以,这里在解析时,即使是退款导致的订单状态同步,我们也忽略不做为“退款同步”,而是订单的回调。
|
||||
// 实际上,支付宝退款只要发起成功,就可以认为退款成功,不需要等待回调。
|
||||
|
||||
// 1. 校验回调数据
|
||||
String body = rawNotify.getBody();
|
||||
Map<String, String> params = rawNotify.getParams();
|
||||
Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
|
||||
AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(),
|
||||
StandardCharsets.UTF_8.name(), "RSA2");
|
||||
StandardCharsets.UTF_8.name(), config.getSignType());
|
||||
|
||||
// 2.1 退款的情况
|
||||
if (bodyObj.containsKey("refund_fee")) {
|
||||
return PayRefundNotifyRespDTO.builder().channelOrderNo(bodyObj.get("trade_no"))
|
||||
.tradeNo(bodyObj.get("out_trade_no")).reqNo(bodyObj.get("out_biz_no"))
|
||||
.status(PayNotifyRefundStatusEnum.SUCCESS)
|
||||
.refundSuccessTime(parseTime(params.get("gmt_refund")))
|
||||
.build();
|
||||
}
|
||||
// 2.2 支付的情况
|
||||
return PayOrderNotifyRespDTO.builder()
|
||||
.orderExtensionNo(bodyObj.get("out_trade_no"))
|
||||
// 2. 解析订单的状态
|
||||
String tradeStatus = bodyObj.get("trade_status");
|
||||
PayOrderStatusRespEnum status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING
|
||||
: Objects.equals("TRADE_SUCCESS", tradeStatus) ? PayOrderStatusRespEnum.SUCCESS
|
||||
: Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED : null;
|
||||
Assert.notNull(status, (Supplier<Throwable>) () -> {
|
||||
throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body));
|
||||
});
|
||||
return PayOrderRespDTO.builder()
|
||||
.status(Objects.requireNonNull(status).getStatus())
|
||||
.outTradeNo(bodyObj.get("out_trade_no"))
|
||||
.channelOrderNo(bodyObj.get("trade_no"))
|
||||
.channelUserId(bodyObj.get("seller_id"))
|
||||
.successTime(parseTime(params.get("notify_time")))
|
||||
.successTime(parseTime(params.get("gmt_payment")))
|
||||
.rawData(body)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
@ -31,10 +31,10 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
|
||||
// 1.1 构建 AlipayTradeAppPayModel 请求
|
||||
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
|
||||
// ① 通用的参数
|
||||
model.setOutTradeNo(reqDTO.getMerchantOrderId());
|
||||
model.setOutTradeNo(reqDTO.getOutTradeNo());
|
||||
model.setSubject(reqDTO.getSubject());
|
||||
model.setBody(reqDTO.getBody());
|
||||
model.setTotalAmount(formatAmount(reqDTO.getAmount()));
|
||||
model.setTotalAmount(formatAmount(reqDTO.getPrice()));
|
||||
model.setProductCode(" QUICK_MSECURITY_PAY"); // 销售产品码:无线快捷支付产品
|
||||
// ② 个性化的参数【无】
|
||||
// ③ 支付宝扫码支付只有一种展示
|
||||
|
@ -39,10 +39,10 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
|
||||
// 1.1 构建 AlipayTradePayModel 请求
|
||||
AlipayTradePayModel model = new AlipayTradePayModel();
|
||||
// ① 通用的参数
|
||||
model.setOutTradeNo(reqDTO.getMerchantOrderId());
|
||||
model.setOutTradeNo(reqDTO.getOutTradeNo());
|
||||
model.setSubject(reqDTO.getSubject());
|
||||
model.setBody(reqDTO.getBody());
|
||||
model.setTotalAmount(formatAmount(reqDTO.getAmount()));
|
||||
model.setTotalAmount(formatAmount(reqDTO.getPrice()));
|
||||
model.setScene("bar_code"); // 当面付条码支付场景
|
||||
// ② 个性化的参数
|
||||
model.setAuthCode(authCode);
|
||||
|
@ -33,10 +33,10 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
|
||||
// 1.1 构建 AlipayTradePagePayModel 请求
|
||||
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
|
||||
// ① 通用的参数
|
||||
model.setOutTradeNo(reqDTO.getMerchantOrderId());
|
||||
model.setOutTradeNo(reqDTO.getOutTradeNo());
|
||||
model.setSubject(reqDTO.getSubject());
|
||||
model.setBody(reqDTO.getBody());
|
||||
model.setTotalAmount(formatAmount(reqDTO.getAmount()));
|
||||
model.setTotalAmount(formatAmount(reqDTO.getPrice()));
|
||||
model.setTimeExpire(formatTime(reqDTO.getExpireTime()));
|
||||
model.setProductCode("FAST_INSTANT_TRADE_PAY"); // 销售产品码. 目前 PC 支付场景下仅支持 FAST_INSTANT_TRADE_PAY
|
||||
// ② 个性化的参数
|
||||
|
@ -29,10 +29,10 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
|
||||
// 1.1 构建 AlipayTradePrecreateModel 请求
|
||||
AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
|
||||
// ① 通用的参数
|
||||
model.setOutTradeNo(reqDTO.getMerchantOrderId());
|
||||
model.setOutTradeNo(reqDTO.getOutTradeNo());
|
||||
model.setSubject(reqDTO.getSubject());
|
||||
model.setBody(reqDTO.getBody());
|
||||
model.setTotalAmount(formatAmount(reqDTO.getAmount()));
|
||||
model.setTotalAmount(formatAmount(reqDTO.getPrice()));
|
||||
model.setProductCode("FACE_TO_FACE_PAYMENT"); // 销售产品码. 目前扫码支付场景下仅支持 FACE_TO_FACE_PAYMENT
|
||||
// ② 个性化的参数【无】
|
||||
// ③ 支付宝扫码支付只有一种展示,考虑到前端可能希望二维码扫描后,手机打开
|
||||
|
@ -30,10 +30,10 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
|
||||
// 1.1 构建 AlipayTradeWapPayModel 请求
|
||||
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
|
||||
// ① 通用的参数
|
||||
model.setOutTradeNo(reqDTO.getMerchantOrderId());
|
||||
model.setOutTradeNo(reqDTO.getOutTradeNo());
|
||||
model.setSubject(reqDTO.getSubject());
|
||||
model.setBody(reqDTO.getBody());
|
||||
model.setTotalAmount(formatAmount(reqDTO.getAmount()));
|
||||
model.setTotalAmount(formatAmount(reqDTO.getPrice()));
|
||||
model.setProductCode("QUICK_WAP_PAY"); // 销售产品码. 目前 Wap 支付场景下仅支持 QUICK_WAP_PAY
|
||||
// ② 个性化的参数【无】
|
||||
// ③ 支付宝 Wap 支付只有一种展示:URL
|
||||
|
@ -6,8 +6,7 @@ import cn.hutool.core.date.TemporalAccessorUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.io.FileUtils;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
||||
@ -22,11 +21,13 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN;
|
||||
import static cn.hutool.core.date.DatePattern.UTC_WITH_XXX_OFFSET_PATTERN;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
|
||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||
|
||||
/**
|
||||
@ -103,47 +104,47 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||
|
||||
|
||||
@Override
|
||||
public Object parseNotify(PayNotifyReqDTO rawNotify) {
|
||||
log.info("[parseNotify][微信支付回调 data 数据: {}]", rawNotify.getBody());
|
||||
public Object parseNotify(Map<String, String> params, String body) {
|
||||
log.info("[parseNotify][微信支付回调 data 数据: {}]", body);
|
||||
try {
|
||||
// 微信支付 v2 回调结果处理
|
||||
switch (config.getApiVersion()) {
|
||||
case WxPayClientConfig.API_VERSION_V2:
|
||||
return parseOrderNotifyV2(rawNotify);
|
||||
return parseOrderNotifyV2(body);
|
||||
case WxPayClientConfig.API_VERSION_V3:
|
||||
return parseOrderNotifyV3(rawNotify);
|
||||
return parseOrderNotifyV3(body);
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
|
||||
}
|
||||
} catch (WxPayException e) {
|
||||
log.error("[parseNotify][rawNotify({}) 解析失败]", toJsonString(rawNotify), e);
|
||||
log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e);
|
||||
// throw buildPayException(e);
|
||||
throw new RuntimeException(e);
|
||||
// TODO 芋艿:缺一个异常翻译
|
||||
}
|
||||
}
|
||||
|
||||
private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException {
|
||||
WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
|
||||
private PayOrderRespDTO parseOrderNotifyV2(String body) throws WxPayException {
|
||||
WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(body);
|
||||
Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
|
||||
// 转换结果
|
||||
return PayOrderNotifyRespDTO
|
||||
return PayOrderRespDTO
|
||||
.builder()
|
||||
.orderExtensionNo(notifyResult.getOutTradeNo())
|
||||
.outTradeNo(notifyResult.getOutTradeNo())
|
||||
.channelOrderNo(notifyResult.getTransactionId())
|
||||
.channelUserId(notifyResult.getOpenid())
|
||||
.successTime(parseDateV2(notifyResult.getTimeEnd()))
|
||||
.build();
|
||||
}
|
||||
|
||||
private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException {
|
||||
WxPayOrderNotifyV3Result notifyResult = client.parseOrderNotifyV3Result(data.getBody(), null);
|
||||
private PayOrderRespDTO parseOrderNotifyV3(String body) throws WxPayException {
|
||||
WxPayOrderNotifyV3Result notifyResult = client.parseOrderNotifyV3Result(body, null);
|
||||
WxPayOrderNotifyV3Result.DecryptNotifyResult result = notifyResult.getResult();
|
||||
// 转换结果
|
||||
Assert.isTrue(Objects.equals(notifyResult.getResult().getTradeState(), "SUCCESS"),
|
||||
"支付结果非 SUCCESS");
|
||||
return PayOrderNotifyRespDTO.builder()
|
||||
.orderExtensionNo(result.getOutTradeNo())
|
||||
return PayOrderRespDTO.builder()
|
||||
.outTradeNo(result.getOutTradeNo())
|
||||
.channelOrderNo(result.getTradeState())
|
||||
.channelUserId(result.getPayer() != null ? result.getPayer().getOpenid() : null)
|
||||
.successTime(parseDateV3(result.getSuccessTime()))
|
||||
@ -175,7 +176,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||
if (Objects.equals(e.getReturnCode(), "FAIL")) {
|
||||
throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getReturnMsg());
|
||||
}
|
||||
// 情况三:系统异常,这里暂时不打,交给上层的 AbstractPayClient 统一打
|
||||
// 情况三:系统异常,这里暂时不打,交给上层的 AbstractPayClient 统一打日志
|
||||
return e;
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import com.github.binarywang.wxpay.constant.WxPayConstants;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
@ -21,7 +21,7 @@ public class WxAppPayClient extends AbstractWxPayClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||
protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,14 @@ import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
|
||||
import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
|
||||
import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
|
||||
import com.github.binarywang.wxpay.constant.WxPayConstants;
|
||||
@ -57,10 +58,10 @@ public class WxBarPayClient extends AbstractWxPayClient {
|
||||
}
|
||||
// 构建 WxPayMicropayRequest 对象
|
||||
WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder()
|
||||
.outTradeNo(reqDTO.getMerchantOrderId())
|
||||
.outTradeNo(reqDTO.getOutTradeNo())
|
||||
.body(reqDTO.getSubject())
|
||||
.detail(reqDTO.getBody())
|
||||
.totalFee(reqDTO.getAmount()) // 单位分
|
||||
.totalFee(reqDTO.getPrice()) // 单位分
|
||||
.timeExpire(formatDateV2(expireTime))
|
||||
.spbillCreateIp(reqDTO.getUserIp())
|
||||
.authCode(getAuthCode(reqDTO))
|
||||
@ -70,15 +71,17 @@ public class WxBarPayClient extends AbstractWxPayClient {
|
||||
try {
|
||||
WxPayMicropayResult response = client.micropay(request);
|
||||
// 支付成功(例如说,用户输入了密码)
|
||||
PayOrderNotifyRespDTO notify = PayOrderNotifyRespDTO.builder()
|
||||
.orderExtensionNo(response.getOutTradeNo())
|
||||
PayOrderRespDTO order = PayOrderRespDTO.builder()
|
||||
.status(PayOrderStatusRespEnum.SUCCESS.getStatus())
|
||||
.outTradeNo(response.getOutTradeNo())
|
||||
.channelOrderNo(response.getTransactionId())
|
||||
.channelUserId(response.getOpenid())
|
||||
.successTime(parseDateV2(response.getTimeEnd()))
|
||||
.rawData(response)
|
||||
.build();
|
||||
return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(),
|
||||
JsonUtils.toJsonString(response))
|
||||
.setNotify(notify);
|
||||
.setOrder(order);
|
||||
} catch (WxPayException ex) {
|
||||
// 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理
|
||||
// 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。
|
||||
@ -102,7 +105,7 @@ public class WxBarPayClient extends AbstractWxPayClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
||||
protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import com.github.binarywang.wxpay.constant.WxPayConstants;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
@ -21,7 +21,7 @@ public class WxH5PayClient extends AbstractWxPayClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||
protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
|
||||
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
|
||||
@ -37,11 +37,11 @@ public class WxNativePayClient extends AbstractWxPayClient {
|
||||
protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
|
||||
// 构建 WxPayUnifiedOrderRequest 对象
|
||||
WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
|
||||
.outTradeNo(reqDTO.getMerchantOrderId())
|
||||
.outTradeNo(reqDTO.getOutTradeNo())
|
||||
.body(reqDTO.getSubject())
|
||||
.detail(reqDTO.getBody())
|
||||
.totalFee(reqDTO.getAmount()) // 单位分
|
||||
.productId(reqDTO.getMerchantOrderId())
|
||||
.totalFee(reqDTO.getPrice()) // 单位分
|
||||
.productId(reqDTO.getOutTradeNo())
|
||||
.timeExpire(formatDateV2(reqDTO.getExpireTime()))
|
||||
.spbillCreateIp(reqDTO.getUserIp())
|
||||
.notifyUrl(reqDTO.getNotifyUrl())
|
||||
@ -58,9 +58,9 @@ public class WxNativePayClient extends AbstractWxPayClient {
|
||||
protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
|
||||
// 构建 WxPayUnifiedOrderRequest 对象
|
||||
WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
|
||||
request.setOutTradeNo(reqDTO.getMerchantOrderId());
|
||||
request.setOutTradeNo(reqDTO.getOutTradeNo());
|
||||
request.setDescription(reqDTO.getBody());
|
||||
request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
|
||||
request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getPrice())); // 单位分
|
||||
request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
|
||||
request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
|
||||
request.setNotifyUrl(reqDTO.getNotifyUrl());
|
||||
@ -73,7 +73,7 @@ public class WxNativePayClient extends AbstractWxPayClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||
protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
|
||||
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
|
||||
@ -47,10 +47,10 @@ public class WxPubPayClient extends AbstractWxPayClient {
|
||||
protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
|
||||
// 构建 WxPayUnifiedOrderRequest 对象
|
||||
WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
|
||||
.outTradeNo(reqDTO.getMerchantOrderId())
|
||||
.outTradeNo(reqDTO.getOutTradeNo())
|
||||
.body(reqDTO.getSubject())
|
||||
.detail(reqDTO.getBody())
|
||||
.totalFee(reqDTO.getAmount()) // 单位分
|
||||
.totalFee(reqDTO.getPrice()) // 单位分
|
||||
.timeExpire(formatDateV2(reqDTO.getExpireTime()))
|
||||
.spbillCreateIp(reqDTO.getUserIp())
|
||||
.openid(getOpenid(reqDTO))
|
||||
@ -68,9 +68,9 @@ public class WxPubPayClient extends AbstractWxPayClient {
|
||||
protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
|
||||
// 构建 WxPayUnifiedOrderRequest 对象
|
||||
WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
|
||||
request.setOutTradeNo(reqDTO.getMerchantOrderId());
|
||||
request.setOutTradeNo(reqDTO.getOutTradeNo());
|
||||
request.setDescription(reqDTO.getSubject());
|
||||
request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
|
||||
request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getPrice())); // 单位分
|
||||
request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
|
||||
request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
|
||||
request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
|
||||
@ -84,7 +84,7 @@ public class WxPubPayClient extends AbstractWxPayClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
||||
protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
||||
// TODO 需要实现
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
@ -3,10 +3,12 @@ package cn.iocoder.yudao.framework.pay.core.enums.order;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 渠道的支付状态枚举
|
||||
*
|
||||
* @author 遇到源码
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@ -20,4 +22,14 @@ public enum PayOrderStatusRespEnum {
|
||||
private final Integer status;
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* 判断是否支付成功
|
||||
*
|
||||
* @param status 状态
|
||||
* @return 是否支付成功
|
||||
*/
|
||||
public static boolean isSuccess(Integer status) {
|
||||
return Objects.equals(status, SUCCESS.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.enums.refund;
|
||||
|
||||
// TODO 芋艿:看看能不能去掉
|
||||
/**
|
||||
* 退款通知, 统一的渠道退款状态
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
public enum PayNotifyRefundStatusEnum {
|
||||
|
||||
/**
|
||||
* 支付宝 中 全额退款 trade_status=TRADE_CLOSED, 部分退款 trade_status=TRADE_SUCCESS
|
||||
* 退款成功
|
||||
*/
|
||||
SUCCESS,
|
||||
|
||||
/**
|
||||
* 支付宝退款通知没有这个状态
|
||||
* 退款异常
|
||||
*/
|
||||
ABNORMAL;
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.enums.refund;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 渠道的退款状态枚举
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PayRefundRespEnum {
|
||||
|
||||
SUCCESS(1, "退款成功"),
|
||||
FAILURE(2, "退款失败"),
|
||||
PROCESSING(3,"退款处理中"),
|
||||
CLOSED(4, "退款关闭");
|
||||
|
||||
private final Integer status;
|
||||
private final String name;
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.enums.refund;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 渠道的退款状态枚举
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PayRefundStatusRespEnum {
|
||||
|
||||
WAITING(0, "未退款"),
|
||||
SUCCESS(10, "退款成功"),
|
||||
FAILURE(20, "退款失败");
|
||||
|
||||
private final Integer status;
|
||||
private final String name;
|
||||
|
||||
public static boolean isSuccess(Integer status) {
|
||||
return Objects.equals(status, SUCCESS.getStatus());
|
||||
}
|
||||
|
||||
}
|
@ -121,10 +121,10 @@ public class PayClientFactoryImplIntegrationTest {
|
||||
|
||||
private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
|
||||
PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
|
||||
reqDTO.setAmount(123);
|
||||
reqDTO.setPrice(123);
|
||||
reqDTO.setSubject("IPhone 13");
|
||||
reqDTO.setBody("biubiubiu");
|
||||
reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
|
||||
reqDTO.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
|
||||
reqDTO.setUserIp("127.0.0.1");
|
||||
reqDTO.setNotifyUrl("http://127.0.0.1:8080");
|
||||
return reqDTO;
|
||||
|
@ -1,8 +1,6 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.DefaultAlipayClient;
|
||||
@ -74,8 +72,8 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
|
||||
// 这里,设置可以直接随机整个对象。
|
||||
Long shopOrderId = System.currentTimeMillis();
|
||||
PayOrderUnifiedReqDTO reqDTO=new PayOrderUnifiedReqDTO();
|
||||
reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
|
||||
reqDTO.setAmount(1);
|
||||
reqDTO.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
|
||||
reqDTO.setPrice(1);
|
||||
reqDTO.setBody("内容:" + shopOrderId);
|
||||
reqDTO.setSubject("标题:"+shopOrderId);
|
||||
String notify="http://niubi.natapp1.cc/api/pay/order/notify";
|
||||
|
Reference in New Issue
Block a user