feat 云闪付对接

This commit is contained in:
xxm1995
2024-03-07 18:06:55 +08:00
parent f6cd4b0a46
commit 0d49b9dadd
33 changed files with 1166 additions and 155 deletions

View File

@@ -0,0 +1,67 @@
package cn.bootx.platform.daxpay.service.code;
/**
* 云闪付常量
* @author xxm
* @since 2024/3/7
*/
public interface UnionPayCode {
/** 成功状态 */
String SUCCESS = "0";
/** 状态 0表示成功 */
String STATUS = "status";
/** 业务结果 0表示成功非0表示失败 */
String RESULT_CODE = "result_code";
/** 网关订单号 */
String TRANSACTION_ID = "transaction_id";
/** 第三方订单号 */
String OUT_TRANSACTION_ID = "out_transaction_id";
/** 退款ID */
String REFUND_ID = "refund_id";
/**
* 支付完成时间
* 格式: yyyyMMddHHmmss
*/
String TIME_END = "time_end";
/** 支付结果 */
String PAY_RESULT = "pay_result";
/** 总金额 */
String TOTAL_FEE = "total_fee";
/** 交易状态 */
String TRADE_STATE = "trade_state";
/** 支付成功 */
String TRADE_SUCCESS = "SUCCESS";
/** 转入退款 */
String TRADE_REFUND = "REFUND";
/** 未支付 */
String TRADE_NOT_PAY = "NOTPAY";
/** 已关闭 */
String TRADE_CLOSED = "CLOSED";
/** 支付失败(其他原因,如银行返回失败) */
String TRADE_PAY_ERROR = "PAYERROR";
/** 返回信息 */
String MESSAGE = "message";
/** 错误代码描述 */
String ERR_MSG = "err_msg";
}

View File

@@ -0,0 +1,22 @@
package cn.bootx.platform.daxpay.service.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 云闪付
* @author xxm
* @since 2024/3/7
*/
@Getter
@AllArgsConstructor
public enum UnionPayRecordTypeEnum {
/** 支付 */
PAY("pay", "支付"),
/** 退款 */
REFUND("refund", "退款");
private final String code;
private final String name;
}

View File

@@ -14,8 +14,6 @@ public interface WeChatPayCode {
String API_V3 = "apiV3";
// 请求参数
/** jsapi发起获取AuthCode时的重定向参数 */
String JSAPI_REDIRECT_URL = "JsapiRedirectUrl";
// 返回参数
/** 二维码链接 */

View File

@@ -1,51 +0,0 @@
package cn.bootx.platform.daxpay.service.common.entity;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.mysql.annotation.DbMySqlIndex;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 基础支付订单信息类
* @author xxm
* @since 2023/12/18
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class BasePayOrder extends MpIdEntity {
/** 交易记录ID */
@DbColumn(comment = "交易记录ID")
@DbMySqlIndex(comment = "交易记录ID")
private Long paymentId;
/** 关联的业务号 */
@DbMySqlIndex(comment = "业务号索引")
@DbColumn(comment = "关联的业务号")
private String businessNo;
/** 交易金额 */
@DbColumn(comment = "交易金额")
private Integer amount;
/** 可退款金额 */
@DbColumn(comment = "可退款金额")
private Integer refundableBalance;
/**
* 支付状态
* @see PayStatusEnum
*/
@DbColumn(comment = "支付状态")
private String status;
/** 支付时间 */
@DbColumn(comment = "支付时间")
private LocalDateTime payTime;
}

View File

@@ -25,7 +25,6 @@ import java.util.List;
@RequiredArgsConstructor
public class AliPayRecordManager extends BaseManager<AliPayRecordMapper, AliPayRecord> {
/**
* 分页
*/

View File

@@ -1,7 +1,9 @@
package cn.bootx.platform.daxpay.service.core.channel.union.convert;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayRecord;
import cn.bootx.platform.daxpay.service.dto.channel.union.UnionPayConfigDto;
import cn.bootx.platform.daxpay.service.dto.channel.union.UnionPayRecordDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -16,4 +18,6 @@ public interface UnionPayConvert {
UnionPayConfigDto convert(UnionPayConfig in);
UnionPayRecordDto convert(UnionPayRecord in);
}

View File

@@ -1,11 +1,20 @@
package cn.bootx.platform.daxpay.service.core.channel.union.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayRecord;
import cn.bootx.platform.daxpay.service.param.channel.union.UnionPayRecordQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author xxm
* @since 2022/3/11
@@ -15,4 +24,21 @@ import org.springframework.stereotype.Repository;
@RequiredArgsConstructor
public class UnionPayRecordManager extends BaseManager<UnionPayRecordMapper, UnionPayRecord> {
/**
* 分页
*/
public Page<UnionPayRecord> page(PageParam pageParam, UnionPayRecordQuery param){
Page<UnionPayRecord> mpPage = MpUtil.getMpPage(pageParam, UnionPayRecord.class);
QueryWrapper<UnionPayRecord> generator = QueryGenerator.generator(param);
return this.page(mpPage, generator);
}
/**
* 按时间范围查询
*/
public List<UnionPayRecord> findByDate(LocalDateTime startDate, LocalDateTime endDate){
return this.lambdaQuery()
.between(UnionPayRecord::getGatewayTime, startDate, endDate)
.list();
}
}

View File

@@ -1,12 +1,18 @@
package cn.bootx.platform.daxpay.service.core.channel.union.entity;
import cn.bootx.platform.daxpay.service.common.entity.BasePayOrder;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.service.code.UnionPayRecordTypeEnum;
import cn.bootx.platform.daxpay.service.dto.channel.union.UnionPayRecordDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 云闪付流水记录
* @author xxm
@@ -17,6 +23,40 @@ import lombok.experimental.Accessors;
@DbTable(comment = "云闪付流水记录")
@Accessors(chain = true)
@TableName("pay_union_pay_record")
public class UnionPayRecord extends BasePayOrder {
public class UnionPayRecord extends MpCreateEntity implements EntityBaseFunction<UnionPayRecordDto> {
/** 标题 */
@DbColumn(comment = "标题")
private String title;
/** 金额 */
@DbColumn(comment = "金额")
private Integer amount;
/**
* 业务类型
* @see UnionPayRecordTypeEnum
*/
@DbColumn(comment = "业务类型")
private String type;
/** 本地订单号 */
@DbColumn(comment = "本地订单号")
private Long orderId;
/** 网关订单号 */
@DbColumn(comment = "网关订单号")
private String gatewayOrderNo;
/** 网关完成时间 */
@DbColumn(comment = "网关完成时间")
private LocalDateTime gatewayTime;
/**
* 转换
*/
@Override
public UnionPayRecordDto toDto() {
return null;
}
}

View File

@@ -0,0 +1,128 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
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.service.code.PaymentTypeEnum;
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.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.func.AbsCallbackStrategy;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
/**
* 云闪付回调处理
* @author xxm
* @since 2024/3/7
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UnionPayCallbackService extends AbsCallbackStrategy {
@Resource
private UnionPayConfigService unionPayConfigService;
/**
* 验证信息格式
*/
@Override
public boolean verifyNotify() {
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
Map<String, String> params = callbackInfo.getCallbackParam();
String callReq = JSONUtil.toJsonStr(params);
log.info("云闪付发起回调 报文: {}", callReq);
String status = params.get(STATUS);
String returnCode = params.get(RESULT_CODE);
// 处理失败
if (!Objects.equals(SUCCESS, status)||!Objects.equals(SUCCESS, returnCode)){
return false;
}
// 支付回调信息校验
UnionPayConfig config = unionPayConfigService.getConfig();
if (Objects.isNull(config)) {
log.warn("云闪付支付配置不存在");
return false;
}
// 注意此处签名方式需与统一下单的签名类型一致
return WxPayKit.verifyNotify(params, config.getAppKey(), SignType.MD5);
}
/**
* 判断类型 支付回调/退款回调, 云闪付只有支付回调
*
* @see PaymentTypeEnum
*/
@Override
public PaymentTypeEnum getCallbackType() {
return PaymentTypeEnum.PAY;
}
/**
* 解析支付回调数据并放到上下文中
*/
@Override
public void resolvePayData() {
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
Map<String, String> callbackParam = callbackInfo.getCallbackParam();
// 网关订单号
callbackInfo.setGatewayOrderNo(callbackParam.get(TRANSACTION_ID));
// 支付订单ID
callbackInfo.setOrderId(Long.valueOf(callbackParam.get(OUT_TRANSACTION_ID)));
// 支付结果
PayStatusEnum payStatus = WxPayKit.codeIsOk(callbackParam.get(PAY_RESULT)) ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL;
callbackInfo.setGatewayStatus(payStatus.getCode());
// 支付金额
callbackInfo.setAmount(callbackParam.get(TOTAL_FEE));
String timeEnd = callbackParam.get(TIME_END);
if (StrUtil.isNotBlank(timeEnd)) {
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
callbackInfo.setFinishTime(time);
} else {
callbackInfo.setFinishTime(LocalDateTime.now());
}
}
/**
* 解析退款回调数据并放到上下文中
*/
@Override
public void resolveRefundData() {
}
/**
* 返回响应结果
*/
@Override
public String getReturnMsg() {
return "success";
}
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.UNION_PAY;
}
}

View File

@@ -0,0 +1,65 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.UnionPayCode;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.hutool.core.util.StrUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.unionpay.enums.ServiceEnum;
import com.ijpay.unionpay.model.CloseOrderModel;
import com.ijpay.wxpay.WxPayApi;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Objects;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
/**
* 云闪付支付关闭
* @author xxm
* @since 2024/3/7
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UnionPayCloseService {
/**
* 关闭订单
*/
public void close(PayOrder payOrder, UnionPayConfig unionPayConfig) {
Map<String, String> params = CloseOrderModel.builder()
.service(ServiceEnum.CLOSE.toString())
.mch_id(unionPayConfig.getMachId())
.out_trade_no(String.valueOf(payOrder.getId()))
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(unionPayConfig.getAppKey(), SignType.HMACSHA256);
String xmlResult = WxPayApi.closeOrder(params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
}
/**
* 验证错误信息
*/
private void verifyErrorMsg(Map<String, String> result) {
String status = result.get(UnionPayCode.STATUS);
String returnCode = result.get(UnionPayCode.RESULT_CODE);
// 判断查询是否成功
if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){
String errorMsg = result.get(ERR_MSG);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(MESSAGE);
}
log.error("订单关闭失败 {}", errorMsg);
throw new PayFailureException(errorMsg);
}
}
}

View File

@@ -0,0 +1,17 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 云闪付对账
* @author xxm
* @since 2024/3/7
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UnionPayReconcileService {
}

View File

@@ -0,0 +1,72 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.service.code.UnionPayRecordTypeEnum;
import cn.bootx.platform.daxpay.service.core.channel.union.dao.UnionPayRecordManager;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayRecord;
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.refund.entity.RefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.dto.channel.union.UnionPayRecordDto;
import cn.bootx.platform.daxpay.service.param.channel.union.UnionPayRecordQuery;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 云闪付支付记录服务
* @author xxm
* @since 2024/3/7
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UnionPayRecordService {
private final UnionPayRecordManager unionPayRecordManager;
/**
* 支付
*/
public void pay(PayOrder order, PayChannelOrder channelOrder){
UnionPayRecord unionPayRecord = new UnionPayRecord()
.setType(UnionPayRecordTypeEnum.PAY.getCode())
.setTitle(order.getTitle())
.setOrderId(order.getId())
.setAmount(channelOrder.getAmount())
.setGatewayOrderNo(order.getGatewayOrderNo())
.setGatewayTime(channelOrder.getPayTime());
unionPayRecordManager.save(unionPayRecord);
}
/**
* 退款
*/
public void refund(RefundOrder order, RefundChannelOrder channelOrder){
UnionPayRecord unionPayRecord = new UnionPayRecord()
.setType(UnionPayRecordTypeEnum.REFUND.getCode())
.setTitle(order.getTitle())
.setOrderId(order.getId())
.setAmount(channelOrder.getAmount())
.setGatewayOrderNo(order.getGatewayOrderNo())
.setGatewayTime(channelOrder.getRefundTime());
unionPayRecordManager.save(unionPayRecord);
}
/**
* 分页
*/
public PageResult<UnionPayRecordDto> page(PageParam pageParam, UnionPayRecordQuery param){
return MpUtil.convert2DtoPageResult(unionPayRecordManager.page(pageParam, param));
}
/**
* 查询详情
*/
public UnionPayRecordDto findById(Long id){
return unionPayRecordManager.findById(id).map(UnionPayRecord::toDto).orElseThrow(DataNotExistException::new);
}
}

View File

@@ -0,0 +1,83 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.UnionPayCode;
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.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.hutool.core.util.StrUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.unionpay.UnionPayApi;
import com.ijpay.unionpay.enums.ServiceEnum;
import com.ijpay.unionpay.model.RefundModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
/**
* 云闪付退款操作
* @author xxm
* @since 2024/3/7
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UnionPayRefundService {
/**
* 退款方法
*/
public void refund(RefundOrder refundOrder, int amount, PayChannelOrder channelOrder, UnionPayConfig unionPayConfig) {
Map<String, String> params = RefundModel.builder()
.service(ServiceEnum.REFUND.toString())
.mch_id(unionPayConfig.getMachId())
.out_trade_no(String.valueOf(refundOrder.getPaymentId()))
.out_refund_no(String.valueOf(refundOrder.getId()))
.total_fee(String.valueOf(channelOrder.getAmount()))
.refund_fee(String.valueOf(amount))
.op_user_id(unionPayConfig.getMachId())
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(unionPayConfig.getAppKey(), SignType.MD5);
String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
// 云闪付退款是否成功需要查询状态, 所以设置为退款中状态
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setStatus(RefundStatusEnum.PROGRESS)
.setGatewayOrderNo(result.get(REFUND_ID));
}
/**
* 验证错误信息
*/
private void verifyErrorMsg(Map<String, String> result) {
String status = result.get(UnionPayCode.STATUS);
String returnCode = result.get(UnionPayCode.RESULT_CODE);
// 判断查询是否成功
if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){
String errorMsg = result.get(ERR_MSG);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(MESSAGE);
}
log.error("订单退款失败 {}", errorMsg);
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setErrorMsg(errorMsg);
refundInfo.setErrorCode(Optional.ofNullable(returnCode).orElse(returnCode));
throw new PayFailureException(errorMsg);
}
}
}

View File

@@ -5,24 +5,30 @@ import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.channel.UnionPayParam;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.service.code.AliPayWay;
import cn.bootx.platform.daxpay.service.code.UnionPayCode;
import cn.bootx.platform.daxpay.service.common.context.PayLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.util.PayUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.unionpay.UnionPayApi;
import com.ijpay.unionpay.enums.ServiceEnum;
import com.ijpay.unionpay.model.MicroPayModel;
import com.ijpay.unionpay.model.UnifiedOrderModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
/**
* 云闪付支付
* @author xxm
@@ -33,45 +39,6 @@ import java.util.Optional;
@RequiredArgsConstructor
public class UnionPayService {
/**
* 支付接口
*/
public void pay(PayOrder payOrder, PayChannelParam payChannelParam, UnionPayParam unionPayParam, UnionPayConfig config){
Integer amount = payChannelParam.getAmount();
String totalFee = String.valueOf(amount);
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();;
String payBody = null;
PayWayEnum payWayEnum = PayWayEnum.findByCode(payChannelParam.getWay());
// 二维码支付
if (payWayEnum == PayWayEnum.QRCODE){
payBody = this.qrCodePay(totalFee, payOrder, config);
}
asyncPayInfo.setPayBody(payBody);
}
/**
* 扫码支付
*/
public String qrCodePay(String totalFee, PayOrder payOrder, UnionPayConfig config){
Map<String, String> params = UnifiedOrderModel.builder()
.service(ServiceEnum.NATIVE.toString())
.mch_id(config.getMachId())
.out_trade_no(String.valueOf(payOrder.getId()))
.body(payOrder.getTitle())
.total_fee(totalFee)
.time_expire(PayUtil.getUnionExpiredTime(payOrder.getExpiredTime()))
.mch_create_ip("127.0.0.1")
.notify_url(config.getNotifyUrl())
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(config.getAppKey(), SignType.MD5);
String xmlResult = UnionPayApi.execution(config.getServerUrl(), params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
return result.get("code_url");
}
/**
* 支付前检查支付方式是否可用
@@ -88,4 +55,203 @@ public class UnionPayService {
throw new PayFailureException("该云闪付支付方式不可用");
}
}
/**
* 支付接口
*/
public void pay(PayOrder payOrder, PayChannelParam payChannelParam, UnionPayParam unionPayParam, UnionPayConfig unionPayConfig){
Integer amount = payChannelParam.getAmount();
String totalFee = String.valueOf(amount);
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();;
String payBody = null;
PayWayEnum payWayEnum = PayWayEnum.findByCode(payChannelParam.getWay());
// 微信APP支付
if (payWayEnum == PayWayEnum.APP) {
payBody = this.wxAppPay(totalFee, payOrder, unionPayParam, unionPayConfig);
}
// 微信公众号支付或者小程序支付
else if (payWayEnum == PayWayEnum.JSAPI_WX_PAY) {
payBody = this.wxJsPay(totalFee, payOrder, unionPayParam.getOpenId(), unionPayConfig);
}
// 支付宝JS支付
else if (payWayEnum == PayWayEnum.JSAPI_ALI_PAY) {
payBody = this.aliJsPay(totalFee, payOrder, unionPayParam, unionPayConfig);
}
// 银联JS支付
else if (payWayEnum == PayWayEnum.JSAPI) {
payBody = this.unionJsPay(totalFee, payOrder, unionPayParam, unionPayConfig);
}
// 二维码支付
else if (payWayEnum == PayWayEnum.QRCODE) {
payBody = this.qrCodePay(totalFee, payOrder, unionPayConfig);
}
// 付款码支付
else if (payWayEnum == PayWayEnum.BARCODE) {
this.barCodePay(totalFee, payOrder, unionPayParam.getAuthCode(), unionPayConfig);
}
asyncPayInfo.setPayBody(payBody);
}
/**
* 支付宝生活号支付
*/
private String aliJsPay(String amount, PayOrder payOrder, UnionPayParam unionPayParam, UnionPayConfig unionPayConfig) {
Map<String, String> params = UnifiedOrderModel.builder()
.service(ServiceEnum.ALI_PAY_JS_PAY.toString())
.mch_id(unionPayConfig.getMachId())
.out_trade_no(WxPayKit.generateStr())
.body(payOrder.getTitle())
.total_fee(amount)
.mch_create_ip("127.0.0.15")
.notify_url(unionPayConfig.getNotifyUrl())
.nonce_str(WxPayKit.generateStr())
.buyer_id(unionPayParam.getBuyerId())
.build()
.createSign(unionPayConfig.getAppKey(), SignType.MD5);
String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
return null;
}
/**
* 银联JS支付
*/
private String unionJsPay(String amount, PayOrder payOrder, UnionPayParam unionPayParam, UnionPayConfig unionPayConfig) {
Map<String, String> params = UnifiedOrderModel.builder()
.service(ServiceEnum.UNION_JS_PAY.toString())
.mch_id(unionPayConfig.getMachId())
.out_trade_no(WxPayKit.generateStr())
.body(payOrder.getTitle())
.user_id(unionPayParam.getUserId())
.customer_ip(unionPayParam.getCustomerIp())
.total_fee(amount)
.mch_create_ip("127.0.0.1")
.notify_url(unionPayConfig.getNotifyUrl())
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(unionPayConfig.getAppKey(), SignType.MD5);
System.out.println(params);
String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
return null;
}
/**
* 微信APP支付
*/
private String wxAppPay(String amount, PayOrder payOrder, UnionPayParam unionPayParam, UnionPayConfig unionPayConfig) {
Map<String, String> params = UnifiedOrderModel.builder()
.service(ServiceEnum.WEI_XIN_APP_PAY.toString())
.mch_id(unionPayConfig.getMachId())
.appid(unionPayParam.getAppId())
.sub_appid(unionPayParam.getSubAppId())
.out_trade_no(WxPayKit.generateStr())
.body(payOrder.getTitle())
.attach("聚合支付 SDK")
.total_fee(amount)
.mch_create_ip("127.0.0.1")
.notify_url(unionPayConfig.getNotifyUrl())
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(unionPayConfig.getAppKey(), SignType.MD5);
String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
return null;
}
/**
* 微信公众号支付或者小程序支付
*/
private String wxJsPay(String amount, PayOrder payOrder, String openId, UnionPayConfig unionPayConfig) {
Map<String, String> params = UnifiedOrderModel.builder()
.service(ServiceEnum.WEI_XIN_JS_PAY.toString())
.mch_id(unionPayConfig.getMachId())
// 原生JS 值为1
.is_raw("1")
.out_trade_no(WxPayKit.generateStr())
.body(payOrder.getTitle())
.sub_openid(openId)
.total_fee(amount)
.mch_create_ip("127.0.0.1")
.notify_url(unionPayConfig.getNotifyUrl())
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(unionPayConfig.getAppKey(), SignType.MD5);
String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
return null;
}
/**
* 扫码支付
*/
private String qrCodePay(String amount, PayOrder payOrder, UnionPayConfig config){
Map<String, String> params = UnifiedOrderModel.builder()
.service(ServiceEnum.NATIVE.toString())
.mch_id(config.getMachId())
.out_trade_no(String.valueOf(payOrder.getId()))
.body(payOrder.getTitle())
.total_fee(amount)
.time_expire(PayUtil.getUnionExpiredTime(payOrder.getExpiredTime()))
.mch_create_ip("127.0.0.1")
.notify_url(config.getNotifyUrl())
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(config.getAppKey(), SignType.MD5);
String xmlResult = UnionPayApi.execution(config.getServerUrl(), params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
return result.get("code_url");
}
/**
* 付款码支付
*/
private void barCodePay(String amount, PayOrder payOrder, String authCode, UnionPayConfig unionPayConfig) {
Map<String, String> params = MicroPayModel.builder()
.service(ServiceEnum.MICRO_PAY.toString())
.mch_id(unionPayConfig.getMachId())
.out_trade_no(WxPayKit.generateStr())
.body(payOrder.getTitle())
.total_fee(amount)
.op_device_id("daxpay")
.mch_create_ip("127.0.0.1")
.auth_code(authCode)
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(unionPayConfig.getAppKey(), SignType.MD5);
String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params);
}
/**
* 验证错误信息
*/
private void verifyErrorMsg(Map<String, String> result) {
String status = result.get(UnionPayCode.STATUS);
String returnCode = result.get(UnionPayCode.RESULT_CODE);
// 判断查询是否成功
if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){
String errorMsg = result.get(ERR_MSG);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(MESSAGE);
}
log.error("订单关闭失败 {}", errorMsg);
throw new PayFailureException(errorMsg);
}
}
}

View File

@@ -0,0 +1,154 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
import cn.bootx.platform.daxpay.code.RefundSyncStatusEnum;
import cn.bootx.platform.daxpay.service.code.UnionPayCode;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.payment.sync.result.PayGatewaySyncResult;
import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.unionpay.UnionPayApi;
import com.ijpay.unionpay.enums.ServiceEnum;
import com.ijpay.unionpay.model.OrderQueryModel;
import com.ijpay.unionpay.model.RefundQueryModel;
import com.ijpay.wxpay.WxPayApi;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
/**
* 云闪付支付同步
* @author xxm
* @since 2024/3/7
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UnionPaySyncService {
/**
* 支付信息查询
*/
public PayGatewaySyncResult syncPayStatus(PayOrder order, UnionPayConfig unionPayConfig) {
PayGatewaySyncResult syncResult = new PayGatewaySyncResult().setSyncStatus(PaySyncStatusEnum.FAIL);
Map<String, String> params = OrderQueryModel.builder()
.service(ServiceEnum.QUERY.toString())
.mch_id(unionPayConfig.getMachId())
.out_trade_no(String.valueOf(order.getId()))
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(unionPayConfig.getAppKey(), SignType.MD5);
String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
syncResult.setSyncInfo(JSONUtil.toJsonStr(result));
String status = result.get(STATUS);
String returnCode = result.get(RESULT_CODE);
// 判断查询是否成功
if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){
log.warn("查询云闪付订单失败:{}", result);
return syncResult;
}
// 设置微信支付网关订单号
syncResult.setGatewayOrderNo(result.get(TRANSACTION_ID));
// 查询到订单的状态
String tradeStatus = result.get(TRADE_STATE);
// 支付完成
if (Objects.equals(tradeStatus, SUCCESS)) {
String timeEnd = result.get(TIME_END);
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
return syncResult.setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS);
}
// 待支付
if (Objects.equals(tradeStatus, TRADE_NOT_PAY)) {
return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS);
}
// 已退款/退款中
if (Objects.equals(tradeStatus, TRADE_REFUND)) {
return syncResult.setSyncStatus(PaySyncStatusEnum.REFUND);
}
// 已关闭
if (Objects.equals(tradeStatus, TRADE_CLOSED)) {
return syncResult.setSyncStatus(PaySyncStatusEnum.CLOSED);
}
return syncResult;
}
/**
* 退款信息查询
*/
public RefundGatewaySyncResult syncRefundStatus(RefundOrder refundOrder, UnionPayConfig unionPayConfig){
RefundGatewaySyncResult syncResult = new RefundGatewaySyncResult();
Map<String, String> params = RefundQueryModel.builder()
.service(ServiceEnum.REFUND_QUERY.toString())
.mch_id(unionPayConfig.getMachId())
.refund_id(refundOrder.getGatewayOrderNo())
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(unionPayConfig.getAppKey(), SignType.MD5);
try {
String xmlResult = WxPayApi.orderRefundQuery(false, params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
syncResult.setSyncInfo(JSONUtil.toJsonStr(result));
// 设置微信支付网关订单号
syncResult.setGatewayOrderNo(result.get(UnionPayCode.REFUND_ID));
// 状态
// String tradeStatus = result.get(UnionPayCode.REFUND_STATUS);
// // 退款成功
// if (Objects.equals(tradeStatus, UnionPayCode.REFUND_SUCCESS)) {
// String timeEnd = result.get(UnionPayCode.REFUND_SUCCESS_TIME);
// LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.NORM_DATETIME_PATTERN);
// return syncResult.setRefundTime(time).setSyncStatus(RefundSyncStatusEnum.SUCCESS);
// }
// // 退款中
// if (Objects.equals(tradeStatus, UnionPayCode.REFUND_PROCESSING)) {
// return syncResult.setSyncStatus(RefundSyncStatusEnum.PROGRESS);
// }
String errorMsg = this.getErrorMsg(result);
return syncResult.setSyncStatus(RefundSyncStatusEnum.FAIL).setErrorMsg(errorMsg);
} catch (Exception e) {
log.error("查询退款订单失败:", e);
syncResult.setSyncStatus(RefundSyncStatusEnum.PROGRESS).setErrorMsg(e.getMessage());
}
return syncResult;
}
/**
* 验证错误信息
*/
private String getErrorMsg(Map<String, String> result) {
String status = result.get(UnionPayCode.STATUS);
String returnCode = result.get(UnionPayCode.RESULT_CODE);
// 判断查询是否成功
if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){
String errorMsg = result.get(ERR_MSG);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(MESSAGE);
}
log.error("订单查询失败 {}", errorMsg);
return errorMsg;
}
return null;
}
}

View File

@@ -1,12 +1,10 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.common.spring.exception.RetryableException;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
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.entity.PayOrder;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.hutool.core.util.StrUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
@@ -18,7 +16,6 @@ import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Optional;
/**
* 微信支付关闭和退款
@@ -62,9 +59,6 @@ public class WeChatPayCloseService {
errorMsg = result.get(WeChatPayCode.RETURN_MSG);
}
log.error("订单关闭失败 {}", errorMsg);
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setErrorMsg(errorMsg);
refundInfo.setErrorCode(Optional.ofNullable(resultCode).orElse(returnCode));
throw new PayFailureException(errorMsg);
}
}

View File

@@ -101,7 +101,7 @@ public class WeChatPayService {
}
// 付款码支付
else if (payWayEnum == PayWayEnum.BARCODE) {
this.barCode(totalFee, payOrder, weChatPayParam.getAuthCode(), weChatPayConfig);
this.barCodePay(totalFee, payOrder, weChatPayParam.getAuthCode(), weChatPayConfig);
}
asyncPayInfo.setPayBody(payBody);
}
@@ -172,7 +172,7 @@ public class WeChatPayService {
/**
* 付款码支付
*/
private void barCode(String amount, PayOrder payment, String authCode, WeChatPayConfig weChatPayConfig) {
private void barCodePay(String amount, PayOrder payment, String authCode, WeChatPayConfig weChatPayConfig) {
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
Map<String, String> params = MicroPayModel.builder()

View File

@@ -142,9 +142,6 @@ public class WeChatPaySyncService {
return syncResult;
}
/**
* 错误处理
*/
/**
* 验证错误信息
*/

View File

@@ -2,11 +2,9 @@ package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
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.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.hutool.core.codec.Base64;
@@ -23,6 +21,8 @@ import java.io.ByteArrayInputStream;
import java.util.Map;
import java.util.Optional;
import static cn.bootx.platform.daxpay.service.code.WeChatPayCode.*;
/**
* 微信退款服务
* @author xxm
@@ -31,9 +31,7 @@ import java.util.Optional;
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatRefundService {
private final PayChannelOrderManager payChannelOrderManager;
public class WechatPayRefundService {
/**
* 退款方法
@@ -70,19 +68,19 @@ public class WechatRefundService {
this.verifyErrorMsg(result);
// 微信退款是否成功需要查询状态或者回调, 所以设置为退款中状态
refundInfo.setStatus(RefundStatusEnum.PROGRESS)
.setGatewayOrderNo(result.get("refund_id"));
.setGatewayOrderNo(result.get(CALLBACK_REFUND_ID));
}
/**
* 验证错误信息
*/
private void verifyErrorMsg(Map<String, String> result) {
String returnCode = result.get(WeChatPayCode.RETURN_CODE);
String resultCode = result.get(WeChatPayCode.RESULT_CODE);
String returnCode = result.get(RETURN_CODE);
String resultCode = result.get(RESULT_CODE);
if (!WxPayKit.codeIsOk(returnCode) || !WxPayKit.codeIsOk(resultCode)) {
String errorMsg = result.get(WeChatPayCode.ERR_CODE_DES);
String errorMsg = result.get(ERR_CODE_DES);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(WeChatPayCode.RETURN_MSG);
errorMsg = result.get(RETURN_MSG);
}
log.error("订单退款失败 {}", errorMsg);
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();

View File

@@ -1,6 +1,9 @@
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.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayCloseService;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService;
import cn.bootx.platform.daxpay.service.func.AbsPayCloseStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -20,17 +23,31 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@RequiredArgsConstructor
public class UnionPayCloseStrategy extends AbsPayCloseStrategy {
private final UnionPayCloseService unionPayCloseService;
private final UnionPayConfigService unionPayConfigService;
private UnionPayConfig unionPayConfig;
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.UNION_PAY;
}
/**
* 关闭前的处理方式
*/
@Override
public void doBeforeCloseHandler() {
this.unionPayConfig = unionPayConfigService.getConfig();
}
/**
* 关闭操作
*/
@Override
public void doCloseHandler() {
unionPayCloseService.close(this.getOrder(), this.unionPayConfig);
}
}

View File

@@ -9,6 +9,7 @@ import cn.bootx.platform.daxpay.service.common.context.PayLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayRecordService;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayService;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
@@ -38,10 +39,13 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
public class UnionPayStrategy extends AbsPayStrategy {
private final PayChannelOrderService channelOrderService;
private final UnionPayService unionPayService;
private final UnionPayConfigService unionPayConfigService;
private final UnionPayRecordService unionPayRecordService;
private UnionPayParam unionPayParam;
private UnionPayConfig unionPayConfig;
@@ -56,7 +60,7 @@ public class UnionPayStrategy extends AbsPayStrategy {
@Override
public void doBeforePayHandler() {
try {
// 支付宝参数验证
// 云闪付参数验证
Map<String, Object> channelParam = this.getPayChannelParam().getChannelParam();
if (CollUtil.isNotEmpty(channelParam)) {
this.unionPayParam = BeanUtil.toBean(channelParam, UnionPayParam.class);
@@ -73,7 +77,7 @@ public class UnionPayStrategy extends AbsPayStrategy {
if (payMode.getAmount() <= 0) {
throw new PayAmountAbnormalException();
}
// 检查并获取支付宝支付配置
// 检查并获取云闪付支付配置
this.unionPayConfig = unionPayConfigService.getAndCheckConfig();
unionPayService.validation(this.getPayChannelParam(), unionPayConfig);
}
@@ -103,7 +107,7 @@ public class UnionPayStrategy extends AbsPayStrategy {
PayChannelOrder payChannelOrder = channelOrderService.switchAsyncPayChannel(this.getOrder(), this.getPayChannelParam());
// 支付完成, 保存记录
if (asyncPayInfo.isPayComplete()) {
// aliRecordService.pay(this.getOrder(), payChannelOrder);
unionPayRecordService.pay(this.getOrder(), payChannelOrder);
}
}

View File

@@ -1,11 +1,20 @@
package cn.bootx.platform.daxpay.service.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayRecordService;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayRefundService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
import cn.bootx.platform.daxpay.service.func.AbsRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.Objects;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -17,6 +26,17 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@Component
@RequiredArgsConstructor
public class UnionRefundStrategy extends AbsRefundStrategy {
private final UnionPayRefundService unionPayRefundService;
private final UnionPayConfigService unionPayConfigService;
private final UnionPayRecordService unionPayRecordService;
private final PayChannelOrderService payChannelOrderService;
private UnionPayConfig unionPayConfig;
/**
* 策略标识
*
@@ -28,11 +48,39 @@ public class UnionRefundStrategy extends AbsRefundStrategy {
}
/**
* 退款
* 退款前对处理, 初始化微信支付配置
*/
@Override
public void doBeforeRefundHandler() {
this.unionPayConfig = unionPayConfigService.getAndCheckConfig();
}
/**
* 退款操作
*/
@Override
public void doRefundHandler() {
unionPayRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), this.unionPayConfig);
}
/**
* 退款发起成功操作
*/
@Override
public void doSuccessHandler() {
// 更新退款订单数据状态
RefundStatusEnum refundStatusEnum = PaymentContextLocal.get()
.getRefundInfo()
.getStatus();
this.getRefundChannelOrder().setStatus(refundStatusEnum.getCode());
// 更新支付通道订单中的属性
payChannelOrderService.updateAsyncPayRefund(this.getPayChannelOrder(), this.getRefundChannelOrder());
// 如果退款完成, 保存流水记录
if (Objects.equals(RefundStatusEnum.SUCCESS.getCode(), refundStatusEnum.getCode())) {
unionPayRecordService.refund(this.getRefundOrder(), this.getRefundChannelOrder());
}
}
}

View File

@@ -6,7 +6,7 @@ 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.WeChatPayRecordService;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WechatRefundService;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WechatPayRefundService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
import cn.bootx.platform.daxpay.service.func.AbsRefundStrategy;
import lombok.RequiredArgsConstructor;
@@ -29,7 +29,7 @@ public class WeChatRefundStrategy extends AbsRefundStrategy {
private final WeChatPayConfigService weChatPayConfigService;
private final WechatRefundService wechatRefundService;
private final WechatPayRefundService wechatPayRefundService;
private final WeChatPayRecordService weChatPayRecordService;
@@ -61,7 +61,7 @@ public class WeChatRefundStrategy extends AbsRefundStrategy {
*/
@Override
public void doRefundHandler() {
wechatRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), this.weChatPayConfig);
wechatPayRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), this.weChatPayConfig);
}
/**

View File

@@ -2,10 +2,11 @@ package cn.bootx.platform.daxpay.service.core.payment.sync.factory;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.func.AbsPaySyncStrategy;
import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.pay.AliPaySyncStrategy;
import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.pay.WeChatPaySyncStrategy;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.pay.AliPaySyncStrategy;
import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.pay.UnionPaySyncStrategy;
import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.pay.WeChatPaySyncStrategy;
import cn.bootx.platform.daxpay.service.func.AbsPaySyncStrategy;
import cn.hutool.extra.spring.SpringUtil;
/**
@@ -29,6 +30,9 @@ public class PaySyncStrategyFactory {
case WECHAT:
strategy = SpringUtil.getBean(WeChatPaySyncStrategy.class);
break;
case UNION_PAY:
strategy = SpringUtil.getBean(UnionPaySyncStrategy.class);
break;
default:
throw new PayUnsupportedMethodException();
}

View File

@@ -0,0 +1,48 @@
package cn.bootx.platform.daxpay.service.core.payment.sync.strategy.pay;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPaySyncService;
import cn.bootx.platform.daxpay.service.core.payment.sync.result.PayGatewaySyncResult;
import cn.bootx.platform.daxpay.service.func.AbsPaySyncStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 云闪付支付同步接口
* @author xxm
* @since 2024/3/7
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class UnionPaySyncStrategy extends AbsPaySyncStrategy {
private final UnionPaySyncService unionPaySyncService;
private final UnionPayConfigService unionPayConfigService;
/**
* 异步支付单与支付网关进行状态比对后的结果
*
* @see PaySyncStatusEnum
*/
@Override
public PayGatewaySyncResult doSyncStatus() {
UnionPayConfig config = unionPayConfigService.getConfig();
return unionPaySyncService.syncPayStatus(this.getOrder(),config);
}
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.UNION_PAY;
}
}

View File

@@ -1,26 +0,0 @@
package cn.bootx.platform.daxpay.service.dto.channel.alipay;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayOrderDto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* @author xxm
* @since 2021/2/27
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "支付宝支付记录")
public class AliPaymentDto extends PayOrderDto implements Serializable {
private static final long serialVersionUID = 6883103229754466130L;
@Schema(description = "支付宝交易号")
private String tradeNo;
}

View File

@@ -0,0 +1,49 @@
package cn.bootx.platform.daxpay.service.dto.channel.union;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.service.code.UnionPayRecordTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 云闪付流水记录
* @author xxm
* @since 2024/3/7
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "云闪付流水记录")
public class UnionPayRecordDto extends BaseDto {
/** 标题 */
@Schema(description = "标题")
private String title;
/** 金额 */
@Schema(description = "金额")
private Integer amount;
/**
* 业务类型
* @see UnionPayRecordTypeEnum
*/
@Schema(description = "业务类型")
private String type;
/** 本地订单号 */
@Schema(description = "本地订单号")
private Long orderId;
/** 网关订单号 */
@Schema(description = "网关订单号")
private String gatewayOrderNo;
/** 网关完成时间 */
@Schema(description = "网关完成时间")
private LocalDateTime gatewayTime;
}

View File

@@ -0,0 +1,33 @@
package cn.bootx.platform.daxpay.service.param.channel.union;
import cn.bootx.platform.common.core.annotation.QueryParam;
import cn.bootx.platform.common.core.rest.param.QueryOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 云闪付流水查询参数
* @author xxm
* @since 2024/3/7
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "云闪付流水查询参数")
public class UnionPayRecordQuery extends QueryOrder {
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
@Schema(description = "标题")
private String title;
@Schema(description = "类型")
private String type;
@Schema(description = "本地订单ID")
private Long orderId;
@Schema(description = "网关订单号")
private String gatewayOrderNo;
}