新增转账异步通知实现
This commit is contained in:
parent
43e2669d24
commit
d430a2b5ad
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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.channel;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jeequan.jeepay.core.beans.RequestKitBean;
|
||||
import com.jeequan.jeepay.pay.service.ConfigContextQueryService;
|
||||
import com.jeequan.jeepay.pay.util.ChannelCertConfigKitBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
|
||||
/*
|
||||
* 实现回调接口抽象类
|
||||
*
|
||||
* @author zx
|
||||
* @site https://www.jeequan.com
|
||||
* @date 2022/12/30 10:18
|
||||
*/
|
||||
public abstract class AbstractTransferNoticeService implements ITransferNoticeService {
|
||||
|
||||
@Autowired private RequestKitBean requestKitBean;
|
||||
@Autowired private ChannelCertConfigKitBean channelCertConfigKitBean;
|
||||
@Autowired protected ConfigContextQueryService configContextQueryService;
|
||||
|
||||
@Override
|
||||
public ResponseEntity doNotifyOrderNotExists(HttpServletRequest request) {
|
||||
return textResp("order not exists");
|
||||
}
|
||||
|
||||
/** 文本类型的响应数据 **/
|
||||
protected ResponseEntity textResp(String text){
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.TEXT_HTML);
|
||||
return new ResponseEntity(text, httpHeaders, HttpStatus.OK);
|
||||
}
|
||||
|
||||
/** json类型的响应数据 **/
|
||||
protected ResponseEntity jsonResp(Object body){
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
|
||||
return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
|
||||
}
|
||||
|
||||
|
||||
/**request.getParameter 获取参数 并转换为JSON格式 **/
|
||||
protected JSONObject getReqParamJSON() {
|
||||
return requestKitBean.getReqParamJSON();
|
||||
}
|
||||
|
||||
/**request.getParameter 获取参数 并转换为JSON格式 **/
|
||||
protected String getReqParamFromBody() {
|
||||
return requestKitBean.getReqParamFromBody();
|
||||
}
|
||||
|
||||
/** 获取文件路径 **/
|
||||
protected String getCertFilePath(String certFilePath) {
|
||||
return channelCertConfigKitBean.getCertFilePath(certFilePath);
|
||||
}
|
||||
|
||||
/** 获取文件File对象 **/
|
||||
protected File getCertFile(String certFilePath) {
|
||||
return channelCertConfigKitBean.getCertFile(certFilePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.channel;
|
||||
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/*
|
||||
* 转账订单通知解析实现 异步回调
|
||||
*
|
||||
* @author zx
|
||||
* @site https://www.jeequan.com
|
||||
* @date 2022/12/30 10:14
|
||||
*/
|
||||
public interface ITransferNoticeService {
|
||||
|
||||
/** 获取到接口code **/
|
||||
String getIfCode();
|
||||
|
||||
/** 解析参数: 转账单号 和 请求参数
|
||||
* 异常需要自行捕捉,并返回null , 表示已响应数据。
|
||||
* **/
|
||||
MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId);
|
||||
|
||||
/** 返回需要更新的订单状态 和响应数据 **/
|
||||
ChannelRetMsg doNotice(HttpServletRequest request,
|
||||
Object params, TransferOrder transferOrder, MchAppConfigContext mchAppConfigContext);
|
||||
|
||||
/** 数据库订单数据不存在 (仅异步通知使用) **/
|
||||
ResponseEntity doNotifyOrderNotExists(HttpServletRequest request);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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.channel.alipay;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alipay.api.internal.util.AlipaySignature;
|
||||
import com.jeequan.jeepay.core.constants.CS;
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.model.params.alipay.AlipayConfig;
|
||||
import com.jeequan.jeepay.core.model.params.alipay.AlipayIsvParams;
|
||||
import com.jeequan.jeepay.core.model.params.alipay.AlipayNormalMchParams;
|
||||
import com.jeequan.jeepay.pay.channel.AbstractTransferNoticeService;
|
||||
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* 支付宝 转账回调接口实现类
|
||||
*
|
||||
* @author zx
|
||||
* @site https://www.jeequan.com
|
||||
* @date 2021/21/01 17:16
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class AlipayTransferNoticeService extends AbstractTransferNoticeService {
|
||||
|
||||
@Override
|
||||
public String getIfCode() {
|
||||
return CS.IF_CODE.ALIPAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId) {
|
||||
|
||||
try {
|
||||
|
||||
JSONObject params = getReqParamJSON();
|
||||
log.info("【支付宝转账】回调通知参数:{}", params.toJSONString());
|
||||
|
||||
JSONObject bizContent = JSONObject.parseObject(params.getString("biz_content"));
|
||||
|
||||
String transferId = bizContent.getString("out_biz_no");
|
||||
return MutablePair.of(transferId, params);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
throw ResponseException.buildText("ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ChannelRetMsg doNotice(HttpServletRequest request, Object params, TransferOrder transferOrder, MchAppConfigContext mchAppConfigContext) {
|
||||
|
||||
String logPrefix = "【支付宝转账通知】";
|
||||
|
||||
try {
|
||||
//配置参数获取
|
||||
Byte useCert = null;
|
||||
String alipaySignType, alipayPublicCert, alipayPublicKey = null;
|
||||
if(mchAppConfigContext.isIsvsubMch()){
|
||||
|
||||
// 获取支付参数
|
||||
AlipayIsvParams alipayParams = (AlipayIsvParams)configContextQueryService.queryIsvParams(mchAppConfigContext.getMchInfo().getIsvNo(), getIfCode());
|
||||
useCert = alipayParams.getUseCert();
|
||||
alipaySignType = alipayParams.getSignType();
|
||||
alipayPublicCert = alipayParams.getAlipayPublicCert();
|
||||
alipayPublicKey = alipayParams.getAlipayPublicKey();
|
||||
|
||||
}else{
|
||||
|
||||
// 获取支付参数
|
||||
AlipayNormalMchParams alipayParams = (AlipayNormalMchParams)configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), getIfCode());
|
||||
|
||||
useCert = alipayParams.getUseCert();
|
||||
alipaySignType = alipayParams.getSignType();
|
||||
alipayPublicCert = alipayParams.getAlipayPublicCert();
|
||||
alipayPublicKey = alipayParams.getAlipayPublicKey();
|
||||
}
|
||||
|
||||
// 获取请求参数
|
||||
JSONObject jsonParams = (JSONObject) params;
|
||||
JSONObject bizContent = JSONObject.parseObject(jsonParams.getString("biz_content"));
|
||||
|
||||
boolean verifyResult;
|
||||
if(useCert != null && useCert == CS.YES){ //证书方式
|
||||
|
||||
verifyResult = AlipaySignature.rsaCertCheckV1(jsonParams.toJavaObject(Map.class), getCertFilePath(alipayPublicCert),
|
||||
AlipayConfig.CHARSET, alipaySignType);
|
||||
|
||||
}else{
|
||||
verifyResult = AlipaySignature.rsaCheckV1(jsonParams.toJavaObject(Map.class), alipayPublicKey, AlipayConfig.CHARSET, alipaySignType);
|
||||
}
|
||||
|
||||
//验签失败
|
||||
if(!verifyResult){
|
||||
log.error("{},验签失败", logPrefix);
|
||||
throw ResponseException.buildText("ERROR");
|
||||
}
|
||||
|
||||
//验签成功后判断上游订单状态
|
||||
ResponseEntity okResponse = textResp("SUCCESS");
|
||||
|
||||
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
|
||||
channelRetMsg.setResponseEntity(okResponse); // 响应数据
|
||||
|
||||
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); // 默认转账中
|
||||
|
||||
// 成功-SUCCESS
|
||||
String status = bizContent.getString("status");
|
||||
if("SUCCESS".equals(status)){
|
||||
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
|
||||
}
|
||||
|
||||
return channelRetMsg;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
throw ResponseException.buildText("ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -104,10 +104,12 @@ public class AlipayTransferService implements ITransferService {
|
|||
if ("SUCCESS".equals(response.getStatus())) {
|
||||
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
|
||||
channelRetMsg.setChannelOrderId(response.getOrderId());
|
||||
return channelRetMsg;
|
||||
}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()));
|
||||
return channelRetMsg;
|
||||
}else {
|
||||
return ChannelRetMsg.waiting();
|
||||
}
|
||||
|
|
@ -122,9 +124,8 @@ public class AlipayTransferService implements ITransferService {
|
|||
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
|
||||
channelRetMsg.setChannelErrCode(response.getSubCode());
|
||||
channelRetMsg.setChannelErrMsg(response.getSubMsg());
|
||||
return channelRetMsg;
|
||||
}
|
||||
|
||||
return channelRetMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* 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.transfer;
|
||||
|
||||
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
|
||||
import com.jeequan.jeepay.core.entity.TransferOrder;
|
||||
import com.jeequan.jeepay.core.exception.BizException;
|
||||
import com.jeequan.jeepay.core.exception.ResponseException;
|
||||
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
|
||||
import com.jeequan.jeepay.pay.channel.ITransferNoticeService;
|
||||
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
|
||||
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
|
||||
import com.jeequan.jeepay.pay.service.ConfigContextQueryService;
|
||||
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
|
||||
import com.jeequan.jeepay.service.impl.TransferOrderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 转账异步通知入口Controller
|
||||
*
|
||||
* @author zx
|
||||
* @site https://www.jeequan.com
|
||||
* @date 2022/12/30 10:26
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class TransferNoticeController extends AbstractCtrl {
|
||||
|
||||
@Autowired private TransferOrderService transferOrderService;
|
||||
@Autowired private ConfigContextQueryService configContextQueryService;
|
||||
@Autowired private PayMchNotifyService payMchNotifyService;
|
||||
|
||||
|
||||
/** 异步回调入口 **/
|
||||
@ResponseBody
|
||||
@RequestMapping(value= {"/api/transfer/notify/{ifCode}", "/api/transfer/notify/{ifCode}/{transferId}"})
|
||||
public ResponseEntity doNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode, @PathVariable(value = "transferId", required = false) String urlOrderId){
|
||||
|
||||
String transferId = null;
|
||||
String logPrefix = "进入[" +ifCode+ "]转账回调:urlOrderId:["+ StringUtils.defaultIfEmpty(urlOrderId, "") + "] ";
|
||||
log.info("===== {} =====" , logPrefix);
|
||||
|
||||
try {
|
||||
|
||||
// 参数有误
|
||||
if(StringUtils.isEmpty(ifCode)){
|
||||
return ResponseEntity.badRequest().body("ifCode is empty");
|
||||
}
|
||||
|
||||
//查询转账接口是否存在
|
||||
ITransferNoticeService transferNotifyService = SpringBeansUtil.getBean(ifCode + "TransferNoticeService", ITransferNoticeService.class);
|
||||
|
||||
// 支付通道转账接口实现不存在
|
||||
if(transferNotifyService == null){
|
||||
log.error("{}, transfer interface not exists ", logPrefix);
|
||||
return ResponseEntity.badRequest().body("[" + ifCode + "] transfer interface not exists");
|
||||
}
|
||||
|
||||
// 解析转账单号 和 请求参数
|
||||
MutablePair<String, Object> mutablePair = transferNotifyService.parseParams(request, urlOrderId);
|
||||
if(mutablePair == null){ // 解析数据失败, 响应已处理
|
||||
log.error("{}, mutablePair is null ", logPrefix);
|
||||
throw new BizException("解析数据异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
// 解析到转账单号
|
||||
transferId = mutablePair.left;
|
||||
log.info("{}, 解析数据为:transferId:{}, params:{}", logPrefix, transferId, mutablePair.getRight());
|
||||
|
||||
// 获取转账单号 和 转账单数据
|
||||
TransferOrder transferOrder = transferOrderService.getById(transferId);
|
||||
|
||||
// 转账单不存在
|
||||
if(transferOrder == null){
|
||||
log.error("{}, 转账单不存在. transferId={} ", logPrefix, transferId);
|
||||
return transferNotifyService.doNotifyOrderNotExists(request);
|
||||
}
|
||||
|
||||
//查询出商户应用的配置信息
|
||||
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(transferOrder.getMchNo(), transferOrder.getAppId());
|
||||
|
||||
//调起接口的回调判断
|
||||
ChannelRetMsg notifyResult = transferNotifyService.doNotice(request, mutablePair.getRight(), transferOrder, mchAppConfigContext);
|
||||
|
||||
// 返回null 表明出现异常, 无需处理通知下游等操作。
|
||||
if(notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null){
|
||||
log.error("{}, 处理回调事件异常 notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
|
||||
throw new BizException("处理回调事件异常!"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
|
||||
}
|
||||
|
||||
// 转账单是 【转账中状态】
|
||||
if(transferOrder.getState() == TransferOrder.STATE_ING) {
|
||||
if(notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
|
||||
// 转账成功
|
||||
transferOrderService.updateIng2Success(transferId, notifyResult.getChannelOrderId());
|
||||
payMchNotifyService.transferOrderNotify(transferOrderService.getById(transferId));
|
||||
|
||||
}else if(notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL){
|
||||
// 转账失败
|
||||
transferOrderService.updateIng2Fail(transferId, notifyResult.getChannelOrderId(), notifyResult.getChannelUserId(), notifyResult.getChannelErrCode());
|
||||
payMchNotifyService.transferOrderNotify(transferOrderService.getById(transferId));
|
||||
}
|
||||
}
|
||||
|
||||
log.info("===== {}, 转账单通知完成。 transferId={}, parseState = {} =====", logPrefix, transferId, notifyResult.getChannelState());
|
||||
|
||||
return notifyResult.getResponseEntity();
|
||||
|
||||
} catch (BizException e) {
|
||||
log.error("{}, transferId={}, BizException", logPrefix, transferId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
|
||||
} catch (ResponseException e) {
|
||||
log.error("{}, transferId={}, ResponseException", logPrefix, transferId, e);
|
||||
return e.getResponseEntity();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("{}, transferId={}, 系统异常", logPrefix, transferId, e);
|
||||
return ResponseEntity.badRequest().body(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue