feat:【PAY 支付】完善支付转账示例,以及转账功能(支付宝)
This commit is contained in:
@@ -66,10 +66,10 @@ public class PayTransferRespDTO {
|
||||
/**
|
||||
* 创建【IN_PROGRESS】状态的转账返回
|
||||
*/
|
||||
public static PayTransferRespDTO dealingOf(String channelTransferNo,
|
||||
String outTransferNo, Object rawData) {
|
||||
public static PayTransferRespDTO processingOf(String channelTransferNo,
|
||||
String outTransferNo, Object rawData) {
|
||||
PayTransferRespDTO respDTO = new PayTransferRespDTO();
|
||||
respDTO.status = PayTransferStatusRespEnum.IN_PROGRESS.getStatus();
|
||||
respDTO.status = PayTransferStatusRespEnum.PROCESSING.getStatus();
|
||||
respDTO.channelTransferNo = channelTransferNo;
|
||||
respDTO.outTransferNo = outTransferNo;
|
||||
respDTO.rawData = rawData;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto.transfer;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
@@ -10,9 +9,6 @@ import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.Alipay;
|
||||
import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.WxPay;
|
||||
|
||||
/**
|
||||
* 统一转账 Request DTO
|
||||
*
|
||||
@@ -27,6 +23,9 @@ public class PayTransferUnifiedReqDTO {
|
||||
@NotEmpty(message = "用户 IP 不能为空")
|
||||
private String userIp;
|
||||
|
||||
/**
|
||||
* 外部转账单编号
|
||||
*/
|
||||
@NotEmpty(message = "外部转账单编号不能为空")
|
||||
private String outTransferNo;
|
||||
|
||||
@@ -44,25 +43,19 @@ public class PayTransferUnifiedReqDTO {
|
||||
@Length(max = 128, message = "转账标题不能超过 128")
|
||||
private String subject;
|
||||
|
||||
// TODO @芋艿:userName、alipayLogonId、openid =》channelExtras;另外:validatePayTransferReqDTO 去掉;
|
||||
/**
|
||||
* 收款人账号
|
||||
*
|
||||
* 微信场景下:openid
|
||||
* 支付宝场景下:支付宝账号
|
||||
*/
|
||||
@NotEmpty(message = "收款人账号不能为空")
|
||||
private String userAccount;
|
||||
/**
|
||||
* 收款人姓名
|
||||
*/
|
||||
@NotBlank(message = "收款人姓名不能为空", groups = {Alipay.class})
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 支付宝登录号
|
||||
*/
|
||||
@NotBlank(message = "支付宝登录号不能为空", groups = {Alipay.class})
|
||||
private String alipayLogonId;
|
||||
|
||||
/**
|
||||
* 微信 openId
|
||||
*/
|
||||
@NotBlank(message = "微信 openId 不能为空", groups = {WxPay.class})
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* 支付渠道的额外参数
|
||||
*/
|
||||
|
||||
@@ -11,13 +11,10 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReq
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,6 @@ import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
@@ -40,8 +39,6 @@ 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.enums.GlobalErrorCodeConstants.ERROR_CONFIGURATION;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
||||
import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_CERTIFICATE;
|
||||
import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_PUBLIC_KEY;
|
||||
|
||||
@@ -235,10 +232,9 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
||||
|
||||
@Override
|
||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws AlipayApiException {
|
||||
// 0. 校验公钥类型:必须使用公钥证书模式
|
||||
if (ObjectUtil.notEqual(config.getMode(), MODE_CERTIFICATE)) {
|
||||
throw exception0(ERROR_CONFIGURATION.getCode(), "支付宝单笔转账必须使用公钥证书模式");
|
||||
}
|
||||
// 补充说明:https://opendocs.alipay.com/open/03dcrm?pathHash=4ba3b20b
|
||||
// 沙箱环境:可通过 公钥模式 或 公钥证书模式 加签进行调试
|
||||
// 生产环境:必须使用 公钥证书模式 加签请求强校验请求
|
||||
|
||||
// 1.1 构建 AlipayFundTransUniTransferModel
|
||||
AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
|
||||
@@ -254,7 +250,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
||||
// ② 个性化的参数
|
||||
Participant payeeInfo = new Participant();
|
||||
payeeInfo.setIdentityType("ALIPAY_LOGON_ID"); // 暂时只考虑转账到支付宝,银行没有权限 https://opendocs.alipay.com/open/02byvc?scene=66dd06f5a923403393b85de68d3c0055
|
||||
payeeInfo.setIdentity(reqDTO.getAlipayLogonId()); // 支付宝登录号
|
||||
payeeInfo.setIdentity(reqDTO.getUserAccount()); // 支付宝登录号
|
||||
payeeInfo.setName(reqDTO.getUserName()); // 支付宝账号姓名
|
||||
model.setPayeeInfo(payeeInfo);
|
||||
// 1.2 构建 AlipayFundTransUniTransferRequest
|
||||
@@ -262,7 +258,12 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
||||
request.setBizModel(model);
|
||||
|
||||
// 2.1 执行请求
|
||||
AlipayFundTransUniTransferResponse response = client.certificateExecute(request);
|
||||
AlipayFundTransUniTransferResponse response;
|
||||
if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) { // 证书模式
|
||||
response = client.certificateExecute(request);
|
||||
} else {
|
||||
response = client.execute(request);
|
||||
}
|
||||
if (!response.isSuccess()) {
|
||||
// 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询,或相同 outBizNo 重新发起转账
|
||||
// 发现 outBizNo 相同 两次请求参数相同. 会返回 "PAYMENT_INFO_INCONSISTENCY", 不知道哪里的问题. 暂时返回 WAIT. 后续job 会轮询
|
||||
@@ -278,7 +279,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
||||
reqDTO.getOutTransferNo(), response);
|
||||
}
|
||||
if (Objects.equals(response.getStatus(), "DEALING")) { // 转账到银行卡会出现 "DEALING" 处理中
|
||||
return PayTransferRespDTO.dealingOf(response.getOrderId(), reqDTO.getOutTransferNo(), response);
|
||||
return PayTransferRespDTO.processingOf(response.getOrderId(), reqDTO.getOutTransferNo(), response);
|
||||
}
|
||||
return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()),
|
||||
response.getOutBizNo(), response);
|
||||
@@ -317,7 +318,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
||||
outTradeNo, response);
|
||||
}
|
||||
if (Objects.equals(response.getStatus(), "DEALING")) { // 转账到银行卡会出现 "DEALING" 处理中
|
||||
return PayTransferRespDTO.dealingOf(response.getOrderId(), outTradeNo, response);
|
||||
return PayTransferRespDTO.processingOf(response.getOrderId(), outTradeNo, response);
|
||||
}
|
||||
return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getPayDate()),
|
||||
response.getOutBizNo(), response);
|
||||
|
||||
@@ -469,7 +469,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||
.outDetailNo(reqDTO.getOutTransferNo())
|
||||
.transferAmount(reqDTO.getPrice())
|
||||
.transferRemark(reqDTO.getSubject())
|
||||
.openid(reqDTO.getOpenid())
|
||||
.openid(reqDTO.getUserAccount())
|
||||
.build());
|
||||
// TODO @luchi:能不能我们搞个 TransferBatchesRequestX extends TransferBatchesRequest,这样更简洁一点。
|
||||
TransferBatchesRequest transferBatches = TransferBatchesRequest.newBuilder()
|
||||
@@ -484,7 +484,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||
// 2.1 执行请求
|
||||
TransferBatchesResult transferBatchesResult = client.getTransferService().transferBatches(transferBatches);
|
||||
// 2.2 创建返回结果
|
||||
return PayTransferRespDTO.dealingOf(transferBatchesResult.getBatchId(), reqDTO.getOutTransferNo(), transferBatchesResult);
|
||||
return PayTransferRespDTO.processingOf(transferBatchesResult.getBatchId(), reqDTO.getOutTransferNo(), transferBatchesResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -509,7 +509,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||
return PayTransferRespDTO.closedOf(transferBatch.getBatchStatus(), transferBatch.getCloseReason(),
|
||||
transferBatch.getOutBatchNo(), response);
|
||||
}
|
||||
return PayTransferRespDTO.dealingOf(transferBatch.getBatchId(), transferBatch.getOutBatchNo(), response);
|
||||
return PayTransferRespDTO.processingOf(transferBatch.getBatchId(), transferBatch.getOutBatchNo(), response);
|
||||
}
|
||||
|
||||
// ========== 各种工具方法 ==========
|
||||
|
||||
@@ -4,7 +4,6 @@ import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
|
||||
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
|
||||
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
|
||||
@@ -28,10 +27,6 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString
|
||||
@Slf4j
|
||||
public class WxPubPayClient extends AbstractWxPayClient {
|
||||
|
||||
public WxPubPayClient(Long channelId, WxPayClientConfig config) {
|
||||
super(channelId, PayChannelEnum.WX_PUB.getCode(), config);
|
||||
}
|
||||
|
||||
protected WxPubPayClient(Long channelId, String channelCode, WxPayClientConfig config) {
|
||||
super(channelId, channelCode, config);
|
||||
}
|
||||
|
||||
@@ -15,18 +15,9 @@ import java.util.Objects;
|
||||
public enum PayTransferStatusRespEnum {
|
||||
|
||||
WAITING(0, "等待转账"),
|
||||
|
||||
/**
|
||||
* TODO 转账到银行卡. 会有T+0 T+1 到账的请情况。 还未实现
|
||||
* TODO @jason:可以看看其它开源项目,针对这个场景,处理策略是怎么样的?例如说,每天主动轮询?这个状态的单子?
|
||||
*/
|
||||
IN_PROGRESS(10, "转账进行中"),
|
||||
|
||||
SUCCESS(20, "转账成功"),
|
||||
/**
|
||||
* 转账关闭 (失败,或者其它情况)
|
||||
*/
|
||||
CLOSED(30, "转账关闭");
|
||||
PROCESSING(5, "转账进行中"),
|
||||
SUCCESS(10, "转账成功"),
|
||||
CLOSED(20, "转账关闭");
|
||||
|
||||
private final Integer status;
|
||||
private final String name;
|
||||
@@ -39,7 +30,8 @@ public enum PayTransferStatusRespEnum {
|
||||
return Objects.equals(status, CLOSED.getStatus());
|
||||
}
|
||||
|
||||
public static boolean isInProgress(Integer status) {
|
||||
return Objects.equals(status, IN_PROGRESS.getStatus());
|
||||
public static boolean isProcessing(Integer status) {
|
||||
return Objects.equals(status, PROCESSING.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.enums.transfer;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
// TODO @芋艿:【转账】这里要不要改成支付平台?
|
||||
/**
|
||||
* 转账类型枚举
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum PayTransferTypeEnum implements ArrayValuable<Integer> {
|
||||
|
||||
ALIPAY_BALANCE(1, "支付宝余额"),
|
||||
WX_BALANCE(2, "微信余额"),
|
||||
BANK_CARD(3, "银行卡"),
|
||||
WALLET_BALANCE(4, "钱包余额");
|
||||
|
||||
public interface WxPay {
|
||||
}
|
||||
|
||||
public interface Alipay {
|
||||
}
|
||||
|
||||
private final Integer type;
|
||||
private final String name;
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(PayTransferTypeEnum::getType).toArray(Integer[]::new);
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static PayTransferTypeEnum typeOf(Integer type) {
|
||||
return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user