提交 9e8d51da 编写于 作者: terrfly's avatar terrfly

添加退款补单和超时关闭; 添加查单控制器

上级 7e119aa2
......@@ -360,7 +360,7 @@ CREATE TABLE `t_refund_order` (
`pay_amount` BIGINT(20) NOT NULL COMMENT '支付金额,单位分',
`refund_amount` BIGINT(20) NOT NULL COMMENT '退款金额,单位分',
`currency` VARCHAR(3) NOT NULL DEFAULT 'cny' COMMENT '三位货币代码,人民币:cny',
`state` TINYINT(6) NOT NULL DEFAULT '0' COMMENT '退款状态:0-订单生成,1-退款中,2-退款成功,3-退款失败',
`state` TINYINT(6) NOT NULL DEFAULT '0' COMMENT '退款状态:0-订单生成,1-退款中,2-退款成功,3-退款失败,4-退款任务关闭',
`client_ip` VARCHAR(32) DEFAULT NULL COMMENT '客户端IP',
`refund_reason` VARCHAR(256) NOT NULL COMMENT '退款原因',
`channel_order_no` VARCHAR(32) DEFAULT NULL COMMENT '渠道订单号',
......@@ -370,6 +370,7 @@ CREATE TABLE `t_refund_order` (
`notify_url` VARCHAR(128) DEFAULT NULL COMMENT '通知地址',
`ext_param` VARCHAR(64) DEFAULT NULL COMMENT '扩展参数',
`success_time` DATETIME DEFAULT NULL COMMENT '订单退款成功时间',
`expired_time` DATETIME DEFAULT NULL COMMENT '退款失效时间(失效后系统更改为退款任务关闭状态)',
`created_at` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
`updated_at` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
PRIMARY KEY (`refund_order_id`),
......
......@@ -43,6 +43,7 @@ public class RefundOrder extends BaseModel {
public static final byte STATE_ING = 1; //退款中
public static final byte STATE_SUCCESS = 2; //退款成功
public static final byte STATE_FAIL = 3; //退款失败
public static final byte STATE_CLOSED = 4; //退款任务关闭
public static final LambdaQueryWrapper<RefundOrder> gw(){
return new LambdaQueryWrapper<>();
......@@ -122,7 +123,7 @@ public class RefundOrder extends BaseModel {
private String currency;
/**
* 退款状态:0-订单生成,1-退款中,2-退款成功,3-退款失败
* 退款状态:0-订单生成,1-退款中,2-退款成功,3-退款失败,4-退款任务关闭
*/
private Byte state;
......@@ -171,6 +172,11 @@ public class RefundOrder extends BaseModel {
*/
private Date successTime;
/**
* 退款失效时间(失效后系统更改为退款任务关闭状态)
*/
private Date expiredTime;
/**
* 创建时间
*/
......
......@@ -39,4 +39,7 @@ public interface IRefundService {
/** 调起退款接口,并响应数据; 内部处理普通商户和服务商模式 **/
ChannelRetMsg refund(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception;
/** 退款查单接口 **/
ChannelRetMsg query(RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext) throws Exception;
}
......@@ -56,6 +56,7 @@ public class AlipayKit {
else if(req instanceof AlipayTradeWapPayRequest) ((AlipayTradeWapPayRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
else if(req instanceof AlipayTradeQueryRequest) ((AlipayTradeQueryRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
else if(req instanceof AlipayTradeRefundRequest) ((AlipayTradeRefundRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
else if(req instanceof AlipayTradeFastpayRefundQueryRequest) ((AlipayTradeFastpayRefundQueryRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
// 服务商信息
ExtendParams extendParams = new ExtendParams();
......
......@@ -15,8 +15,11 @@
*/
package com.jeequan.jeepay.pay.channel.alipay;
import com.alipay.api.domain.AlipayTradeFastpayRefundQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
......@@ -82,5 +85,37 @@ public class AlipayRefundService extends AbstractRefundService {
return channelRetMsg;
}
@Override
public ChannelRetMsg query(RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
model.setTradeNo(refundOrder.getChannelPayOrderNo());
model.setOutTradeNo(refundOrder.getPayOrderId());
model.setOutRequestNo(refundOrder.getRefundOrderId());
request.setBizModel(model);
//统一放置 isv接口必传信息
AlipayKit.putApiIsvInfo(mchAppConfigContext, request, model);
AlipayTradeFastpayRefundQueryResponse response = mchAppConfigContext.getAlipayClientWrapper().execute(request);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
channelRetMsg.setChannelAttach(response.getBody());
channelRetMsg.setChannelOrderId(response.getTradeNo());
// 调用成功 & 金额相等 (传入不存在的outRequestNo支付宝仍然返回响应成功只是数据不存在, 调用isSuccess() 仍是成功, 此处需判断金额是否相等)
Long channelRefundAmount = response.getRefundAmount() == null ? null : Long.parseLong(AmountUtil.convertDollar2Cent(response.getRefundAmount()));
if(response.isSuccess() && refundOrder.getRefundAmount().equals(channelRefundAmount)){
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); //认为是处理中
}
return channelRetMsg;
}
}
......@@ -19,8 +19,8 @@ import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.ApiRes;
import com.jeequan.jeepay.pay.ctrl.ApiController;
import com.jeequan.jeepay.pay.rqrs.QueryPayOrderRQ;
import com.jeequan.jeepay.pay.rqrs.QueryPayOrderRS;
import com.jeequan.jeepay.pay.rqrs.payorder.QueryPayOrderRQ;
import com.jeequan.jeepay.pay.rqrs.payorder.QueryPayOrderRS;
import com.jeequan.jeepay.pay.service.ConfigContextService;
import com.jeequan.jeepay.service.impl.PayOrderService;
import lombok.extern.slf4j.Slf4j;
......
/*
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.ctrl.refund;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.ApiRes;
import com.jeequan.jeepay.pay.ctrl.ApiController;
import com.jeequan.jeepay.pay.rqrs.refund.QueryRefundOrderRQ;
import com.jeequan.jeepay.pay.rqrs.refund.QueryRefundOrderRS;
import com.jeequan.jeepay.pay.service.ConfigContextService;
import com.jeequan.jeepay.service.impl.RefundOrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 商户退款单查询controller
*
* @author terrfly
* @site https://www.jeepay.vip
* @date 2021/6/17 15:20
*/
@Slf4j
@RestController
public class QueryRefundOrderController extends ApiController {
@Autowired private RefundOrderService refundOrderService;
@Autowired private ConfigContextService configContextService;
/**
* 查单接口
* **/
@RequestMapping("/api/refund/query")
public ApiRes queryRefundOrder(){
//获取参数 & 验签
QueryRefundOrderRQ rq = getRQByWithMchSign(QueryRefundOrderRQ.class);
if(StringUtils.isAllEmpty(rq.getMchRefundNo(), rq.getRefundOrderId())){
throw new BizException("mchRefundNo 和 refundOrderId不能同时为空");
}
RefundOrder refundOrder = refundOrderService.queryMchOrder(rq.getMchNo(), rq.getMchRefundNo(), rq.getRefundOrderId());
if(refundOrder == null){
throw new BizException("订单不存在");
}
QueryRefundOrderRS bizRes = QueryRefundOrderRS.buildByRefundOrder(refundOrder);
return ApiRes.okWithSign(bizRes, configContextService.getMchAppConfigContext(rq.getMchNo(), rq.getAppId()).getMchApp().getAppSecret());
}
}
......@@ -15,6 +15,7 @@
*/
package com.jeequan.jeepay.pay.ctrl.refund;
import cn.hutool.core.date.DateUtil;
import com.jeequan.jeepay.core.entity.MchApp;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.entity.PayOrder;
......@@ -32,6 +33,7 @@ import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.pay.rqrs.refund.RefundOrderRQ;
import com.jeequan.jeepay.pay.rqrs.refund.RefundOrderRS;
import com.jeequan.jeepay.pay.service.ConfigContextService;
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
import com.jeequan.jeepay.service.impl.PayOrderService;
import com.jeequan.jeepay.service.impl.RefundOrderService;
import lombok.extern.slf4j.Slf4j;
......@@ -56,6 +58,7 @@ public class RefundOrderController extends ApiController {
@Autowired private PayOrderService payOrderService;
@Autowired private RefundOrderService refundOrderService;
@Autowired private ConfigContextService configContextService;
@Autowired private PayMchNotifyService payMchNotifyService;
/** 申请退款 **/
@PostMapping("/api/refund/refundOrder")
......@@ -69,7 +72,6 @@ public class RefundOrderController extends ApiController {
try {
if(StringUtils.isAllEmpty(rq.getMchOrderNo(), rq.getPayOrderId())){
throw new BizException("mchOrderNo 和 payOrderId不能同时为空");
}
......@@ -174,6 +176,7 @@ public class RefundOrderController extends ApiController {
private RefundOrder genRefundOrder(RefundOrderRQ rq, PayOrder payOrder, MchInfo mchInfo, MchApp mchApp){
Date nowTime = new Date();
RefundOrder refundOrder = new RefundOrder();
refundOrder.setRefundOrderId(SeqKit.genPayOrderId()); //退款订单号
refundOrder.setPayOrderId(payOrder.getPayOrderId()); //支付订单号
......@@ -198,8 +201,9 @@ public class RefundOrderController extends ApiController {
refundOrder.setChannelExtra(rq.getChannelExtra()); //特定渠道发起时额外参数
refundOrder.setNotifyUrl(rq.getNotifyUrl()); //通知地址
refundOrder.setExtParam(rq.getExtParam()); //扩展参数
refundOrder.setExpiredTime(DateUtil.offsetHour(nowTime, 2)); //订单超时关闭时间 默认两个小时
refundOrder.setSuccessTime(null); //订单退款成功时间
refundOrder.setCreatedAt(new Date()); //创建时间
refundOrder.setCreatedAt(nowTime); //创建时间
return refundOrder;
}
......@@ -219,7 +223,7 @@ public class RefundOrderController extends ApiController {
if(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == channelRetMsg.getChannelState()) {
this.updateInitOrderStateThrowException(RefundOrder.STATE_SUCCESS, refundOrder, channelRetMsg);
// payMchNotifyService.payOrderNotify(payOrder);
payMchNotifyService.refundOrderNotify(refundOrder);
//明确失败
}else if(ChannelRetMsg.ChannelState.CONFIRM_FAIL == channelRetMsg.getChannelState()) {
......@@ -235,7 +239,7 @@ public class RefundOrderController extends ApiController {
this.updateInitOrderStateThrowException(RefundOrder.STATE_ING, refundOrder, channelRetMsg);
// 系统异常: 退款单不再处理。 为: 生成状态
}else if( ChannelRetMsg.ChannelState.SYS_ERROR == channelRetMsg.getChannelState()){
}else if( ChannelRetMsg.ChannelState.SYS_ERROR == channelRetMsg.getChannelState() ){
}else{
......
......@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.rqrs;
package com.jeequan.jeepay.pay.rqrs.payorder;
import com.jeequan.jeepay.pay.rqrs.AbstractMchAppRQ;
import lombok.Data;
import javax.validation.constraints.NotBlank;
......@@ -27,7 +28,7 @@ import javax.validation.constraints.NotBlank;
* @date 2021/6/8 17:40
*/
@Data
public class QueryPayOrderRQ extends AbstractMchAppRQ{
public class QueryPayOrderRQ extends AbstractMchAppRQ {
/** 商户订单号 **/
private String mchOrderNo;
......
......@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.rqrs;
package com.jeequan.jeepay.pay.rqrs.payorder;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.pay.rqrs.AbstractRS;
import lombok.Data;
import org.springframework.beans.BeanUtils;
......@@ -27,7 +28,7 @@ import org.springframework.beans.BeanUtils;
* @date 2021/6/8 17:40
*/
@Data
public class QueryPayOrderRS extends AbstractRS{
public class QueryPayOrderRS extends AbstractRS {
/**
* 支付订单号
......
/*
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.rqrs.refund;
import com.jeequan.jeepay.pay.rqrs.AbstractMchAppRQ;
import lombok.Data;
/*
* 查询退款单请求参数对象
*
* @author terrfly
* @site https://www.jeepay.vip
* @date 2021/6/17 14:07
*/
@Data
public class QueryRefundOrderRQ extends AbstractMchAppRQ {
/** 商户退款单号 **/
private String mchRefundNo;
/** 支付系统退款订单号 **/
private String refundOrderId;
}
/*
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.rqrs.refund;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.pay.rqrs.AbstractRS;
import lombok.Data;
import org.springframework.beans.BeanUtils;
/*
* 查询退款单 响应参数
*
* @author terrfly
* @site https://www.jeepay.vip
* @date 2021/6/17 14:08
*/
@Data
public class QueryRefundOrderRS extends AbstractRS {
/**
* 退款订单号(支付系统生成订单号)
*/
private String refundOrderId;
/**
* 支付订单号(与t_pay_order对应)
*/
private String payOrderId;
/**
* 商户号
*/
private String mchNo;
/**
* 应用ID
*/
private String appId;
/**
* 商户退款单号(商户系统的订单号)
*/
private String mchRefundNo;
/**
* 支付金额,单位分
*/
private Long payAmount;
/**
* 退款金额,单位分
*/
private Long refundAmount;
/**
* 三位货币代码,人民币:cny
*/
private String currency;
/**
* 退款状态:0-订单生成,1-退款中,2-退款成功,3-退款失败
*/
private Byte state;
/**
* 渠道订单号
*/
private String channelOrderNo;
/**
* 渠道错误码
*/
private String channelErrCode;
/**
* 渠道错误描述
*/
private String channelErrMsg;
/**
* 特定渠道发起时额外参数
*/
private String channelExtra;
/**
* 扩展参数
*/
private String extParam;
/**
* 订单退款成功时间
*/
private Long successTime;
/**
* 创建时间
*/
private Long createdAt;
public static QueryRefundOrderRS buildByRefundOrder(RefundOrder refundOrder){
if(refundOrder == null){
return null;
}
QueryRefundOrderRS result = new QueryRefundOrderRS();
BeanUtils.copyProperties(refundOrder, result);
result.setSuccessTime(refundOrder.getSuccessTime() == null ? null : refundOrder.getSuccessTime().getTime());
result.setCreatedAt(refundOrder.getCreatedAt() == null ? null : refundOrder.getCreatedAt().getTime());
return result;
}
}
......@@ -15,10 +15,8 @@
*/
package com.jeequan.jeepay.pay.rqrs.refund;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.pay.rqrs.AbstractRS;
import com.jeequan.jeepay.pay.rqrs.QueryPayOrderRS;
import lombok.Data;
import org.springframework.beans.BeanUtils;
......
......@@ -16,11 +16,14 @@
package com.jeequan.jeepay.pay.service;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.pay.channel.IPayOrderQueryService;
import com.jeequan.jeepay.pay.channel.IRefundService;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.service.impl.PayOrderService;
import com.jeequan.jeepay.service.impl.RefundOrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -33,12 +36,14 @@ import org.springframework.stereotype.Service;
* @site https://www.jeepay.vip
* @date 2021/6/8 17:40
*/
@Service
@Slf4j
public class ChannelOrderReissueService {
@Autowired private ConfigContextService configContextService;
@Autowired private PayOrderService payOrderService;
@Autowired private RefundOrderService refundOrderService;
@Autowired private PayMchNotifyService payMchNotifyService;
......@@ -95,5 +100,58 @@ public class ChannelOrderReissueService {
}
/** 处理退款订单 **/
public ChannelRetMsg processRefundOrder(RefundOrder refundOrder){
try {
String refundOrderId = refundOrder.getRefundOrderId();
//查询支付接口是否存在
IRefundService queryService = SpringBeansUtil.getBean(refundOrder.getIfCode() + "RefundService", IRefundService.class);
// 支付通道接口实现不存在
if(queryService == null){
log.error("退款补单:{} interface not exists!", refundOrder.getIfCode());
return null;
}
//查询出商户应用的配置信息
MchAppConfigContext mchAppConfigContext = configContextService.getMchAppConfigContext(refundOrder.getMchNo(), refundOrder.getAppId());
ChannelRetMsg channelRetMsg = queryService.query(refundOrder, mchAppConfigContext);
if(channelRetMsg == null){
log.error("退款补单:channelRetMsg is null");
return null;
}
log.info("退款补单:[{}]查询结果为:{}", refundOrderId, channelRetMsg);
// 查询成功
if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
if (refundOrderService.updateIng2Success(refundOrderId, channelRetMsg.getChannelOrderId())) {
// 通知商户系统
if(StringUtils.isNotEmpty(refundOrder.getNotifyUrl())){
payMchNotifyService.refundOrderNotify(refundOrderService.getById(refundOrderId));
}
}
}else if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL){ //确认失败
//1. 更新支付订单表为失败状态
refundOrderService.updateIng2Fail(refundOrderId, channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelErrCode(), channelRetMsg.getChannelErrMsg());
}
return channelRetMsg;
} catch (Exception e) { //继续下一次迭代查询
log.error("退款补单:error refundOrderId = {}", refundOrder.getRefundOrderId(), e);
return null;
}
}
}
......@@ -16,14 +16,14 @@
package com.jeequan.jeepay.pay.service;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.entity.MchNotifyRecord;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.core.utils.StringKit;
import com.jeequan.jeepay.pay.mq.queue.MqQueue4PayOrderMchNotify;
import com.jeequan.jeepay.pay.rqrs.QueryPayOrderRS;
import com.jeequan.jeepay.service.impl.MchInfoService;
import com.jeequan.jeepay.pay.rqrs.payorder.QueryPayOrderRS;
import com.jeequan.jeepay.pay.rqrs.refund.QueryRefundOrderRS;
import com.jeequan.jeepay.service.impl.MchNotifyRecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
......@@ -91,6 +91,51 @@ public class PayMchNotifyService {
}
}
/** 商户通知信息,退款成功的发送通知 **/
public void refundOrderNotify(RefundOrder dbRefundOrder){
try {
// 通知地址为空
if(StringUtils.isEmpty(dbRefundOrder.getNotifyUrl())){
return ;
}
//获取到通知对象
MchNotifyRecord mchNotifyRecord = mchNotifyRecordService.findByRefundOrder(dbRefundOrder.getRefundOrderId());
if(mchNotifyRecord != null){
log.info("当前已存在通知消息, 不再发送。");
return ;
}
//商户app私钥
String appSecret = configContextService.getMchAppConfigContext(dbRefundOrder.getMchNo(), dbRefundOrder.getAppId()).getMchApp().getAppSecret();
// 封装通知url
String notifyUrl = createNotifyUrl(dbRefundOrder, appSecret);
mchNotifyRecord = new MchNotifyRecord();
mchNotifyRecord.setOrderId(dbRefundOrder.getRefundOrderId());
mchNotifyRecord.setOrderType(MchNotifyRecord.TYPE_REFUND_ORDER);
mchNotifyRecord.setMchNo(dbRefundOrder.getMchNo());
mchNotifyRecord.setMchOrderNo(dbRefundOrder.getMchRefundNo()); //商户订单号
mchNotifyRecord.setIsvNo(dbRefundOrder.getIsvNo());
mchNotifyRecord.setAppId(dbRefundOrder.getAppId());
mchNotifyRecord.setNotifyUrl(notifyUrl);
mchNotifyRecord.setResResult("");
mchNotifyRecord.setNotifyCount(0);
mchNotifyRecord.setState(MchNotifyRecord.STATE_ING); // 通知中
mchNotifyRecordService.save(mchNotifyRecord);
//推送到MQ
Long notifyId = mchNotifyRecord.getNotifyId();
mqPayOrderMchNotifyQueue.send(notifyId + "");
} catch (Exception e) {
log.error("推送失败!", e);
}
}
/**
* 创建响应URL
......@@ -109,6 +154,23 @@ public class PayMchNotifyService {
}
/**
* 创建响应URL
*/
public String createNotifyUrl(RefundOrder refundOrder, String appSecret) {
QueryRefundOrderRS queryPayOrderRS = QueryRefundOrderRS.buildByRefundOrder(refundOrder);
JSONObject jsonObject = (JSONObject)JSONObject.toJSON(queryPayOrderRS);
jsonObject.put("reqTime", System.currentTimeMillis()); //添加请求时间
// 报文签名
jsonObject.put("sign", JeepayKit.getSign(jsonObject, appSecret));
// 生成通知
return StringKit.appendUrlQuery(refundOrder.getNotifyUrl(), jsonObject);
}
/**
* 创建响应URL
*/
......
/*
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.task;
import com.jeequan.jeepay.service.impl.PayOrderService;
import com.jeequan.jeepay.service.impl.RefundOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/*
* 退款订单过期定时任务
*
* @author terrfly
* @site https://www.jeepay.vip
* @date 2021/6/17 14:36
*/
@Slf4j
@Component
public class RefundOrderExpiredTask {
@Autowired private RefundOrderService refundOrderService;
@Scheduled(cron="0 0/1 * * * ?") // 每分钟执行一次
public void start() {
int updateCount = refundOrderService.updateOrderExpired();
log.info("处理退款订单超时{}条.", updateCount);
}
}
/*
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.task;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.pay.service.ChannelOrderReissueService;
import com.jeequan.jeepay.service.impl.RefundOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
/*
* 补单定时任务(退款单)
*
* @author terrfly
* @site https://www.jeepay.vip
* @date 2021/6/17 14:22
*/
@Slf4j
@Component
public class RefundOrderReissueTask {
private static final int QUERY_PAGE_SIZE = 100; //每次查询数量
@Autowired private RefundOrderService refundOrderService;
@Autowired private ChannelOrderReissueService channelOrderReissueService;
@Scheduled(cron="0 0/1 * * * ?") // 每分钟执行一次
public void start() {
//当前时间 减去10分钟。
Date offsetDate = DateUtil.offsetMinute(new Date(), -10);
//查询条件: 支付中的订单 & ( 订单创建时间 + 10分钟 >= 当前时间 )
LambdaQueryWrapper<RefundOrder> lambdaQueryWrapper = RefundOrder.gw().eq(RefundOrder::getState, RefundOrder.STATE_ING).le(RefundOrder::getCreatedAt, offsetDate);
int currentPageIndex = 1; //当前页码
while(true){
try {
IPage<RefundOrder> refundOrderIPage = refundOrderService.page(new Page(currentPageIndex, QUERY_PAGE_SIZE), lambdaQueryWrapper);
if(refundOrderIPage == null || refundOrderIPage.getRecords().isEmpty()){ //本次查询无结果, 不再继续查询;
break;
}
for(RefundOrder refundOrder: refundOrderIPage.getRecords()){
channelOrderReissueService.processRefundOrder(refundOrder);
}
//已经到达页码最大量,无需再次查询
if(refundOrderIPage.getPages() <= currentPageIndex){
break;
}
currentPageIndex++;
} catch (Exception e) { //出现异常,直接退出,避免死循环。
log.error("error", e);
break;
}
}
}
}
......@@ -21,10 +21,12 @@ import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.service.mapper.PayOrderMapper;
import com.jeequan.jeepay.service.mapper.RefundOrderMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.Date;
/**
......@@ -40,6 +42,18 @@ public class RefundOrderService extends ServiceImpl<RefundOrderMapper, RefundOrd
@Autowired private PayOrderMapper payOrderMapper;
/** 查询商户订单 **/
public RefundOrder queryMchOrder(String mchNo, String mchRefundNo, String refundOrderId){
if(StringUtils.isNotEmpty(refundOrderId)){
return getOne(RefundOrder.gw().eq(RefundOrder::getMchNo, mchNo).eq(RefundOrder::getRefundOrderId, refundOrderId));
}else if(StringUtils.isNotEmpty(mchRefundNo)){
return getOne(RefundOrder.gw().eq(RefundOrder::getMchNo, mchNo).eq(RefundOrder::getMchRefundNo, mchRefundNo));
}else{
return null;
}
}
/** 更新退款单状态 【退款单生成】 --》 【退款中】 **/
public boolean updateInit2Ing(String refundOrderId){
......@@ -108,4 +122,18 @@ public class RefundOrderService extends ServiceImpl<RefundOrderMapper, RefundOrd
}
/** 更新退款单为 关闭状态 **/
public Integer updateOrderExpired(){
RefundOrder refundOrder = new RefundOrder();
refundOrder.setState(RefundOrder.STATE_CLOSED);
return baseMapper.update(refundOrder,
RefundOrder.gw()
.in(RefundOrder::getState, Arrays.asList(RefundOrder.STATE_INIT, RefundOrder.STATE_ING))
.le(RefundOrder::getExpiredTime, new Date())
);
}
}
......@@ -28,6 +28,7 @@
<result column="notify_url" property="notifyUrl" />
<result column="ext_param" property="extParam" />
<result column="success_time" property="successTime" />
<result column="expired_time" property="expiredTime" />
<result column="created_at" property="createdAt" />
<result column="updated_at" property="updatedAt" />
</resultMap>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册