mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-09-27 06:01:33 +00:00
feat 同步支付方式功能
This commit is contained in:
@@ -123,14 +123,14 @@
|
|||||||
|
|
||||||
QQ扫码加入QQ交流群
|
QQ扫码加入QQ交流群
|
||||||
<p>
|
<p>
|
||||||
<img src="https://jsd.cdn.zzko.cn/gh/xxm1995/bootx-img@master/bootx/qq_qun.3qs0zpity9q0.webp" width = "330" height = "500"/>
|
<img src="_doc/images/qq_qun.jpg" width = "330" height = "500"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
## 🍻 鸣谢
|
## 🍻 鸣谢
|
||||||
感谢 JetBrains 提供的免费开源 License:
|
感谢 JetBrains 提供的免费开源 License:
|
||||||
|
|
||||||
[](https://www.jetbrains.com/?from=bootx)
|
[](https://www.jetbrains.com/?from=bootx)
|
||||||
|
|
||||||
感谢其他提供灵感和思路的开源项目
|
感谢其他提供灵感和思路的开源项目
|
||||||
|
|
||||||
|
@@ -4,8 +4,10 @@
|
|||||||
- [x] 钱包支付
|
- [x] 钱包支付
|
||||||
- [x] 储值卡支付
|
- [x] 储值卡支付
|
||||||
- [ ] 支付回调通知和退款回调通知功能
|
- [ ] 支付回调通知和退款回调通知功能
|
||||||
|
- [ ] 个通道配置支持启用停用校验
|
||||||
- [x] 支付对账的序列化生成器有问题,待platform更新
|
- [x] 支付对账的序列化生成器有问题,待platform更新
|
||||||
2.0.x 版本内容
|
2.0.x 版本内容
|
||||||
|
- [ ] 云闪付接入
|
||||||
- [ ] 退款操作支持重试
|
- [ ] 退款操作支持重试
|
||||||
- [ ] 增加各类日志记录,例如钱包的各项操作
|
- [ ] 增加各类日志记录,例如钱包的各项操作
|
||||||
- [ ] 支付流程也改为先落库后支付情况, 避免极端情况导致掉单
|
- [ ] 支付流程也改为先落库后支付情况, 避免极端情况导致掉单
|
||||||
|
@@ -0,0 +1,74 @@
|
|||||||
|
package cn.bootx.platform.daxpay.admin.controller.channel.wallet;
|
||||||
|
|
||||||
|
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.common.core.util.ValidationUtil;
|
||||||
|
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletQueryService;
|
||||||
|
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletService;
|
||||||
|
import cn.bootx.platform.daxpay.service.dto.channel.wallet.WalletDto;
|
||||||
|
import cn.bootx.platform.daxpay.service.param.channel.wallet.CreateWalletParam;
|
||||||
|
import cn.bootx.platform.daxpay.service.param.channel.wallet.WalletQueryParam;
|
||||||
|
import cn.bootx.platform.daxpay.service.param.channel.wallet.WalletRechargeParam;
|
||||||
|
import cn.bootx.platform.daxpay.service.param.channel.wallet.WalleteeDductParam;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钱包管理
|
||||||
|
* @author xxm
|
||||||
|
* @since 2024/2/17
|
||||||
|
*/
|
||||||
|
@Tag(name = "钱包管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/wallet")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class WalletController {
|
||||||
|
private final WalletService walletService;
|
||||||
|
private final WalletQueryService walletQueryService;
|
||||||
|
|
||||||
|
@Operation(summary = "创建钱包")
|
||||||
|
@PostMapping("/create")
|
||||||
|
public ResResult<Void> create(@RequestBody CreateWalletParam param){
|
||||||
|
ValidationUtil.validateParam(param);
|
||||||
|
walletService.create(param);
|
||||||
|
return Res.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "充值")
|
||||||
|
@PostMapping("/recharge")
|
||||||
|
public ResResult<Void> recharge(@RequestBody WalletRechargeParam param){
|
||||||
|
ValidationUtil.validateParam(param);
|
||||||
|
walletService.recharge(param);
|
||||||
|
return Res.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "扣减")
|
||||||
|
@PostMapping("/deduct")
|
||||||
|
public ResResult<Void> deduct(@RequestBody WalleteeDductParam param){
|
||||||
|
ValidationUtil.validateParam(param);
|
||||||
|
walletService.deduct(param);
|
||||||
|
return Res.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "分页")
|
||||||
|
@GetMapping("/page")
|
||||||
|
public ResResult<PageResult<WalletDto>> page(PageParam pageParam, WalletQueryParam query){
|
||||||
|
return Res.ok(walletQueryService.page(pageParam, query));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "查询详情")
|
||||||
|
@GetMapping("/findById")
|
||||||
|
public ResResult<WalletDto> findById(Long id){
|
||||||
|
return Res.ok(walletQueryService.findById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "判断用户是否开通了钱包")
|
||||||
|
@GetMapping("/existsByUserId")
|
||||||
|
public ResResult<Boolean> existsByUserId(String userId){
|
||||||
|
return Res.ok(walletQueryService.existsByUserId(userId));
|
||||||
|
}
|
||||||
|
}
|
@@ -77,4 +77,10 @@ public class VoucherManager extends BaseManager<VoucherMapper, Voucher> {
|
|||||||
.update();
|
.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卡号是否存在
|
||||||
|
*/
|
||||||
|
public boolean existsByCardNo(String cardNo) {
|
||||||
|
return this.existedByField(Voucher::getCardNo, cardNo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,10 @@ public class VoucherService {
|
|||||||
*/
|
*/
|
||||||
public void voucherImport(VoucherImportParam param){
|
public void voucherImport(VoucherImportParam param){
|
||||||
Voucher voucher = VoucherConvert.CONVERT.convert(param);
|
Voucher voucher = VoucherConvert.CONVERT.convert(param);
|
||||||
|
// 判断重复
|
||||||
|
if (voucherManager.existsByCardNo(param.getCardNo())) {
|
||||||
|
throw new BizException("钱包已经开通");
|
||||||
|
}
|
||||||
voucherManager.save(voucher);
|
voucherManager.save(voucher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,7 +37,7 @@ public class Wallet extends MpBaseEntity implements EntityBaseFunction<WalletDto
|
|||||||
private String userId;
|
private String userId;
|
||||||
|
|
||||||
/** 钱包名称 */
|
/** 钱包名称 */
|
||||||
@DbColumn("钱包名称")
|
@DbColumn(comment = "钱包名称")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
/** 余额 */
|
/** 余额 */
|
||||||
|
@@ -29,21 +29,28 @@ public class WalletQueryService {
|
|||||||
private final WalletManager walletManager;
|
private final WalletManager walletManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据钱包ID查询Wallet
|
* 根据钱包ID查询钱包
|
||||||
*/
|
*/
|
||||||
public WalletDto findById(Long walletId) {
|
public WalletDto findById(Long id) {
|
||||||
return walletManager.findById(walletId).map(Wallet::toDto).orElseThrow(DataNotExistException::new);
|
return walletManager.findById(id).map(Wallet::toDto).orElseThrow(DataNotExistException::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取钱包综合信息
|
* 获取钱包综合信息
|
||||||
*/
|
*/
|
||||||
public WalletDto findById(String userId) {
|
public WalletDto findByUserId(String userId) {
|
||||||
return walletManager.findByUser(userId).map(Wallet::toDto).orElseThrow(DataNotExistException::new);
|
return walletManager.findByUser(userId).map(Wallet::toDto).orElseThrow(DataNotExistException::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询用户 分页
|
* 判断用户是否开通了钱包
|
||||||
|
*/
|
||||||
|
public boolean existsByUserId(String userId) {
|
||||||
|
return walletManager.existsByUser(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页
|
||||||
*/
|
*/
|
||||||
public PageResult<WalletDto> page(PageParam pageParam, WalletQueryParam param) {
|
public PageResult<WalletDto> page(PageParam pageParam, WalletQueryParam param) {
|
||||||
return MpUtil.convert2DtoPageResult(walletManager.page(pageParam, param));
|
return MpUtil.convert2DtoPageResult(walletManager.page(pageParam, param));
|
||||||
@@ -51,20 +58,18 @@ public class WalletQueryService {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取钱包, 获取顺序: 1. 显式传入的钱包ID 2. 显式传入的用户ID 3.
|
* 获取钱包, 获取顺序: 1. 钱包ID 2. 用户ID
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public Wallet getWallet(WalletPayParam walletPayParam){
|
public Wallet getWallet(WalletPayParam param){
|
||||||
|
|
||||||
Wallet wallet = null;
|
Wallet wallet = null;
|
||||||
// 首先根据钱包ID查询
|
if (Objects.nonNull(param.getWalletId())){
|
||||||
if (Objects.nonNull(walletPayParam.getWalletId())) {
|
wallet = walletManager.findById(param.getWalletId()).orElseThrow(DataNotExistException::new);
|
||||||
wallet = walletManager.findById(walletPayParam.getWalletId()).orElseThrow(null);
|
}
|
||||||
|
if (Objects.isNull(wallet)){
|
||||||
|
wallet = walletManager.findByUser(param.getUserId()).orElseThrow(DataNotExistException::new);
|
||||||
}
|
}
|
||||||
if (Objects.nonNull(wallet)){
|
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ import cn.bootx.platform.daxpay.service.core.channel.wallet.dao.WalletManager;
|
|||||||
import cn.bootx.platform.daxpay.service.core.channel.wallet.entity.Wallet;
|
import cn.bootx.platform.daxpay.service.core.channel.wallet.entity.Wallet;
|
||||||
import cn.bootx.platform.daxpay.service.param.channel.wallet.CreateWalletParam;
|
import cn.bootx.platform.daxpay.service.param.channel.wallet.CreateWalletParam;
|
||||||
import cn.bootx.platform.daxpay.service.param.channel.wallet.WalletRechargeParam;
|
import cn.bootx.platform.daxpay.service.param.channel.wallet.WalletRechargeParam;
|
||||||
import cn.bootx.platform.daxpay.service.param.channel.wallet.WalletReduceParam;
|
import cn.bootx.platform.daxpay.service.param.channel.wallet.WalleteeDductParam;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -16,7 +16,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* 钱包管理接口
|
||||||
* @author xxm
|
* @author xxm
|
||||||
* @since 2023/6/26
|
* @since 2023/6/26
|
||||||
*/
|
*/
|
||||||
@@ -27,10 +27,10 @@ public class WalletService {
|
|||||||
private final WalletManager walletManager;
|
private final WalletManager walletManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开通钱包操作,默认为启用状态, 不传输余额则默认为0
|
* 创建钱包操作,默认为启用状态, 不传输余额则默认为0
|
||||||
*/
|
*/
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void createWallet(CreateWalletParam param) {
|
public void create(CreateWalletParam param) {
|
||||||
// 判断钱包是否已开通
|
// 判断钱包是否已开通
|
||||||
if (walletManager.existsByUser(param.getUserId())) {
|
if (walletManager.existsByUser(param.getUserId())) {
|
||||||
throw new BizException("钱包已经开通");
|
throw new BizException("钱包已经开通");
|
||||||
@@ -85,7 +85,7 @@ public class WalletService {
|
|||||||
* 余额扣减
|
* 余额扣减
|
||||||
*/
|
*/
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void reduce(WalletReduceParam param) {
|
public void deduct(WalleteeDductParam param) {
|
||||||
Wallet wallet = null;
|
Wallet wallet = null;
|
||||||
if (Objects.nonNull(param.getWalletId())){
|
if (Objects.nonNull(param.getWalletId())){
|
||||||
wallet = walletManager.findById(param.getWalletId()).orElseThrow(DataNotExistException::new);
|
wallet = walletManager.findById(param.getWalletId()).orElseThrow(DataNotExistException::new);
|
||||||
|
@@ -7,6 +7,8 @@ import cn.bootx.platform.daxpay.exception.waller.WalletLackOfBalanceException;
|
|||||||
import cn.bootx.platform.daxpay.param.channel.WalletPayParam;
|
import cn.bootx.platform.daxpay.param.channel.WalletPayParam;
|
||||||
import cn.bootx.platform.daxpay.service.code.WalletCode;
|
import cn.bootx.platform.daxpay.service.code.WalletCode;
|
||||||
import cn.bootx.platform.daxpay.service.core.channel.wallet.entity.Wallet;
|
import cn.bootx.platform.daxpay.service.core.channel.wallet.entity.Wallet;
|
||||||
|
import cn.bootx.platform.daxpay.service.core.channel.wallet.entity.WalletConfig;
|
||||||
|
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletConfigService;
|
||||||
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletPayService;
|
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletPayService;
|
||||||
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletQueryService;
|
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletQueryService;
|
||||||
import cn.bootx.platform.daxpay.service.func.AbsPayStrategy;
|
import cn.bootx.platform.daxpay.service.func.AbsPayStrategy;
|
||||||
@@ -33,6 +35,7 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class WalletPayStrategy extends AbsPayStrategy {
|
public class WalletPayStrategy extends AbsPayStrategy {
|
||||||
|
|
||||||
|
private final WalletConfigService walletConfigService;
|
||||||
|
|
||||||
private final WalletPayService walletPayService;
|
private final WalletPayService walletPayService;
|
||||||
|
|
||||||
@@ -40,6 +43,8 @@ public class WalletPayStrategy extends AbsPayStrategy {
|
|||||||
|
|
||||||
private Wallet wallet;
|
private Wallet wallet;
|
||||||
|
|
||||||
|
private WalletConfig walletConfig;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PayChannelEnum getChannel() {
|
public PayChannelEnum getChannel() {
|
||||||
return PayChannelEnum.WALLET;
|
return PayChannelEnum.WALLET;
|
||||||
@@ -60,6 +65,9 @@ public class WalletPayStrategy extends AbsPayStrategy {
|
|||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
throw new PayFailureException("支付参数错误");
|
throw new PayFailureException("支付参数错误");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.walletConfig = walletConfigService.getConfig();
|
||||||
|
|
||||||
// 获取钱包
|
// 获取钱包
|
||||||
this.wallet = walletQueryService.getWallet(walletPayParam);
|
this.wallet = walletQueryService.getWallet(walletPayParam);
|
||||||
if (Objects.isNull(this.wallet)){
|
if (Objects.isNull(this.wallet)){
|
||||||
@@ -69,6 +77,8 @@ public class WalletPayStrategy extends AbsPayStrategy {
|
|||||||
if (Objects.equals(WalletCode.STATUS_FORBIDDEN, this.wallet.getStatus())) {
|
if (Objects.equals(WalletCode.STATUS_FORBIDDEN, this.wallet.getStatus())) {
|
||||||
throw new WalletBannedException();
|
throw new WalletBannedException();
|
||||||
}
|
}
|
||||||
|
// 判断是否超过限额
|
||||||
|
|
||||||
// 判断余额
|
// 判断余额
|
||||||
if (this.wallet.getBalance() < getPayChannelParam().getAmount()) {
|
if (this.wallet.getBalance() < getPayChannelParam().getAmount()) {
|
||||||
throw new WalletLackOfBalanceException();
|
throw new WalletLackOfBalanceException();
|
||||||
|
@@ -21,17 +21,12 @@ public class WalletDto extends BaseDto implements Serializable {
|
|||||||
|
|
||||||
private static final long serialVersionUID = -1563719305334334625L;
|
private static final long serialVersionUID = -1563719305334334625L;
|
||||||
|
|
||||||
@Schema(description = "ID,钱包的唯一标识")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@Schema(description = "钱包关联的账号ID")
|
@Schema(description = "钱包关联的账号ID")
|
||||||
private Long userId;
|
private String userId;
|
||||||
|
|
||||||
@Schema(description = "商户编码")
|
/** 钱包名称 */
|
||||||
private String mchCode;
|
@Schema(description = "钱包名称")
|
||||||
|
private String name;
|
||||||
@Schema(description = "商户应用编码")
|
|
||||||
private String mchAppCode;
|
|
||||||
|
|
||||||
@Schema(description = "钱包余额")
|
@Schema(description = "钱包余额")
|
||||||
private BigDecimal balance;
|
private BigDecimal balance;
|
||||||
|
@@ -4,6 +4,10 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开通钱包参数
|
* 开通钱包参数
|
||||||
* @author xxm
|
* @author xxm
|
||||||
@@ -15,12 +19,16 @@ import lombok.experimental.Accessors;
|
|||||||
public class CreateWalletParam {
|
public class CreateWalletParam {
|
||||||
|
|
||||||
@Schema(description = "用户id")
|
@Schema(description = "用户id")
|
||||||
|
@NotBlank(message = "用户ID不可为空")
|
||||||
private String userId;
|
private String userId;
|
||||||
|
|
||||||
@Schema(description = "钱包名称")
|
@Schema(description = "钱包名称")
|
||||||
|
@NotBlank(message = "钱包名称不可为空")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Schema(description = "钱包金额")
|
@Schema(description = "钱包金额")
|
||||||
|
@NotNull(message = "钱包金额不可为空")
|
||||||
|
@Min(value = 0, message = "钱包金额不可以低于0")
|
||||||
private Integer balance;
|
private Integer balance;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ import javax.validation.constraints.NotNull;
|
|||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
@Schema(title = "钱包扣减参数")
|
@Schema(title = "钱包扣减参数")
|
||||||
public class WalletReduceParam {
|
public class WalleteeDductParam {
|
||||||
|
|
||||||
|
|
||||||
@Schema(description = "钱包ID")
|
@Schema(description = "钱包ID")
|
Reference in New Issue
Block a user