mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-11 15:15:33 +08:00
feat 云闪付相关接口对接
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package org.dromara.daxpay.channel.union.code;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 云闪付对账单字段
|
||||
* @author xxm
|
||||
* @since 2024/3/24
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum UnionReconcileFieldEnum {
|
||||
|
||||
/** 交易代码 */
|
||||
TRADE_TYPE(0, "tradeType"),
|
||||
/** 交易传输时间 MMDDhhmmss */
|
||||
txnTime(4, "txnTime"),
|
||||
/** 交易金额 */
|
||||
TXN_AMT(6, "txnAmt"),
|
||||
/** 查询流水号 */
|
||||
QUERY_ID(9, "queryId"),
|
||||
/** 商户订单号 */
|
||||
ORDER_ID(11, "orderId");
|
||||
|
||||
|
||||
|
||||
/** 序号 */
|
||||
private final int no;
|
||||
/** 字段名 */
|
||||
private final String filed;
|
||||
|
||||
/**
|
||||
* 根据序号查询
|
||||
*/
|
||||
public static UnionReconcileFieldEnum findByNo(int no){
|
||||
return Arrays.stream(values())
|
||||
.filter(o->o.no == no)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
package org.dromara.daxpay.channel.union.controller;
|
||||
|
||||
import cn.bootx.platform.core.annotation.IgnoreAuth;
|
||||
import org.dromara.daxpay.service.service.assist.PaymentAssistService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.dromara.daxpay.service.service.assist.PaymentAssistService;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -23,13 +19,20 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequiredArgsConstructor
|
||||
public class UnionPayCallbackController {
|
||||
|
||||
|
||||
private final PaymentAssistService paymentAssistService;
|
||||
|
||||
@Operation(summary = "银联回调")
|
||||
@PostMapping("/union")
|
||||
public String UnionPayNotify(@PathVariable("AppId") String appId, HttpServletRequest request) {
|
||||
paymentAssistService.initMchApp(appId);
|
||||
return "";
|
||||
}
|
||||
// @Operation(summary = "云闪付支付回调")
|
||||
// @PostMapping("/pay")
|
||||
// public String wechatPayNotify(@PathVariable("AppId") String appId, HttpServletRequest request) {
|
||||
// paymentAssistService.initMchApp(appId);
|
||||
// return payCallbackService.payHandle(request);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Operation(summary = "云闪付退款回调")
|
||||
// @PostMapping("/refund")
|
||||
// public String wechatRefundNotify(@PathVariable("AppId") String appId,HttpServletRequest request) {
|
||||
// paymentAssistService.initMchApp(appId);
|
||||
// return refundCallbackService.refundHandle(request);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.dromara.daxpay.channel.union.entity.reconcile;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 云闪付业务明细对账单
|
||||
* @author xxm
|
||||
* @since 2024/3/24
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class UnionReconcileBillDetail {
|
||||
|
||||
/** 关联对账订单ID */
|
||||
private String reconcileId;
|
||||
/** 交易代码 */
|
||||
private String tradeType;
|
||||
/** 代理机构标识码 */
|
||||
/** 发送机构标识码 */
|
||||
/** 系统跟踪号 */
|
||||
/** 交易传输时间 */
|
||||
private String txnTime;
|
||||
/** 帐号 */
|
||||
/** 交易金额 */
|
||||
private String txnAmt;
|
||||
/** 商户类别 */
|
||||
/** 终端类型 */
|
||||
/** 查询流水号 */
|
||||
private String queryId;
|
||||
/** 支付方式(旧) */
|
||||
/** 商户订单号 */
|
||||
private String orderId;
|
||||
/** 支付卡类型 */
|
||||
/** 原始交易的系统跟踪号 */
|
||||
/** 原始交易日期时间 */
|
||||
/** 商户手续费 */
|
||||
/** 结算金额 */
|
||||
/** 支付方式 */
|
||||
/** 集团商户代码 */
|
||||
/** 交易类型 */
|
||||
/** 交易子类 */
|
||||
/** 业务类型 */
|
||||
/** 帐号类型 */
|
||||
/** 账单类型 */
|
||||
/** 账单号码 */
|
||||
/** 交互方式 */
|
||||
/** 商户代码 */
|
||||
/** 分账入账方式 */
|
||||
/** 二级商户代码 */
|
||||
/** 二级商户简称 */
|
||||
/** 二级商户分账入账金额 */
|
||||
/** 清算净额 */
|
||||
/** 终端号 */
|
||||
/** 商户自定义域 */
|
||||
/** 优惠金额 */
|
||||
/** 发票金额 */
|
||||
/** 分期付款附加手续费 */
|
||||
/** 分期付款期数 */
|
||||
/** 交易介质 */
|
||||
/** 原始交易订单号 */
|
||||
/** 清算金额 */
|
||||
/** 服务点输入方式码 */
|
||||
/** 移动支付产品标志 */
|
||||
/** 交易代码 */
|
||||
/** 检索参考号 */
|
||||
/** 保留使用 */
|
||||
}
|
||||
@@ -51,7 +51,7 @@ public class UnionPayConfigService {
|
||||
return channelConfigManager.findById(id)
|
||||
.map(UnionPayConfig::convertConfig)
|
||||
.map(UnionPayConfig::toResult)
|
||||
.orElseThrow(() -> new ConfigNotEnableException("支付宝配置不存在"));
|
||||
.orElseThrow(() -> new ConfigNotEnableException("云闪付配置不存在"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,8 +84,8 @@ public class UnionPayConfigService {
|
||||
*/
|
||||
public void update(UnionPayConfigParam param){
|
||||
ChannelConfig channelConfig = channelConfigManager.findById(param.getId())
|
||||
.orElseThrow(() -> new ConfigNotEnableException("支付宝配置不存在"));
|
||||
// 通道配置 --转换--> 支付宝配置 ----> 从更新参数赋值 --转换--> 通道配置 ----> 保存更新
|
||||
.orElseThrow(() -> new ConfigNotEnableException("云闪付配置不存在"));
|
||||
// 通道配置 --转换--> 云闪付配置 ----> 从更新参数赋值 --转换--> 通道配置 ----> 保存更新
|
||||
UnionPayConfig unionPayConfig = UnionPayConfig.convertConfig(channelConfig);
|
||||
BeanUtil.copyProperties(param, unionPayConfig, CopyOptions.create().ignoreNullValue());
|
||||
ChannelConfig channelConfigParam = unionPayConfig.toChannelConfig();
|
||||
@@ -116,7 +116,7 @@ public class UnionPayConfigService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支步通知地址
|
||||
* 获取异步通知地址
|
||||
*/
|
||||
public String getNotifyUrl() {
|
||||
var mchAppInfo = PaymentContextLocal.get().getMchAppInfo();
|
||||
@@ -124,6 +124,24 @@ public class UnionPayConfigService {
|
||||
return StrUtil.format("{}/unipay/callback/{}/{}/union",platformInfo.getGatewayServiceUrl(),mchAppInfo.getAppId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付异步通知地址
|
||||
*/
|
||||
public String getPayNotifyUrl() {
|
||||
MchAppLocal mchAppInfo = PaymentContextLocal.get().getMchAppInfo();
|
||||
var platformInfo = platformConfigService.getConfig();
|
||||
return StrUtil.format("{}/unipay/callback/{}/{}/wechat/pay",platformInfo.getGatewayServiceUrl(),mchAppInfo.getAppId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取退款异步通知地址
|
||||
*/
|
||||
public String getRefundNotifyUrl() {
|
||||
MchAppLocal mchAppInfo = PaymentContextLocal.get().getMchAppInfo();
|
||||
var platformInfo = platformConfigService.getConfig();
|
||||
return StrUtil.format("{}/unipay/callback/{}/{}/wechat/refund",platformInfo.getGatewayServiceUrl(), mchAppInfo.getAppId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取同步通知地址
|
||||
*/
|
||||
@@ -136,14 +154,14 @@ public class UnionPayConfigService {
|
||||
/**
|
||||
* 生成云闪付支付接口
|
||||
*/
|
||||
public UnionPayKit initPayService(){
|
||||
UnionPayConfig config = this.getUnionPayConfig();
|
||||
return this.initPayService(config);
|
||||
public UnionPayKit initPayKit(){
|
||||
UnionPayConfig config = this.getAndCheckConfig();
|
||||
return this.initPayKit(config);
|
||||
}
|
||||
/**
|
||||
* 生成云闪付支付接口
|
||||
*/
|
||||
public UnionPayKit initPayService(UnionPayConfig config){
|
||||
public UnionPayKit initPayKit(UnionPayConfig config){
|
||||
|
||||
UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage();
|
||||
unionPayConfigStorage.setInputCharset(CharsetUtil.UTF_8);
|
||||
@@ -167,8 +185,6 @@ public class UnionPayConfigService {
|
||||
unionPayConfigStorage.setSignType(config.getSignType());
|
||||
//是否为测试账号,沙箱环境
|
||||
unionPayConfigStorage.setTest(config.isSandbox());
|
||||
// 回调地址
|
||||
unionPayConfigStorage.setNotifyUrl(getNotifyUrl());
|
||||
|
||||
// 网络请求配置
|
||||
HttpConfigStorage httpConfigStorage = new HttpConfigStorage();
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.dromara.daxpay.channel.union.param.pay.UnionPayParam;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayKit;
|
||||
import org.dromara.daxpay.channel.union.sdk.bean.UnionPayOrder;
|
||||
import org.dromara.daxpay.channel.union.sdk.bean.UnionTransactionType;
|
||||
import org.dromara.daxpay.channel.union.service.config.UnionPayConfigService;
|
||||
import org.dromara.daxpay.core.enums.PayMethodEnum;
|
||||
import org.dromara.daxpay.core.exception.AmountExceedLimitException;
|
||||
import org.dromara.daxpay.core.exception.TradeFailException;
|
||||
@@ -36,6 +37,8 @@ import java.util.Objects;
|
||||
@RequiredArgsConstructor
|
||||
public class UnionPayService {
|
||||
|
||||
private final UnionPayConfigService configService;
|
||||
|
||||
/**
|
||||
* 支付前检查支付方式是否可用
|
||||
*/
|
||||
@@ -91,23 +94,10 @@ public class UnionPayService {
|
||||
unionPayOrder.setPrice(amount);
|
||||
unionPayOrder.setExpirationTime(expiredTime);
|
||||
unionPayOrder.setTransactionType(type);
|
||||
unionPayOrder.setNotifyUrl(configService.getPayNotifyUrl());
|
||||
return unionPayKit.toPay(unionPayOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* jsapi支付
|
||||
*/
|
||||
private String b2bPay(BigDecimal amount, PayOrder payOrder, UnionPayKit unionPayKit) {
|
||||
Date expiredTime = DateUtil.date(payOrder.getExpiredTime());
|
||||
|
||||
UnionPayOrder unionPayOrder = new UnionPayOrder();
|
||||
unionPayOrder.setOutTradeNo(payOrder.getOrderNo());
|
||||
unionPayOrder.setSubject(payOrder.getTitle());
|
||||
unionPayOrder.setPrice(amount);
|
||||
unionPayOrder.setExpirationTime(expiredTime);
|
||||
unionPayOrder.setTransactionType(UnionTransactionType.B2B);
|
||||
return unionPayKit.toPay(unionPayOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* APP支付
|
||||
@@ -122,6 +112,7 @@ public class UnionPayService {
|
||||
unionPayOrder.setSubject(payOrder.getTitle());
|
||||
unionPayOrder.setPrice(amount);
|
||||
unionPayOrder.setExpirationTime(expiredTime);
|
||||
unionPayOrder.setNotifyUrl(configService.getPayNotifyUrl());
|
||||
|
||||
Map<String, Object> result = unionPayKit.app(unionPayOrder);
|
||||
String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE);
|
||||
@@ -147,6 +138,7 @@ public class UnionPayService {
|
||||
unionPayOrder.setSubject(payOrder.getTitle());
|
||||
unionPayOrder.setPrice(amount);
|
||||
unionPayOrder.setExpirationTime(expiredTime);
|
||||
unionPayOrder.setNotifyUrl(configService.getPayNotifyUrl());
|
||||
return unionPayKit.getQrPay(unionPayOrder);
|
||||
}
|
||||
|
||||
@@ -162,6 +154,7 @@ public class UnionPayService {
|
||||
unionPayOrder.setSubject(payOrder.getTitle());
|
||||
unionPayOrder.setPrice(amount);
|
||||
unionPayOrder.setExpirationTime(expiredTime);
|
||||
unionPayOrder.setNotifyUrl(configService.getPayNotifyUrl());
|
||||
Map<String, Object> result = unionPayKit.microPay(unionPayOrder);
|
||||
|
||||
if (!unionPayKit.verify(new NoticeParams(result))) {
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.dromara.daxpay.channel.union.service.reconcile;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 云闪付对账
|
||||
* @author xxm
|
||||
* @since 2024/3/7
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UnionPayReconcileService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package org.dromara.daxpay.channel.union.service.reconcile;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.compress.Deflate;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
|
||||
import org.dromara.daxpay.channel.union.code.UnionPayCode;
|
||||
import org.dromara.daxpay.channel.union.code.UnionReconcileFieldEnum;
|
||||
import org.dromara.daxpay.channel.union.entity.reconcile.UnionReconcileBillDetail;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayKit;
|
||||
import org.dromara.daxpay.channel.union.sdk.bean.SDKConstants;
|
||||
import org.dromara.daxpay.core.enums.TradeStatusEnum;
|
||||
import org.dromara.daxpay.core.enums.TradeTypeEnum;
|
||||
import org.dromara.daxpay.core.exception.OperationFailException;
|
||||
import org.dromara.daxpay.core.util.PayUtil;
|
||||
import org.dromara.daxpay.service.bo.reconcile.ChannelReconcileTradeBo;
|
||||
import org.dromara.daxpay.service.bo.reconcile.ReconcileResolveResultBo;
|
||||
import org.dromara.daxpay.service.entity.reconcile.ReconcileStatement;
|
||||
import org.dromara.x.file.storage.core.FileInfo;
|
||||
import org.dromara.x.file.storage.core.FileStorageService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.*;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 云闪付对账
|
||||
* @author xxm
|
||||
* @since 2024/3/7
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UnionReconcileService {
|
||||
|
||||
private final FileStorageService fileStorageService;
|
||||
|
||||
/**
|
||||
* 下载对账单
|
||||
*/
|
||||
public ReconcileResolveResultBo downAndResolve(ReconcileStatement statement, Date date, UnionPayKit unionPayKit){
|
||||
// 下载对账单
|
||||
Map<String, Object> map = unionPayKit.downloadBill(date, UnionPayCode.RECONCILE_BILL_TYPE);
|
||||
Object o = map.get(UnionPayCode.FILE_CONTENT);
|
||||
if (o == null) {
|
||||
log.warn("云闪付获取对账文件失败");
|
||||
throw new OperationFailException("云闪付获取对账文件失败");
|
||||
}
|
||||
String fileContent = o.toString();
|
||||
// 判断是否成功
|
||||
if (!SDKConstants.OK_RESP_CODE.equals(map.get(SDKConstants.param_respCode))) {
|
||||
log.warn("云闪付获取对账文件失败");
|
||||
throw new OperationFailException("云闪付获取对账文件失败");
|
||||
}
|
||||
|
||||
try {
|
||||
// 先解base64,再DEFLATE解压为zip流
|
||||
byte[] decode = Base64.decode(fileContent);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Deflate deflate = Deflate.of(new ByteArrayInputStream(decode), out, false);
|
||||
deflate.inflater();
|
||||
deflate.close();
|
||||
|
||||
// 读取zip文件, 解析出对账单内容
|
||||
byte[] zipBytes = out.toByteArray();
|
||||
ZipArchiveInputStream zipArchiveInputStream = new ZipArchiveInputStream(new ByteArrayInputStream(zipBytes),"GBK");
|
||||
ZipArchiveEntry entry;
|
||||
List<UnionReconcileBillDetail> billDetails = new ArrayList<>();
|
||||
byte[] bytes = null;
|
||||
while ((entry= zipArchiveInputStream.getNextZipEntry()) != null){
|
||||
if (StrUtil.startWith(entry.getName(), UnionPayCode.RECONCILE_FILE_PREFIX)){
|
||||
bytes = IoUtil.readBytes(zipArchiveInputStream);
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes),"GBK"));
|
||||
// 明细解析
|
||||
List<String> strings = IoUtil.readLines(bufferedReader, new ArrayList<>());
|
||||
billDetails = this.parseDetail(strings);
|
||||
}
|
||||
}
|
||||
// 保存原始对账文件
|
||||
String originalFile = this.saveOriginalFile(statement, bytes);
|
||||
// 解析账单文件
|
||||
var tradeBos = this.convertAndSave(billDetails, statement);
|
||||
return new ReconcileResolveResultBo()
|
||||
.setOriginalFileUrl(originalFile)
|
||||
.setChannelTrades(tradeBos);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析对账单明细
|
||||
*/
|
||||
private List<UnionReconcileBillDetail> parseDetail(List<String> list){
|
||||
return list.stream()
|
||||
.map(this::convertDetail)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析明细条目
|
||||
*/
|
||||
private UnionReconcileBillDetail convertDetail(String line){
|
||||
//解析的结果MAP,key为对账文件列序号,value为解析的值
|
||||
Map<String,String> zmDataMap = new HashMap<>();
|
||||
//左侧游标
|
||||
int leftIndex = 0;
|
||||
for(int i = 0; i < UnionPayCode.RECONCILE_BILL_SPLIT.length; i++){
|
||||
//右侧游标
|
||||
int rightIndex = leftIndex + UnionPayCode.RECONCILE_BILL_SPLIT[i];
|
||||
String filed = StrUtil.sub(line, leftIndex, rightIndex);
|
||||
leftIndex = rightIndex+1;
|
||||
// 映射到数据对象
|
||||
UnionReconcileFieldEnum fieldEnum = UnionReconcileFieldEnum.findByNo(i);
|
||||
if (Objects.nonNull(fieldEnum)){
|
||||
zmDataMap.put(fieldEnum.getFiled(), filed.trim());
|
||||
}
|
||||
}
|
||||
return BeanUtil.toBean(zmDataMap, UnionReconcileBillDetail.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为通用对账记录对象
|
||||
*/
|
||||
private List<ChannelReconcileTradeBo> convertAndSave(List<UnionReconcileBillDetail> billDetails, ReconcileStatement statement){
|
||||
// 只处理支付和退款的对账记录
|
||||
return billDetails.stream()
|
||||
.map(billDetail->this.convert(billDetail, statement))
|
||||
// 只处理支付和退款的对账记录
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为通用对账记录对象
|
||||
*/
|
||||
private ChannelReconcileTradeBo convert(UnionReconcileBillDetail billDetail, ReconcileStatement statement){
|
||||
// 金额
|
||||
String orderAmount = billDetail.getTxnAmt();
|
||||
int amount = Integer.parseInt(orderAmount);
|
||||
|
||||
// 默认为支付对账记录
|
||||
ChannelReconcileTradeBo reconcileTradeDetail = new ChannelReconcileTradeBo()
|
||||
.setTradeType(TradeTypeEnum.PAY.getCode())
|
||||
.setAmount(PayUtil.conversionAmount(amount))
|
||||
.setTradeStatus(TradeStatusEnum.SUCCESS.getCode())
|
||||
.setTradeNo(billDetail.getOrderId())
|
||||
.setOutTradeNo(billDetail.getQueryId());
|
||||
|
||||
// 时间, 从对账订单获取年份
|
||||
LocalDate date = statement.getDate();
|
||||
String year = LocalDateTimeUtil.format(date, DatePattern.NORM_YEAR_PATTERN);
|
||||
|
||||
String txnTime = year + billDetail.getTxnTime();
|
||||
if (StrUtil.isNotBlank(txnTime)) {
|
||||
LocalDateTime time = LocalDateTimeUtil.parse(txnTime, DatePattern.PURE_DATETIME_PATTERN);
|
||||
reconcileTradeDetail.setTradeTime(time);
|
||||
}
|
||||
|
||||
// 退款覆盖更新对应的字段
|
||||
if (Objects.equals(billDetail.getTradeType(), UnionPayCode.RECONCILE_TYPE_REFUND)){
|
||||
reconcileTradeDetail.setTradeType(TradeTypeEnum.REFUND.getCode());
|
||||
}
|
||||
return reconcileTradeDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存下载的原始对账文件
|
||||
*/
|
||||
private String saveOriginalFile(ReconcileStatement statement, byte[] bytes) {
|
||||
|
||||
String date = LocalDateTimeUtil.format(statement.getDate(), DatePattern.PURE_DATE_PATTERN);
|
||||
// 将原始文件进行保存 通道-日期
|
||||
String fileName = StrUtil.format("交易对账单-云闪付-{}.csv",date);
|
||||
var uploadPretreatment = fileStorageService.of(bytes);
|
||||
if (StrUtil.isNotBlank(fileName)) {
|
||||
uploadPretreatment.setOriginalFilename(fileName);
|
||||
}
|
||||
FileInfo upload = uploadPretreatment.upload();
|
||||
return upload.getUrl();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import org.dromara.daxpay.channel.union.code.UnionPayCode;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayKit;
|
||||
import org.dromara.daxpay.channel.union.sdk.bean.UnionRefundOrder;
|
||||
import org.dromara.daxpay.channel.union.sdk.bean.UnionRefundResult;
|
||||
import org.dromara.daxpay.channel.union.service.config.UnionPayConfigService;
|
||||
import org.dromara.daxpay.core.enums.RefundStatusEnum;
|
||||
import org.dromara.daxpay.service.bo.trade.RefundResultBo;
|
||||
import org.dromara.daxpay.service.entity.order.refund.RefundOrder;
|
||||
@@ -21,14 +22,14 @@ import java.math.BigDecimal;
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UnionPayRefundService {
|
||||
public class UnionRefundService {
|
||||
private final UnionPayConfigService unionPayConfigService;
|
||||
|
||||
/**
|
||||
* 退款方法
|
||||
*/
|
||||
public RefundResultBo refund(RefundOrder refundOrder, UnionPayKit unionPayKit) {
|
||||
|
||||
// 金额转换
|
||||
BigDecimal refundAmount = refundOrder.getAmount();
|
||||
BigDecimal orderAmount = refundOrder.getOrderAmount();
|
||||
|
||||
@@ -37,6 +38,7 @@ public class UnionPayRefundService {
|
||||
unionRefundOrder.setTradeNo(refundOrder.getOutOrderNo());
|
||||
unionRefundOrder.setRefundAmount(refundAmount);
|
||||
unionRefundOrder.setTotalAmount(orderAmount);
|
||||
unionRefundOrder.setNotifyUrl(unionPayConfigService.getRefundNotifyUrl());
|
||||
UnionRefundResult refund = unionPayKit.refund(unionRefundOrder);
|
||||
|
||||
String outRefundNo = (String) refund.getAttr(UnionPayCode.QUERY_ID);
|
||||
@@ -1,9 +1,25 @@
|
||||
package org.dromara.daxpay.channel.union.service.sync;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.daxpay.channel.union.code.UnionPayCode;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayKit;
|
||||
import org.dromara.daxpay.channel.union.sdk.bean.SDKConstants;
|
||||
import org.dromara.daxpay.core.enums.PayStatusEnum;
|
||||
import org.dromara.daxpay.service.bo.sync.PaySyncResultBo;
|
||||
import org.dromara.daxpay.service.entity.order.pay.PayOrder;
|
||||
import org.dromara.daxpay.unisdk.common.bean.AssistOrder;
|
||||
import org.dromara.daxpay.unisdk.common.bean.NoticeParams;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 云闪付支付同步
|
||||
* @author xxm
|
||||
@@ -13,5 +29,59 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UnionPaySyncService {
|
||||
/**
|
||||
* 同步
|
||||
*/
|
||||
public PaySyncResultBo sync(PayOrder order, UnionPayKit unionPayKit) {
|
||||
|
||||
PaySyncResultBo syncResult = new PaySyncResultBo();
|
||||
AssistOrder query = new AssistOrder();
|
||||
query.setOutTradeNo(order.getOrderNo());
|
||||
|
||||
Map<String, Object> result = unionPayKit.query(query);
|
||||
syncResult.setSyncData(JSONUtil.toJsonStr(result));
|
||||
if (!unionPayKit.verify(new NoticeParams(result))) {
|
||||
log.warn("查询云闪付订单验签失败:{}", result);
|
||||
return syncResult.setSyncSuccess(false).setSyncErrorMsg("查询订单验签失败");
|
||||
}
|
||||
|
||||
String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE);
|
||||
|
||||
// 订单不存在
|
||||
if (Objects.equals(resultCode, "34")) {
|
||||
return syncResult.setPayStatus(PayStatusEnum.CLOSE);
|
||||
}
|
||||
// 查询失败
|
||||
if (!UnionPayCode.RESP_SUCCESS.equals(resultCode)) {
|
||||
log.warn("查询云闪付订单失败:{}", result);
|
||||
return syncResult.setSyncErrorMsg(MapUtil.getStr(result, SDKConstants.param_respMsg)).setSyncSuccess(false);
|
||||
}
|
||||
// 状态响应码
|
||||
String origRespCode = MapUtil.getStr(result, SDKConstants.param_origRespCode);
|
||||
|
||||
// 成功
|
||||
if (Objects.equals(origRespCode, UnionPayCode.RESP_SUCCESS)) {
|
||||
// 查询流水号, 相当于网关订单号
|
||||
String queryId = MapUtil.getStr(result, UnionPayCode.QUERY_ID);
|
||||
String timeEnd = MapUtil.getStr(result, UnionPayCode.TXN_TIME);
|
||||
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
|
||||
return syncResult.setOutOrderNo(queryId).setFinishTime(time).setPayStatus(PayStatusEnum.SUCCESS);
|
||||
}
|
||||
|
||||
// 支付超时 交易不在受理时间范围内
|
||||
if (Objects.equals(origRespCode, "39")) {
|
||||
return syncResult.setPayStatus(PayStatusEnum.CLOSE);
|
||||
}
|
||||
|
||||
// 待支付
|
||||
if (Objects.equals(origRespCode, "05")) {
|
||||
return syncResult.setPayStatus(PayStatusEnum.PROGRESS);
|
||||
}
|
||||
|
||||
// 二维码支付无效
|
||||
if (Objects.equals(origRespCode, "86")) {
|
||||
return syncResult.setPayStatus(PayStatusEnum.CLOSE);
|
||||
}
|
||||
return syncResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.dromara.daxpay.channel.union.service.sync;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.daxpay.channel.union.code.UnionPayCode;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayKit;
|
||||
import org.dromara.daxpay.channel.union.sdk.bean.SDKConstants;
|
||||
import org.dromara.daxpay.core.enums.RefundStatusEnum;
|
||||
import org.dromara.daxpay.service.bo.sync.RefundSyncResultBo;
|
||||
import org.dromara.daxpay.service.entity.order.refund.RefundOrder;
|
||||
import org.dromara.daxpay.unisdk.common.bean.AssistOrder;
|
||||
import org.dromara.daxpay.unisdk.common.bean.NoticeParams;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 云闪付退款同步
|
||||
* @author xxm
|
||||
* @since 2024/10/9
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UnionRefundSyncService {
|
||||
|
||||
/**
|
||||
* 同步
|
||||
*/
|
||||
public RefundSyncResultBo sync(RefundOrder refundOrder, UnionPayKit unionPayKit) {
|
||||
RefundSyncResultBo syncResult = new RefundSyncResultBo();
|
||||
AssistOrder query = new AssistOrder();
|
||||
query.setOutTradeNo(String.valueOf(refundOrder.getRefundNo()));
|
||||
|
||||
Map<String, Object> result = unionPayKit.query(query);
|
||||
|
||||
if (!unionPayKit.verify(new NoticeParams(result))) {
|
||||
log.warn("查询云闪付订单验签失败:{}", result);
|
||||
return syncResult.setSyncErrorMsg("查询订单验签失败").setSyncSuccess(false);
|
||||
}
|
||||
String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE);
|
||||
|
||||
// 订单不存在
|
||||
if (Objects.equals(resultCode, "34")) {
|
||||
return syncResult.setRefundStatus(RefundStatusEnum.CLOSE);
|
||||
}
|
||||
|
||||
// 查询失败
|
||||
if (!UnionPayCode.RESP_SUCCESS.equals(resultCode)) {
|
||||
log.warn("查询云闪付订单失败:{}", result);
|
||||
return syncResult.setSyncErrorMsg(MapUtil.getStr(result, SDKConstants.param_respMsg)).setSyncSuccess(false);
|
||||
}
|
||||
|
||||
// 状态响应码
|
||||
String origRespCode = MapUtil.getStr(result, SDKConstants.param_origRespCode);
|
||||
|
||||
// 成功
|
||||
if (Objects.equals(origRespCode, UnionPayCode.RESP_SUCCESS)) {
|
||||
// 查询流水号, 相当于网关订单号
|
||||
String queryId = MapUtil.getStr(result, UnionPayCode.QUERY_ID);
|
||||
String timeEnd = MapUtil.getStr(result, UnionPayCode.TXN_TIME);
|
||||
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
|
||||
return syncResult.setOutRefundNo(queryId).setFinishTime(time).setRefundStatus(RefundStatusEnum.SUCCESS);
|
||||
}
|
||||
|
||||
// 退款中
|
||||
if (Objects.equals(origRespCode, "05")) {
|
||||
return syncResult.setRefundStatus(RefundStatusEnum.PROGRESS);
|
||||
}
|
||||
return syncResult;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,38 +3,44 @@ package org.dromara.daxpay.channel.union.strategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.daxpay.core.enums.ChannelEnum;
|
||||
import org.dromara.daxpay.service.bo.reconcile.ReconcileResolveResultBo;
|
||||
import org.dromara.daxpay.service.enums.ReconcileFileTypeEnum;
|
||||
import org.dromara.daxpay.service.strategy.AbsReconcileStrategy;
|
||||
import org.dromara.daxpay.core.enums.CloseTypeEnum;
|
||||
import org.dromara.daxpay.core.exception.UnsupportedAbilityException;
|
||||
import org.dromara.daxpay.service.strategy.AbsPayCloseStrategy;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
* 银联对账
|
||||
* 云闪付, 云闪付的关闭使用冲正
|
||||
* @author xxm
|
||||
* @since 2024/3/8
|
||||
* @since 2023/12/30
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Scope(SCOPE_PROTOTYPE)
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UnionPayReconcileStrategy extends AbsReconcileStrategy {
|
||||
public class UnionPayCloseStrategy extends AbsPayCloseStrategy {
|
||||
|
||||
@Override
|
||||
public ReconcileResolveResultBo uploadAndResolve(MultipartFile file, ReconcileFileTypeEnum fileType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReconcileResolveResultBo downAndResolve() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannel() {
|
||||
return ChannelEnum.UNION_PAY.getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭前的处理方式
|
||||
*/
|
||||
@Override
|
||||
public void doBeforeCloseHandler() {
|
||||
throw new UnsupportedAbilityException("云闪付没有关闭订单功能!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭操作
|
||||
*/
|
||||
@Override
|
||||
public CloseTypeEnum doCloseHandler() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import org.springframework.stereotype.Service;
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
* 云闪付
|
||||
* 云闪付支付
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2022/3/8
|
||||
@@ -71,7 +71,7 @@ public class UnionPayStrategy extends AbsPayStrategy {
|
||||
*/
|
||||
@Override
|
||||
public PayResultBo doPayHandler() {
|
||||
UnionPayKit unionPayKit = unionPayConfigService.initPayService();
|
||||
UnionPayKit unionPayKit = unionPayConfigService.initPayKit(unionPayConfig);
|
||||
return unionPayService.pay(this.getOrder(), this.unionPayParam, unionPayKit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.dromara.daxpay.channel.union.strategy;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.daxpay.channel.union.entity.config.UnionPayConfig;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayKit;
|
||||
import org.dromara.daxpay.channel.union.service.config.UnionPayConfigService;
|
||||
import org.dromara.daxpay.channel.union.service.reconcile.UnionReconcileService;
|
||||
import org.dromara.daxpay.core.enums.ChannelEnum;
|
||||
import org.dromara.daxpay.service.bo.reconcile.ReconcileResolveResultBo;
|
||||
import org.dromara.daxpay.service.enums.ReconcileFileTypeEnum;
|
||||
import org.dromara.daxpay.service.strategy.AbsReconcileStrategy;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
* 银联对账
|
||||
* @author xxm
|
||||
* @since 2024/3/8
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Scope(SCOPE_PROTOTYPE)
|
||||
@RequiredArgsConstructor
|
||||
public class UnionReconcileStrategy extends AbsReconcileStrategy {
|
||||
|
||||
private final UnionReconcileService reconcileService;
|
||||
|
||||
private final UnionPayConfigService unionPayConfigService;
|
||||
|
||||
private UnionPayConfig config;
|
||||
|
||||
/**
|
||||
* 对账前处理, 主要是初始化支付SDK配置
|
||||
*/
|
||||
@Override
|
||||
public void doBeforeHandler() {
|
||||
UnionPayConfig config = unionPayConfigService.getAndCheckConfig();
|
||||
// 测试环境使用测试号
|
||||
if (config.isSandbox()) {
|
||||
config.setUnionMachId("700000000000001");
|
||||
}
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReconcileResolveResultBo uploadAndResolve(MultipartFile file, ReconcileFileTypeEnum fileType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReconcileResolveResultBo downAndResolve() {
|
||||
Date date = DateUtil.date(this.getStatement().getDate());
|
||||
UnionPayKit unionPayKit = unionPayConfigService.initPayKit(config);
|
||||
return reconcileService.downAndResolve(this.getStatement(), date, unionPayKit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannel() {
|
||||
return ChannelEnum.UNION_PAY.getCode();
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package org.dromara.daxpay.channel.union.strategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayKit;
|
||||
import org.dromara.daxpay.channel.union.service.config.UnionPayConfigService;
|
||||
import org.dromara.daxpay.channel.union.service.refund.UnionPayRefundService;
|
||||
import org.dromara.daxpay.channel.union.service.refund.UnionRefundService;
|
||||
import org.dromara.daxpay.core.enums.ChannelEnum;
|
||||
import org.dromara.daxpay.service.bo.trade.RefundResultBo;
|
||||
import org.dromara.daxpay.service.strategy.AbsRefundStrategy;
|
||||
@@ -22,7 +22,7 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
|
||||
@RequiredArgsConstructor
|
||||
public class UnionRefundStrategy extends AbsRefundStrategy {
|
||||
|
||||
private final UnionPayRefundService unionPayRefundService;
|
||||
private final UnionRefundService unionRefundService;
|
||||
|
||||
private final UnionPayConfigService unionPayConfigService;
|
||||
|
||||
@@ -41,7 +41,7 @@ public class UnionRefundStrategy extends AbsRefundStrategy {
|
||||
*/
|
||||
@Override
|
||||
public RefundResultBo doRefundHandler() {
|
||||
UnionPayKit unionPayKit = unionPayConfigService.initPayService();
|
||||
return unionPayRefundService.refund(this.getRefundOrder(), unionPayKit);
|
||||
UnionPayKit unionPayKit = unionPayConfigService.initPayKit();
|
||||
return unionRefundService.refund(this.getRefundOrder(), unionPayKit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.dromara.daxpay.channel.union.strategy;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayKit;
|
||||
import org.dromara.daxpay.channel.union.service.config.UnionPayConfigService;
|
||||
import org.dromara.daxpay.channel.union.service.sync.UnionPaySyncService;
|
||||
import org.dromara.daxpay.core.enums.ChannelEnum;
|
||||
import org.dromara.daxpay.service.bo.sync.PaySyncResultBo;
|
||||
import org.dromara.daxpay.service.strategy.AbsSyncPayOrderStrategy;
|
||||
@@ -19,9 +22,14 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
|
||||
@RequiredArgsConstructor
|
||||
public class UnionSyncPayStrategy extends AbsSyncPayOrderStrategy {
|
||||
|
||||
private final UnionPayConfigService unionPayConfigService;
|
||||
|
||||
private final UnionPaySyncService unionPaySyncService;
|
||||
|
||||
@Override
|
||||
public PaySyncResultBo doSync() {
|
||||
return null;
|
||||
UnionPayKit unionPayKit = unionPayConfigService.initPayKit();
|
||||
return unionPaySyncService.sync(this.getOrder(),unionPayKit);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.dromara.daxpay.channel.union.strategy;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayKit;
|
||||
import org.dromara.daxpay.channel.union.service.config.UnionPayConfigService;
|
||||
import org.dromara.daxpay.channel.union.service.sync.UnionRefundSyncService;
|
||||
import org.dromara.daxpay.core.enums.ChannelEnum;
|
||||
import org.dromara.daxpay.service.bo.sync.RefundSyncResultBo;
|
||||
import org.dromara.daxpay.service.strategy.AbsSyncRefundOrderStrategy;
|
||||
@@ -18,9 +21,13 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class UnionSyncRefundStrategy extends AbsSyncRefundOrderStrategy {
|
||||
private final UnionPayConfigService unionPayConfigService;
|
||||
private final UnionRefundSyncService unionSyncRefundService;
|
||||
|
||||
@Override
|
||||
public RefundSyncResultBo doSync() {
|
||||
return null;
|
||||
UnionPayKit unionPayKit = unionPayConfigService.initPayKit();
|
||||
return unionSyncRefundService.sync(this.getRefundOrder(),unionPayKit);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user