feat 拆分网关同步相关代码/记录网关同步记录/保存各通道的支付单/储值卡多卡退款到单卡, 不传卡号自动设置为默认卡

This commit is contained in:
xxm1995
2023-07-14 17:03:35 +08:00
parent 9f4ce55ce2
commit ea3443e7fb
48 changed files with 953 additions and 210 deletions

View File

@@ -125,4 +125,4 @@ QQ扫码加入QQ交流群
## 🍷License
Apache License Version 2.0_
Apache License Version 2.0

11
_doc/Task.md Normal file
View File

@@ -0,0 +1,11 @@
- 微信V3支付接口
- x 拆分网关同步相关代码
- x 记录网关同步记录
- 重构支付消息通知结构
- x 保存各通道的支付单
- 钱包支持设置开通时的默认金额
- 储值卡多卡支付和退款演示
- x 储值卡信息调整
- x 储值卡多卡退款到单卡, 不传卡号自动设置为默认卡
- 储值卡批量导入
- 储值卡支持多卡合一

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.daxpay.core.pay.service.PayCancelService;
import cn.bootx.platform.daxpay.core.refund.service.PayRefundService;
import cn.bootx.platform.daxpay.core.pay.service.PayService;
import cn.bootx.platform.daxpay.core.pay.service.PaySyncService;
import cn.bootx.platform.daxpay.core.sync.service.PaySyncService;
import cn.bootx.platform.daxpay.dto.pay.PayResult;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.refund.RefundParam;

View File

@@ -4,8 +4,8 @@ import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.daxpay.core.refund.record.service.RefundRecordService;
import cn.bootx.platform.daxpay.dto.refund.RefundRecordDto;
import cn.bootx.platform.daxpay.core.refund.record.service.PayRefundRecordService;
import cn.bootx.platform.daxpay.dto.refund.PayRefundRecordDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
@@ -23,20 +23,20 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/pay/refund")
@RequiredArgsConstructor
public class RefundRecordController {
public class PayRefundRecordController {
private final RefundRecordService refundRecordService;
private final PayRefundRecordService payRefundRecordService;
@Operation(summary = "分页")
@GetMapping("/page")
public ResResult<PageResult<RefundRecordDto>> page(PageParam pageParam, RefundRecordDto param) {
return Res.ok(refundRecordService.page(pageParam, param));
public ResResult<PageResult<PayRefundRecordDto>> page(PageParam pageParam, PayRefundRecordDto param) {
return Res.ok(payRefundRecordService.page(pageParam, param));
}
@Operation(summary = "根据id查询")
@GetMapping("/findById")
public ResResult<RefundRecordDto> findById(Long id) {
return Res.ok(refundRecordService.findById(id));
public ResResult<PayRefundRecordDto> findById(Long id) {
return Res.ok(payRefundRecordService.findById(id));
}
}

View File

@@ -0,0 +1,34 @@
package cn.bootx.platform.daxpay.controller;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.daxpay.core.sync.record.service.PaySyncRecordService;
import cn.bootx.platform.daxpay.dto.sync.PaySyncRecordDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "支付同步记录")
@RestController
@RequestMapping("/")
@RequiredArgsConstructor
public class PaySyncRecordController {
private final PaySyncRecordService syncRecordService;
@Operation(summary = "分页")
@GetMapping("/page")
public ResResult<PageResult<PaySyncRecordDto>> page(PageParam pageParam, PaySyncRecordDto param) {
return Res.ok(syncRecordService.page(pageParam, param));
}
@Operation(summary = "根据id查询")
@GetMapping("/findById")
public ResResult<PaySyncRecordDto> findById(Long id) {
return Res.ok(syncRecordService.findById(id));
}
}

View File

@@ -8,12 +8,24 @@ import cn.bootx.platform.daxpay.core.channel.voucher.service.VoucherQueryService
import cn.bootx.platform.daxpay.core.channel.voucher.service.VoucherService;
import cn.bootx.platform.daxpay.dto.channel.voucher.VoucherDto;
import cn.bootx.platform.daxpay.param.channel.voucher.VoucherGenerationParam;
import cn.bootx.platform.daxpay.param.channel.voucher.VoucherImportParam;
import cn.bootx.platform.daxpay.param.channel.voucher.VoucherParam;
import cn.hutool.core.io.IoUtil;
import com.alibaba.excel.EasyExcel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.List;
/**
@@ -89,4 +101,37 @@ public class VoucherController {
return Res.ok();
}
@SneakyThrows
@Operation(summary = "导入已有的储值卡")
@PostMapping("/importBatch")
public ResResult<Void> importBatch(Boolean skip, MultipartFile file){
List<VoucherImportParam> voucherImportParams = EasyExcel.read(file.getInputStream())
// 设置与Excel表映射的类
.head(VoucherImportParam.class)
// 设置sheet,默认读取第一个
.sheet()
// 设置标题所在行数
.headRowNumber(1)
// 异步读取
.doReadSync();
voucherService.importBatch(skip,voucherImportParams);
return Res.ok();
}
@SneakyThrows
@Operation(summary = "下载导入模板")
@GetMapping("/excelTemplate")
public ResponseEntity<byte[]> excelTemplate(){
//设置header信息
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment",
"ImportVoucher.xlsx");
ResourceLoader resourceLoader = new DefaultResourceLoader();
InputStream inputStream = resourceLoader.getResource("classpath:templates//ImportVoucher.xlsx")
.getInputStream();
return new ResponseEntity<>(IoUtil.readBytes(inputStream),headers, HttpStatus.OK);
}
}

View File

@@ -3,11 +3,11 @@ package cn.bootx.platform.daxpay.core.channel.alipay.service;
import cn.bootx.platform.common.core.util.BigDecimalUtil;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.code.pay.PayStatusCode;
import cn.bootx.platform.daxpay.core.channel.alipay.dao.AliPaymentManager;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AliPayment;
import cn.bootx.platform.daxpay.core.pay.local.AsyncPayInfoLocal;
import cn.bootx.platform.daxpay.core.payment.dao.PaymentManager;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.core.channel.alipay.dao.AliPaymentManager;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AliPayment;
import cn.bootx.platform.daxpay.dto.pay.AsyncPayInfo;
import cn.bootx.platform.daxpay.dto.payment.PayChannelInfo;
import cn.bootx.platform.daxpay.dto.payment.RefundableInfo;
@@ -25,6 +25,9 @@ import java.util.Optional;
/**
* 支付宝支付记录
* 1.创建: 支付调起并支付成功后才会创建
* 2.撤销: 关闭本地支付记录
* 3.退款: 发起退款时记录
*
* @author xxm
* @since 2021/2/26
@@ -39,7 +42,7 @@ public class AliPaymentService {
private final PaymentManager paymentManager;
/**
* 支付调起成功 更新 payment异步支付类型信息
* 支付调起成功 更新payment异步支付类型信息, 如果支付完成, 创建支付宝支付单
*/
public void updatePaySuccess(Payment payment, PayWayParam payWayParam) {
AsyncPayInfo asyncPayInfo = AsyncPayInfoLocal.get();
@@ -71,22 +74,22 @@ public class AliPaymentService {
public void updateAsyncSuccess(Long id, PayWayParam payWayParam, String tradeNo) {
// 更新支付记录
Payment payment = paymentManager.findById(id).orElseThrow(() -> new PayFailureException("支付记录不存在"));
this.createAliPayment(payment,payWayParam,tradeNo);
}
/**
* 创建支付宝支付记录
* 创建支付宝支付记录(支付调起成功后才会创建)
*/
private void createAliPayment(Payment payment, PayWayParam payWayParam, String tradeNo) {
// 创建支付宝支付记录
AliPayment aliPayment = new AliPayment();
aliPayment.setTradeNo(tradeNo)
.setPaymentId(payment.getId())
.setAmount(payWayParam.getAmount())
.setRefundableBalance(payWayParam.getAmount())
.setBusinessId(payment.getBusinessId())
.setPayStatus(PayStatusCode.TRADE_SUCCESS)
.setPayTime(LocalDateTime.now());
.setPaymentId(payment.getId())
.setAmount(payWayParam.getAmount())
.setRefundableBalance(payWayParam.getAmount())
.setBusinessId(payment.getBusinessId())
.setPayStatus(PayStatusCode.TRADE_SUCCESS)
.setPayTime(LocalDateTime.now());
aliPaymentManager.save(aliPayment);
}
@@ -118,5 +121,4 @@ public class AliPaymentService {
aliPaymentManager.updateById(payment);
});
}
}

View File

@@ -2,8 +2,9 @@ package cn.bootx.platform.daxpay.core.channel.alipay.service;
import cn.bootx.platform.daxpay.code.pay.PaySyncStatus;
import cn.bootx.platform.daxpay.code.paymodel.AliPayCode;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.sync.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.response.AlipayTradeQueryResponse;
@@ -39,7 +40,7 @@ public class AlipaySyncService {
// 查询退款参数
AlipayTradeQueryResponse response = AliPayApi.tradeQueryToResponse(queryModel);
String tradeStatus = response.getTradeStatus();
paySyncResult.setJson(JSONUtil.toJsonStr(response));
// 支付完成
if (Objects.equals(tradeStatus, AliPayCode.PAYMENT_TRADE_SUCCESS)
|| Objects.equals(tradeStatus, AliPayCode.PAYMENT_TRADE_FINISHED)) {

View File

@@ -164,7 +164,7 @@ public class VoucherPayService {
}
/**
* 直接支付
* 直接支付 (同步支付方式下)
*/
@Transactional(rollbackFor = Exception.class)
public List<VoucherRecord> pay(BigDecimal amount, Payment payment, List<Voucher> vouchers) {
@@ -272,9 +272,16 @@ public class VoucherPayService {
.map(VoucherRecord::getCardNo)
.collect(Collectors.toList());
List<Voucher> vouchers = voucherManager.findByCardNoList(cardNoList);
// 如果未传入卡号, 默认退到最抗用的一张卡上
if (StrUtil.isBlank(refundVoucherNo)){
List<Voucher> sort = this.sort(vouchers);
refundVoucherNo = sort.get(sort.size()-1).getCardNo();
}
// 筛选出来要进行退款的卡
String finalRefundVoucherNo = refundVoucherNo;
Voucher voucher = vouchers.stream()
.filter(vr -> Objects.equals(vr.getCardNo(), refundVoucherNo))
.filter(vr -> Objects.equals(vr.getCardNo(), finalRefundVoucherNo))
.findFirst()
.orElseThrow(() -> new PayFailureException("退款卡号不存在"));
// 将金额全部推到指定的卡上

View File

@@ -1,12 +1,16 @@
package cn.bootx.platform.daxpay.core.channel.voucher.service;
import cn.bootx.platform.common.core.exception.BizException;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.daxpay.code.paymodel.VoucherCode;
import cn.bootx.platform.daxpay.core.channel.voucher.dao.VoucherLogManager;
import cn.bootx.platform.daxpay.core.channel.voucher.dao.VoucherManager;
import cn.bootx.platform.daxpay.core.channel.voucher.entity.Voucher;
import cn.bootx.platform.daxpay.core.channel.voucher.entity.VoucherLog;
import cn.bootx.platform.daxpay.param.channel.voucher.VoucherChangeParam;
import cn.bootx.platform.daxpay.param.channel.voucher.VoucherGenerationParam;
import cn.bootx.platform.daxpay.param.channel.voucher.VoucherImportParam;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import lombok.RequiredArgsConstructor;
@@ -68,8 +72,24 @@ public class VoucherService {
/**
* 批量导入
* @param skip 是否跳过已经导入的储值卡,false时将会异常
*/
public void importBatch(VoucherImportParam param) {
public void importBatch(Boolean skip,List<VoucherImportParam> voucherImports) {
List<String> cardNoList = voucherImports.stream()
.map(VoucherImportParam::getCardNo)
.distinct()
.collect(Collectors.toList());
// 卡号不能重复
if (voucherImports.size()!=cardNoList.size()){
throw new BizException("卡号不能重复");
}
// 查询库中是否已经有对应的储值卡号
List<Voucher> vouchersByDB = voucherManager.findByCardNoList(cardNoList);
// 不跳过已经导入的储值卡且存在数据, 抛出异常
if (Objects.equals(skip,true)&& CollUtil.isNotEmpty(vouchersByDB)){
log.warn("数据库中已经存在的卡号:{}",vouchersByDB.stream().map(Voucher::getCardNo).collect(Collectors.toList()));
throw new BizException("要导入的卡号在数据中已经存在");
}
}
@@ -102,10 +122,12 @@ public class VoucherService {
}
/**
* 更改有效期
* 更改储值卡信息
*/
public void changeEnduring() {
public void changeInfo(VoucherChangeParam voucherChangeParam) {
// 查询对应的卡
Voucher voucher = voucherManager.findByCardNo(voucherChangeParam.getCardNo())
.orElseThrow(DataNotExistException::new);
}
}

View File

@@ -0,0 +1,18 @@
package cn.bootx.platform.daxpay.core.channel.wallet.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.WalletConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
* 钱包配置
* @author xxm
* @since 2023/7/14
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class WalletConfigManager extends BaseManager<WalletConfigMapper, WalletConfig> {
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.core.channel.wallet.dao;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.WalletConfig;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 钱包配置
* @author xxm
* @since 2023/7/14
*/
@Mapper
public interface WalletConfigMapper extends BaseMapper<WalletConfig> {
}

View File

@@ -0,0 +1,43 @@
package cn.bootx.platform.daxpay.core.channel.wallet.entity;
import cn.bootx.mybatis.table.modify.annotation.DbColumn;
import cn.bootx.mybatis.table.modify.annotation.DbTable;
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlIndex;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
/**
* 钱包配置
* @author xxm
* @since 2023/7/14
*/
@EqualsAndHashCode(callSuper = true)
@Data
@DbTable(comment = "钱包配置")
@Accessors(chain = true)
@TableName("pay_wallet")
public class WalletConfig extends MpBaseEntity {
/** 商户编码 */
@TableField(updateStrategy = FieldStrategy.NEVER)
@DbColumn(comment = "商户编码")
private String mchCode;
/** 商户应用编码 */
@TableField(updateStrategy = FieldStrategy.NEVER)
@DbMySqlIndex(comment = "商户应用编码唯一索引")
@DbColumn(comment = "商户应用编码")
private String mchAppCode;
/** 默认余额 */
@DbColumn(comment = "默认余额")
private BigDecimal defaultBalance;
}

View File

@@ -0,0 +1,20 @@
package cn.bootx.platform.daxpay.core.channel.wallet.service;
import cn.bootx.platform.daxpay.core.channel.wallet.dao.WalletConfigManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 钱包配置
* @author xxm
* @since 2023/7/14
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class WalletConfigService {
private final WalletConfigManager walletConfigManager;
}

View File

@@ -7,8 +7,8 @@ import cn.bootx.platform.daxpay.code.pay.PayWayEnum;
import cn.bootx.platform.daxpay.code.paymodel.WeChatPayCode;
import cn.bootx.platform.daxpay.code.paymodel.WeChatPayWay;
import cn.bootx.platform.daxpay.core.pay.local.AsyncPayInfoLocal;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.pay.service.PaySyncService;
import cn.bootx.platform.daxpay.core.sync.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.sync.service.PaySyncService;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.dto.pay.AsyncPayInfo;

View File

@@ -2,8 +2,9 @@ package cn.bootx.platform.daxpay.core.channel.wechat.service;
import cn.bootx.platform.daxpay.code.pay.PaySyncStatus;
import cn.bootx.platform.daxpay.code.paymodel.WeChatPayCode;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.sync.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.hutool.json.JSONUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
@@ -41,6 +42,7 @@ public class WeChatPaySyncService {
try {
String xmlResult = WxPayApi.orderQuery(params);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
paySyncResult.setJson(JSONUtil.toJsonStr(result));
// 查询失败
if (!WxPayKit.codeIsOk(result.get(WeChatPayCode.RETURN_CODE))) {
log.warn("查询微信订单失败:{}", result);

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.core.channel.wechat.service;
import cn.bootx.platform.common.core.exception.BizException;
import cn.bootx.platform.common.core.util.BigDecimalUtil;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.code.pay.PayStatusCode;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPaymentManager;
@@ -38,7 +39,7 @@ public class WeChatPaymentService {
private final WeChatPaymentManager weChatPaymentManager;
/**
* 支付调起成功 更新 payment异步支付类型信息
* 支付调起成功 更新payment异步支付类型信息, 如果支付完成, 创建微信支付单
*/
public void updatePaySuccess(Payment payment, PayWayParam payWayParam) {
AsyncPayInfo asyncPayInfo = AsyncPayInfoLocal.get();
@@ -74,10 +75,9 @@ public class WeChatPaymentService {
}
/**
* 更新支付记录成功状态, 并创建微信支付记录
* 并创建微信支付记录
*/
private void createWeChatPayment(Payment payment, PayWayParam payWayParam, String tradeNo) {
// 创建微信支付记录
WeChatPayment wechatPayment = new WeChatPayment();
wechatPayment.setTradeNo(tradeNo)
@@ -105,6 +105,18 @@ public class WeChatPaymentService {
* 更新退款
*/
public void updatePayRefund(Long paymentId, BigDecimal amount) {
Optional<WeChatPayment> weChatPayment = weChatPaymentManager.findByPaymentId(paymentId);
weChatPayment.ifPresent(payment -> {
BigDecimal refundableBalance = payment.getRefundableBalance().subtract(amount);
payment.setRefundableBalance(refundableBalance);
if (BigDecimalUtil.compareTo(refundableBalance, BigDecimal.ZERO) == 0) {
payment.setPayStatus(PayStatusCode.TRADE_REFUNDED);
}
else {
payment.setPayStatus(PayStatusCode.TRADE_REFUNDING);
}
weChatPaymentManager.updateById(payment);
});
}
}

View File

@@ -1,11 +1,10 @@
package cn.bootx.platform.daxpay.core.notify.entity;
import cn.bootx.mybatis.table.modify.annotation.DbComment;
import cn.bootx.mybatis.table.modify.annotation.DbTable;
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlFieldType;
import cn.bootx.mybatis.table.modify.mybatis.mysq.constants.MySqlFieldTypeEnum;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.code.pay.PayStatusCode;
import cn.bootx.platform.daxpay.core.notify.convert.PayNotifyConvert;
@@ -28,7 +27,7 @@ import java.time.LocalDateTime;
//@DbTable(comment = "回调记录")
@Accessors(chain = true)
@TableName("pay_pay_notify_record")
public class PayNotifyRecord extends MpBaseEntity implements EntityBaseFunction<PayNotifyRecordDto> {
public class PayNotifyRecord extends MpCreateEntity implements EntityBaseFunction<PayNotifyRecordDto> {
/** 支付记录id */
@DbComment("支付记录id")

View File

@@ -21,7 +21,7 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class PayNotifyRecordService {
public class PayNotifyRecordService {
private final PayNotifyRecordManager payNotifyRecordManager;

View File

@@ -1,9 +1,7 @@
package cn.bootx.platform.daxpay.core.pay.func;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.code.pay.PaySyncStatus;
import cn.bootx.platform.daxpay.core.pay.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
@@ -93,12 +91,4 @@ public abstract class AbsPayStrategy {
*/
public abstract void doCloseHandler();
/**
* 异步支付单与支付网关进行状态比对
* @see PaySyncStatus
*/
public PaySyncResult doSyncPayStatusHandler() {
return new PaySyncResult();
}
}

View File

@@ -8,7 +8,6 @@ import cn.bootx.platform.daxpay.core.channel.alipay.entity.AlipayConfig;
import cn.bootx.platform.daxpay.core.channel.alipay.service.*;
import cn.bootx.platform.daxpay.core.pay.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.exception.payment.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
import cn.bootx.platform.daxpay.param.channel.alipay.AliPayParam;
@@ -153,14 +152,6 @@ public class AliPayStrategy extends AbsPayStrategy {
aliPaymentService.updateClose(this.getPayment().getId());
}
/**
* 异步支付单与支付网关进行状态比对
*/
@Override
public PaySyncResult doSyncPayStatusHandler() {
this.initAlipayConfig(this.getPayParam().getMchAppCode());
return alipaySyncService.syncPayStatus(this.getPayment());
}
/**
* 初始化支付宝配置信息

View File

@@ -11,7 +11,6 @@ import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPaySyncService
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPaymentService;
import cn.bootx.platform.daxpay.core.pay.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.exception.payment.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
import cn.bootx.platform.daxpay.param.channel.wechat.WeChatPayParam;
@@ -155,16 +154,6 @@ public class WeChatPayStrategy extends AbsPayStrategy {
weChatPaymentService.updateClose(this.getPayment().getId());
}
/**
* 异步支付单与支付网关进行状态比对
*/
@Override
public PaySyncResult doSyncPayStatusHandler() {
// 检查并获取微信支付配置
this.initWeChatPayConfig(this.getPayParam().getMchAppCode());
return weChatPaySyncService.syncPayStatus(this.getPayment().getId(), this.weChatPayConfig);
}
/**
* 初始化微信支付
*/

View File

@@ -1,7 +1,7 @@
package cn.bootx.platform.daxpay.core.refund.record.convert;
import cn.bootx.platform.daxpay.core.refund.record.entity.RefundRecord;
import cn.bootx.platform.daxpay.dto.refund.RefundRecordDto;
import cn.bootx.platform.daxpay.core.refund.record.entity.PayRefundRecord;
import cn.bootx.platform.daxpay.dto.refund.PayRefundRecordDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -14,6 +14,6 @@ public interface RefundConvert {
RefundConvert CONVERT = Mappers.getMapper(RefundConvert.class);
RefundRecordDto convert(RefundRecord in);
PayRefundRecordDto convert(PayRefundRecord in);
}

View File

@@ -0,0 +1,34 @@
package cn.bootx.platform.daxpay.core.refund.record.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.core.refund.record.entity.PayRefundRecord;
import cn.bootx.platform.daxpay.dto.refund.PayRefundRecordDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.Objects;
/**
* @author xxm
* @since 2022/3/2
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class PayRefundRecordManager extends BaseManager<PayRefundRecordMapper, PayRefundRecord> {
public Page<PayRefundRecord> page(PageParam pageParam, PayRefundRecordDto param) {
Page<PayRefundRecord> mpPage = MpUtil.getMpPage(pageParam, PayRefundRecord.class);
return lambdaQuery().orderByDesc(MpIdEntity::getId)
.like(Objects.nonNull(param.getPaymentId()), PayRefundRecord::getPaymentId, param.getPaymentId())
.like(Objects.nonNull(param.getBusinessId()), PayRefundRecord::getBusinessId, param.getBusinessId())
.like(Objects.nonNull(param.getTitle()), PayRefundRecord::getTitle, param.getTitle())
.page(mpPage);
}
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.core.refund.record.dao;
import cn.bootx.platform.daxpay.core.refund.record.entity.RefundRecord;
import cn.bootx.platform.daxpay.core.refund.record.entity.PayRefundRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@@ -9,6 +9,6 @@ import org.apache.ibatis.annotations.Mapper;
* @since 2022/3/2
*/
@Mapper
public interface RefundRecordMapper extends BaseMapper<RefundRecord> {
public interface PayRefundRecordMapper extends BaseMapper<PayRefundRecord> {
}

View File

@@ -1,34 +0,0 @@
package cn.bootx.platform.daxpay.core.refund.record.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.core.refund.record.entity.RefundRecord;
import cn.bootx.platform.daxpay.dto.refund.RefundRecordDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.Objects;
/**
* @author xxm
* @since 2022/3/2
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class RefundRecordManager extends BaseManager<RefundRecordMapper, RefundRecord> {
public Page<RefundRecord> page(PageParam pageParam, RefundRecordDto param) {
Page<RefundRecord> mpPage = MpUtil.getMpPage(pageParam, RefundRecord.class);
return lambdaQuery().orderByDesc(MpIdEntity::getId)
.like(Objects.nonNull(param.getPaymentId()), RefundRecord::getPaymentId, param.getPaymentId())
.like(Objects.nonNull(param.getBusinessId()), RefundRecord::getBusinessId, param.getBusinessId())
.like(Objects.nonNull(param.getTitle()), RefundRecord::getTitle, param.getTitle())
.page(mpPage);
}
}

View File

@@ -6,7 +6,7 @@ import cn.bootx.platform.common.mybatisplus.handler.JacksonRawTypeHandler;
import cn.bootx.platform.daxpay.code.pay.PayStatusCode;
import cn.bootx.platform.daxpay.core.refund.record.convert.RefundConvert;
import cn.bootx.platform.daxpay.dto.payment.RefundableInfo;
import cn.bootx.platform.daxpay.dto.refund.RefundRecordDto;
import cn.bootx.platform.daxpay.dto.refund.PayRefundRecordDto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -27,7 +27,7 @@ import java.util.List;
@Data
@Accessors(chain = true)
@TableName(value = "pay_refund_record", autoResultMap = true)
public class RefundRecord extends MpBaseEntity implements EntityBaseFunction<RefundRecordDto> {
public class PayRefundRecord extends MpBaseEntity implements EntityBaseFunction<PayRefundRecordDto> {
/** 支付单号 */
private Long paymentId;
@@ -75,7 +75,7 @@ public class RefundRecord extends MpBaseEntity implements EntityBaseFunction<Ref
private String errorMsg;
@Override
public RefundRecordDto toDto() {
public PayRefundRecordDto toDto() {
return RefundConvert.CONVERT.convert(this);
}

View File

@@ -4,9 +4,9 @@ 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.core.refund.record.dao.RefundRecordManager;
import cn.bootx.platform.daxpay.core.refund.record.entity.RefundRecord;
import cn.bootx.platform.daxpay.dto.refund.RefundRecordDto;
import cn.bootx.platform.daxpay.core.refund.record.dao.PayRefundRecordManager;
import cn.bootx.platform.daxpay.core.refund.record.entity.PayRefundRecord;
import cn.bootx.platform.daxpay.dto.refund.PayRefundRecordDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -21,23 +21,23 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class RefundRecordService {
public class PayRefundRecordService {
private final RefundRecordManager refundRecordManager;
private final PayRefundRecordManager refundRecordManager;
/**
* 分页查询
*/
public PageResult<RefundRecordDto> page(PageParam pageParam, RefundRecordDto param) {
Page<RefundRecord> page = refundRecordManager.page(pageParam, param);
public PageResult<PayRefundRecordDto> page(PageParam pageParam, PayRefundRecordDto param) {
Page<PayRefundRecord> page = refundRecordManager.page(pageParam, param);
return MpUtil.convert2DtoPageResult(page);
}
/**
* 根据id查询
*/
public RefundRecordDto findById(Long id) {
return refundRecordManager.findById(id).map(RefundRecord::toDto).orElseThrow(DataNotExistException::new);
public PayRefundRecordDto findById(Long id) {
return refundRecordManager.findById(id).map(PayRefundRecord::toDto).orElseThrow(DataNotExistException::new);
}
}

View File

@@ -10,8 +10,8 @@ import cn.bootx.platform.daxpay.core.refund.factory.PayRefundStrategyFactory;
import cn.bootx.platform.daxpay.core.refund.func.AbsPayRefundStrategy;
import cn.bootx.platform.daxpay.core.refund.func.PayRefundStrategyConsumer;
import cn.bootx.platform.daxpay.core.refund.local.AsyncRefundLocal;
import cn.bootx.platform.daxpay.core.refund.record.dao.RefundRecordManager;
import cn.bootx.platform.daxpay.core.refund.record.entity.RefundRecord;
import cn.bootx.platform.daxpay.core.refund.record.dao.PayRefundRecordManager;
import cn.bootx.platform.daxpay.core.refund.record.entity.PayRefundRecord;
import cn.bootx.platform.daxpay.dto.payment.RefundableInfo;
import cn.bootx.platform.daxpay.exception.payment.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
@@ -54,7 +54,7 @@ public class PayRefundService {
private final PaymentManager paymentManager;
private final RefundRecordManager refundRecordManager;
private final PayRefundRecordManager refundRecordManager;
/**
* 退款
@@ -229,7 +229,7 @@ public class PayRefundService {
.collect(Collectors.toList());
HttpServletRequest request = WebServletUtil.getRequest();
String ip = ServletUtil.getClientIP(request);
RefundRecord refundRecord = new RefundRecord().setRefundRequestNo(AsyncRefundLocal.get())
PayRefundRecord refundRecord = new PayRefundRecord().setRefundRequestNo(AsyncRefundLocal.get())
.setRefundableInfo(refundableInfos)
.setAmount(amount)
.setRefundableBalance(payment.getRefundableBalance())

View File

@@ -2,7 +2,6 @@ package cn.bootx.platform.daxpay.core.refund.strategy;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPayConfigManager;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPaymentManager;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPayCancelService;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPaymentService;
@@ -25,7 +24,6 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@RequiredArgsConstructor
public class WeChatPayRefundStrategy extends AbsPayRefundStrategy {
private final WeChatPaymentManager weChatPaymentManager;
private final WeChatPayConfigManager weChatPayConfigManager;
private final WeChatPayCancelService weChatPayCancelService;

View File

@@ -0,0 +1,38 @@
package cn.bootx.platform.daxpay.core.sync.factory;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.sync.func.AbsPaySyncStrategy;
import cn.bootx.platform.daxpay.core.sync.strategy.AliPaySyncStrategy;
import cn.bootx.platform.daxpay.core.sync.strategy.WeChatPaySyncStrategy;
import cn.bootx.platform.daxpay.exception.payment.PayUnsupportedMethodException;
import cn.hutool.extra.spring.SpringUtil;
/**
* 支付同步策略工厂类
* @author xxm
* @since 2023/7/14
*/
public class PaySyncStrategyFactory {
/**
* 获取支付同步策略
* @param payChannelCode
* @return
*/
public static AbsPaySyncStrategy create(String payChannelCode) {
AbsPaySyncStrategy strategy;
PayChannelEnum channelEnum = PayChannelEnum.findByCode(payChannelCode);
switch (channelEnum) {
case ALI:
strategy = SpringUtil.getBean(AliPaySyncStrategy.class);
break;
case WECHAT:
strategy = SpringUtil.getBean(WeChatPaySyncStrategy.class);
break;
default:
throw new PayUnsupportedMethodException();
}
// noinspection ConstantConditions
return strategy;
}
}

View File

@@ -0,0 +1,35 @@
package cn.bootx.platform.daxpay.core.sync.func;
import cn.bootx.platform.daxpay.code.pay.PaySyncStatus;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.core.sync.result.PaySyncResult;
import lombok.Getter;
import lombok.Setter;
/**
* 支付同步抽象类
* @author xxm
* @since 2023/7/14
*/
@Getter
@Setter
public abstract class AbsPaySyncStrategy {
/** 支付对象 */
private Payment payment = null;
/**
* 初始化支付的参数
*/
public void initPayParam(Payment payment) {
this.payment = payment;
}
/**
* 异步支付单与支付网关进行状态比对
* @see PaySyncStatus
*/
public abstract PaySyncResult doSyncPayStatusHandler();
}

View File

@@ -0,0 +1,19 @@
package cn.bootx.platform.daxpay.core.sync.record.convert;
import cn.bootx.platform.daxpay.core.sync.record.entity.PaySyncRecord;
import cn.bootx.platform.daxpay.dto.sync.PaySyncRecordDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 支付同步记录同步
* @author xxm
* @since 2023/7/14
*/
@Mapper
public interface PaySyncRecordConvert {
PaySyncRecordConvert CONVERT = Mappers.getMapper(PaySyncRecordConvert.class);
PaySyncRecordDto convert(PaySyncRecord in);
}

View File

@@ -0,0 +1,35 @@
package cn.bootx.platform.daxpay.core.sync.record.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.core.sync.record.entity.PaySyncRecord;
import cn.bootx.platform.daxpay.dto.sync.PaySyncRecordDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.Objects;
/**
*
* @author xxm
* @since 2023/7/14
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class PaySyncRecordManager extends BaseManager<PaySyncRecordMapper, PaySyncRecord> {
public Page<PaySyncRecord> page(PageParam pageParam, PaySyncRecordDto param) {
Page<PaySyncRecord> mpPage = MpUtil.getMpPage(pageParam, PaySyncRecord.class);
return lambdaQuery().orderByDesc(MpIdEntity::getId)
.like(Objects.nonNull(param.getPaymentId()), PaySyncRecord::getPaymentId, param.getPaymentId())
.eq(Objects.nonNull(param.getPayChannel()), PaySyncRecord::getPayChannel, param.getPayChannel())
.eq(Objects.nonNull(param.getStatus()), PaySyncRecord::getStatus, param.getStatus())
.page(mpPage);
}
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.core.sync.record.dao;
import cn.bootx.platform.daxpay.core.sync.record.entity.PaySyncRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 支付同步记录
* @author xxm
* @since 2023/7/14
*/
@Mapper
public interface PaySyncRecordMapper extends BaseMapper<PaySyncRecord> {
}

View File

@@ -0,0 +1,74 @@
package cn.bootx.platform.daxpay.core.sync.record.entity;
import cn.bootx.mybatis.table.modify.annotation.DbComment;
import cn.bootx.mybatis.table.modify.annotation.DbTable;
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlFieldType;
import cn.bootx.mybatis.table.modify.mybatis.mysq.constants.MySqlFieldTypeEnum;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.code.pay.PaySyncStatus;
import cn.bootx.platform.daxpay.core.sync.record.convert.PaySyncRecordConvert;
import cn.bootx.platform.daxpay.dto.sync.PaySyncRecordDto;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 支付同步记录
* @author xxm
* @since 2023/7/14
*/
@EqualsAndHashCode(callSuper = true)
@Data
@DbTable(comment = "支付同步记录")
@Accessors(chain = true)
@TableName("pay_sync_record")
public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<PaySyncRecordDto> {
/** 支付记录id */
@DbComment("支付记录id")
private Long paymentId;
/** 商户编码 */
@DbComment("商户编码")
private String mchCode;
/** 商户应用编码 */
@DbComment("商户应用编码")
private String mchAppCode;
/**
* 支付通道
* @see PayChannelEnum#getCode()
*/
@DbComment("支付通道")
private String payChannel;
/** 通知消息 */
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
@DbComment("通知消息")
private String syncInfo;
/**
* 同步状态
* @see PaySyncStatus#WAIT_BUYER_PAY
*/
@DbComment("同步状态")
private String status;
/** 同步时间 */
@DbComment("同步时间")
private LocalDateTime syncTime;
/**
* 转换
*/
@Override
public PaySyncRecordDto toDto() {
return PaySyncRecordConvert.CONVERT.convert(this);
}
}

View File

@@ -0,0 +1,59 @@
package cn.bootx.platform.daxpay.core.sync.record.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.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.core.sync.record.dao.PaySyncRecordManager;
import cn.bootx.platform.daxpay.core.sync.record.entity.PaySyncRecord;
import cn.bootx.platform.daxpay.core.sync.result.PaySyncResult;
import cn.bootx.platform.daxpay.dto.sync.PaySyncRecordDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
/**
* 支付同步记录
* @author xxm
* @since 2023/7/14
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PaySyncRecordService {
private final PaySyncRecordManager syncRecordManager;
/**
* 记录同步记录
*/
public void saveRecord(PaySyncResult paySyncResult, Payment payment){
PaySyncRecord paySyncRecord = new PaySyncRecord()
.setPaymentId(payment.getId())
.setMchCode(payment.getMchCode())
.setMchAppCode(payment.getMchAppCode())
.setPayChannel(payment.getAsyncPayChannel())
.setSyncInfo(paySyncResult.getJson())
.setStatus(paySyncResult.getPaySyncStatus())
.setSyncTime(LocalDateTime.now());
syncRecordManager.save(paySyncRecord);
}
/**
* 分页查询
*/
public PageResult<PaySyncRecordDto> page(PageParam pageParam, PaySyncRecordDto param) {
Page<PaySyncRecord> page = syncRecordManager.page(pageParam, param);
return MpUtil.convert2DtoPageResult(page);
}
/**
* 根据id查询
*/
public PaySyncRecordDto findById(Long id) {
return syncRecordManager.findById(id).map(PaySyncRecord::toDto).orElseThrow(DataNotExistException::new);
}
}

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.pay.result;
package cn.bootx.platform.daxpay.core.sync.result;
import cn.bootx.platform.daxpay.code.pay.PaySyncStatus;
import lombok.Data;
@@ -24,7 +24,10 @@ public class PaySyncResult {
*/
private String paySyncStatus = NOT_SYNC;
/** 网关返回参数 */
/** 网关返回参数(会被用到的参数) */
private Map<String, String> map;
/** 网关返回对象的json字符串 */
private String json;
}

View File

@@ -1,20 +1,11 @@
package cn.bootx.platform.daxpay.core.pay.service;
package cn.bootx.platform.daxpay.core.sync.service;
import cn.bootx.platform.daxpay.code.pay.PaySyncStatus;
import cn.bootx.platform.daxpay.core.pay.builder.PayEventBuilder;
import cn.bootx.platform.daxpay.core.pay.builder.PaymentBuilder;
import cn.bootx.platform.daxpay.core.pay.factory.PayStrategyFactory;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
import cn.bootx.platform.daxpay.exception.payment.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.core.sync.result.PaySyncResult;
import cn.bootx.platform.daxpay.mq.PaymentEventSender;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.util.PayWaylUtil;
import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
@@ -49,63 +40,63 @@ public class PayExpiredTimeService {
@Transactional(rollbackFor = Exception.class)
public void expiredTime(Long paymentId) {
Payment payment = paymentService.findById(paymentId).orElseThrow(() -> new PayFailureException("支付单未找到"));
// 只处理支付中
if (!Objects.equals(payment.getPayStatus(), TRADE_PROGRESS)) {
return;
}
// 获取支付网关状态
PayParam payParam = PaymentBuilder.buildPayParamByPayment(payment);
// 1.获取支付方式通过工厂生成对应的策略组
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.create(payParam.getPayWayList());
if (CollUtil.isEmpty(paymentStrategyList)) {
throw new PayUnsupportedMethodException();
}
// 2.初始化支付的参数
for (AbsPayStrategy paymentStrategy : paymentStrategyList) {
paymentStrategy.initPayParam(payment, payParam);
}
// 3 拿到异步支付方法, 与支付网关进行同步
PayWayParam asyncPayMode = PayWaylUtil.getAsyncPayModeParam(payParam);
AbsPayStrategy syncPayStrategy = PayStrategyFactory.create(asyncPayMode);
syncPayStrategy.initPayParam(payment, payParam);
PaySyncResult paySyncResult = syncPayStrategy.doSyncPayStatusHandler();
// 4 对返回的支付网关各种状态进行处理
String paySyncStatus = paySyncResult.getPaySyncStatus();
switch (paySyncStatus) {
// 成功状态
case PaySyncStatus.TRADE_SUCCESS: {
this.paySuccess(payment, syncPayStrategy, paySyncResult);
break;
}
// 待付款/ 支付中
case PaySyncStatus.WAIT_BUYER_PAY: {
this.payCancel(payment, paymentStrategyList);
break;
}
// 超时关闭 网关没找到记录
case PaySyncStatus.TRADE_CLOSED:
case PaySyncStatus.NOT_FOUND: {
this.payClose(payment, paymentStrategyList);
break;
}
// 交易退款
case PaySyncStatus.TRADE_REFUND: {
log.info("交易退款不需要关闭: {}", payment.getId());
break;
}
// 调用出错 进行重试
case PaySyncStatus.FAIL: {
log.warn("支付状态同步接口调用出错");
}
case PaySyncStatus.NOT_SYNC:
default: {
log.error("支付超时代码有问题");
}
}
// Payment payment = paymentService.findById(paymentId).orElseThrow(() -> new PayFailureException("支付单未找到"));
// // 只处理支付中
// if (!Objects.equals(payment.getPayStatus(), TRADE_PROGRESS)) {
// return;
// }
// // 获取支付网关状态
// PayParam payParam = PaymentBuilder.buildPayParamByPayment(payment);
// // 1.获取支付方式通过工厂生成对应的策略组
// List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.create(payParam.getPayWayList());
// if (CollUtil.isEmpty(paymentStrategyList)) {
// throw new PayUnsupportedMethodException();
// }
//
// // 2.初始化支付的参数
// for (AbsPayStrategy paymentStrategy : paymentStrategyList) {
// paymentStrategy.initPayParam(payment, payParam);
// }
//
// // 3 拿到异步支付方法, 与支付网关进行同步
// PayWayParam asyncPayMode = PayWaylUtil.getAsyncPayModeParam(payParam);
// AbsPayStrategy syncPayStrategy = PayStrategyFactory.create(asyncPayMode);
// syncPayStrategy.initPayParam(payment, payParam);
// PaySyncResult paySyncResult = syncPayStrategy.doSyncPayStatusHandler();
//
// // 4 对返回的支付网关各种状态进行处理
// String paySyncStatus = paySyncResult.getPaySyncStatus();
// switch (paySyncStatus) {
// // 成功状态
// case PaySyncStatus.TRADE_SUCCESS: {
// this.paySuccess(payment, syncPayStrategy, paySyncResult);
// break;
// }
// // 待付款/ 支付中
// case PaySyncStatus.WAIT_BUYER_PAY: {
// this.payCancel(payment, paymentStrategyList);
// break;
// }
// // 超时关闭 网关没找到记录
// case PaySyncStatus.TRADE_CLOSED:
// case PaySyncStatus.NOT_FOUND: {
// this.payClose(payment, paymentStrategyList);
// break;
// }
// // 交易退款
// case PaySyncStatus.TRADE_REFUND: {
// log.info("交易退款不需要关闭: {}", payment.getId());
// break;
// }
// // 调用出错 进行重试
// case PaySyncStatus.FAIL: {
// log.warn("支付状态同步接口调用出错");
// }
// case PaySyncStatus.NOT_SYNC:
// default: {
// log.error("支付超时代码有问题");
// }
// }
}
/**

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.pay.service;
package cn.bootx.platform.daxpay.core.sync.service;
import cn.bootx.platform.common.core.exception.BizException;
import cn.bootx.platform.daxpay.code.pay.PaySyncStatus;
@@ -6,14 +6,17 @@ import cn.bootx.platform.daxpay.core.pay.builder.PayEventBuilder;
import cn.bootx.platform.daxpay.core.pay.builder.PaymentBuilder;
import cn.bootx.platform.daxpay.core.pay.factory.PayStrategyFactory;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.core.sync.factory.PaySyncStrategyFactory;
import cn.bootx.platform.daxpay.core.sync.func.AbsPaySyncStrategy;
import cn.bootx.platform.daxpay.core.sync.record.service.PaySyncRecordService;
import cn.bootx.platform.daxpay.core.sync.result.PaySyncResult;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
import cn.bootx.platform.daxpay.exception.payment.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.mq.PaymentEventSender;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.bootx.platform.daxpay.util.PayWaylUtil;
import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
@@ -39,6 +42,8 @@ public class PaySyncService {
private final PaymentService paymentService;
private final PaySyncRecordService paySyncRecordService;
private final PaymentEventSender eventSender;
/**
@@ -67,25 +72,39 @@ public class PaySyncService {
* 同步支付状态 传入 payment 对象
*/
public void syncPayment(Payment payment) {
// 获取同步策略类
AbsPaySyncStrategy syncPayStrategy = PaySyncStrategyFactory.create(payment.getAsyncPayChannel());
syncPayStrategy.initPayParam(payment);
// 同步
PaySyncResult paySyncResult = syncPayStrategy.doSyncPayStatusHandler();
// 处理
this.resultHandler(paySyncResult,payment);
// 记录
paySyncRecordService.saveRecord(paySyncResult,payment);
}
/**
* 对同步结果进行处理
*/
public void resultHandler(PaySyncResult paySyncResult, Payment payment){
String paySyncStatus = paySyncResult.getPaySyncStatus();
PayParam payParam = PaymentBuilder.buildPayParamByPayment(payment);
// 1.获取支付方式通过工厂生成对应的策略组
// 获取支付方式通过工厂生成对应的策略组
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.create(payParam.getPayWayList());
if (CollUtil.isEmpty(paymentStrategyList)) {
throw new PayUnsupportedMethodException();
}
// 2.初始化支付的参数
// 初始化支付的参数
for (AbsPayStrategy paymentStrategy : paymentStrategyList) {
paymentStrategy.initPayParam(payment, payParam);
}
// 3 拿到异步支付方法, 与支付网关进行同步
// 拿到对应的支付方式
PayWayParam asyncPayMode = PayWaylUtil.getAsyncPayModeParam(payParam);
AbsPayStrategy syncPayStrategy = PayStrategyFactory.create(asyncPayMode);
syncPayStrategy.initPayParam(payment, payParam);
PaySyncResult paySyncResult = syncPayStrategy.doSyncPayStatusHandler();
String paySyncStatus = paySyncResult.getPaySyncStatus();
// 对同步结果处理
switch (paySyncStatus) {
// 支付成功 支付宝退款时也是支付成功状态, 除非支付完成
case PaySyncStatus.TRADE_SUCCESS: {

View File

@@ -0,0 +1,53 @@
package cn.bootx.platform.daxpay.core.sync.strategy;
import cn.bootx.platform.daxpay.core.channel.alipay.dao.AlipayConfigManager;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AlipayConfig;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AlipayConfigService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AlipaySyncService;
import cn.bootx.platform.daxpay.core.sync.func.AbsPaySyncStrategy;
import cn.bootx.platform.daxpay.core.sync.result.PaySyncResult;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
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 2023/7/14
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class AliPaySyncStrategy extends AbsPaySyncStrategy {
private final AlipayConfigManager alipayConfigManager;
private final AlipaySyncService alipaySyncService;
private final AlipayConfigService alipayConfigService;
private AlipayConfig alipayConfig;
/**
* 异步支付单与支付网关进行状态比对
*/
@Override
public PaySyncResult doSyncPayStatusHandler() {
this.initAlipayConfig(this.getPayment().getMchAppCode());
return alipaySyncService.syncPayStatus(this.getPayment());
}
/**
* 初始化支付宝配置信息
*/
private void initAlipayConfig(String mchAppCode) {
// 检查并获取支付宝支付配置
this.alipayConfig = alipayConfigManager.findByMchAppCode(mchAppCode)
.orElseThrow(() -> new PayFailureException("支付配置不存在"));
alipayConfigService.initApiConfig(this.alipayConfig);
}
}

View File

@@ -0,0 +1,49 @@
package cn.bootx.platform.daxpay.core.sync.strategy;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPayConfigManager;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPaySyncService;
import cn.bootx.platform.daxpay.core.sync.func.AbsPaySyncStrategy;
import cn.bootx.platform.daxpay.core.sync.result.PaySyncResult;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
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 2023/7/14
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class WeChatPaySyncStrategy extends AbsPaySyncStrategy {
private final WeChatPayConfigManager weChatPayConfigManager;
private final WeChatPaySyncService weChatPaySyncService;
private WeChatPayConfig weChatPayConfig;
/**
* 异步支付单与支付网关进行状态比对
*/
@Override
public PaySyncResult doSyncPayStatusHandler() {
// 检查并获取微信支付配置
this.initWeChatPayConfig(this.getPayment().getMchAppCode());
return weChatPaySyncService.syncPayStatus(this.getPayment().getId(), this.weChatPayConfig);
}
/**
* 初始化微信支付
*/
private void initWeChatPayConfig(String appCode) {
// 检查并获取微信支付配置
this.weChatPayConfig = weChatPayConfigManager.findByMchAppCode(appCode)
.orElseThrow(() -> new PayFailureException("支付配置不存在"));
}
}

View File

@@ -22,7 +22,7 @@ import java.util.List;
@Data
@Accessors(chain = true)
@Schema(title = "退款记录")
public class RefundRecordDto extends BaseDto {
public class PayRefundRecordDto extends BaseDto {
@Schema(description = "关联的业务id")
private String businessId;

View File

@@ -0,0 +1,61 @@
package cn.bootx.platform.daxpay.dto.sync;
import cn.bootx.mybatis.table.modify.annotation.DbComment;
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlFieldType;
import cn.bootx.mybatis.table.modify.mybatis.mysq.constants.MySqlFieldTypeEnum;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.code.pay.PaySyncStatus;
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 2023/7/14
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "支付同步记录")
public class PaySyncRecordDto extends BaseDto {
/** 支付记录id */
@DbComment("支付记录id")
private Long paymentId;
/** 商户编码 */
@DbComment("商户编码")
private String mchCode;
/** 商户应用编码 */
@DbComment("商户应用编码")
private String mchAppCode;
/**
* 支付通道
* @see PayChannelEnum#getCode()
*/
@DbComment("支付通道")
private String payChannel;
/** 通知消息 */
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
@DbComment("通知消息")
private String syncInfo;
/**
* 同步状态
* @see PaySyncStatus#WAIT_BUYER_PAY
*/
@DbComment("同步状态")
private String status;
/** 同步时间 */
@DbComment("同步时间")
private LocalDateTime syncTime;
}

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.mq;
import cn.bootx.platform.common.rabbit.conditional.ConditionalOnRabbit;
import cn.bootx.platform.daxpay.code.PaymentEventCode;
import cn.bootx.platform.daxpay.core.pay.service.PayExpiredTimeService;
import cn.bootx.platform.daxpay.core.sync.service.PayExpiredTimeService;
import cn.bootx.platform.daxpay.event.PayCancelEvent;
import cn.bootx.platform.daxpay.event.PayCompleteEvent;
import cn.bootx.platform.daxpay.event.PayRefundEvent;

View File

@@ -0,0 +1,57 @@
package cn.bootx.platform.daxpay.param.channel.voucher;
import cn.bootx.platform.daxpay.code.paymodel.VoucherCode;
import cn.hutool.core.date.DatePattern;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
*
* @author xxm
* @since 2023/7/13
*/
@Data
@Accessors(chain = true)
@Schema(title = "储值卡信息更改参数")
public class VoucherChangeParam {
@ExcelProperty("卡号")
@Schema(description = "卡号")
private String cardNo;
@ExcelProperty("卡号")
@Schema(description = "面值")
private BigDecimal faceValue;
@ExcelProperty("卡号")
@Schema(description = "余额")
private BigDecimal balance;
@ExcelProperty("卡号")
@Schema(description = "是否长期有效")
private Boolean enduring;
@ExcelProperty("开始时间")
@Schema(description = "开始时间")
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
private LocalDateTime startTime;
@ExcelProperty("结束时间")
@Schema(description = "结束时间")
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
private LocalDateTime endTime;
/**
* @see VoucherCode#STATUS_NORMAL
*/
@ExcelProperty("默认状态")
@Schema(description = "默认状态")
private String status;
}

View File

@@ -1,16 +1,54 @@
package cn.bootx.platform.daxpay.param.channel.voucher;
import cn.bootx.platform.daxpay.code.paymodel.VoucherCode;
import cn.hutool.core.date.DatePattern;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* @author xxm
* @since 2022/3/14
*/
@Data
@Accessors(chain = true)
@Schema(title = "储值卡导入参数")
public class VoucherImportParam {
@ExcelProperty("卡号")
@Schema(description = "卡号")
private String cardNo;
@ExcelProperty("面值")
@Schema(description = "面值")
private BigDecimal faceValue;
@ExcelProperty("余额")
@Schema(description = "余额")
private BigDecimal balance;
@ExcelProperty("是否长期有效")
@Schema(description = "是否长期有效")
private Boolean enduring;
@ExcelProperty("开始时间")
@Schema(description = "开始时间")
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
private LocalDateTime startTime;
@ExcelProperty("结束时间")
@Schema(description = "结束时间")
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
private LocalDateTime endTime;
/**
* @see VoucherCode#STATUS_NORMAL
*/
@ExcelProperty("默认状态")
@Schema(description = "默认状态")
private String status;
}