Commit 71b4cc66 authored by xiaoyu's avatar xiaoyu
Browse files

微信退款回调,微信、支付宝、云闪付关闭订单接口

parent e6c0f794
/*
* 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.PayOrder;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
/**
* 关闭订单(渠道侧)接口定义
*
* @author xiaoyu
* @site https://www.jeequan.com
* @date 2022/1/24 17:23
*/
public interface IPayOrderCloseService {
/** 获取到接口code **/
String getIfCode();
/** 查询订单 **/
ChannelRetMsg close(PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception;
}
......@@ -77,6 +77,8 @@ public class AlipayKit {
((AlipayTradeRoyaltyRelationBindRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradeOrderSettleRequest) {
((AlipayTradeOrderSettleRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
} else if(req instanceof AlipayTradeCloseRequest) {
((AlipayTradeCloseRequest)req).putOtherTextParam("app_auth_token", isvsubMchParams.getAppAuthToken());
}
// 服务商信息
......
/*
* 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.alipay.api.domain.AlipayTradeCloseModel;
import com.alipay.api.request.AlipayTradeCloseRequest;
import com.alipay.api.response.AlipayTradeCloseResponse;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.pay.channel.IPayOrderCloseService;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.pay.service.ConfigContextQueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 支付宝 关闭订单接口实现类
*
* @author xiaoyu
* @site https://www.jeequan.com
* @date 2022/1/25 11:17
*/
@Service
public class AlipayPayOrderCloseService implements IPayOrderCloseService {
@Autowired private ConfigContextQueryService configContextQueryService;
@Override
public String getIfCode() {
return CS.IF_CODE.ALIPAY;
}
@Override
public ChannelRetMsg close(PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AlipayTradeCloseRequest req = new AlipayTradeCloseRequest();
// 商户订单号,商户网站订单系统中唯一订单号,必填
AlipayTradeCloseModel model = new AlipayTradeCloseModel();
model.setOutTradeNo(payOrder.getPayOrderId());
req.setBizModel(model);
//通用字段
AlipayKit.putApiIsvInfo(mchAppConfigContext, req, model);
AlipayTradeCloseResponse resp = configContextQueryService.getAlipayClientWrapper(mchAppConfigContext).execute(req);
// 返回状态成功
if (resp.isSuccess()) {
return ChannelRetMsg.confirmSuccess(resp.getTradeNo());
}else {
return ChannelRetMsg.sysError(resp.getSubMsg());
}
}
}
/*
* 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.wxpay;
import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.auth.AutoUpdateCertificatesVerifier;
import com.github.binarywang.wxpay.v3.auth.PrivateKeySigner;
import com.github.binarywang.wxpay.v3.auth.WxPayCredentials;
import com.github.binarywang.wxpay.v3.util.PemUtils;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.RefundOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ResponseException;
import com.jeequan.jeepay.core.model.params.wxpay.WxpayIsvParams;
import com.jeequan.jeepay.core.model.params.wxpay.WxpayNormalMchParams;
import com.jeequan.jeepay.pay.channel.AbstractChannelRefundNoticeService;
import com.jeequan.jeepay.pay.model.IsvConfigContext;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.model.WxServiceWrapper;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.service.impl.RefundOrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
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.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.FileInputStream;
import java.math.BigDecimal;
import java.security.PrivateKey;
/*
* 微信支付 退款回调接口实现类
*
* @author xiaoyu
* @site https://www.jeequan.com
* @date 2022/1/24 9:59
*/
@Service
@Slf4j
public class WxpayChannelRefundNoticeService extends AbstractChannelRefundNoticeService {
@Autowired RefundOrderService refundOrderService;
@Override
public String getIfCode() {
return CS.IF_CODE.WXPAY;
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId, NoticeTypeEnum noticeTypeEnum) {
try {
// 获取订单信息
RefundOrder refundOrder = refundOrderService.getById(urlOrderId);
if(refundOrder == null){
throw new BizException("订单不存在");
}
//获取支付参数 (缓存数据) 和 商户信息
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(refundOrder.getMchNo(), refundOrder.getAppId());
if(mchAppConfigContext == null){
throw new BizException("获取商户信息失败");
}
String apiVersion = "";
Byte mchType = mchAppConfigContext.getMchType();
if (CS.MCH_TYPE_NORMAL == mchType) {
WxpayNormalMchParams normalMchParams = mchAppConfigContext.getNormalMchParamsByIfCode(getIfCode(), WxpayNormalMchParams.class);
apiVersion = normalMchParams.getApiVersion();
}else if (CS.MCH_TYPE_ISVSUB == mchType) {
IsvConfigContext configContext = mchAppConfigContext.getIsvConfigContext();
WxpayIsvParams wxpayIsvParams = configContext.getIsvParamsByIfCode(getIfCode(), WxpayIsvParams.class);
apiVersion = wxpayIsvParams.getApiVersion();
}else {
throw new BizException("商户类型错误");
}
if(CS.PAY_IF_VERSION.WX_V3.equals(apiVersion)){ // V3接口回调
// 验签 && 获取订单回调数据
WxPayRefundNotifyV3Result.DecryptNotifyResult result = parseOrderNotifyV3Result(request, mchAppConfigContext);
return MutablePair.of(urlOrderId, result);
} else if (CS.PAY_IF_VERSION.WX_V2.equals(apiVersion)){ // V2接口回调
String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
if(StringUtils.isEmpty(xmlResult)) {
return null;
}
WxpayNormalMchParams normalMchParams = mchAppConfigContext.getNormalMchParamsByIfCode(getIfCode(), WxpayNormalMchParams.class);
WxPayRefundNotifyResult result = WxPayRefundNotifyResult.fromXML(xmlResult, normalMchParams.getKey());
return MutablePair.of(urlOrderId, result.getReqInfo());
}
return null;
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
@Override
public ChannelRetMsg doNotice(HttpServletRequest request, Object params, RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext, NoticeTypeEnum noticeTypeEnum) {
try {
ChannelRetMsg result = new ChannelRetMsg();
WxServiceWrapper wxServiceWrapper = configContextQueryService.getWxServiceWrapper(mchAppConfigContext);
result.setChannelState(ChannelRetMsg.ChannelState.WAITING); // 默认支付中
if (CS.PAY_IF_VERSION.WX_V3.equals(wxServiceWrapper.getApiVersion())) {
WxPayRefundNotifyV3Result.DecryptNotifyResult notifyResult = (WxPayRefundNotifyV3Result.DecryptNotifyResult) params;
// 验证参数
verifyWxPay3Params(notifyResult, refundOrder);
String refundStatus = notifyResult.getRefundStatus();
if ("SUCCESS".equals(refundStatus)) {
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else { //CHANGE—退款异常, REFUNDCLOSE—退款关闭
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); //退款失败
}
result.setChannelOrderId(notifyResult.getTransactionId()); // 渠道订单号
JSONObject resJSON = new JSONObject();
resJSON.put("code", "SUCCESS");
resJSON.put("message", "成功");
ResponseEntity okResponse = jsonResp(resJSON);
result.setResponseEntity(okResponse); //响应数据
}else if (CS.PAY_IF_VERSION.WX_V2.equals(wxServiceWrapper.getApiVersion())) {
// 获取回调参数
WxPayRefundNotifyResult.ReqInfo notifyResult = (WxPayRefundNotifyResult.ReqInfo) params;
// 验证参数
verifyWxPay2Params(notifyResult, refundOrder);
result.setChannelOrderId(notifyResult.getTransactionId()); //渠道订单号
if ("SUCCESS".equals(notifyResult.getRefundStatus())) {
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else { //CHANGE—退款异常, REFUNDCLOSE—退款关闭
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); //退款失败
}
result.setResponseEntity(textResp(WxPayNotifyResponse.successResp("OK")));
}else {
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); //退款失败
}
return result;
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
/**
* V3校验通知签名
* @param request 请求信息
* @param mchAppConfigContext 商户配置
* @return true:校验通过 false:校验不通过
*/
private WxPayRefundNotifyV3Result.DecryptNotifyResult parseOrderNotifyV3Result(HttpServletRequest request, MchAppConfigContext mchAppConfigContext) throws Exception {
SignatureHeader header = new SignatureHeader();
header.setTimeStamp(request.getHeader("Wechatpay-Timestamp"));
header.setNonce(request.getHeader("Wechatpay-Nonce"));
header.setSerial(request.getHeader("Wechatpay-Serial"));
header.setSignature(request.getHeader("Wechatpay-Signature"));
// 获取加密信息
String params = getReqParamFromBody();
log.info("\n【请求头信息】:{}\n【加密数据】:{}", header.toString(), params);
WxPayService wxPayService = configContextQueryService.getWxServiceWrapper(mchAppConfigContext).getWxPayService();
WxPayConfig wxPayConfig = wxPayService.getConfig();
// 自动获取微信平台证书
PrivateKey privateKey = PemUtils.loadPrivateKey(new FileInputStream(wxPayConfig.getPrivateKeyPath()));
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WxPayCredentials(wxPayConfig.getMchId(), new PrivateKeySigner(wxPayConfig.getCertSerialNo(), privateKey)),
wxPayConfig.getApiV3Key().getBytes("utf-8"));
wxPayConfig.setVerifier(verifier);
wxPayService.setConfig(wxPayConfig);
WxPayRefundNotifyV3Result result = wxPayService.parseRefundNotifyV3Result(params, header);
return result.getResult();
}
/**
* V3接口验证微信支付通知参数
* @return
*/
public void verifyWxPay3Params(WxPayRefundNotifyV3Result.DecryptNotifyResult result, RefundOrder refundOrder) {
try {
// 核对金额
Integer total_fee = result.getAmount().getTotal(); // 退款金额
long wxPayAmt = new BigDecimal(total_fee).longValue();
long dbPayAmt = refundOrder.getRefundAmount().longValue();
if (dbPayAmt != wxPayAmt) {
throw ResponseException.buildText("AMOUNT ERROR");
}
} catch (Exception e) {
throw ResponseException.buildText("ERROR");
}
}
/**
* V2接口验证微信支付通知参数
* @return
*/
public void verifyWxPay2Params(WxPayRefundNotifyResult.ReqInfo result, RefundOrder refundOrder) {
try {
// 核对金额
Integer total_fee = result.getRefundFee(); // 退款金额
long wxPayAmt = new BigDecimal(total_fee).longValue();
long dbPayAmt = refundOrder.getRefundAmount().longValue();
if (dbPayAmt != wxPayAmt) {
throw ResponseException.buildText("AMOUNT ERROR");
}
} catch (Exception e) {
throw ResponseException.buildText("ERROR");
}
}
}
/*
* 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.wxpay;
import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.request.WxPayOrderCloseRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderCloseResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.model.params.wxpay.WxpayIsvsubMchParams;
import com.jeequan.jeepay.pay.channel.IPayOrderCloseService;
import com.jeequan.jeepay.pay.channel.wxpay.kits.WxpayKit;
import com.jeequan.jeepay.pay.channel.wxpay.kits.WxpayV3Util;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.model.WxServiceWrapper;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.pay.service.ConfigContextQueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 微信关闭订单
*
* @author xiaoyu
* @site https://www.jeequan.com
* @date 2022/1/24 17:25
*/
@Service
public class WxpayPayOrderCloseService implements IPayOrderCloseService {
@Autowired private ConfigContextQueryService configContextQueryService;
@Override
public String getIfCode() {
return CS.IF_CODE.WXPAY;
}
@Override
public ChannelRetMsg close(PayOrder payOrder, MchAppConfigContext mchAppConfigContext) {
try {
WxServiceWrapper wxServiceWrapper = configContextQueryService.getWxServiceWrapper(mchAppConfigContext);
if (CS.PAY_IF_VERSION.WX_V2.equals(wxServiceWrapper.getApiVersion())) { //V2
WxPayOrderCloseRequest req = new WxPayOrderCloseRequest();
//放置isv信息
WxpayKit.putApiIsvInfo(mchAppConfigContext, req);
req.setOutTradeNo(payOrder.getPayOrderId());
WxPayService wxPayService = wxServiceWrapper.getWxPayService();
WxPayOrderCloseResult result = wxPayService.closeOrder(req);
if("SUCCESS".equals(result.getResultCode())){ //关闭订单成功
return ChannelRetMsg.confirmSuccess(null);
}else if("FAIL".equals(result.getResultCode())){ //关闭订单失败
return ChannelRetMsg.confirmFail(); //关闭失败
}else{
return ChannelRetMsg.waiting(); //关闭中
}
}else if (CS.PAY_IF_VERSION.WX_V3.equals(wxServiceWrapper.getApiVersion())) { //V3
String reqUrl;
JSONObject reqJson = new JSONObject();
if(mchAppConfigContext.isIsvsubMch()){ // 特约商户
WxpayIsvsubMchParams isvsubMchParams = (WxpayIsvsubMchParams) configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), getIfCode());
reqUrl = String.format("/v3/pay/partner/transactions/out-trade-no/%s/close", payOrder.getMchOrderNo());
reqJson.put("sp_mchid", wxServiceWrapper.getWxPayService().getConfig().getMchId());
reqJson.put("sub_mchid", isvsubMchParams.getSubMchId());
}else {
reqUrl = String.format("/v3/pay/transactions/out-trade-no/%s/close", payOrder.getMchOrderNo());
reqJson.put("mchid", wxServiceWrapper.getWxPayService().getConfig().getMchId());
}
WxpayV3Util.closeOrderV3(reqUrl, reqJson, wxServiceWrapper.getWxPayService().getConfig());
return ChannelRetMsg.confirmSuccess(null);
}
return ChannelRetMsg.confirmFail();
} catch (WxPayException e) {
return ChannelRetMsg.sysError(e.getReturnMsg());
} catch (Exception e) {
return ChannelRetMsg.sysError(e.getMessage());
}
}
}
......@@ -79,6 +79,7 @@ public class WxpayRefundService extends AbstractRefundService {
req.setOutRefundNo(refundOrder.getRefundOrderId()); // 退款单号
req.setTotalFee(payOrder.getAmount().intValue()); // 订单总金额
req.setRefundFee(refundOrder.getRefundAmount().intValue()); // 退款金额
req.setNotifyUrl(getNotifyUrl(refundOrder.getRefundOrderId())); // 回调url
WxPayService wxPayService = wxServiceWrapper.getWxPayService();
setCretPath(mchAppConfigContext, wxPayService); // 证书路径
......@@ -96,6 +97,7 @@ public class WxpayRefundService extends AbstractRefundService {
JSONObject reqJSON = new JSONObject();
reqJSON.put("out_trade_no", refundOrder.getPayOrderId()); // 订单号
reqJSON.put("out_refund_no", refundOrder.getRefundOrderId()); // 退款订单号
reqJSON.put("notify_url", getNotifyUrl(refundOrder.getRefundOrderId())); // 回调地址
JSONObject amountJson = new JSONObject();
amountJson.put("refund", refundOrder.getRefundAmount());// 退款金额
......
......@@ -83,6 +83,11 @@ public class WxpayV3Util {
return JSON.parseObject(response);
}
public static JSONObject closeOrderV3(String url, JSONObject reqJSON, WxPayConfig wxPayConfig) throws WxPayException {
String response = postV3(PAY_BASE_URL + url, reqJSON.toJSONString(), wxPayConfig);
return JSON.parseObject(response);
}
public static JSONObject refundV3(JSONObject reqJSON, WxPayConfig wxPayConfig) throws WxPayException {
String url = String.format("%s/v3/refund/domestic/refunds", PAY_BASE_URL);
String response = postV3(url, reqJSON.toJSONString(), wxPayConfig);
......
/*
* 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.ysfpay;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.pay.channel.IPayOrderCloseService;
import com.jeequan.jeepay.pay.channel.IPayOrderQueryService;
import com.jeequan.jeepay.pay.channel.ysfpay.utils.YsfHttpUtil;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 云闪付 关闭订单接口实现类
*
* @author pangxiaoyu
* @site https://www.jeequan.com
* @date 2021-06-07 07:15
*/
@Service
@Slf4j
public class YsfpayPayOrderCloseService implements IPayOrderCloseService {
@Override
public String getIfCode() {
return CS.IF_CODE.YSFPAY;
}
@Autowired
private YsfpayPaymentService ysfpayPaymentService;
@Override
public ChannelRetMsg close(PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
JSONObject reqParams = new JSONObject();
String orderType = YsfHttpUtil.getOrderTypeByCommon(payOrder.getWayCode());
String logPrefix = "【云闪付("+orderType+")关闭订单】";
try {
reqParams.put("orderNo", payOrder.getPayOrderId()); //订单号
reqParams.put("orderType", orderType); //订单类型
//封装公共参数 & 签名 & 调起http请求 & 返回响应数据并包装为json格式。
JSONObject resJSON = ysfpayPaymentService.packageParamAndReq("/gateway/api/pay/closeOrder", reqParams, logPrefix, mchAppConfigContext);
log.info("关闭订单 payorderId:{}, 返回结果:{}", payOrder.getPayOrderId(), resJSON);
if(resJSON == null){
return ChannelRetMsg.sysError("【云闪付】请求关闭订单异常");
}
//请求 & 响应成功, 判断业务逻辑
String respCode = resJSON.getString("respCode"); //应答码
String respMsg = resJSON.getString("respMsg"); //应答信息
if(("00").equals(respCode)){// 请求成功
return ChannelRetMsg.confirmSuccess(null); //关单成功
}
return ChannelRetMsg.sysError(respMsg); // 关单失败
}catch (Exception e) {
return ChannelRetMsg.sysError(e.getMessage()); // 关单失败
}
}
}
/*
* 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.payorder;
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.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.pay.channel.IPayOrderCloseService;
import com.jeequan.jeepay.pay.ctrl.ApiController;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.pay.rqrs.payorder.ClosePayOrderRQ;
import com.jeequan.jeepay.pay.rqrs.payorder.ClosePayOrderRS;
import com.jeequan.jeepay.pay.service.ConfigContextQueryService;
import com.jeequan.jeepay.service.impl.PayOrderService;
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 xiaoyu
* @site https://www.jeequan.com
* @date 2022/1/25 9:19
*/
@Slf4j
@RestController
public class CloseOrderController extends ApiController {
@Autowired private PayOrderService payOrderService;
@Autowired private ConfigContextQueryService configContextQueryService;
/**
* @author: xiaoyu
* @date: 2022/1/25 9:19
* @describe: 关闭订单
*/
@RequestMapping("/api/pay/close")
public ApiRes queryOrder(){
//获取参数 & 验签
ClosePayOrderRQ rq = getRQByWithMchSign(ClosePayOrderRQ.class);
if(StringUtils.isEmpty(rq.getPayOrderId())){
throw new BizException("payOrderId不能为空");
}
PayOrder payOrder = payOrderService.queryMchOrder(rq.getMchNo(), rq.getPayOrderId(), rq.getMchOrderNo());
if(payOrder == null){
throw new BizException("订单不存在");
}
if (payOrder.getState() != PayOrder.STATE_INIT && payOrder.getState() != PayOrder.STATE_ING) {
throw new BizException("当前订单不可关闭");
}
ClosePayOrderRS bizRes = new ClosePayOrderRS();
try {
String payOrderId = payOrder.getPayOrderId();
//查询支付接口是否存在
IPayOrderCloseService closeService = SpringBeansUtil.getBean(payOrder.getIfCode() + "PayOrderCloseService", IPayOrderCloseService.class);
// 支付通道接口实现不存在
if(closeService == null){
log.error("{} interface not exists!", payOrder.getIfCode());
return null;
}
//查询出商户应用的配置信息
MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(payOrder.getMchNo(), payOrder.getAppId());
ChannelRetMsg channelRetMsg = closeService.close(payOrder, mchAppConfigContext);
if(channelRetMsg == null){
log.error("channelRetMsg is null");
return null;
}
log.info("关闭订单[{}]结果为:{}", payOrderId, channelRetMsg);
// 关闭订单 成功
if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
payOrderService.updateIng2Close(payOrderId);
}else {
return ApiRes.customFail(channelRetMsg.getChannelErrMsg());
}
bizRes.setChannelRetMsg(channelRetMsg);
} catch (Exception e) { // 关闭订单异常
log.error("error payOrderId = {}", payOrder.getPayOrderId(), e);
return null;
}
return ApiRes.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()).getAppSecret());
}
}
......@@ -261,7 +261,7 @@ public class RefundOrderController extends ApiController {
refundOrder.setErrMsg(channelRetMsg.getChannelErrMsg());
boolean isSuccess = refundOrderService.updateInit2Ing(refundOrder.getRefundOrderId());
boolean isSuccess = refundOrderService.updateInit2Ing(refundOrder.getRefundOrderId(), channelRetMsg.getChannelOrderId());
if(!isSuccess){
throw new BizException("更新退款单异常!");
}
......
/*
* 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.payorder;
import com.jeequan.jeepay.pay.rqrs.AbstractMchAppRQ;
import lombok.Data;
/*
* 关闭订单 请求参数对象
*
* @author xiaoyu
* @site https://www.jeequan.com
* @date 2022/1/25 9:16
*/
@Data
public class ClosePayOrderRQ extends AbstractMchAppRQ {
/** 商户订单号 **/
private String mchOrderNo;
/** 支付系统订单号 **/
private String payOrderId;
}
/*
* 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.payorder;
import com.alibaba.fastjson.annotation.JSONField;
import com.jeequan.jeepay.pay.rqrs.AbstractRS;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import lombok.Data;
/*
* 关闭订单 响应参数
*
* @author xiaoyu
* @site https://www.jeequan.com
* @date 2022/1/25 9:17
*/
@Data
public class ClosePayOrderRS extends AbstractRS {
/** 上游渠道返回数据包 (无需JSON序列化) **/
@JSONField(serialize = false)
private ChannelRetMsg channelRetMsg;
}
......@@ -66,6 +66,7 @@ public class PayOrderService extends ServiceImpl<PayOrderMapper, PayOrder> {
updateRecord.setMchFeeRate(payOrder.getMchFeeRate());
updateRecord.setMchFeeAmount(payOrder.getMchFeeAmount());
updateRecord.setChannelUser(payOrder.getChannelUser());
updateRecord.setChannelOrderNo(payOrder.getChannelOrderNo());
return update(updateRecord, new LambdaUpdateWrapper<PayOrder>()
.eq(PayOrder::getPayOrderId, payOrderId).eq(PayOrder::getState, PayOrder.STATE_INIT));
......@@ -84,6 +85,17 @@ public class PayOrderService extends ServiceImpl<PayOrderMapper, PayOrder> {
.eq(PayOrder::getPayOrderId, payOrderId).eq(PayOrder::getState, PayOrder.STATE_ING));
}
/** 更新订单状态 【支付中】 --》 【订单关闭】 **/
public boolean updateIng2Close(String payOrderId){
PayOrder updateRecord = new PayOrder();
updateRecord.setState(PayOrder.STATE_CLOSED);
updateRecord.setSuccessTime(new Date());
return update(updateRecord, new LambdaUpdateWrapper<PayOrder>()
.eq(PayOrder::getPayOrderId, payOrderId).eq(PayOrder::getState, PayOrder.STATE_ING));
}
/** 更新订单状态 【支付中】 --》 【支付失败】 **/
public boolean updateIng2Fail(String payOrderId, String channelOrderNo, String channelUserId, String channelErrCode, String channelErrMsg){
......
......@@ -59,10 +59,11 @@ public class RefundOrderService extends ServiceImpl<RefundOrderMapper, RefundOrd
/** 更新退款单状态 【退款单生成】 --》 【退款中】 **/
public boolean updateInit2Ing(String refundOrderId){
public boolean updateInit2Ing(String refundOrderId, String channelOrderNo){
RefundOrder updateRecord = new RefundOrder();
updateRecord.setState(RefundOrder.STATE_ING);
updateRecord.setChannelOrderNo(channelOrderNo);
return update(updateRecord, new LambdaUpdateWrapper<RefundOrder>()
.eq(RefundOrder::getRefundOrderId, refundOrderId).eq(RefundOrder::getState, RefundOrder.STATE_INIT));
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment