feat 云闪付对接

This commit is contained in:
bootx
2024-03-06 23:23:59 +08:00
parent cfbd7e2e32
commit f6cd4b0a46
12 changed files with 303 additions and 29 deletions

View File

@@ -92,6 +92,7 @@
<artifactId>IJPay-WxPay</artifactId>
<version>${IJPay.version}</version>
</dependency>
<!-- 微信支付 wxjava -->
<dependency>
<groupId>com.github.binarywang</groupId>
@@ -99,6 +100,13 @@
<version>${wxjava.version}</version>
</dependency>
<!-- 云闪付 -->
<dependency>
<groupId>com.github.javen205</groupId>
<artifactId>IJPay-UnionPay</artifactId>
<version>${IJPay.version}</version>
</dependency>
<!-- 微信工具包 -->
<dependency>
<groupId>com.github.binarywang</groupId>

View File

@@ -79,7 +79,7 @@ public class AliPayConfigService {
public AliPayConfig getAndCheckConfig() {
AliPayConfig alipayConfig = this.getConfig();
if (!alipayConfig.getEnable()){
throw new PayFailureException("支付宝支付方式未启用");
throw new PayFailureException("支付宝支付未启用");
}
return alipayConfig;
}

View File

@@ -2,14 +2,21 @@ package cn.bootx.platform.daxpay.service.core.channel.union.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.common.mybatisplus.handler.StringListTypeHandler;
import cn.bootx.platform.daxpay.service.core.channel.union.convert.UnionPayConvert;
import cn.bootx.platform.daxpay.service.dto.channel.union.UnionPayConfigDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import cn.bootx.table.modify.mysql.annotation.DbMySqlFieldType;
import cn.bootx.table.modify.mysql.constants.MySqlFieldTypeEnum;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 云闪付支付配置
*
@@ -20,9 +27,44 @@ import lombok.experimental.Accessors;
@Data
@DbTable(comment = "云闪付支付配置")
@Accessors(chain = true)
@TableName("pay_union_pay_config")
@TableName(value = "pay_union_pay_config",autoResultMap = true)
public class UnionPayConfig extends MpBaseEntity implements EntityBaseFunction<UnionPayConfigDto> {
/** 商户号 */
@DbColumn(comment = "商户号")
private String machId;
/** 是否启用, 只影响支付和退款操作 */
@DbColumn(comment = "是否启用")
private Boolean enable;
/** 密钥 */
@DbColumn(comment = "密钥")
private String appKey;
/** 支付网关地址 */
@DbColumn(comment = "支付网关地址")
private String serverUrl;
/**
* 服务器异步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址
* 1. 需http://或者https://格式的完整路径,
* 2. 不能加?id=123这类自定义参数必须外网可以正常访问
* 3. 消息顺序 银联网关 -> 本网关进行处理 -> 发送消息通知业务系统
*/
@DbColumn(comment = "异步通知路径")
private String notifyUrl;
/** 可用支付方式 */
@DbColumn(comment = "可用支付方式")
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
@TableField(typeHandler = StringListTypeHandler.class)
private List<String> payWays;
/** 备注 */
@DbColumn(comment = "备注")
private String remark;
@Override
public UnionPayConfigDto toDto() {
return UnionPayConvert.CONVERT.convert(this);

View File

@@ -1,11 +1,27 @@
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.dto.LabelValue;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.AliPayWay;
import cn.bootx.platform.daxpay.service.core.channel.union.dao.UnionPayConfigManager;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.system.config.service.PayChannelConfigService;
import cn.bootx.platform.daxpay.service.param.channel.alipay.AliPayConfigParam;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 云闪付支付配置
* @author xxm
* @since 2022/3/11
*/
@@ -13,7 +29,52 @@ import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class UnionPayConfigService {
/** 默认云闪付配置的主键ID */
private final static Long ID = 0L;
private final UnionPayConfigManager unionPayConfigManager;
private final PayChannelConfigService payChannelConfigService;
/**
* 修改
*/
@Transactional(rollbackFor = Exception.class)
public void update(AliPayConfigParam param) {
UnionPayConfig unionPayConfig = unionPayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("支付宝配置不存在"));
// 启用或停用
if (!Objects.equals(param.getEnable(), unionPayConfig.getEnable())){
payChannelConfigService.setEnable(PayChannelEnum.UNION_PAY.getCode(), param.getEnable());
}
BeanUtil.copyProperties(param, unionPayConfig, CopyOptions.create().ignoreNullValue());
unionPayConfigManager.updateById(unionPayConfig);
}
/**
* 支付宝支持支付方式
*/
public List<LabelValue> findPayWays() {
return AliPayWay.getPayWays()
.stream()
.map(e -> new LabelValue(e.getName(),e.getCode()))
.collect(Collectors.toList());
}
/**
* 获取支付配置
*/
public UnionPayConfig getConfig(){
return unionPayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("支付宝配置不存在"));
}
/**
* 获取并检查支付配置
*/
public UnionPayConfig getAndCheckConfig() {
UnionPayConfig unionPayConfig = this.getConfig();
if (!unionPayConfig.getEnable()){
throw new PayFailureException("云闪付支付未启用");
}
return unionPayConfig;
}
}

View File

@@ -1,10 +1,30 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
import cn.bootx.platform.daxpay.code.PayWayEnum;
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.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 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.UnifiedOrderModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Optional;
/**
* 云闪付支付
* @author xxm
* @since 2022/3/11
*/
@@ -13,4 +33,59 @@ import org.springframework.stereotype.Service;
@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");
}
/**
* 支付前检查支付方式是否可用
*/
public void validation(PayChannelParam payChannelParam, UnionPayConfig unionPayConfig) {
if (CollUtil.isEmpty(unionPayConfig.getPayWays())){
throw new PayFailureException("云闪付未配置可用的支付方式");
}
// 发起的支付类型是否在支持的范围内
PayWayEnum payWayEnum = Optional.ofNullable(AliPayWay.findByCode(payChannelParam.getWay()))
.orElseThrow(() -> new PayFailureException("非法的云闪付支付类型"));
if (!unionPayConfig.getPayWays().contains(payWayEnum.getCode())) {
throw new PayFailureException("该云闪付支付方式不可用");
}
}
}

View File

@@ -1,16 +0,0 @@
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 2022/3/11
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UnionPaymentService {
}

View File

@@ -63,7 +63,7 @@ public class WeChatPayConfigService {
public WeChatPayConfig getAndCheckConfig(){
WeChatPayConfig weChatPayConfig = getConfig();
if (!weChatPayConfig.getEnable()){
throw new PayFailureException("微信支付配置未启用");
throw new PayFailureException("微信支付未启用");
}
return weChatPayConfig;
}

View File

@@ -1,12 +1,28 @@
package cn.bootx.platform.daxpay.service.core.payment.pay.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
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.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.UnionPayService;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
import cn.bootx.platform.daxpay.service.func.AbsPayStrategy;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.util.Map;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -21,14 +37,74 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@RequiredArgsConstructor
public class UnionPayStrategy extends AbsPayStrategy {
private final PayChannelOrderService channelOrderService;
private final UnionPayService unionPayService;
private final UnionPayConfigService unionPayConfigService;
private UnionPayParam unionPayParam;
private UnionPayConfig unionPayConfig;
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.UNION_PAY;
}
/**
* 支付前操作
*/
@Override
public void doBeforePayHandler() {
try {
// 支付宝参数验证
Map<String, Object> channelParam = this.getPayChannelParam().getChannelParam();
if (CollUtil.isNotEmpty(channelParam)) {
this.unionPayParam = BeanUtil.toBean(channelParam, UnionPayParam.class);
}
else {
this.unionPayParam = new UnionPayParam();
}
}
catch (JSONException e) {
throw new PayFailureException("支付参数错误");
}
// 检查金额
PayChannelParam payMode = this.getPayChannelParam();
if (payMode.getAmount() <= 0) {
throw new PayAmountAbnormalException();
}
// 检查并获取支付宝支付配置
this.unionPayConfig = unionPayConfigService.getAndCheckConfig();
unionPayService.validation(this.getPayChannelParam(), unionPayConfig);
}
/**
* 发起支付操作
*/
@Override
public void doPayHandler() {
unionPayService.pay(this.getOrder(), this.getPayChannelParam(), this.unionPayParam, this.unionPayConfig);
}
/**
* 不使用默认的生成通道支付单方法, 异步支付通道的支付订单自己管理
* channelOrderService.switchAsyncPayChannel 进行切换
*/
@Override
public void generateChannelOrder() {
}
/**
* 支付调起成功, 保存或更新通道支付订单
*/
@Override
public void doSuccessHandler() {
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
PayChannelOrder payChannelOrder = channelOrderService.switchAsyncPayChannel(this.getOrder(), this.getPayChannelParam());
// 支付完成, 保存记录
if (asyncPayInfo.isPayComplete()) {
// aliRecordService.pay(this.getOrder(), payChannelOrder);
}
}