feat 云闪付支付对接

This commit is contained in:
xxm1995
2024-03-11 18:47:01 +08:00
parent c5ab2fe9b0
commit 3c2deabfdf
28 changed files with 252 additions and 194 deletions

View File

@@ -126,6 +126,7 @@ public class SimplePayOrderTest {
.serviceUrl("http://127.0.0.1:9000")
// 需要跟网关中配置一致
.signSecret("123456")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}

View File

@@ -1,9 +1,9 @@
2.0.3:
- [ ] 云闪付接入
- [ ] 支付
- [ ] 退款
- [ ] 同步
- [x] 支付
- [x] 退款
- [x] 同步
- [ ] 对账
- [ ] 回调
- [ ] 统一关闭接口增加使用撤销关闭订单

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum;
import cn.bootx.platform.daxpay.sdk.model.pay.PayCloseModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
@@ -22,6 +23,7 @@ public class PayCloseOrderTest {
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum;
import cn.bootx.platform.daxpay.sdk.model.sync.PaySyncModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
@@ -22,6 +23,7 @@ public class PayOrderSyncTest {
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}

View File

@@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import cn.bootx.platform.daxpay.sdk.code.PayWayEnum;
import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum;
import cn.bootx.platform.daxpay.sdk.model.pay.PayOrderModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
@@ -30,6 +31,7 @@ public class PayOrderTest {
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum;
import cn.bootx.platform.daxpay.sdk.model.sync.RefundSyncModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
@@ -22,6 +23,7 @@ public class RefundOrderSyncTest {
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum;
import cn.bootx.platform.daxpay.sdk.model.refund.RefundModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
@@ -29,6 +30,7 @@ public class RefundOrderTest {
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}

View File

@@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import cn.bootx.platform.daxpay.sdk.code.PayWayEnum;
import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum;
import cn.bootx.platform.daxpay.sdk.model.pay.PayOrderModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
@@ -24,6 +25,7 @@ public class SimplePayOrderTest {
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.sdk.payment;
import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum;
import cn.bootx.platform.daxpay.sdk.model.refund.RefundModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
@@ -26,6 +27,7 @@ public class SimpleRefundOrderTest {
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.sdk.query;
import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum;
import cn.bootx.platform.daxpay.sdk.model.pay.QueryPayOrderModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
@@ -21,6 +22,7 @@ public class QueryPayOrderTest {
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.sdk.query;
import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum;
import cn.bootx.platform.daxpay.sdk.model.refund.QueryRefundOrderModel;
import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
@@ -21,6 +22,7 @@ public class QueryRefundOrderTest {
DaxPayConfig config = DaxPayConfig.builder()
.serviceUrl("http://127.0.0.1:9000")
.signSecret("123456")
.signType(SignTypeEnum.HMAC_SHA256)
.build();
DaxPayKit.initConfig(config);
}

View File

@@ -11,9 +11,11 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public enum RefundSyncStatusEnum {
SUCCESS("refund_success","退款成功"),
FAIL("refund_fail","退款失败"),
PROGRESS("refund_progress","退款中");
PROGRESS("refund_progress","退款中"),
NOT_FOUND("pay_not_found", "交易不存在");
/** 编码 */
private final String code;

View File

@@ -1,5 +1,7 @@
package cn.bootx.platform.daxpay.service.code;
import com.egzosn.pay.union.bean.SDKConstants;
/**
* 云闪付常量
* @author xxm
@@ -11,21 +13,29 @@ public interface UnionPayCode {
/** 成功状态 */
String SUCCESS = "0";
/** 状态 0表示成功 */
String STATUS = "status";
/** 状态 00表示成功 */
String RESP_CODE = SDKConstants.param_respCode;
/** 业务结果 0表示成功非0表示失败 */
String RESULT_CODE = "result_code";
/** 业务结果 00表示成功 */
String RESP_SUCCESS = SDKConstants.OK_RESP_CODE;
/** 交易类型 */
String TXN_TYPE = "txnType";
/** 网关订单号 */
String QUERY_ID = "queryId";
/** 第三方订单号 */
/** 第三方订单号(本地订单号) */
String ORDER_ID = "orderId";
/** 退款ID */
String REFUND_ID = "refund_id";
/** 交易类型 支付 */
String TXN_TYPE_PAY = "01";
/** 交易类型 退款 */
String TXN_TYPE_REFUND = "04";
/**
* 订单发送时间
@@ -33,35 +43,13 @@ public interface UnionPayCode {
*/
String TXN_TIME = "txnTime";
/** 支付结果 */
String PAY_RESULT = "pay_result";
/** 退款金额 */
String TXN_AMT = "txnAmt";
/** 总金额 */
String TOTAL_FEE = "settleAmt";
/** 交易状态 */
String TRADE_STATE = "trade_state";
/** 支付成功 */
String TRADE_SUCCESS = "SUCCESS";
/** 转入退款 */
String TRADE_REFUND = "REFUND";
/** 未支付 */
String TRADE_NOT_PAY = "NOTPAY";
/** 已关闭 */
String TRADE_CLOSED = "CLOSED";
/** 支付失败(其他原因,如银行返回失败) */
String TRADE_PAY_ERROR = "PAYERROR";
/** 返回信息 */
String MESSAGE = "message";
/** 错误代码描述 */
String ERR_MSG = "err_msg";
/** 对账单下载类型编码 */

View File

@@ -3,17 +3,19 @@ package cn.bootx.platform.daxpay.service.core.channel.union.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
import cn.bootx.platform.daxpay.service.code.UnionPayCode;
import cn.bootx.platform.daxpay.service.common.context.CallbackLocal;
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.func.AbsCallbackStrategy;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.egzosn.pay.common.bean.NoticeParams;
import com.ijpay.core.kit.WxPayKit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -22,7 +24,6 @@ import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
@@ -49,21 +50,9 @@ public class UnionPayCallbackService extends AbsCallbackStrategy {
String callReq = JSONUtil.toJsonStr(params);
log.info("云闪付发起回调 报文: {}", callReq);
String status = params.get(STATUS);
String returnCode = params.get(RESULT_CODE);
// 处理失败
if (!Objects.equals(SUCCESS, status)||!Objects.equals(SUCCESS, returnCode)){
return false;
}
// 支付回调信息校验
UnionPayConfig config = unionPayConfigService.getConfig();
UnionPayKit unionPayKit = unionPayConfigService.initPayService(config);
if (Objects.isNull(config)) {
log.warn("云闪付支付配置不存在");
return false;
}
NoticeParams noticeParams = new NoticeParams();
noticeParams.setBody(Collections.unmodifiableMap(params));
@@ -71,13 +60,20 @@ public class UnionPayCallbackService extends AbsCallbackStrategy {
}
/**
* 判断类型 支付回调/退款回调, 云闪付只有支付回调
* 判断类型 支付回调/退款回调
*
* @see PaymentTypeEnum
*/
@Override
public PaymentTypeEnum getCallbackType() {
return PaymentTypeEnum.PAY;
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
Map<String, String> params = callbackInfo.getCallbackParam();
String txnType = params.get(TXN_TYPE);
if (UnionPayCode.TXN_TYPE_PAY.equals(txnType)){
return PaymentTypeEnum.PAY;
} else {
return PaymentTypeEnum.REFUND;
}
}
/**
@@ -93,10 +89,12 @@ public class UnionPayCallbackService extends AbsCallbackStrategy {
// 支付订单ID
callbackInfo.setOrderId(Long.valueOf(callbackParam.get(ORDER_ID)));
// 支付结果
PayStatusEnum payStatus = WxPayKit.codeIsOk(callbackParam.get(PAY_RESULT)) ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL;
String resultCode = callbackParam.get(UnionPayCode.RESP_CODE);
PayStatusEnum payStatus = UnionPayCode.RESP_SUCCESS.equals(resultCode) ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL;
callbackInfo.setGatewayStatus(payStatus.getCode());
// 支付金额
callbackInfo.setAmount(callbackParam.get(TOTAL_FEE));
callbackInfo.setAmount(callbackParam.get(UnionPayCode.TXN_AMT));
String timeEnd = callbackParam.get(TXN_TIME);
if (StrUtil.isNotBlank(timeEnd)) {
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
@@ -111,6 +109,31 @@ public class UnionPayCallbackService extends AbsCallbackStrategy {
*/
@Override
public void resolveRefundData() {
// 云闪付需要延迟半秒再进行处理, 不然会出现业务未处理完, 但回调已经到达的情况
ThreadUtil.sleep(300);
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
Map<String, String> callbackParam = callbackInfo.getCallbackParam();
// 网关订单号
callbackInfo.setGatewayOrderNo(callbackParam.get(QUERY_ID));
// 退款订单Id
callbackInfo.setOrderId(Long.valueOf(callbackParam.get(ORDER_ID)));
// 退款金额
callbackInfo.setAmount(callbackParam.get(TXN_AMT));
// 交易状态
String resultCode = callbackParam.get(UnionPayCode.RESP_CODE);
RefundStatusEnum refundStatus = UnionPayCode.RESP_SUCCESS.equals(resultCode) ? RefundStatusEnum.SUCCESS : RefundStatusEnum.FAIL;
callbackInfo.setGatewayStatus(refundStatus.getCode());
// 退款时间
String timeEnd = callbackParam.get(TXN_TIME);
if (StrUtil.isNotBlank(timeEnd)) {
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
callbackInfo.setFinishTime(time);
} else {
callbackInfo.setFinishTime(LocalDateTime.now());
}
}

View File

@@ -93,19 +93,19 @@ public class UnionPayConfigService {
unionPayConfigStorage.setInputCharset(CharsetUtil.UTF_8);
// 商户号
unionPayConfigStorage.setMerId(config.getMachId());
//是否为证书签名
unionPayConfigStorage.setCertSign(config.isCertSign());
// 云闪付必须使用证书才可以进行调用
unionPayConfigStorage.setCertSign(true);
//中级证书 流
// 中级证书 流
unionPayConfigStorage.setAcpMiddleCert(new ByteArrayInputStream(Base64.decode(config.getAcpMiddleCert())));
//根证书 流
// 根证书 流
unionPayConfigStorage.setAcpRootCert(new ByteArrayInputStream(Base64.decode(config.getAcpRootCert())));
// 私钥证书 流
unionPayConfigStorage.setKeyPrivateCert(new ByteArrayInputStream(Base64.decode(config.getKeyPrivateCert())));
//私钥证书对应的密码 私钥证书对应的密码
unionPayConfigStorage.setKeyPrivateCertPwd(config.getKeyPrivateCertPwd());
//设置证书对应的存储方式,证书字符串信息
//设置证书对应的存储方式,证书
unionPayConfigStorage.setCertStoreType(CertStoreType.INPUT_STREAM);
// 回调地址

View File

@@ -1,25 +1,20 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.code.UnionPayCode;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionRefundOrder;
import cn.hutool.core.util.StrUtil;
import com.egzosn.pay.union.bean.UnionRefundResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
/**
* 云闪付退款操作
@@ -34,46 +29,22 @@ public class UnionPayRefundService {
/**
* 退款方法
*/
public void refund(RefundOrder refundOrder, int amount, PayChannelOrder channelOrder, UnionPayKit unionPayKit) {
public void refund(RefundOrder refundOrder, PayOrder payOrder, int amount, PayChannelOrder channelOrder, UnionPayKit unionPayKit) {
// 金额转换
BigDecimal refundAmount = BigDecimal.valueOf(amount * 0.01);
BigDecimal orderAmount = BigDecimal.valueOf(channelOrder.getAmount() * 0.01);
UnionRefundOrder unionRefundOrder = new UnionRefundOrder();
unionRefundOrder.setOutTradeNo(String.valueOf(refundOrder.getPaymentId()));
unionRefundOrder.setRefundNo(String.valueOf(refundOrder.getId()));
unionRefundOrder.setTradeNo(String.valueOf(payOrder.getGatewayOrderNo()));
unionRefundOrder.setRefundAmount(refundAmount);
unionRefundOrder.setTotalAmount(orderAmount);
UnionRefundResult refund = unionPayKit.refund(unionRefundOrder);
// String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params);
// Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
// this.verifyErrorMsg(result);
// // 云闪付退款是否成功需要查询状态, 所以设置为退款中状态
// RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
// refundInfo.setStatus(RefundStatusEnum.PROGRESS)
// .setGatewayOrderNo(result.get(REFUND_ID));
}
/**
* 验证错误信息
*/
private void verifyErrorMsg(Map<String, String> result) {
String status = result.get(UnionPayCode.STATUS);
String returnCode = result.get(UnionPayCode.RESULT_CODE);
// 判断查询是否成功
if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){
String errorMsg = result.get(ERR_MSG);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(MESSAGE);
}
log.error("订单退款失败 {}", errorMsg);
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setErrorMsg(errorMsg);
refundInfo.setErrorCode(Optional.ofNullable(returnCode).orElse(returnCode));
throw new PayFailureException(errorMsg);
}
String gatewayNo = (String) refund.getAttr(UnionPayCode.QUERY_ID);
// 云闪付退款是否成功需要查询状态, 所以设置为退款中状态
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setStatus(RefundStatusEnum.PROGRESS).setGatewayOrderNo(gatewayNo);
}
}

View File

@@ -5,7 +5,6 @@ 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.code.UnionPayCode;
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;
@@ -14,7 +13,6 @@ import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionPayOrder;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -22,11 +20,8 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
/**
* 云闪付支付
* @author xxm
@@ -163,23 +158,4 @@ public class UnionPayService {
// String xmlResult = UnionPayApi.execution(unionPayKit.getServerUrl(), params);
}
/**
* 验证错误信息
*/
private void verifyErrorMsg(Map<String, String> result) {
String status = result.get(UnionPayCode.STATUS);
String returnCode = result.get(UnionPayCode.RESULT_CODE);
// 判断查询是否成功
if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){
String errorMsg = result.get(ERR_MSG);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(MESSAGE);
}
log.error("订单关闭失败 {}", errorMsg);
throw new PayFailureException(errorMsg);
}
}
}

View File

@@ -9,10 +9,8 @@ import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.payment.sync.result.PayGatewaySyncResult;
import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionRefundOrder;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.egzosn.pay.common.bean.AssistOrder;
import com.egzosn.pay.common.bean.NoticeParams;
@@ -25,7 +23,8 @@ import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.QUERY_ID;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.TXN_TIME;
/**
* 云闪付支付同步
@@ -44,6 +43,7 @@ public class UnionPaySyncService {
PayGatewaySyncResult syncResult = new PayGatewaySyncResult().setSyncStatus(PaySyncStatusEnum.FAIL);
AssistOrder query = new AssistOrder();
// query.setOutTradeNo("1766811070348001280");
query.setOutTradeNo(String.valueOf(order.getId()));
Map<String, Object> result = unionPayKit.query(query);
@@ -53,24 +53,29 @@ public class UnionPaySyncService {
return syncResult.setErrorMsg("查询订单验签失败");
}
String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE);
// 订单不存在
if (Objects.equals(resultCode, "34")) {
return syncResult.setSyncStatus(PaySyncStatusEnum.NOT_FOUND);
}
// 查询失败
String resultCode = MapUtil.getStr(result, SDKConstants.param_respCode);
if (!SDKConstants.OK_RESP_CODE.equals(resultCode)) {
if (!UnionPayCode.RESP_SUCCESS.equals(resultCode)) {
log.warn("查询云闪付订单失败:{}", result);
return syncResult.setErrorMsg(MapUtil.getStr(result, SDKConstants.param_respMsg));
}
// 状态响应码
String origRespCode = MapUtil.getStr(result, SDKConstants.param_origRespCode);
// 查询流水号, 相当于网关订单号
// 成功
if (Objects.equals(origRespCode, SDKConstants.OK_RESP_CODE)) {
if (Objects.equals(origRespCode, UnionPayCode.RESP_SUCCESS)) {
// 查询流水号, 相当于网关订单号
String queryId = MapUtil.getStr(result, QUERY_ID);
String timeEnd = MapUtil.getStr(result, TXN_TIME);
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
return syncResult.setGatewayOrderNo(queryId).setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS);
}
// 支付超时 交易不在受理时间范围内
if (Objects.equals(origRespCode, "39")) {
return syncResult.setSyncStatus(PaySyncStatusEnum.TIMEOUT)
@@ -81,74 +86,55 @@ public class UnionPaySyncService {
if (Objects.equals(origRespCode, "05")) {
return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS);
}
//
// // 已退款/退款中
// if (Objects.equals(tradeStatus, TRADE_REFUND)) {
// return syncResult.setSyncStatus(PaySyncStatusEnum.REFUND);
// }
// // 已关闭
// if (Objects.equals(tradeStatus, TRADE_CLOSED)) {
// return syncResult.setSyncStatus(PaySyncStatusEnum.CLOSED);
// }
return syncResult;
}
/**
* 退款信息查询
* 云闪付退款和支付查询接口是一个
*/
public RefundGatewaySyncResult syncRefundStatus(RefundOrder refundOrder, UnionPayKit unionPayKit){
RefundGatewaySyncResult syncResult = new RefundGatewaySyncResult();
UnionRefundOrder query = new UnionRefundOrder();
query.setRefundNo(String.valueOf(refundOrder.getId()));
Map<String, Object> results = unionPayKit.refundquery(query);
AssistOrder query = new AssistOrder();
query.setOutTradeNo(String.valueOf(refundOrder.getId()));
Map<String, Object> result = unionPayKit.query(query);
try {
// String xmlResult = WxPayApi.orderRefundQuery(false, params);
// Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
// syncResult.setSyncInfo(JSONUtil.toJsonStr(result));
if (!unionPayKit.verify(new NoticeParams(result))) {
log.warn("查询云闪付订单验签失败:{}", result);
return syncResult.setErrorMsg("查询订单验签失败");
}
String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE);
// 设置微信支付网关订单号
// syncResult.setGatewayOrderNo(result.get(UnionPayCode.REFUND_ID));
// 状态
// String tradeStatus = result.get(UnionPayCode.REFUND_STATUS);
// // 退款成功
// if (Objects.equals(tradeStatus, UnionPayCode.REFUND_SUCCESS)) {
// String timeEnd = result.get(UnionPayCode.REFUND_SUCCESS_TIME);
// LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.NORM_DATETIME_PATTERN);
// return syncResult.setRefundTime(time).setSyncStatus(RefundSyncStatusEnum.SUCCESS);
// }
// // 退款中
// if (Objects.equals(tradeStatus, UnionPayCode.REFUND_PROCESSING)) {
// return syncResult.setSyncStatus(RefundSyncStatusEnum.PROGRESS);
// }
// String errorMsg = this.getErrorMsg(result);
// return syncResult.setSyncStatus(RefundSyncStatusEnum.FAIL).setErrorMsg(errorMsg);
} catch (Exception e) {
log.error("查询退款订单失败:", e);
syncResult.setSyncStatus(RefundSyncStatusEnum.PROGRESS).setErrorMsg(e.getMessage());
// 订单不存在
if (Objects.equals(resultCode, "34")) {
return syncResult.setSyncStatus(RefundSyncStatusEnum.NOT_FOUND);
}
// 查询失败
if (!UnionPayCode.RESP_SUCCESS.equals(resultCode)) {
log.warn("查询云闪付订单失败:{}", result);
return syncResult.setErrorMsg(MapUtil.getStr(result, SDKConstants.param_respMsg));
}
// 状态响应码
String origRespCode = MapUtil.getStr(result, SDKConstants.param_origRespCode);
// 成功
if (Objects.equals(origRespCode, UnionPayCode.RESP_SUCCESS)) {
// 查询流水号, 相当于网关订单号
String queryId = MapUtil.getStr(result, QUERY_ID);
String timeEnd = MapUtil.getStr(result, TXN_TIME);
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
return syncResult.setGatewayOrderNo(queryId).setRefundTime(time).setSyncStatus(RefundSyncStatusEnum.SUCCESS);
}
// 退款中
if (Objects.equals(origRespCode, "05")) {
return syncResult.setSyncStatus(RefundSyncStatusEnum.PROGRESS);
}
return syncResult;
}
/**
* 验证错误信息
*/
private String getErrorMsg(Map<String, String> result) {
String status = result.get(UnionPayCode.STATUS);
String returnCode = result.get(UnionPayCode.RESULT_CODE);
// 判断查询是否成功
if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){
String errorMsg = result.get(ERR_MSG);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(MESSAGE);
}
log.error("订单查询失败 {}", errorMsg);
return errorMsg;
}
return null;
}
}

View File

@@ -3,6 +3,7 @@ package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
import cn.bootx.platform.daxpay.service.common.context.CallbackLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
@@ -126,9 +127,9 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy {
callbackInfo.setAmount(callbackParam.get(CALLBACK_REFUND_FEE));
// 交易状态
PayStatusEnum payStatus = Objects.equals(callbackParam.get(CALLBACK_REFUND_STATUS), REFUND_SUCCESS)
? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL;
callbackInfo.setGatewayStatus(payStatus.getCode());
RefundStatusEnum refundStatus = Objects.equals(callbackParam.get(CALLBACK_REFUND_STATUS), REFUND_SUCCESS)
? RefundStatusEnum.SUCCESS : RefundStatusEnum.FAIL;
callbackInfo.setGatewayStatus(refundStatus.getCode());
// 退款时间
String timeEnd = callbackParam.get(CALLBACK_SUCCESS_TIME);

View File

@@ -244,6 +244,7 @@ public class RefundAssistService {
refundOrder.setErrorMsg(refundInfo.getErrorMsg());
// 退款失败不保存剩余可退余额, 否则数据看起开会产生困惑
refundOrder.setRefundableBalance(null);
refundOrderManager.updateById(refundOrder);
}
}

View File

@@ -63,7 +63,7 @@ public class UnionRefundStrategy extends AbsRefundStrategy {
@Override
public void doRefundHandler() {
UnionPayKit unionPayKit = unionPayConfigService.initPayService(unionPayConfig);
unionPayRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), unionPayKit);
unionPayRefundService.refund(this.getRefundOrder(), this.getPayOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), unionPayKit);
}
/**

View File

@@ -1,12 +1,17 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayRecordService;
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -20,6 +25,7 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@RequiredArgsConstructor
public class UnionPayRepairStrategy extends AbsPayRepairStrategy {
private final UnionPayRecordService unionPayRecordService;
/**
* 策略标识
@@ -30,10 +36,31 @@ public class UnionPayRepairStrategy extends AbsPayRepairStrategy {
}
/**
* 取消支付
* 支付成功处理
*/
@Override
public void doPaySuccessHandler() {
LocalDateTime payTime = PaymentContextLocal.get()
.getRepairInfo()
.getFinishTime();
this.getChannelOrder().setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(payTime);
unionPayRecordService.pay(this.getOrder(), this.getChannelOrder());
}
/**
* 等待支付处理
*/
@Override
public void doWaitPayHandler(){
this.getChannelOrder().setPayTime(null).setStatus(PayStatusEnum.PROGRESS.getCode());
}
/**
* 关闭本地支付
*/
@Override
public void doCloseLocalHandler() {
this.getChannelOrder().setStatus(PayStatusEnum.CLOSE.getCode());
}
}

View File

@@ -3,6 +3,7 @@ package cn.bootx.platform.daxpay.service.core.payment.sync.factory;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.Refund.AliRefundSyncStrategy;
import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.Refund.UnionRefundSyncStrategy;
import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.Refund.WeChatRefundSyncStrategy;
import cn.bootx.platform.daxpay.service.func.AbsRefundSyncStrategy;
import cn.hutool.extra.spring.SpringUtil;
@@ -27,6 +28,9 @@ public class RefundSyncStrategyFactory {
case ALI:
strategy = SpringUtil.getBean(AliRefundSyncStrategy.class);
break;
case UNION_PAY:
strategy = SpringUtil.getBean(UnionRefundSyncStrategy.class);
break;
case WECHAT:
strategy = SpringUtil.getBean(WeChatRefundSyncStrategy.class);
break;

View File

@@ -181,7 +181,11 @@ public class PaySyncService {
}
// 退款比对状态不做额外处理, 需要通过退款接口进行处理
if (orderStatus.equals(PayStatusEnum.REFUNDED.getCode()) && syncStatus.equals(PaySyncStatusEnum.REFUND)){
List<String> orderClose = Arrays.asList(
PayStatusEnum.REFUNDED.getCode(),
PayStatusEnum.REFUNDING.getCode(),
PayStatusEnum.PARTIAL_REFUND.getCode());
if (orderClose.contains(orderStatus) || syncStatus.equals(PaySyncStatusEnum.REFUND)){
return true;
}
return false;

View File

@@ -170,18 +170,19 @@ public class RefundSyncService {
RefundRepairResult repair = new RefundRepairResult();
// 对支付网关同步的结果进行处理
switch (syncStatusEnum) {
// 调用出错
case SUCCESS:
repair = repairService.repair(order, RefundRepairWayEnum.REFUND_SUCCESS);
break;
case PROGRESS:
// 不进行处理
log.warn("退款状态同步接口调用出错");
break;
case FAIL: {
repair = repairService.repair(order, RefundRepairWayEnum.REFUND_FAIL);
break;
}
case NOT_FOUND:
repair = repairService.repair(order, RefundRepairWayEnum.REFUND_FAIL);
break;
default: {
throw new BizException("代码有问题");
}

View File

@@ -0,0 +1,49 @@
package cn.bootx.platform.daxpay.service.core.payment.sync.strategy.Refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
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.UnionPaySyncService;
import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult;
import cn.bootx.platform.daxpay.service.func.AbsRefundSyncStrategy;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
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 2024/3/11
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class UnionRefundSyncStrategy extends AbsRefundSyncStrategy {
private final UnionPaySyncService unionPaySyncService;
private final UnionPayConfigService unionPayConfigService;
/**
* 异步支付单与支付网关进行状态比对后的结果
*
* @see PaySyncStatusEnum
*/
@Override
public RefundGatewaySyncResult doSyncStatus() {
UnionPayConfig config = unionPayConfigService.getConfig();
UnionPayKit unionPayKit = unionPayConfigService.initPayService(config);
return unionPaySyncService.syncRefundStatus(this.getRefundOrder(),unionPayKit);
}
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.UNION_PAY;
}
}

View File

@@ -81,6 +81,11 @@ public class UnionPayKit extends UnionPayService {
if (null != certDescriptor) {
return this;
}
//
if (!payConfigStorage.isCertSign()){
return this;
}
try {
certDescriptor = new CertDescriptor();
certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivateCertInputStream(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12");
@@ -683,7 +688,7 @@ public class UnionPayKit extends UnionPayService {
*/
@Override
public Map<String, Object> refundquery(RefundOrder refundOrder) {
return Collections.emptyMap();
return this.query(refundOrder);
}
/**

View File

@@ -21,8 +21,6 @@
3. 您拥有使用本软件构建的网站全部内容所有权,并独立承担与这些内容的相关法律义务
4. 本软件由济南易杯光年软件技术有限公司进行分发,没有授权分销商,也没有任何分公司、代理商、办事处、经销商等销售和分发本产品
四、有限担保和免责声明
1. 本软件及所附带的文件是作为不提供任何明确的或隐含的赔偿或担保的形式提供的。
@@ -38,6 +36,9 @@
7. 如发现客户在使用产品时有任何的非法行为,有权解除授权停止技术支持,并配合有关机关进行调查或向政府部门举报。
8. 本软件由济南易杯光年软件技术有限公司进行分发,没有授权分销商,也没有任何分公司、代理商、办事处、经销商等销售和分发本产品
五、禁止接入的内容包括但不限于以下内容
1. 诈骗、BC严禁接入、赛车、V盘、X资金盘、贷款、P2P、汇兑、ICO、二清支付严禁接入
@@ -52,5 +53,5 @@
6. 微信/支付宝/银联官方要求禁止接入的、其他支付通道禁止接入的、以及所有国家法规规定禁止的一切情景
协议时间2023年01月01日
协议日期2023年01月01日
济南易杯光年软件技术有限公司