提交 072aa10c 编写于 作者: X xiaoyu

微信、云闪付退款

上级 c24d2561
/*
* 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.WxPayRefundQueryRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
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.entity.RefundOrder;
import com.jeequan.jeepay.core.model.params.wxpay.WxpayIsvParams;
import com.jeequan.jeepay.core.model.params.wxpay.WxpayIsvsubMchParams;
import com.jeequan.jeepay.core.model.params.wxpay.WxpayNormalMchParams;
import com.jeequan.jeepay.pay.channel.AbstractRefundService;
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.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.pay.rqrs.refund.RefundOrderRQ;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/*
* 退款接口: 微信官方
*
* @author xiaoyu
* @site https://www.jeepay.vip
* @date 2021/6/17 16:38
*/
@Slf4j
@Service
public class WxpayRefundService extends AbstractRefundService {
@Override
public String getIfCode() {
return CS.IF_CODE.WXPAY;
}
@Override
public String preCheck(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder) {
return null;
}
/** 微信退款接口 **/
@Override
public ChannelRetMsg refund(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
try {
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
if (CS.PAY_IF_VERSION.WX_V2.equals(mchAppConfigContext.getWxServiceWrapper().getApiVersion())) { //V2
WxPayRefundRequest req = new WxPayRefundRequest();
//放置isv信息
WxpayKit.putApiIsvInfo(mchAppConfigContext, req);
req.setOutTradeNo(payOrder.getPayOrderId()); // 商户订单号
req.setOutRefundNo(refundOrder.getRefundOrderId()); // 退款单号
req.setTotalFee(payOrder.getAmount().intValue()); // 订单总金额
req.setRefundFee(refundOrder.getRefundAmount().intValue()); // 退款金额
WxPayService wxPayService = mchAppConfigContext.getWxServiceWrapper().getWxPayService();
setCretPath(mchAppConfigContext, wxPayService); // 证书路径
WxPayRefundResult result = wxPayService.refundV2(req);
if("SUCCESS".equals(result.getResultCode())){ //支付成功
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
channelRetMsg.setChannelOrderId(result.getRefundId());
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrMsg(result.getReturnMsg());
}
}else if (CS.PAY_IF_VERSION.WX_V3.equals(mchAppConfigContext.getWxServiceWrapper().getApiVersion())) { //V3
// 微信统一下单请求对象
JSONObject reqJSON = new JSONObject();
reqJSON.put("out_trade_no", refundOrder.getPayOrderId()); // 订单号
reqJSON.put("out_refund_no", refundOrder.getRefundOrderId()); // 退款订单号
JSONObject amountJson = new JSONObject();
amountJson.put("refund", refundOrder.getRefundAmount());// 退款金额
amountJson.put("total", payOrder.getAmount());// 订单总金额
amountJson.put("currency", "CNY");// 币种
reqJSON.put("amount", amountJson.toJSONString());
WxPayService wxPayService = mchAppConfigContext.getWxServiceWrapper().getWxPayService();
setCretPath(mchAppConfigContext, wxPayService); // 证书路径
if(mchAppConfigContext.isIsvsubMch()){ // 特约商户
WxpayIsvsubMchParams isvsubMchParams = mchAppConfigContext.getIsvsubMchParamsByIfCode(getIfCode(), WxpayIsvsubMchParams.class);
reqJSON.put("sp_appid", wxPayService.getConfig().getAppId());
reqJSON.put("sp_mchid", wxPayService.getConfig().getMchId());
reqJSON.put("sub_mchid", isvsubMchParams.getSubMchId());
reqJSON.put("sub_appid", isvsubMchParams.getSubMchAppId());
}else { // 普通商户
reqJSON.put("appid", wxPayService.getConfig().getAppId());
reqJSON.put("mchid", wxPayService.getConfig().getMchId());
}
JSONObject resultJSON = WxpayV3Util.refundV3(reqJSON, mchAppConfigContext.getWxServiceWrapper().getWxPayService().getConfig());
String status = resultJSON.getString("status");
if("SUCCESS".equals(status)){ // 退款成功
String refundId = resultJSON.getString("refund_id");
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
channelRetMsg.setChannelOrderId(refundId);
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrMsg(status);
}
}
return channelRetMsg;
} catch (WxPayException e) {
return ChannelRetMsg.sysError(e.getReturnMsg());
} catch (Exception e) {
return ChannelRetMsg.sysError(e.getMessage());
}
}
/** 微信退款查单接口 **/
@Override
public ChannelRetMsg query(RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
try {
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
if (CS.PAY_IF_VERSION.WX_V2.equals(mchAppConfigContext.getWxServiceWrapper().getApiVersion())) { //V2
WxPayRefundQueryRequest req = new WxPayRefundQueryRequest();
//放置isv信息
WxpayKit.putApiIsvInfo(mchAppConfigContext, req);
req.setOutRefundNo(refundOrder.getRefundOrderId()); // 退款单号
WxPayService wxPayService = mchAppConfigContext.getWxServiceWrapper().getWxPayService();
setCretPath(mchAppConfigContext, wxPayService); // 证书路径
WxPayRefundQueryResult result = wxPayService.refundQueryV2(req);
if("SUCCESS".equals(result.getResultCode())){ // 退款成功
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
channelRetMsg.setChannelErrMsg(result.getReturnMsg());
}
}else if (CS.PAY_IF_VERSION.WX_V3.equals(mchAppConfigContext.getWxServiceWrapper().getApiVersion())) { //V3
WxPayService wxPayService = mchAppConfigContext.getWxServiceWrapper().getWxPayService();
setCretPath(mchAppConfigContext, wxPayService); // 证书路径
JSONObject resultJSON = WxpayV3Util.refundQueryV3(refundOrder.getRefundOrderId(), wxPayService.getConfig());
String status = resultJSON.getString("status");
if("SUCCESS".equals(status)){ // 退款成功
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
channelRetMsg.setChannelErrMsg(status);
}
}
return channelRetMsg;
} catch (WxPayException e) {
return ChannelRetMsg.sysError(e.getReturnMsg());
} catch (Exception e) {
return ChannelRetMsg.sysError(e.getMessage());
}
}
private void setCretPath(MchAppConfigContext mchAppConfigContext, WxPayService wxPayService) {
if(mchAppConfigContext.isIsvsubMch()){
// 获取服务商配置信息
WxpayIsvParams wxpayIsvParams = mchAppConfigContext.getIsvConfigContext().getIsvParamsByIfCode(CS.IF_CODE.WXPAY, WxpayIsvParams.class);
wxPayService.getConfig().setKeyPath(channelCertConfigKitBean.getCertFilePath(wxpayIsvParams.getCert()));
}else{
// 获取商户配置信息
WxpayNormalMchParams normalMchParams = mchAppConfigContext.getNormalMchParamsByIfCode(CS.IF_CODE.WXPAY, WxpayNormalMchParams.class);
wxPayService.getConfig().setKeyPath(channelCertConfigKitBean.getCertFilePath(normalMchParams.getCert()));
}
}
}
......@@ -82,6 +82,18 @@ public class WxpayV3Util {
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);
return JSON.parseObject(response);
}
public static JSONObject refundQueryV3(String refundOrderId, WxPayConfig wxPayConfig) throws WxPayException {
String url = String.format("%s/v3/refund/domestic/refunds/%s", PAY_BASE_URL, refundOrderId);
String response = getV3(url, wxPayConfig);
return JSON.parseObject(response);
}
public static String postV3(String url, String requestStr, WxPayConfig wxPayConfig) throws WxPayException {
CloseableHttpClient httpClient = createApiV3HttpClient(wxPayConfig);
HttpPost httpPost = createHttpPost(url, requestStr);
......
......@@ -27,7 +27,6 @@ import com.jeequan.jeepay.core.model.params.ysf.YsfpayIsvsubMchParams;
import com.jeequan.jeepay.pay.channel.AbstractPaymentService;
import com.jeequan.jeepay.pay.channel.ysfpay.utils.YsfHttpUtil;
import com.jeequan.jeepay.pay.channel.ysfpay.utils.YsfSignUtils;
import com.jeequan.jeepay.pay.config.SystemYmlConfig;
import com.jeequan.jeepay.pay.model.IsvConfigContext;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.AbstractRS;
......@@ -35,7 +34,6 @@ import com.jeequan.jeepay.pay.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.pay.util.PaywayUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
......@@ -51,8 +49,6 @@ import java.util.Date;
@Slf4j
public class YsfpayPaymentService extends AbstractPaymentService {
@Autowired private SystemYmlConfig mainConfig;
@Override
public String getIfCode() {
return CS.IF_CODE.YSFPAY;
......
/*
* 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.core.entity.RefundOrder;
import com.jeequan.jeepay.pay.channel.AbstractRefundService;
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 com.jeequan.jeepay.pay.rqrs.refund.RefundOrderRQ;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/*
* 退款接口: 云闪付官方
*
* @author xiaoyu
* @site https://www.jeepay.vip
* @date 2021/6/17 16:38
*/
@Slf4j
@Service
public class YsfpayRefundService extends AbstractRefundService {
@Override
public String getIfCode() {
return CS.IF_CODE.YSFPAY;
}
@Autowired
private YsfpayPaymentService ysfpayPaymentService;
@Override
public String preCheck(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder) {
return null;
}
@Override
public ChannelRetMsg refund(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
JSONObject reqParams = new JSONObject();
String orderType = YsfHttpUtil.getOrderTypeByBar(payOrder.getWayCode());
String logPrefix = "【云闪付("+orderType+")退款】";
try {
reqParams.put("origOrderNo", payOrder.getPayOrderId()); // 原交易订单号
reqParams.put("origTxnAmt", payOrder.getAmount()); // 原交易金额
reqParams.put("orderNo", refundOrder.getRefundOrderId()); // 退款订单号
reqParams.put("txnAmt ", refundOrder.getRefundAmount()); // 退款金额
reqParams.put("orderType ", orderType); // 订单类型
//封装公共参数 & 签名 & 调起http请求 & 返回响应数据并包装为json格式。
JSONObject resJSON = ysfpayPaymentService.packageParamAndReq("/gateway/api/pay/refund", reqParams, logPrefix, mchAppConfigContext.getIsvConfigContext(), mchAppConfigContext);
log.info("查询订单 payorderId:{}, 返回结果:{}", payOrder.getPayOrderId(), resJSON);
if(resJSON == null){
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.UNKNOWN); // 状态不明确
}
//请求 & 响应成功, 判断业务逻辑
String respCode = resJSON.getString("respCode"); //应答码
String respMsg = resJSON.getString("respMsg"); //应答信息
channelRetMsg.setChannelOrderId(refundOrder.getRefundOrderId());
if("00".equals(respCode)){ // 交易成功
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
log.info("{} >>> 退款成功", logPrefix);
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(respCode);
channelRetMsg.setChannelErrMsg(respMsg);
log.info("{} >>> 退款失败, {}", logPrefix, respMsg);
}
}catch (Exception e) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.SYS_ERROR); // 系统异常
}
return channelRetMsg;
}
@Override
public ChannelRetMsg query(RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
JSONObject reqParams = new JSONObject();
String orderType = YsfHttpUtil.getOrderTypeByBar(refundOrder.getWayCode());
String logPrefix = "【云闪付("+orderType+")退款查询】";
try {
reqParams.put("orderNo", refundOrder.getRefundOrderId()); // 退款订单号
reqParams.put("origOrderNo", refundOrder.getPayOrderId()); // 原交易订单号
//封装公共参数 & 签名 & 调起http请求 & 返回响应数据并包装为json格式。
JSONObject resJSON = ysfpayPaymentService.packageParamAndReq("/gateway/api/pay/refundQuery", reqParams, logPrefix, mchAppConfigContext.getIsvConfigContext(), mchAppConfigContext);
log.info("查询订单 refundOrderId:{}, 返回结果:{}", refundOrder.getRefundOrderId(), resJSON);
if(resJSON == null){
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.UNKNOWN); // 状态不明确
}
//请求 & 响应成功, 判断业务逻辑
String respCode = resJSON.getString("respCode"); //应答码
String respMsg = resJSON.getString("respMsg"); //应答信息
String origRespCode = resJSON.getString("origRespCode"); //原交易应答码
String origRespMsg = resJSON.getString("origRespMsg"); //原交易应答信息
channelRetMsg.setChannelOrderId(refundOrder.getRefundOrderId());
if("00".equals(respCode)){ // 请求成功
if("00".equals(origRespCode)){ //明确退款成功
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
log.info("{} >>> 退款成功", logPrefix);
} else if("01".equals(origRespCode)){ //明确退款失败
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode(respCode);
channelRetMsg.setChannelErrMsg(respMsg);
log.info("{} >>> 退款失败, {}", logPrefix, origRespMsg);
} else if("02".equals(origRespCode)){ //退款中
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
log.info("{} >>> 退款中", logPrefix);
}
}else{
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.UNKNOWN);
channelRetMsg.setChannelErrCode(respCode);
channelRetMsg.setChannelErrMsg(respMsg);
log.info("{} >>> 请求失败, {}", logPrefix, respMsg);
}
}catch (Exception e) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.SYS_ERROR); // 系统异常
}
return channelRetMsg;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册