feat(payment): 添加分账功能并优化相关服务

- 新增分账功能,支持支付宝和微信支付的分账操作
- 重构支付宝支付流程,使用新的SDK- 更新微信支付示例,调整相关参数
- 优化收银台配置和支付订单相关服务- 调整分账比例的验证规则,支持更灵活的比例设置
This commit is contained in:
DaxPay
2024-11-24 17:14:55 +08:00
parent 1f3b19d79b
commit 8901fb7463
9 changed files with 81 additions and 28 deletions

View File

@@ -76,25 +76,32 @@
```xml
<!-- 支付SDK -->
<dependency>
<groupId>cn.daxpay.single</groupId>
<groupId>org.dromara.daxpay</groupId>
<artifactId>daxpay-single-sdk</artifactId>
<version>${latest.version}</version>
</dependency>
```
### SDK调用示例
```java
package org.dromara.daxpay.test;
package org.dromara.daxpay.single.sdk.test.trade;
import cn.daxpay.single.sdk.code.SignTypeEnum;
import cn.daxpay.single.sdk.model.trade.pay.PayOrderModel;
import cn.daxpay.single.sdk.net.DaxPayConfig;
import cn.daxpay.single.sdk.net.DaxPayKit;
import cn.daxpay.single.sdk.param.trade.pay.PayQueryParam;
import cn.daxpay.single.sdk.response.DaxPayResult;
import cn.daxpay.single.sdk.util.JsonUtil;
import org.dromara.daxpay.single.sdk.code.ChannelEnum;
import org.dromara.daxpay.single.sdk.code.PayMethodEnum;
import org.dromara.daxpay.single.sdk.code.SignTypeEnum;
import org.dromara.daxpay.single.sdk.model.trade.pay.PayResultModel;
import org.dromara.daxpay.single.sdk.net.DaxPayConfig;
import org.dromara.daxpay.single.sdk.net.DaxPayKit;
import org.dromara.daxpay.single.sdk.param.channel.AlipayParam;
import org.dromara.daxpay.single.sdk.param.channel.WechatPayParam;
import org.dromara.daxpay.single.sdk.param.trade.pay.PayParam;
import org.dromara.daxpay.single.sdk.response.DaxPayResult;
import org.dromara.daxpay.single.sdk.util.JsonUtil;
import org.dromara.daxpay.single.sdk.util.PaySignUtil;
import org.junit.Before;
import org.junit.Test;
import java.math.BigDecimal;
/**
* 统一支付接口
* @author xxm
@@ -108,24 +115,24 @@ public class PayOrderTest {
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9999")
.signSecret("123456")
.appId("M7934041241299655")
.appId("123")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}
/**
* 支付宝支付(二维码扫码)
/**
* 微信支付(二维码扫码)
*/
@Test
public void aliPayQrPay() {
public void wxQrPay() {
PayParam param = new PayParam();
param.setClientIp("127.0.0.1");
param.setBizOrderNo("SDK_"+ System.currentTimeMillis());
param.setTitle("测试支付宝扫码支付");
param.setDescription("这是支付宝扫码支付");
param.setAmount(BigDecimal.valueOf(10));
param.setChannel(ChannelEnum.ALI.getCode());
param.setTitle("测试微信扫码支付");
param.setDescription("这是支付备注");
param.setAmount(BigDecimal.valueOf(1.00));
param.setChannel(ChannelEnum.WECHAT.getCode());
param.setMethod(PayMethodEnum.QRCODE.getCode());
param.setAttach("{回调参数}");
param.setAllocation(false);
@@ -134,8 +141,10 @@ public class PayOrderTest {
DaxPayResult<PayResultModel> execute = DaxPayKit.execute(param);
System.out.println(JsonUtil.toJsonStr(execute));
System.out.println(PaySignUtil.hmacSha256Sign(execute, "123456"));
}
}
```
## 🍎 系统截图

View File

@@ -19,6 +19,9 @@
- 在微信支付宝时为结算页, 可以直接发起支付
- [ ] PC收银台, PC收银台可以生成聚合收银码, 也可以通道特殊方式的支付(微信扫码/支付宝PC支付)
- [ ] H5收银台只在浏览器中才会出现, 在软件中会直接跳转到结算页
- [ ] 支付码牌配置
- [x] 一个应用支持多码牌
- [x] 码牌不再使用应用号座位标识, 使用独立的编码
## bugs
- [x] 修复 BigDecimal 类型数据序列化和签名异常问题

View File

@@ -1,7 +1,6 @@
package org.dromara.daxpay.channel.alipay.service.allocation.receiver;
import cn.hutool.core.util.StrUtil;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayResponse;
import com.alipay.api.domain.AlipayTradeRoyaltyRelationBindModel;
import com.alipay.api.domain.AlipayTradeRoyaltyRelationUnbindModel;
@@ -54,7 +53,6 @@ public class AliPayAllocReceiverService {
*/
@SneakyThrows
public void bind(AllocReceiver allocReceiver){
AlipayClient alipayClient = aliPayConfigService.getAlipayClient();
AlipayTradeRoyaltyRelationBindModel model = new AlipayTradeRoyaltyRelationBindModel();
RoyaltyEntity entity = new RoyaltyEntity();
@@ -69,7 +67,7 @@ public class AliPayAllocReceiverService {
AlipayTradeRoyaltyRelationBindRequest request = new AlipayTradeRoyaltyRelationBindRequest();
model.setOutRequestNo(String.valueOf(allocReceiver.getId()));
request.setBizModel(model);
AlipayTradeRoyaltyRelationBindResponse response = alipayClient.execute(request);
AlipayTradeRoyaltyRelationBindResponse response = aliPayConfigService.execute(request);
this.verifyErrorMsg(response);
}
@@ -78,7 +76,6 @@ public class AliPayAllocReceiverService {
*/
@SneakyThrows
public void unbind(AllocReceiver allocReceiver){
AlipayClient alipayClient = aliPayConfigService.getAlipayClient();
AlipayTradeRoyaltyRelationUnbindModel model = new AlipayTradeRoyaltyRelationUnbindModel();
model.setOutRequestNo(String.valueOf(allocReceiver.getId()));
@@ -90,7 +87,7 @@ public class AliPayAllocReceiverService {
model.setReceiverList(Collections.singletonList(entity));
AlipayTradeRoyaltyRelationUnbindRequest request = new AlipayTradeRoyaltyRelationUnbindRequest();
request.setBizModel(model);
AlipayTradeRoyaltyRelationUnbindResponse response = alipayClient.execute(request);
AlipayTradeRoyaltyRelationUnbindResponse response = aliPayConfigService.execute(request);
System.out.println(response);
// 如果出现分账方不存在也视为成功
if (Objects.equals(response.getSubCode(), AliPayCode.USER_NOT_EXIST)) {

View File

@@ -7,6 +7,7 @@ import cn.bootx.platform.core.rest.dto.LabelValue;
import cn.bootx.platform.core.rest.param.PageParam;
import cn.bootx.platform.core.rest.result.PageResult;
import cn.bootx.platform.core.rest.result.Result;
import cn.bootx.platform.core.util.ValidationUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
@@ -62,7 +63,8 @@ public class AllocReceiverController {
@RequestPath("添加")
@Operation(summary = "添加")
@PostMapping("/add")
public Result<Void> add(@RequestBody @Validated AllocReceiverAddParam param){
public Result<Void> add(@RequestBody AllocReceiverAddParam param){
ValidationUtil.validateParam(param);
paymentAssistService.initMchApp(param.getAppId());
receiverService.addAndSync(param);
return Res.ok();
@@ -71,7 +73,8 @@ public class AllocReceiverController {
@RequestPath("删除")
@Operation(summary = "删除")
@PostMapping("/delete")
public Result<Void> delete(@RequestBody @Validated AllocReceiverRemoveParam param){
public Result<Void> delete(@RequestBody AllocReceiverRemoveParam param){
ValidationUtil.validateParam(param);
paymentAssistService.initMchApp(param.getAppId());
receiverService.removeAndSync(param);
return Res.ok();

View File

@@ -0,0 +1,16 @@
package org.dromara.daxpay.service.controller.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 收银台配置
* @author xxm
* @since 2024/11/22
*/
@Data
@Accessors(chain = true)
@Schema(title = "收银台配置")
public class CheckoutConfig {
}

View File

@@ -97,4 +97,12 @@ public class PayOrderController {
payOrderService.cancel(id);
return Res.ok();
}
@RequestPath("分账")
@Operation(summary = "分账")
@PostMapping("/allocation")
public Result<Void> allocation(@NotNull(message = "支付订单id不能为空") Long id){
payOrderService.allocation(id);
return Res.ok();
}
}

View File

@@ -1,6 +1,7 @@
package org.dromara.daxpay.service.entity.allocation.transaction;
import cn.bootx.platform.common.mybatisplus.function.ToResult;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@@ -21,6 +22,7 @@ import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@TableName("pay_alloc_detail")
public class AllocDetail extends MchAppBaseEntity implements ToResult<AllocDetailResult> {
/** 分账订单ID */

View File

@@ -1,14 +1,13 @@
package org.dromara.daxpay.service.param.allocation.group;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.Min;
import java.math.BigDecimal;
/**
@@ -27,9 +26,8 @@ public class AllocGroupReceiverParam {
@Schema(description = "分账比例(百分之多少)")
@NotNull(message = "分账比例不可为空")
@Min(value = 0,message = "分账比例不可为负数")
@DecimalMax(value = "100",message = "分账比例不可大于100%")
@DecimalMin(value = "0.01", message = "分账比例不可小于0.01%")
@DecimalMin(value = "0.00", message = "分账比例不可为负数")
@Digits(integer = 3, fraction = 2, message = "分账比例最多只允许两位小数")
private BigDecimal rate;
}

View File

@@ -1,8 +1,12 @@
package org.dromara.daxpay.service.service.order.pay;
import jakarta.validation.constraints.NotNull;
import org.dromara.daxpay.core.exception.TradeNotExistException;
import org.dromara.daxpay.core.param.allocation.transaction.AllocationParam;
import org.dromara.daxpay.core.util.TradeNoGenerateUtil;
import org.dromara.daxpay.service.dao.order.pay.PayOrderManager;
import org.dromara.daxpay.service.entity.order.pay.PayOrder;
import org.dromara.daxpay.service.service.allocation.AllocationService;
import org.dromara.daxpay.service.service.assist.PaymentAssistService;
import org.dromara.daxpay.service.service.trade.pay.PayCloseService;
import org.dromara.daxpay.service.service.trade.pay.PaySyncService;
@@ -24,6 +28,7 @@ public class PayOrderService {
private final PaymentAssistService paymentAssistService;
private final PayCloseService payCloseService;
private final AllocationService allocationService;
/**
* 同步
@@ -55,4 +60,16 @@ public class PayOrderService {
payCloseService.closeOrder(payOrder,true);
}
/**
* 分账
*/
public void allocation(@NotNull(message = "支付订单id不能为空") Long id) {
PayOrder payOrder = payOrderManager.findById(id).orElseThrow(() -> new TradeNotExistException("支付订单不存在"));
paymentAssistService.initMchApp(payOrder.getAppId());
AllocationParam param = new AllocationParam()
.setBizAllocNo("B"+TradeNoGenerateUtil.allocation());
param.setAppId(payOrder.getAppId());
allocationService.allocation(param, payOrder);
}
}