美文网首页
微信退款

微信退款

作者: 小别墅是毛坯 | 来源:发表于2020-01-09 14:10 被阅读0次

    微信退款

     @ApiOperation(value = "确认并退款", notes = "确认并退款")
        @PutMapping(value = "/updateAfter/{refundId}")
        @ApiImplicitParams({@ApiImplicitParam(name = "ACCESS_TOKEN", value = "接口调用凭证", defaultValue = "06855244f2f221da4cd0a395c0d3c68c", dataType = "string", required = true, paramType = "query")})
        public void updateAfter(@ApiParam(value = "售后单Id", required = true) @PathVariable("refundId") Integer refundId,
                                @ApiParam(value = "订单类型(0 购物车付款 1 直接支付)")@RequestParam(value = "orderType")Integer orderType)  {
             AdminUser adminUser = SessionUtil.getUser(request, AdminUser.class);
            goodsOrderRefundService.updateAfter(refundId,adminUser.getId(), HttpUtil.getIpAddr(request),orderType);
        }
    
        @ApiOperation(value = "微信退款回调通知", notes = "订单退款回调通知", hidden = true)
        @PostMapping(value = "/notify")
        @ResponseAdvice(value = ResponseAdviceEnum.NOT_REQUIRED)
        public String payNotify() {
            boolean checkFlag = true;
            try {
                String notifyInfo = RequestUtil.parseWechatNotifyParams(request);
                Map<String, String> map = WechatPayUtil.checkCallBackWechatRefund(notifyInfo);
                checkFlag = goodsOrderRefundService.updateNotice(map);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return WechatPayUtil.generateWxCallBackResponse(checkFlag);
        }
    
     /**
         * 确认收货并退款
         *
         * @param refundId
         * @param orderType
         */
        @Transactional(rollbackFor = Exception.class)
        public void updateAfter(Integer refundId, Integer adminId, String ip, Integer orderType) {
            GoodsOrderRefund goodsOrderRefund = new GoodsOrderRefund();
            if (Objects.equals(orderType, shoppingPay.getCode())){
                 goodsOrderRefund = shoppingPay(refundId);
    
            }else {
    
                OrderRefundBo orderRefundBo = goodsOrderRefundMapper.getOrderRefundBo(refundId);
                AssertUtil.notNull(orderRefundBo.getPayType(), ExceptionEnum.OrderRefundInvalid);
    
                goodsOrderRefund.setId(refundId);
                goodsOrderRefund.setDeleteFlag(DeleteFlagEnum.NotDeleted.getCode());
                goodsOrderRefund = goodsOrderRefundMapper.selectOne(goodsOrderRefund);
                AssertUtil.notNull(goodsOrderRefund, ExceptionEnum.invalidOrderRefund);
                //生成退款单号
                String refundNo = "R" + DateUtils.getTimeString(DateUtils.DATE_YYYYMMDDHHMMSS, new Date()) + RandomStringUtils.randomNumeric(6);
    
                //更新退款单状态为-退款中
                GoodsOrderRefund temp = new GoodsOrderRefund();
                temp.setRefundStatus(OrderRefundStatusEnum.refunding.getCode());
                temp.setRefundNo(refundNo);
                Example examples = new Example(GoodsOrderRefund.class);
                examples.createCriteria()
                        .andEqualTo("id", refundId)
                        .andEqualTo("deleteFlag", DeleteFlagEnum.NotDeleted.getCode());
                goodsOrderRefundMapper.updateByExampleSelective(temp, examples);
                //更新订单详情为-退款中
                GoodsOrderDetail goodsOrderDetail = new GoodsOrderDetail();
                goodsOrderDetail.setRefundStatus(OrderRefundStatusEnum.refunding.getCode());
                Example goodsOrderDetailExample = new Example(GoodsOrderDetail.class);
                goodsOrderDetailExample.createCriteria().andEqualTo("orderId", goodsOrderRefund.getOrderId())
                        .andEqualTo("goodsProductId", goodsOrderRefund.getGoodsProductId())
                        .andEqualTo("refundStatus", OrderRefundStatusEnum.checking.getCode());
                goodsOrderDetailMapper.updateByExampleSelective(goodsOrderDetail, goodsOrderDetailExample);
    
                WechatPayDto dto = goodsOrderRefundMapper.selectRefund(refundId);
                //订单id
                String outTradeNo = dto.getOutTradeNo();
                //订单总金额
                String totalFee = dto.getTotalFee();
                //退款金额
                String refundFee = dto.getRefundFee();
                //退款单号
                String outRefundNo = dto.getOutRefundNo();
                //微信反的订单号
                String transactionId = dto.getTransactionId();
                /******************微信退款 ***************/
                if (Objects.equals(orderRefundBo.getPayType(), PayTypeEnum.waitDelivery.getCode())) {
                    Map<String, String> refund = WechatPayUtil.refund(transactionId, outTradeNo, outRefundNo,
                            new BigDecimal(totalFee), new BigDecimal(refundFee), WechatPayUtil.REFUND_NOTIFY_URL);
                    //用来判断数据是否为真
                    System.out.println(refund);
                    AssertUtil.isTrue(Objects.equals(refund.get("result_code"), "SUCCESS") &&
                            Objects.equals(refund.get("return_code"), "SUCCESS"), ExceptionEnum.wechatRefundError);
                }
    

    /************************wechat.properties***************/

    app.id=*****
    app.secret=*****
    mch.id=1488883392
    pay.secret=*********
    ## 自己电脑ip
    refund.notify.url=http://*****/refund/notify
    ##微信退款证书
    cert.url=C:/Users/Administrator/Desktop/apiclient_cert.p12
    
    package com.qcdl.web.wechat;
    
    import com.google.common.collect.Maps;
    import com.qcdl.utils.CodecUtil;
    import com.qcdl.utils.HttpUtil;
    import com.qcdl.utils.PropertiesUtil;
    import com.qcdl.web.config.advice.ExceptionEnum;
    import com.qcdl.web.config.advice.WebException;
    import lombok.NonNull;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.RandomStringUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.dom4j.DocumentException;
    
    import javax.annotation.Nonnull;
    import java.math.BigDecimal;
    import java.nio.charset.StandardCharsets;
    import java.util.Base64;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author qcdl
     * @date 2019/6/11
     */
    @Slf4j
    public class WechatPayUtil {
    
        private static final PropertiesUtil WECHAT_PRO = new PropertiesUtil("/configs/wechat.properties");
        public static final String REFUND_NOTIFY_URL;
        private static final String APP_ID;
        private static final String MCH_ID;
        private static final String PAY_SECRET;
        private static final String CERT_URL;
    
        static {
            APP_ID = WECHAT_PRO.getProperty("app.id");
            MCH_ID = WECHAT_PRO.getProperty("mch.id");
            REFUND_NOTIFY_URL = WECHAT_PRO.getProperty("refund.notify.url");
            PAY_SECRET = WECHAT_PRO.getProperty("pay.secret");
            CERT_URL = WECHAT_PRO.getProperty("cert.url");
        }
    
        private static final String TRADE_STATUS_SUCCESS = "SUCCESS";
    
        private static String generateWechatPaySign(Map<String, String> params) {
            String plainValue = MapUtils.mapParamOrderByAscJoinUrlDelimiter(params) + "&key=" + PAY_SECRET;
            return CodecUtil.getMD5Cryptography(plainValue).toUpperCase();
        }
    
        /**
         * 校验微信支付回调信息
         *
         * @param notifyInfo 微信支付回调信息
         * @return true/false
         */
        public static boolean checkCallBackWechatPaySign(String notifyInfo) throws DocumentException {
            Map<String, String> params = XmlUtils.xmlStr2Map(notifyInfo);
            String oldSignStr = params.get("sign");
            params.remove("sign");
    
            String newSignStr = generateWechatPaySign(params);
            if (!oldSignStr.equals(newSignStr)) {
                return false;
            }
    
            return StringUtils.equalsIgnoreCase(params.get("return_code"), TRADE_STATUS_SUCCESS)
                    && StringUtils.equalsIgnoreCase(params.get("result_code"), TRADE_STATUS_SUCCESS);
        }
    
        /**
         * 生成微信支付回调响应信息
         *
         * @param checkFlag 签名校验结果
         * @return 微信支付回调响应信息
         */
        public static String generateWxCallBackResponse(boolean checkFlag) {
            Map<String, String> params = Maps.newHashMap();
            String returnCode = "FAIL";
            String returnMsg = "校验签名失败";
            if (checkFlag) {
                returnCode = "SUCCESS";
                returnMsg = "OK";
            }
            params.put("return_code", returnCode);
            params.put("return_msg", returnMsg);
    
            return XmlUtils.map2XmlStr(params);
        }
    
        /**
         * 微信订单申请退款
         * 1、交易时间超过一年的订单无法提交退款
         * 2、微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。
         * 申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号
         * 3、每个支付订单的部分退款次数不能超过50次
         * <p>
         * 退款金额<=订单总金额
         *
         * @param transactionId 微信订单ID
         * @param orderNo       系统订单号
         * @param refundNo      系统退款单号
         * @param orderAmount   订单总金额
         * @param refundAmount  退款金额
         * @param notifyUrl     退款回调通知地址
         * @return 微信退款
         */
        public static Map<String, String> refund(String transactionId, String orderNo, String refundNo, BigDecimal orderAmount, BigDecimal refundAmount, String notifyUrl) {
            try {
                String refundOrder = generateRefund(transactionId, orderNo, refundNo, orderAmount, refundAmount, notifyUrl);
                String refundResult = RequestUtil.httpsRequestAndCert(WechatConstant.REFUND_ORDER, refundOrder, CERT_URL, MCH_ID);
                return XmlUtils.xmlStr2Map(refundResult);
            } catch (Exception e) {
                e.printStackTrace();
            }
            throw new WebException(ExceptionEnum.Exception);
        }
    
        private static String generateRefund(String transactionId, @Nonnull String orderNo, String refundNo,
                                             BigDecimal orderAmount, @Nonnull BigDecimal refundAmount, @Nonnull String notifyUrl) {
            Map<String, String> params = Maps.newHashMap();
            params.put("appid", APP_ID);
            params.put("mch_id", MCH_ID);
            params.put("nonce_str", RandomStringUtils.randomAlphabetic(6));
            params.put("transaction_id", transactionId);
            params.put("out_trade_no", orderNo);
            params.put("out_refund_no", refundNo);
            orderAmount = orderAmount.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_DOWN);
            refundAmount = refundAmount.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_DOWN);
            params.put("total_fee", orderAmount.toString());
            params.put("refund_fee", refundAmount.toString());
            params.put("notify_url", notifyUrl);
            params.put("refund_account", "REFUND_SOURCE_RECHARGE_FUNDS");
            params.put("sign", generateWechatPaySign(params));
    
            return XmlUtils.map2XmlStr(MapUtils.mapParamOrderByAsc(params));
        }
    
        public static Map<String, String> checkCallBackWechatRefund(String notifyInfo) throws Exception {
            Map<String, String> params = XmlUtils.xmlStr2Map(notifyInfo);
            String reqInfo = params.get("req_info");
    
            byte[] encryptMsg = Base64.getDecoder().decode(reqInfo.getBytes(StandardCharsets.UTF_8));
            String encryptKey = CodecUtil.getMD5Cryptography(PAY_SECRET).toLowerCase();
            String plainNotifyInfo = AESUtil.decryptData(encryptMsg, encryptKey);
    
            return XmlUtils.xmlStr2Map(plainNotifyInfo);
        }
    
        public static Map<String, String> queryOrderRefund(@NonNull String orderNo, @NonNull Integer page) throws DocumentException {
            String refundParam = generateOrderRefundParam(orderNo, page);
            String result = HttpUtil.getResult(WechatConstant.QUERY_ORDER_REFUND, refundParam);
    
            return XmlUtils.xmlStr2Map(result);
        }
    
        public static Map<String, String> queryOrderPayStart(@NonNull String orderNo) throws DocumentException {
            String queryOrderPayParam = generateQueryOrderParam(orderNo);
            String result = HttpUtil.getResult(WechatConstant.QUERY_ORDER, queryOrderPayParam);
    
            return XmlUtils.xmlStr2Map(result);
        }
    
        private static String generateQueryOrderParam(String orderNo) {
            HashMap<String, String> params = Maps.newHashMap();
            params.put("appid", APP_ID);
            params.put("mch_id", MCH_ID);
            params.put("out_trade_no", orderNo);
            params.put("nonce_str", RandomStringUtils.randomAlphabetic(6));
            params.put("sign", generateWechatPaySign(params));
    
            return XmlUtils.map2XmlStr(MapUtils.mapParamOrderByAsc(params));
        }
    
        private static String generateOrderRefundParam(String orderNo, Integer page) {
            Map<String, String> params = Maps.newHashMap();
            params.put("appid", APP_ID);
            params.put("mch_id", MCH_ID);
            params.put("nonce_str", RandomStringUtils.randomAlphabetic(6));
            params.put("out_trade_no", orderNo);
            params.put("offset", page * 10 + "");
            params.put("sign", generateWechatPaySign(params));
    
            return XmlUtils.map2XmlStr(MapUtils.mapParamOrderByAsc(params));
        }
    }
    

    /*****************RequestUtil************/

    package com.qcdl.web.wechat;
    
    import com.alipay.api.AlipayApiException;
    import com.alipay.api.AlipayClient;
    import com.alipay.api.domain.AlipayTradeQueryModel;
    import com.alipay.api.request.AlipayOfflineMaterialImageUploadRequest;
    import com.alipay.api.request.AlipayTradeQueryRequest;
    import com.alipay.api.response.AlipayTradeQueryResponse;
    import com.google.common.collect.Maps;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.http.HttpEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.entity.InputStreamEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.ssl.SSLContexts;
    
    import javax.net.ssl.SSLContext;
    import javax.servlet.http.HttpServletRequest;
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.nio.charset.StandardCharsets;
    import java.security.KeyStore;
    import java.util.*;
    
    /**
     * @author qcdl
     * @date 2019/6/10
     */
    public final class RequestUtil {
        /**
         * 封装前台请求参数并转换为map保存
         *
         * @param request request
         * @return map
         */
        public static Map<String, String> parseRequestParam(HttpServletRequest request) {
            Map<String, String> result = new HashMap<>(16);
            Enumeration<String> enumerations = request.getParameterNames();
            while (enumerations.hasMoreElements()) {
                String key = enumerations.nextElement();
                result.put(key, request.getParameter(key));
            }
            return result;
        }
    
        public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
            try {
                URL url = new URL(requestUrl);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setDoOutput(true);
                conn.setDoInput(true);
                conn.setUseCaches(false);
                // 设置请求方式(GET/POST)
                conn.setRequestMethod(requestMethod);
                conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
                // 当outputStr不为null时向输出流写数据
                if (null != outputStr) {
                    OutputStream outputStream = conn.getOutputStream();
                    // 注意编码格式
                    outputStream.write(outputStr.getBytes(StandardCharsets.UTF_8));
                    outputStream.close();
                }
                // 从输入流读取返回内容
                InputStream inputStream = conn.getInputStream();
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                String str;
                StringBuilder buffer = new StringBuilder();
                while ((str = bufferedReader.readLine()) != null) {
                    buffer.append(str);
                }
                // 释放资源
                bufferedReader.close();
                inputStreamReader.close();
                inputStream.close();
                conn.disconnect();
                return buffer.toString();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 解析微信回调通知参数(简单封装,仅限微信回调用)
         *
         * @param request request
         * @return 微信回调通知参数
         * @throws IOException e
         */
        public static String parseWechatNotifyParams(HttpServletRequest request) throws IOException {
            BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = in.readLine()) != null) {
                sb.append(line);
            }
            in.close();
            return sb.toString();
        }
    
        public static String getFullRequestUrl(HttpServletRequest request) {
            String url = request.getRequestURL().toString();
            if (StringUtils.isNotBlank(request.getQueryString())) {
                url += "?" + request.getQueryString();
            }
            return url;
        }
    
        public static String httpsRequestAndCert(String requestUrl, String param, String certUrl, String certPass) throws Exception {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            try (FileInputStream inputStream = new FileInputStream(new File(certUrl))) {
                keyStore.load(inputStream, certPass.toCharArray());
            }
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, certPass.toCharArray())
                    .build();
            SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(
                    sslcontext,
                    new String[]{"TLSv1"},
                    null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpClient = HttpClients.custom()
                    .setSSLSocketFactory(sslFactory)
                    .build();
            StringBuilder stringBuffer = new StringBuilder();
            try {
                HttpPost httpPost = new HttpPost(requestUrl);
                InputStream is = new ByteArrayInputStream(param.getBytes(StandardCharsets.UTF_8));
                InputStreamEntity inputStreamEntity = new InputStreamEntity(is, is.available());
                httpPost.setEntity(inputStreamEntity);
                try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                    HttpEntity entity = response.getEntity();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(
                            entity.getContent(), StandardCharsets.UTF_8));
                    String inputLine;
                    while ((inputLine = reader.readLine()) != null) {
                        stringBuffer.append(inputLine);
                    }
                }
            } finally {
                httpClient.close();
            }
            return stringBuffer.toString();
        }
    }
    
    @Data
    @ApiModel
    public class WechatPayDto {
    
        @ApiModelProperty("微信订单号")
        private String transactionId;
    
        @ApiModelProperty("订单总金额")
        private String totalFee;
    
        @ApiModelProperty("系统支付单号")
        private String outTradeNo;
    
        @ApiModelProperty("系统退款单号")
        private String outRefundNo;
    
        @ApiModelProperty("微信退款单号")
        private String refundId;
    
        //退款资金来源 (REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款/基本账户、REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款)
        @ApiModelProperty("退款资金来源")
        private String refundAccount;
    
        //退款总金额,单位为分,可以做部分退款
        @ApiModelProperty("退款总金额")
        private String refundFee;
    
        @ApiModelProperty("退款入账账户")
        private String refundRecvAccount;
    
        //退款状态(SUCCESS—退款成功、REFUNDCLOSE—退款关闭、PROCESSING—退款处理中、CHANGE—退款异常)
        @ApiModelProperty("退款状态")
        private String refundStatus;
    
        @ApiModelProperty("退款成功时间")
        private String successTime;
    
    }
    
    

    相关文章

      网友评论

          本文标题:微信退款

          本文链接:https://www.haomeiwen.com/subject/vduyactx.html