新增查询转账单状态,对接支付宝、微信转账查询接口

This commit is contained in:
zhuxiao 2023-02-17 18:12:41 +08:00
parent a4e4c990c3
commit 43e2669d24
5 changed files with 351 additions and 9 deletions

View File

@ -41,4 +41,7 @@ public interface ITransferService {
/** 调起退款接口,并响应数据; 内部处理普通商户和服务商模式 **/
ChannelRetMsg transfer(TransferOrderRQ bizRQ, TransferOrder refundOrder, MchAppConfigContext mchAppConfigContext) throws Exception;
/** 调起转账查询接口 **/
ChannelRetMsg query(TransferOrder transferOrder, MchAppConfigContext mchAppConfigContext);
}

View File

@ -15,9 +15,12 @@
*/
package com.jeequan.jeepay.pay.channel.alipay;
import com.alipay.api.domain.AlipayFundTransCommonQueryModel;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.request.AlipayFundTransCommonQueryRequest;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.response.AlipayFundTransCommonQueryResponse;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.TransferOrder;
@ -98,8 +101,17 @@ public class AlipayTransferService implements ITransferService {
// 调用成功
if(response.isSuccess()){
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
channelRetMsg.setChannelOrderId(response.getOrderId());
if ("SUCCESS".equals(response.getStatus())) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
channelRetMsg.setChannelOrderId(response.getOrderId());
}else if ("FAIL".equals(response.getStatus())) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(AlipayKit.appendErrCode(response.getCode(), response.getSubCode()));
channelRetMsg.setChannelErrMsg(AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}else {
return ChannelRetMsg.waiting();
}
}else{
// 系统繁忙 无法确认结果
@ -115,4 +127,38 @@ public class AlipayTransferService implements ITransferService {
return channelRetMsg;
}
@Override
public ChannelRetMsg query(TransferOrder transferOrder, MchAppConfigContext mchAppConfigContext) {
AlipayFundTransCommonQueryRequest request = new AlipayFundTransCommonQueryRequest();
AlipayFundTransCommonQueryModel model = new AlipayFundTransCommonQueryModel();
model.setProductCode(TransferOrder.ENTRY_BANK_CARD.equals(transferOrder.getEntryType()) ? "TRANS_BANKCARD_NO_PWD" : "TRANS_ACCOUNT_NO_PWD");
model.setBizScene("DIRECT_TRANSFER");
model.setOutBizNo(transferOrder.getTransferId()); // 商户转账唯一订单号
AlipayKit.putApiIsvInfo(mchAppConfigContext, request, model);
request.setBizModel(model);
// 调起支付宝接口
AlipayFundTransCommonQueryResponse response = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(request);
if (response.isSuccess()) {
// SUCCESS明确成功
if("SUCCESS".equalsIgnoreCase(response.getStatus())){
return ChannelRetMsg.confirmSuccess(response.getOrderId());
}
// REFUND退票适用于"单笔转账到银行卡" FAIL失败适用于"单笔转账到银行卡"
else if ("REFUND".equalsIgnoreCase(response.getStatus()) || "FAIL".equalsIgnoreCase(response.getStatus())){
return ChannelRetMsg.confirmFail(response.getErrorCode(), response.getFailReason());
} else{
return ChannelRetMsg.waiting();
}
} else {
// 如果查询单号对应的数据不存在那么数据不存在的原因可能是1付款还在处理中2付款处理失败导致付款订单没有落地务必再次查询确认此次付款的结果
if("ORDER_NOT_EXIST".equalsIgnoreCase(response.getSubCode())){
return ChannelRetMsg.waiting();
}
return ChannelRetMsg.confirmFail(AlipayKit.appendErrCode(response.getCode(), response.getSubCode()), AlipayKit.appendErrMsg(response.getMsg(), response.getSubMsg()));
}
}
}

View File

@ -15,8 +15,11 @@
*/
package com.jeequan.jeepay.pay.channel.wxpay;
import com.github.binarywang.wxpay.bean.entpay.EntPayQueryRequest;
import com.github.binarywang.wxpay.bean.entpay.EntPayQueryResult;
import com.github.binarywang.wxpay.bean.entpay.EntPayRequest;
import com.github.binarywang.wxpay.bean.entpay.EntPayResult;
import com.github.binarywang.wxpay.bean.transfer.TransferBatchDetailResult;
import com.github.binarywang.wxpay.bean.transfer.TransferBatchesRequest;
import com.github.binarywang.wxpay.bean.transfer.TransferBatchesResult;
import com.github.binarywang.wxpay.exception.WxPayException;
@ -110,13 +113,8 @@ public class WxpayTransferService implements ITransferService {
}
EntPayResult entPayResult = wxServiceWrapper.getWxPayService().getEntPayService().entPay(request);
return ChannelRetMsg.waiting();
// SUCCESS/FAIL注意当状态为FAIL时存在业务结果未明确的情况如果状态为FAIL请务必关注错误代码err_code字段通过查询接口确认此次付款的结果
if("SUCCESS".equalsIgnoreCase(entPayResult.getResultCode())){
return ChannelRetMsg.confirmSuccess(entPayResult.getPaymentNo());
}else{
return ChannelRetMsg.waiting();
}
} else if (CS.PAY_IF_VERSION.WX_V3.equals(wxServiceWrapper.getApiVersion())) {
TransferBatchesRequest request = new TransferBatchesRequest();
request.setAppid(wxServiceWrapper.getWxPayService().getConfig().getAppId());
@ -137,7 +135,7 @@ public class WxpayTransferService implements ITransferService {
request.setTransferDetailList(list);
TransferBatchesResult transferBatchesResult = wxServiceWrapper.getWxPayService().getTransferService().transferBatches(request);
return ChannelRetMsg.confirmSuccess(transferBatchesResult.getBatchId());
return ChannelRetMsg.waiting();
} else {
return ChannelRetMsg.sysError("请选择微信V2或V3模式");
}
@ -159,4 +157,56 @@ public class WxpayTransferService implements ITransferService {
}
}
@Override
public ChannelRetMsg query(TransferOrder transferOrder, MchAppConfigContext mchAppConfigContext) {
try {
WxServiceWrapper wxServiceWrapper = configContextQueryService.getWxServiceWrapper(mchAppConfigContext);
if (CS.PAY_IF_VERSION.WX_V2.equals(wxServiceWrapper.getApiVersion())) { //V2
EntPayQueryResult entPayQueryResult = wxServiceWrapper.getWxPayService().getEntPayService().queryEntPay(transferOrder.getTransferId());
// SUCCESS明确成功
if("SUCCESS".equalsIgnoreCase(entPayQueryResult.getStatus())){
return ChannelRetMsg.confirmSuccess(entPayQueryResult.getDetailId());
} else if ("FAILED".equalsIgnoreCase(entPayQueryResult.getStatus())){ // FAILED明确失败
return ChannelRetMsg.confirmFail(entPayQueryResult.getStatus(), entPayQueryResult.getReason());
} else{
return ChannelRetMsg.waiting();
}
} else if (CS.PAY_IF_VERSION.WX_V3.equals(wxServiceWrapper.getApiVersion())) {
TransferBatchDetailResult transferBatchDetailResult =
wxServiceWrapper.getWxPayService().getTransferService().transferBatchesOutBatchNoDetail(transferOrder.getTransferId(), transferOrder.getTransferId());
// SUCCESS明确成功
if("SUCCESS".equalsIgnoreCase(transferBatchDetailResult.getDetailStatus())){
return ChannelRetMsg.confirmSuccess(transferBatchDetailResult.getDetailId());
} else if ("FAIL".equalsIgnoreCase(transferBatchDetailResult.getDetailStatus())){ // FAIL明确失败
return ChannelRetMsg.confirmFail(transferBatchDetailResult.getDetailStatus(), transferBatchDetailResult.getFailReason());
} else{
return ChannelRetMsg.waiting();
}
} else {
return ChannelRetMsg.sysError("请选择微信V2或V3模式");
}
} catch (WxPayException e) {
// 如果查询单号对应的数据不存在那么数据不存在的原因可能是1付款还在处理中2付款处理失败导致付款订单没有落地务必再次查询确认此次付款的结果
if("NOT_FOUND".equalsIgnoreCase(e.getErrCode())){
return ChannelRetMsg.waiting();
}
return ChannelRetMsg.confirmFail(null,
WxpayKit.appendErrCode(e.getReturnMsg(), e.getErrCode()),
WxpayKit.appendErrMsg(e.getReturnMsg(), StringUtils.defaultIfEmpty(e.getErrCodeDes(), e.getCustomErrorMsg())));
} catch (Exception e) {
log.error("转账状态查询异常:", e);
return ChannelRetMsg.waiting();
}
}
}

View File

@ -0,0 +1,158 @@
/*
* 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.service;
import com.jeequan.jeepay.core.entity.TransferOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.pay.channel.ITransferService;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.service.impl.TransferOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/*
* 转账补单服务实现类
*
* @author zx
* @site https://www.jeequan.com
* @date 2022/12/29 17:47
*/
@Service
@Slf4j
public class TransferOrderReissueService {
@Autowired private ConfigContextQueryService configContextQueryService;
@Autowired private TransferOrderService transferOrderService;
@Autowired private PayMchNotifyService payMchNotifyService;
/** 处理转账订单 **/
public ChannelRetMsg processOrder(TransferOrder transferOrder){
try {
String transferId = transferOrder.getTransferId();
// 查询转账接口是否存在
ITransferService transferService = SpringBeansUtil.getBean(transferOrder.getIfCode() + "TransferService", ITransferService.class);
// 支付通道转账接口实现不存在
if(transferService == null){
log.error("{} interface not exists!", transferOrder.getIfCode());
return null;
}
// 查询出商户应用的配置信息
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(transferOrder.getMchNo(), transferOrder.getAppId());
ChannelRetMsg channelRetMsg = transferService.query(transferOrder, mchAppConfigContext);
if(channelRetMsg == null){
log.error("channelRetMsg is null");
return null;
}
log.info("补单[{}]查询结果为:{}", transferId, channelRetMsg);
// 查询成功
if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
// 转账成功
transferOrderService.updateIng2Success(transferId, channelRetMsg.getChannelOrderId());
payMchNotifyService.transferOrderNotify(transferOrderService.getById(transferId));
}else if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL){
// 转账失败
transferOrderService.updateIng2Fail(transferId, channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelUserId(), channelRetMsg.getChannelErrCode());
payMchNotifyService.transferOrderNotify(transferOrderService.getById(transferId));
}
return channelRetMsg;
} catch (Exception e) { //继续下一次迭代查询
log.error("error transferId = {}", transferOrder.getTransferId(), e);
return null;
}
}
/**
* 处理返回的渠道信息并更新订单状态
* TransferOrder将对部分信息进行 赋值操作
* **/
public void processChannelMsg(ChannelRetMsg channelRetMsg, TransferOrder transferOrder){
//对象为空 || 上游返回状态为空 则无需操作
if(channelRetMsg == null || channelRetMsg.getChannelState() == null){
return ;
}
//明确成功
if(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == channelRetMsg.getChannelState()) {
this.updateInitOrderStateThrowException(TransferOrder.STATE_SUCCESS, transferOrder, channelRetMsg);
payMchNotifyService.transferOrderNotify(transferOrderService.getById(transferOrder.getTransferId()));
//明确失败
}else if(ChannelRetMsg.ChannelState.CONFIRM_FAIL == channelRetMsg.getChannelState()) {
this.updateInitOrderStateThrowException(TransferOrder.STATE_FAIL, transferOrder, channelRetMsg);
payMchNotifyService.transferOrderNotify(transferOrderService.getById(transferOrder.getTransferId()));
// 上游处理中 || 未知 || 上游接口返回异常 订单为支付中状态
}else if( ChannelRetMsg.ChannelState.WAITING == channelRetMsg.getChannelState() ||
ChannelRetMsg.ChannelState.UNKNOWN == channelRetMsg.getChannelState() ||
ChannelRetMsg.ChannelState.API_RET_ERROR == channelRetMsg.getChannelState()
){
this.updateInitOrderStateThrowException(TransferOrder.STATE_ING, transferOrder, channelRetMsg);
// 系统异常 订单不再处理 生成状态
}else if( ChannelRetMsg.ChannelState.SYS_ERROR == channelRetMsg.getChannelState()){
}else{
throw new BizException("ChannelState 返回异常!");
}
}
/** 更新转账单状态 --》 转账单生成--》 其他状态 (向外抛出异常) **/
private void updateInitOrderStateThrowException(byte orderState, TransferOrder transferOrder, ChannelRetMsg channelRetMsg){
transferOrder.setState(orderState);
transferOrder.setChannelOrderNo(channelRetMsg.getChannelOrderId());
transferOrder.setErrCode(channelRetMsg.getChannelErrCode());
transferOrder.setErrMsg(channelRetMsg.getChannelErrMsg());
boolean isSuccess = transferOrderService.updateInit2Ing(transferOrder.getTransferId());
if(!isSuccess){
throw new BizException("更新转账单异常!");
}
isSuccess = transferOrderService.updateIng2SuccessOrFail(transferOrder.getTransferId(), transferOrder.getState(),
channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelErrCode(), channelRetMsg.getChannelErrMsg());
if(!isSuccess){
throw new BizException("更新转账订单异常!");
}
}
}

View File

@ -0,0 +1,85 @@
/*
* 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.TransferOrder;
import com.jeequan.jeepay.pay.service.TransferOrderReissueService;
import com.jeequan.jeepay.service.impl.TransferOrderService;
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 zx
* @site https://www.jeequan.com
* @date 2022/12/29 17:47
*/
@Slf4j
@Component
public class TransferOrderReissueTask {
private static final int QUERY_PAGE_SIZE = 100; //每次查询数量
@Autowired private TransferOrderService transferOrderService;
@Autowired private TransferOrderReissueService transferOrderReissueService;
@Scheduled(cron="0 0/1 * * * ?") // 每分钟执行一次
public void start() {
//查询条件
LambdaQueryWrapper<TransferOrder> lambdaQueryWrapper = TransferOrder.gw()
.eq(TransferOrder::getState, TransferOrder.STATE_ING) // 转账中
.ge(TransferOrder::getCreatedAt, DateUtil.offsetDay(new Date(), -1)); // 只查询一天内的转账单;
int currentPageIndex = 1; //当前页码
while(true){
try {
IPage<TransferOrder> iPage = transferOrderService.page(new Page(currentPageIndex, QUERY_PAGE_SIZE), lambdaQueryWrapper);
if(iPage == null || iPage.getRecords().isEmpty()){ //本次查询无结果, 不再继续查询;
break;
}
for(TransferOrder transferOrder: iPage.getRecords()){
transferOrderReissueService.processOrder(transferOrder);
}
//已经到达页码最大量无需再次查询
if(iPage.getPages() <= currentPageIndex){
break;
}
currentPageIndex++;
} catch (Exception e) { //出现异常直接退出避免死循环
log.error("error", e);
break;
}
}
}
}